webpack.config.js 文件解析
const path = require('path') const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const ClearWebpackPlugin = require('clear-webpack-plugin') module.exports = { mode: 'development', // 打包模式 測試環境/生產環境 devtool: 'cheap-module-eval-source-map', // 是否打包SourceMap文件, 映射代碼錯誤位置 // 打包入口配置 entry: { main: './src/index.js', }, // 使用webpack-dev-server 插件,實現監聽代碼改動自動打包 devServer: { contentBase: './dist', // 監聽變化的文件 open: true, // 是否自動打開瀏覽器 port: 8080, // 監聽的端口 hot: true, // 是否啟動模塊代碼熱更新 hotOnly: true, // 是否只刷新一次瀏覽器 }, // 配置編譯的各模塊 loader module: { noParse: /es6-promise\.js$/, // avoid webpack shimming process rules: [ { test: /\.(js|vue)$/, loader: 'eslint-loader', enforce: 'pre', include: [path.resolve(__dirname, '../src/')], options: { formatter: require('eslint-friendly-formatter') } }, // 使用 babel 把ES6轉換成ES5 ,需要配合 @babel/preset-env // 配置ES6轉換成ES5過程中,按需補充必要代碼,在js文件中,需要引入 @babel/polyfill 插件,或者使用 @babel/plugin-transform-runtime { test: /\.(js|es6)$/, loader: 'babel-loader', exclude: /node_modules/, // 表示除了該文件外 options:{ presets: [['@babel/preset-env', { useBuiltIns: 'usage' }]] }, }, ] }, // 編譯輸出配置 output: { path: path.resolve(__dirname, '../dist2'), // 輸出目錄 publicPath: '/gsh/dist/', // 輸出目錄添加地址前綴 filename: '[name].[chunkhash].js' // 編譯后輸出的文件名稱 },// 使用 plugins 插件,實現編譯過程中自動使用某個 html模板,以及編譯前清除dist里的文件 plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html', // 使用某個 html模板 }), new ClearWebpackPlugin(['dist']), // 編譯前清除dist里的文件 new webpack.HotModuleReplacementPlugin(), // 使用HMR實現模塊代碼熱更新 ], }
在生產環境和開發環境中,webpack的配置是有所區別的,可以把相同部分提取出來作為 common.js ,最后使用 webpack-merge 插件進行合並
Tree Shaking
含義為 ’搖樹‘ ,其作用是在打包過程中,去除某些在項目中並沒有引用的模塊, 只支持 ES Module 方式, 即 import
某些在代碼中引入的模塊,假如在邏輯中沒有使用到,那么在最后打包中不會被打包進去。
在 development 模式下,在webpack.config.js 文件中添加下列配置:
optimization:{ usedExports: true, }
在 package.js 文件中添加下列配置,表示除了某個文件外進行 Tree Shaking :
"sideEffects": ['*.css', '@babel/pollyfill'] // 表示遇到這兩個模塊忽略,可取值為 false
打包分析
可以使用webpack 官方提供的工具進行打包分析, 打開鏈接 https://github.com/webpack/analyse 查閱文檔, 點擊 analyse 位置進入分析工具,上傳 stats.json 文件
在打包的時候,生成 stats.json 文件, 生成方法為 : 在 package.json 文件打包命令中添加 --profile --json > stats.json
"scripts": { "build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js", },
也可以采用 webpack 提供的圖形化分析工具 https://wenpack.js.org/guides 選擇 Code Splitting 目錄下的 Bundel Analysis
Code Splitting 代碼分割
根據代碼的應用場景和邏輯,打包的時候自動將代碼分割成多個 JS 文件, 避免過大的JS 文件出現,提升頁面渲染速度
同步代碼分割,在webpack.config.js 文件中添加下列配置:
optimization:{ splitChunks: { chunks: 'all' // 開啟代碼分割 } }
異步代碼(import):無需任何配置,webpack會自動進行代碼分割
chunks 取值 all , 表示同步代碼和異步代碼都進行代碼分割, 默認取值是 async , 表示只對異步代碼進行分割
關於代碼分割,webpack 推薦使用的是 async 的方式,這個涉及到頁面的代碼使用率,在瀏覽器控制台中, 使用 command + shift + p 的方式,搜索 Coverage 查看頁面代碼使用率
對於頁面渲染速度的優化,使用代碼緩存,並不能解決首屏加載速度的問題,應該盡量提升頁面代碼使用率,對於暫未使用到的代碼邏輯,采用懶加載的方式。如頁面調用的某個方法,方法內的邏輯在未被調用時就是多余的,可以采用異步的方法進行懶加載。
使用 webpack 里的 Prefetching / Preloading實現代碼的懶加載 , 在引入代碼邏輯的時候添加 /*webpackPrefetch: true*/
例子:將某個方法里的邏輯 導出, 在調用時再作為模塊導入
// 新建 click.js 文件,將方法的執行邏輯導出 function handleClick() { const el = document.getElementById('test') el.innerHTML = '點擊頁面后生成' } export default handleClick; // 在 main.js 文件中,方法導入執行邏輯 document.addEventListener('click', () => { import(/*webpackPrefetch: true*/ './click.js').then(({default: handleClick}) => { handleClick() }) })
PWA
全稱 Progressive Web Application , 這是一項新的技術,可以在用戶進入頁面的時候,緩存頁面內容。當服務器故障后,用戶重新進入這個頁面的時候,可以利用緩存正常顯示頁面。
在webpack 里,可以使用 workbox-webpack-plugin 插件實現。
const WorkboxPlugin = require('workbox-webpack-plugin') // 在 plugins 添加下列配置 new WorkboxPlugin.GenerateSw({ clientsClaim: true, skipWaiting: true })
VUE 多頁面打包webpack配置
/** 修改entry配置項 */ entry: { style: './src/style/app.scss', // 不變 app: './src/main.ts', //不變, 這個是index.html的組件 analysis: './src/analysis.ts' // analysis是新增的模塊名字(名字自定義,項目中因為是分析界面模塊所以取名為為analysis) // 如果還有多個其他頁面,可以繼續添加模塊。。。 // P.S. 注意是模塊不是組件,例如個analysis模塊中實際包含ValueAnalysis,ValueAnalysisBar,ValueAnalysisType等組件,但是不必引入。 // P.S. 最后build打包的時候記得放出所有的模塊 }
/** 和main.src同級,創建analysis.ts文件,注意文件名字是對應上面的./src/analysis.ts文件 */ // analysis.ts import Vue from 'vue' // register plugins hooks fo vue component import 'common/registerHooks' import * as svgicon from 'vue-svgicon' // import all icons import 'components/icons' import App from 'pages/App' Vue.use(svgicon, { tagName: 'icon' }) new Vue({ el: '#app', render: h => h(App) }) // 內容基本上和main.src一樣, 但是注意router有所變化,這里router導入了一個新的路由文件routerAnalysis
// 配置build中的文件輸出路徑,當然也可以不配置然后在下一步中直接書寫文件路徑,但是統一比較方便維護 build: { index: path.resolve(__dirname, `../../dist/${env}/index.html`), // 默認的 analysis: path.resolve(__dirname, `../../dist/${env}/analysis.html`), //新加的模塊 ... ... // 其他配置項不變 }
/** 用HTMLWebpackPlugin生成多個文件 */ // 原本的htmlWebpackPlugin配置 new HtmlWebpackPlugin({ filename: config.build.index, template: 'build/tpl/index.html', inject: true, dllName, assetsPublicPath: config.build.assetsPublicPath, staticHost: '', minify: { removeComments: true, collapseWhitespace: true }, excludeChunks: ['analysis'], // index.html沒有用到analysis模塊的內容 chunksSortMode: 'dependency' }), // 想要生成多少不同的html就配置多少個new HtmlWebpackPlugin new HtmlWebpackPlugin({ filename: config.build.analysis, // 生成的新的文件位置,步驟4中配置的路徑 template: 'build/tpl/index.html', // html依據模板,可以沿用index.html或者另外寫一個,看具體需求 inject: true, dllName, assetsPublicPath: config.build.assetsPublicPath, staticHost: '', minify: { removeComments: true, collapseWhitespace: true }, excludeChunks: ['app'], // 該模塊沒有用到app模塊中的組件 chunksSortMode: 'dependency' }),