webpack4專案中的實踐

學習webpack4的配置更改
webpack作為一個模組打包器,主要用於前端工程中的依賴梳理和模組打包,將我們開發的具有高可讀性和可維護性的程式碼檔案打包成瀏覽器可以識別並正常執行的壓縮程式碼,主要包括樣式檔案處理成css
,各種新式的JavaScript
轉換成瀏覽器認識的寫法等,也是前端工程師進階的不二法門。
webpack.config.js配置項簡介
- Entry:入口檔案配置,Webpack 執行構建的第一步將從 Entry 開始,完成整個工程的打包。
-
Module:模組,在
Webpack
裡一切皆模組,Webpack
會從配置的Entry
開始遞迴找出所有依賴的模組,最常用的是rules
配置項,功能是匹配對應的字尾,從而針對程式碼檔案完成格式轉換和壓縮合並等指定的操作。 -
Loader:模組轉換器,用於把模組原內容按照需求轉換成新內容,這個是配合
Module
模組中的rules
中的配置項來使用。 -
Plugins:擴充套件外掛,在
Webpack
構建流程中的特定時機注入擴充套件邏輯來改變構建結果或做你想要的事情。(外掛API
) -
Output:輸出結果,在
Webpack
經過一系列處理並得出最終想要的程式碼後輸出結果,配置項用於指定輸出資料夾,預設是./dist
。 -
DevServer:用於配置開發過程中使用的本機伺服器配置,屬於
webpack-dev-server
這個外掛的配置項。
webpack打包流程簡介
-
根據傳入的引數模式(
development
|production
)來載入對應的預設配置 -
在
entry
裡配置的module
開始遞迴解析entry
所依賴的所有module
-
每一個
module
都會根據rules
的配置項去尋找用到的loader
,接受所配置的loader
的處理 -
以
entry
中的配置物件為分組,每一個配置入口和其對應的依賴檔案最後組成一個程式碼塊檔案(chunk)並輸出 -
整個流程中
webpack
會在恰當的時機執行plugin
的邏輯,來完成自定義的外掛邏輯
基本的webpack配置搭建
首先通過以下的指令碼命令來建立初始化檔案:
npm init -y npm i webpack webpack-cli -D // 針對webpack4的安裝 mkdir src && cd src && touch index.html index.js cd ../ && mkdir dist && mkdir static touch webpack.config.js npm i webpack-dev-server --save-dev 複製程式碼
修改生成的package.json
檔案,來引入webpack
打包命令:
"scripts": { "build": "webpack --mode production", "dev": "webpack-dev-server --open --mode development" } 複製程式碼
對webpack.config.js
檔案加入一些基本配置loader
,從而基本的webpack4.x
的配置成型(以兩個頁面入口為例):
const path = require('path'); const CopyWebpackPlugin = require('copy-webpack-plugin') // 複製靜態資源的外掛 const CleanWebpackPlugin = require('clean-webpack-plugin') // 清空打包目錄的外掛 const HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html的外掛 const ExtractTextWebapckPlugin = require('extract-text-webpack-plugin') //CSS檔案單獨提取出來 const webpack = require('webpack') module.exports = { entry: { index: path.resolve(__dirname, 'src', 'index.js'), page: path.resolve(__dirname, 'src', 'page.js'), vendor:'lodash' // 多個頁面所需的公共庫檔案,防止重複打包帶入 }, output:{ publicPath: '/',//這裡要放的是靜態資源CDN的地址 path: path.resolve(__dirname,'dist'), filename:'[name].[hash].js' }, resolve:{ extensions: [".js",".css",".json"], alias: {} //配置別名可以加快webpack查詢模組的速度 }, module: { // 多個loader是有順序要求的,從右往左寫,因為轉換的時候是從右往左轉換的 rules:[ { test: /\.css$/, use: ExtractTextWebapckPlugin.extract({ fallback: 'style-loader', use: ['css-loader', 'postcss-loader'] // 不再需要style-loader放到html檔案內 }), include: path.join(__dirname, 'src'), //限制範圍,提高打包速度 exclude: /node_modules/ }, { test:/\.less$/, use: ExtractTextWebapckPlugin.extract({ fallback: 'style-loader', use: ['css-loader', 'postcss-loader', 'less-loader'] }), include: path.join(__dirname, 'src'), exclude: /node_modules/ }, { test:/\.scss$/, use: ExtractTextWebapckPlugin.extract({ fallback: 'style-loader', use:['css-loader', 'postcss-loader', 'sass-loader'] }), include: path.join(__dirname, 'src'), exclude: /node_modules/ }, { test: /\.jsx?$/, use: { loader: 'babel-loader', query: { //同時可以把babel配置寫到根目錄下的.babelrc中 presets: ['env', 'stage-0'] // env轉換es6 stage-0轉es7 } } }, { //file-loader 解決css等檔案中引入圖片路徑的問題 // url-loader 當圖片較小的時候會把圖片BASE64編碼,大於limit引數的時候還是使用file-loader 進行拷貝 test: /\.(png|jpg|jpeg|gif|svg)/, use: { loader: 'url-loader', options: { outputPath: 'images/', // 圖片輸出的路徑 limit: 1 * 1024 } } } ] }, plugins: [ // 多入口的html檔案用chunks這個引數來區分 new HtmlWebpackPlugin({ template: path.resolve(__dirname,'src','index.html'), filename:'index.html', chunks:['index', 'vendor'], hash:true,//防止快取 minify:{ removeAttributeQuotes:true//壓縮 去掉引號 } }), new HtmlWebpackPlugin({ template: path.resolve(__dirname,'src','page.html'), filename:'page.html', chunks:['page', 'vendor'], hash:true,//防止快取 minify:{ removeAttributeQuotes:true//壓縮 去掉引號 } }), new webpack.ProvidePlugin({ _:'lodash' //所有頁面都會引入 _ 這個變數,不用再import引入 }), new ExtractTextWebapckPlugin('css/[name].[hash].css'), // 其實這個特性只用於打包生產環境,測試環境這樣設定會影響HMR new CopyWebpackPlugin([ { from: path.resolve(__dirname, 'static'), to: path.resolve(__dirname, 'dist/static'), ignore: ['.*'] } ]), new CleanWebpackPlugin([path.join(__dirname, 'dist')]), ], devtool: 'eval-source-map', // 指定加source-map的方式 devServer: { contentBase: path.join(__dirname, "dist"), //靜態檔案根目錄 port: 3824, // 埠 host: 'localhost', overlay: true, compress: false // 伺服器返回瀏覽器的時候是否啟動gzip壓縮 }, watch: true, // 開啟監聽檔案更改,自動重新整理 watchOptions: { ignored: /node_modules/, //忽略不用監聽變更的目錄 aggregateTimeout: 500, //防止重複儲存頻繁重新編譯,500毫米內重複儲存不打包 poll:1000 //每秒詢問的檔案變更的次數 }, } 複製程式碼
在命令列下用以下命令安裝loader
和依賴的外掛,生成完全的package.json
專案依賴樹。
npm install extract-text-webpack-plugin@next --save-dev npm i style-loader css-loader postcss-loader --save-dev npm i less less-loader --save-dev npm i node-sass sass-loader --save-dev npm i babel-core babel-loader babel-preset-env babel-preset-stage-0 --save-dev npm i file-loader url-loader --save-dev npm i html-webpack-plugin ---save-dev npm i clean-webpack-plugin --save-dev npm i copy-webpack-plugin --save-dev npm run dev 複製程式碼
預設開啟的頁面是index.html
頁面,可以加上/page.html來開啟page頁面看效果。
PS: 關於loader
的詳細說明可以參考webpack3.x
的學習介紹,上面配置中需要注意的是多頁面的公共庫的引入採用的是vendor
+暴露全域性變數的方式,其實這種方式有諸多弊端,而webpack4
針對這種情況設定了新的API,有興趣的話,就繼續看下面的高階配置吧。
進階的webpack4配置搭建
包含以下幾個方面:
-
針對
CSS
和JS
的TreeShaking
來減少無用程式碼,針對JS
需要對已有的uglifyjs
進行一些自定義的配置(生產環境配置) -
新的公共程式碼抽取工具(
optimization.SplitChunksPlugin
)提取重用程式碼,減小打包檔案。(代替commonchunkplugin
,生產和開發環境都需要) -
使用
HappyPack
進行javascript
的多程序打包操作,提升打包速度,並增加打包時間顯示。(生產和開發環境都需要) -
建立一個
webpack.dll.config.js
檔案打包常用類庫到dll中,使得開發過程中基礎模組不會重複打包,而是去動態連線庫裡獲取,代替上一節使用的vendor
。(注意這個是在開發環境使用,生產環境打包對時間要求並不高,後者往往是專案持續整合的一部分) - 模組熱替換,還需要在專案中增加一些配置,不過大型框架把這塊都封裝好了。(開發環境配置)
-
webpack3
新增的作用域提升會預設在production
模式下啟用,不用特別配置,但只有在使用ES6模組才能生效。
關於第四點,需要在package.json中的script中增加指令碼:"build:dll": "webpack --config webpack.dll.config.js --mode development",
補充安裝外掛的命令列:
npm i purify-css purifycss-webpack -D // 用於css的tree-shaking npm i webpack-parallel-uglify-plugin -D // 用於js的tree-shaking npm i happypack@next -D //用於多程序打包js npm i progress-bar-webpack-plugin -D //用於顯示打包時間和程序 npm i webpack-merge -D //優化配置程式碼的工具 npm i optimize-css-assets-webpack-plugin -D //壓縮CSS npm i chalk -D npm install css-hot-loader -D // css熱更新 npm i mini-css-extract-plugin -D npm i cross-env -D 複製程式碼
TreeShaking
需要增加的配置程式碼,這一塊參考ofollow,noindex">
webpack
文件
,需要三方面因素,分別是:
-
使用
ES6
模組(import/export
) -
在
package.json
檔案中宣告sideEffects
指定可以treeShaking
的模組 -
啟用
UglifyJSPlugin
,多入口下用WebpackParallelUglifyPlugin
(這是下面的配置程式碼做的事情)
/*最上面要增加的宣告變數*/ const glob = require('glob') const PurifyCSSPlugin = require('purifycss-webpack') const WebpackParallelUglifyPlugin = require('webpack-parallel-uglify-plugin') /*在`plugins`配置項中需要增加的兩個外掛設定*/ new PurifyCSSPlugin({ paths: glob.sync(path.join(__dirname, 'src/*.html')) }), new WebpackParallelUglifyPlugin({ uglifyJS: { output: { beautify: false, //不需要格式化 comments: false //不保留註釋 }, compress: { warnings: false, // 在UglifyJs刪除沒有用到的程式碼時不輸出警告 drop_console: true, // 刪除所有的 `console` 語句,可以相容ie瀏覽器 collapse_vars: true, // 內嵌定義了但是隻用到一次的變數 reduce_vars: true // 提取出出現多次但是沒有定義成變數去引用的靜態值 } } // 有興趣可以探究一下使用uglifyES }), 複製程式碼
關於ES6
模組這個事情,上文的第六點也提到了只有ES6
模組寫法才能用上最新的作用域提升的特性,首先webpack4.x
並不需要額外修改babelrc
的配置來實現去除無用程式碼,這是從webpack2.x
升級後支援的,改用sideEffect
宣告來實現。但作用域提升仍然需要把babel
配置中的module
轉換去掉,修改後的.babelrc
程式碼如下:
{ "presets": [["env", {"loose": true, "modules": false}], "stage-0"] } 複製程式碼
但這個時候會發現import
引入樣式檔案就被去掉了……只能使用require
來改寫了。
打包DLL
第三方類庫的配置項,用於開發環境:
-
webpack.dll.config.js
配置檔案具體內容:
const path = require('path') const webpack = require('webpack') const pkg = require('../package.json') /** * 儘量減小搜尋範圍 * target: '_dll_[name]' 指定匯出變數名字 */ module.exports = { context: path.resolve(__dirname, '../'), entry: { vendor: Object.keys(pkg.dependencies) }, output: { path: path.join(__dirname, 'dist'), filename: '[name].dll.js', library: '_dll_[name]' // 全域性變數名,其他模組會從此變數上獲取裡面模組 }, // manifest是描述檔案 plugins: [ new webpack.DllPlugin({ name: '_dll_[name]', path: path.join(__dirname, 'dist', 'manifest.json'), context: path.resolve(__dirname, '../') }) ] } 複製程式碼
-
在
webpack.config.js
中增加的配置項:
/*找到上一步生成的`manifest.json`檔案配置到`plugins`裡面*/ new webpack.DllReferencePlugin({ manifest: require(path.join(__dirname, '..', 'dist', 'manifest.json')), }), 複製程式碼
多檔案入口的公用程式碼提取外掛配置:
/*webpack4.x的最新優化配置項,用於提取公共程式碼,跟`entry`是同一層級*/ optimization: { splitChunks: { cacheGroups: { commons: { chunks: "initial", name: "common", minChunks: 2, maxInitialRequests: 5, minSize: 0 } } } } /*針對生成HTML的外掛,需增加common,也去掉上一節加的vendor*/ new HtmlWebpackPlugin({ template: path.resolve(__dirname,'src','index.html'), filename:'index.html', chunks:['index', 'common'], vendor: './vendor.dll.js', //與dll配置檔案中output.fileName對齊 hash:true,//防止快取 minify:{ removeAttributeQuotes:true//壓縮 去掉引號 } }), new HtmlWebpackPlugin({ template: path.resolve(__dirname,'src','page.html'), filename:'page.html', chunks:['page', 'common'], vendor: './vendor.dll.js', //與dll配置檔案中output.fileName對齊 hash:true,//防止快取 minify:{ removeAttributeQuotes:true//壓縮 去掉引號 } }), 複製程式碼
PS: 這一塊要多注意,對應入口的HTML
檔案也要處理,關鍵是自定義的vendor
項,在開發環境中引入到html
中
HappyPack
的多程序打包處理:
/*最上面要增加的宣告變數*/ const HappyPack = require('happypack') const os = require('os') //獲取電腦的處理器有幾個核心,作為配置傳入 const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }) const ProgressBarPlugin = require('progress-bar-webpack-plugin') /*在`module.rules`配置項中需要更改的`loader`設定*/ { test: /\.jsx?$/, loader: 'happypack/loader?id=happy-babel-js', include: [path.resolve('src')], exclude: /node_modules/, }, /*在`plugins`配置項中需要增加的外掛設定*/ new HappyPack({ //開啟多執行緒打包 id: 'happy-babel-js', loaders: ['babel-loader?cacheDirectory=true'], threadPool: happyThreadPool }), new ProgressBarPlugin({ format: 'build [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)' }) 複製程式碼
PS:要記住這種使用方法下一定要在根目錄下加.babelrc
檔案來設定babel
的打包配置。
開發環境的程式碼熱更新:
其實針對熱重新整理,還有兩個方面要提及,一個是html檔案裡面寫程式碼的熱跟新(這個對於框架不需要,如果要實現,建議使用glup
,後面有程式碼),一個是寫的樣式程式碼的熱更新,這兩部分也要加進去。讓我們一起看看熱更新需要增加的配置程式碼:
/*在`devServer`配置項中需增加的設定*/ hot:true /*在`plugins`配置項中需要增加的外掛設定*/ new webpack.HotModuleReplacementPlugin(), //模組熱更新 new webpack.NamedModulesPlugin(), //模組熱更新 複製程式碼
在業務程式碼中要做一些改動,一個比較low
的例子為:
if(module.hot) { //設定訊息監聽,重新執行函式 module.hot.accept('./hello.js', function() { div.innerHTML = hello() }) } 複製程式碼
但還是不能實現在html
修改後自動重新整理頁面,這裡有個概念是熱更新不是針對頁面級別的修改,這個問題有一些解決方法,但目前都不是很完美,可以參考這裡,現在針對CSS的熱過載有一套解決方案如下,需要放棄使用上文提到的ExtractTextWebapckPlugin
,引入mini-css-extract-plugin
和hot-css-loader
來實現,前者在webpack4.x上與hot-css-loader
有報錯,讓我們改造一番:
/*最上面要增加的宣告變數*/ const MiniCssExtractPlugin = require('mini-css-extract-plugin') /*在樣式的`loader`配置項中需增加的設定,實現css熱更新,以css為例,其他可以參照我的倉庫來寫*/ { test: /\.css$/, use: ['css-hot-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'], include: [resolve('src')], //限制範圍,提高打包速度 exclude: /node_modules/ } /*在`plugins`配置項中需要增加的外掛設定,注意這裡不能寫[hash],否則無法實現熱跟新,如果有hash需要,可以開發環境和生產環境分開配置*/ new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" }) 複製程式碼
用於生產環境壓縮css
的外掛,看官方文件說明,樣式檔案壓縮沒有內建的,所以暫時引用第三方外掛來做,以下是配置示例。
/*要增加的宣告變數*/ const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') /*在`plugins`配置項中需要增加的外掛設定*/ new OptimizeCSSPlugin({ cssProcessorOptions: {safe: true} }) 複製程式碼
最終成果
在進階部分我們對webpack
配置檔案根據開發環境和生產環境的不同做了分別的配置,因此有必要分成兩個檔案,然後發現重複的配置程式碼很多,作為有程式碼潔癖的人不能忍,果斷引入webpack-merge
,來把相同的配置抽出來,放到build/webpack.base.js
中,而後在build/webpack.dev.config.js
(開發環境)和build/webpack.prod.config.js
(生產環境)中分別引用,在這個過程中也要更改之前檔案的路徑設定,以免打包或者找檔案的路徑出錯,同時將package.json
中的指令碼命令修改為:
"scripts": { "build": "cross-env NODE_ENV='production' webpack --config build/webpack.prod.config.js --mode production", "dev": "cross-env NODE_ENV='development' webpack-dev-server --open --config build/webpack.dev.config.js --mode development", "dll": "webpack --config build/webpack.dll.config.js --mode production", "start": "npm run dll && npm run dev", "prod": "npm run dll && npm run build" } 複製程式碼
接下來就是程式碼的重構過程,這個過程其實我建議大家自己動手做一做,就能對webpack
配置檔案結構更加清晰。
build
資料夾下的webpack.base.js
檔案:
'use strict' const path = require('path'); const chalk = require('chalk'); const ProgressBarPlugin = require('progress-bar-webpack-plugin') const HappyPack = require('happypack') const os = require('os') const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }) const MiniCssExtractPlugin = require('mini-css-extract-plugin') function resolve (dir) { return path.join(__dirname, '..', dir) } function assetsPath(_path_) { let assetsSubDirectory; if (process.env.NODE_ENV === 'production') { // 這裡需要用cross-env來注入Node變數 assetsSubDirectory = 'static' //可根據實際情況修改 } else { assetsSubDirectory = 'static' } return path.posix.join(assetsSubDirectory, _path_) } module.exports = { context: path.resolve(__dirname, '../'), entry: { index: './src/index.js', page: './src/page.js' }, output:{ path: resolve('dist'), filename:'[name].[hash].js' }, resolve: { extensions: [".js",".css",".json"], alias: {} //配置別名可以加快webpack查詢模組的速度 }, module: { // 多個loader是有順序要求的,從右往左寫,因為轉換的時候是從右往左轉換的 rules:[ { test: /\.css$/, use: ['css-hot-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'], include: [resolve('src')], //限制範圍,提高打包速度 exclude: /node_modules/ }, { test:/\.less$/, use: ['css-hot-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'], include: [resolve('src')], exclude: /node_modules/ }, { test:/\.scss$/, use: ['css-hot-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'], include: [resolve('src')], exclude: /node_modules/ }, { test: /\.jsx?$/, loader: 'happypack/loader?id=happy-babel-js', include: [resolve('src')], exclude: /node_modules/, }, { //file-loader 解決css等檔案中引入圖片路徑的問題 // url-loader 當圖片較小的時候會把圖片BASE64編碼,大於limit引數的時候還是使用file-loader 進行拷貝 test: /\.(png|jpg|jpeg|gif|svg)/, use: { loader: 'url-loader', options: { name: assetsPath('images/[name].[hash:7].[ext]'), // 圖片輸出的路徑 limit: 1 * 1024 } } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: assetsPath('media/[name].[hash:7].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: assetsPath('fonts/[name].[hash:7].[ext]') } } ] }, optimization: { //webpack4.x的最新優化配置項,用於提取公共程式碼 splitChunks: { cacheGroups: { commons: { chunks: "initial", name: "common", minChunks: 2, maxInitialRequests: 5, // The default limit is too small to showcase the effect minSize: 0 // This is example is too small to create commons chunks } } } }, plugins: [ new HappyPack({ id: 'happy-babel-js', loaders: ['babel-loader?cacheDirectory=true'], threadPool: happyThreadPool }), new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" }), new ProgressBarPlugin({ format: 'build [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)' }), ] } 複製程式碼
webpack.dev.config.js
檔案內容:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html的外掛 const webpack = require('webpack') const baseConfig = require('./webpack.base') const merge = require('webpack-merge') const devWebpackConfig = merge(baseConfig, { output:{ publicPath: '/' }, devtool: 'eval-source-map', // 指定加source-map的方式 devServer: { inline:true,//打包後加入一個websocket客戶端 hot:true,//熱載入 contentBase: path.join(__dirname, "..", "dist"), //靜態檔案根目錄 port: 3824, // 埠 host: 'localhost', overlay: true, compress: false // 伺服器返回瀏覽器的時候是否啟動gzip壓縮 }, watchOptions: { ignored: /node_modules/, //忽略不用監聽變更的目錄 aggregateTimeout: 500, //防止重複儲存頻繁重新編譯,500毫米內重複儲存不打包 poll:1000 //每秒詢問的檔案變更的次數 }, plugins: [ // 多入口的html檔案用chunks這個引數來區分 new HtmlWebpackPlugin({ template: path.resolve(__dirname, '..', 'src','index.html'), filename:'index.html', chunks:['index', 'common'], vendor: './vendor.dll.js', //與dll配置檔案中output.fileName對齊 hash:true,//防止快取 minify:{ removeAttributeQuotes:true//壓縮 去掉引號 } }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '..', 'src','page.html'), filename:'page.html', chunks:['page', 'common'], vendor: './vendor.dll.js', //與dll配置檔案中output.fileName對齊 hash:true,//防止快取 minify:{ removeAttributeQuotes:true//壓縮 去掉引號 } }), new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, '..', 'dist', 'manifest.json') }), new webpack.HotModuleReplacementPlugin(), //HMR new webpack.NamedModulesPlugin() // HMR ] }) module.exports = devWebpackConfig 複製程式碼
webpack.dev.config.js
檔案內容:
'use strict' const path = require('path'); const CopyWebpackPlugin = require('copy-webpack-plugin') // 複製靜態資源的外掛 const CleanWebpackPlugin = require('clean-webpack-plugin') // 清空打包目錄的外掛 const HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html的外掛 const webpack = require('webpack') const baseConfig = require('./webpack.base') const merge = require('webpack-merge') const glob = require('glob') const PurifyCSSPlugin = require('purifycss-webpack') const WebpackParallelUglifyPlugin = require('webpack-parallel-uglify-plugin') const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') module.exports = merge(baseConfig, { output:{ publicPath: './' //這裡要放的是靜態資源CDN的地址(只在生產環境下配置) }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, '..', 'src', 'index.html'), filename:'index.html', chunks:['index', 'common'], hash:true,//防止快取 minify:{ removeAttributeQuotes:true//壓縮 去掉引號 } }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '..', 'src', 'page.html'), filename:'page.html', chunks:['page', 'common'], hash:true,//防止快取 minify:{ removeAttributeQuotes:true//壓縮 去掉引號 } }), new CopyWebpackPlugin([ { from: path.join(__dirname, '..', 'static'), to: path.join(__dirname,'..', 'dist', 'static'), ignore: ['.*'] } ]), new CleanWebpackPlugin(['dist'], { root: path.join(__dirname, '..'), verbose: true, dry:false }), new OptimizeCSSPlugin({ cssProcessorOptions: {safe: true} }), new PurifyCSSPlugin({ paths: glob.sync(path.join(__dirname, '../src/*.html')) }), new WebpackParallelUglifyPlugin({ uglifyJS: { output: { beautify: false, //不需要格式化 comments: false //不保留註釋 }, compress: { warnings: false, // 在UglifyJs刪除沒有用到的程式碼時不輸出警告 drop_console: true, // 刪除所有的 `console` 語句,可以相容ie瀏覽器 collapse_vars: true, // 內嵌定義了但是隻用到一次的變數 reduce_vars: true // 提取出出現多次但是沒有定義成變數去引用的靜態值 } } }), ] }) 複製程式碼
多說一句,就是實現JS打包的treeShaking
還有一種方法是編譯期分析依賴,利用uglifyjs來完成,這種情況需要保留ES6模組才能實現,因此在使用這一特性的倉庫中,.babelrc
檔案的配置為:"presets": [["env", { "modules": false }], "stage-0"]
,就是打包的時候不要轉換模組引入方式的含義。
接下來就可以執行npm start
,看一下進階配置後的成果啦,吼吼,之後只要不進行build
打包操作,通過npm run dev
啟動,不用重複打包vendor
啦。生產環境打包使用的是npm run build
。
以上就是對webpack4.x
配置的踩坑過程,期間參考了大量谷歌英文資料,希望能幫助大家更好地掌握wepback
最新版本的配置,以上內容親測跑通,有問題的話,歡迎加我微信(kashao3824)討論,到
github
地址
提issue
也可,歡迎fork/star
。
最新更改:
-
修復了
common
會重複打包已有dll
庫的問題 -
現在的
dll
庫會自動根據package.json
中的配置項生成 -
dll
現在是生產環境打包模式,並且vendor.dll.js
現在在生產環境下也會注入HTML
模板中 -
生產環境打包使用命令
npm run prod
-
修復了
process.env.NODE_ENV
在打包過程中取不到的問題issue2