webpack配置解析
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' }),