Dev tool
| devtool | build | rebuild | production | quality |
|---|---|---|---|---|
(none) |
fastest | fastest | yes | bundled code |
eval |
fastest | fastest | no | generated code |
eval-cheap-source-map |
fast | faster | no | transformed code (lines only) |
eval-cheap-module-source-map |
slow | faster | no | original source (lines only) |
eval-source-map |
slowest | fast | no | original source |
cheap-source-map |
fast | slow | yes | transformed code (lines only) |
cheap-module-source-map |
slow | slower | yes | original source (lines only) |
inline-cheap-source-map |
fast | slow | no | transformed code (lines only) |
inline-cheap-module-source-map |
slow | slower | no | original source (lines only) |
inline-source-map |
slowest | slowest | no | original source |
source-map |
slowest | slowest | yes | original source |
hidden-source-map |
slowest | slowest | yes | original source |
nosources-source-map |
slowest | slowest | yes | without source content |
devtool的匹配规则[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
eval表示每个模块都使用eval()执行,inline表示打包后合并到目标文件中,cheap表示只提示出错行数,不提示列数,module表示loader中的报错也会提示,source-map表示会生成map文件
source map:解决出错时,及时匹配到源代码报错位置
devServer
常用API
contentBase目录地址proxy代理open编译后自动打开浏览器hot开启HMRhotOnly禁止刷新
WEBPACK-DEV-SERVER
// package.json"script": {"server": "webpack-dev-server"}
仿webpack-dev-server
const express = require('express');const webpackDevMiddleware = require('webpack-dev-middleware');const webpack = require('webpack');const config = require('./webpack.config.js');const complier = webpack(config);const app = express();app.use(webpackDevMiddleware(complier, {publicPath: config.output.publicPath,// do something...});app.listen(3000, () => {console.log('server is running');})
Tree Shaking
tree shaking通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)
// 主要演示development环境下,production环境自带// package.json// sideEffects向compiler提供提示,表明项目中的哪些文件是 "pure(纯的 ES2015 模块)",由此可以安全地删除文件中未使用的部分。"sideEffects": false, // false代表所有模块都要检测"sideEffects": ['*.css'] // css后缀文件都不检测// webpack.config.jsmode: 'development',optimization: {useExports: true //}
注意事项
- 使用
ES Module(即import和export) package.json文件中,添加sideEffects属性mode为development时需要配置optimization: {useExports: true}开启,production环境下默认启用minification(代码压缩) 和tree shaking
production与development配置整合
使用
webpack-merge对production与development共享配置进行单独整合。
const merge = require('webpack-merge');module.exports = merge(commonCofig, devConfig/proConfig);
Code Splitting
代码分离
// 同步代码加载// import的插件进行单独分离,挂载全局,再次访问时,直接读取缓存import _ from 'lodash';window._ = _;// 进行webpack配置,将单独分离出的vendor作为单独入口编译entry: {lodash: './src/lib/lodash.js',// other entry...}// 异步代码加载import(/*webpackChunkName: 'lodash'*/'lodash').then(({default: _}) => {// 业务code...});// 处理异步代码加载时,出现编译报错,需要babel支持@babel/plugin-syntax-dynamic-import
split Chunks
// 使用webpack optimization的splitChunks配置optimization: {splitChunks: {// 代码分离的类型 all: 同异步代码 async: 异步代码 initial: 同步代码chunks: 'all' | 'async' | 'initial',// 代码大小最小30kb,才进行代码分离minSize: 30000,minRemainingSize: 0,maxSize: 0,// 代码引入最小次数,eg: 引入一次就会进行代码打包minChunks: 1,// 同时加载代码库数量maxAsyncRequests: 6,// 入口文件加载的库数量maxInitialRequests: 4,// 输出的文件名连接符automaticNameDelimiter: '~',cacheGroups: {defaultVendors: { // 组名// 检测从node_modules中引入,则分离到vendors组test: /[\\/]node_modules[\\/]/,// 进行匹配时的优先级priority: -10,// 输出的vendors组的文件名name: 'vendors.js',// 组成员代码类型chunks: 'all' | 'async' | 'initial'},// 上面分组条件都不符合,则归属于default组default: {minChunks: 2,priority: -20,// 如果已经被打包过,不再进行打包操作reuseExistingChunk: true}}}}
总结
// 代码分割,与webpack无关/**webpack中实现代码分割两种方式:第一种:同步代码:只需要webpack.config.js中配置optimization的splitChunks第二种:异步代码(import):无需做任何配置,会自动代码分割*/
Lazy Loading
// 使用import进行异步加载,达到懒加载效果async function lazyLoading() {const {default: _} = await import('lodash');// other code...}
chunk
// webpack中optimization配置的minChunks是表示被引用的最小次数 chunks为检测代码分离的类型,默认为异步chunks: 'async' | 'all' | 'initial',minChunks: 2 // 被引用小于2次不会进行整合打包分组// output:chunkFilenameoutput: {chunkFilename: '[name].chunk.js' // 打包后的文件直接引入chunk后的文件}
webpack analyse
// webpack官方推荐的打包分析插件 webpack/analyse 在package.json中配置如下代码"script": { "dev": "webpack --profile --json > stats.json" }
preload prefetch
// 在异步加载中添加注释 /*webpackPrefetch: true*/import(/*webpackPrefetch: true*/'lodash');
CSS代码分割
MiniCssExtractPlugin
开发环境中不适用,
MiniCssExtractPlugin不支持HMR
const MiniCssExtractPlugin = require('mini-css-extract-plugin');module: {rules: [{test: /\.css$/,loader: MiniCssExtractPlugin.loader,// other code...}]},// tree shaking 控制过滤文件optimization: {useExports: true},plugins: [new MiniCssExtractPlugin({filename: '[name].css',chunkFilename: '[name].chunk.css'})]// package.json"sideEffects": ["*.css"] // 不检测css后缀文件
optimize-css-assets-webpack-plugin
css代码合并压缩
optimization: { minimizer: [new OptimizeCssAssetsWebpackPlugin({})] }
webpack与浏览器缓存(Caching)
webpack打包编译成功后,浏览器进行访问,可以配置output: {filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js'},如果修改内容不涉及某些chunk,再次进行打包后,浏览器再次访问,会使用浏览器缓存。
webpack版本4之前可能会出现问题,主要是因为mainfest会发生变化,此时可以添加配置optimization:{runtimeChunk: {name:'runtime'}}
Shimming
webpack进行打包时,会出现不同兼容问题,比如引入的第三方库中有其他库的JQuery依赖,但是webpack是模块打包,不识别$,此时需要垫片shimming
webpack内置了ProvidePlugin,
const webpack = require('webpack');plugins: [new webpack.ProvidePlugin({'$': 'jquery', // 识别文件中$,自动引入jquery'_join': ['lodash', 'join'] // 自动引入lodash中的join})]
如何使模块中的this指向window
// npm i imprts-loader -Dmodule: {rules: [{test: /\.js$/,use: ['babel-loader', 'imports-laoder?this=>window']}]}
环境变量
// 添加环境变量,控制执行脚本时的配置"script": {"dev": "webpack --env.development --config webpack.dev.config.js","prod": "webpack --env.production --cinfig webpack.prod.config.js"}// webpack.dev.config.js// 可以直接获取到env的值
Library打包
打包输出一个库时,使用者可能会通过不同方式引入,
ES module CommonJS等方式,此时需要进行webpack的output配置,libraryTarget: 'umd',意思为通用不同引入方式。但是不支持script标签引入,此时需要配置library: 'library',在全局增加一个library变量。libraryTarget的属性还可以为其他,比如window this global等,代表library挂载的位置。
自定义库中引入其他插件库
比如自定义库中引入
lodash,但是使用者也引入lodash,这样会造成重复引入,增加项目体积,此时需要对webpack进行配置,externals:['lodash'],这样自定义库就不会引入lodash。
externals的属性值类型可以为stringarrayobjectfunctionregex
externals : {react: 'react'}// 或者externals : {lodash : {commonjs: "lodash",amd: "lodash",root: "_" // 指向全局变量}}// 或者externals : {subtract : {root: ["math", "subtract"]}}
发布NPM
通过webpack打包编译后,需要在
package.json中修改main入口。
npm adduser—->npm publish
PWA配置
针对上线的项目,需要使用PWA,即使服务器宕机,用户可以使用第一次访问时缓存的页面。
workbox-webpack-plugin插件可以进行相关需求配置。
// webpack.config.jsconst WorkboxWebpackPlugin = require('workbox-webpack-plugin');plugins: [new WrokboxWebpackPlugin.GenerateSW({clientsClaim: true,skipWaiting: true})]// 业务代码if('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('打包编译后的service文件名').then(registration =>{// success coding...}).catch(error => {// fail coding...})})}
TypeScript配置
javascript的超集,需要在webpack.config.js中进行配置,额外需要在根目录添加tsconfig.json
// webpack.config.jsmodule: {rules: [{ test: /\.tsx?$/, loader: 'ts-loader', exclude: /node_modules/ }]}// tsconfig.json{"compilerOptions": {"outDir": "./dist","module": "es6","target": "es5","allowJs": true}}
注意⚠️:调用第三方库时,不会进行报错,这时需要安装
types/lodash等类似的约束。
WebpackDevServer请求转发
本地跨域,使用
devServer中的proxy进行配置。
devServer: {proxy: {'/api': {target: 'http://deankwan.com', // 首先请求目标路径secure: true, // 可以对https请求进行转发// 拦截bypass: function(req, res, proxyOptions) {if (req.headers.accept.indexOf('html') !== -1) {return '/index.html';}},pathRewrite: {'productList.json': '' // 所有请求路径为productList都清空},changeOrigin: true, // 改变origin}}}// 更多知识请详情见webpack官网
WebpackDevServer解决单页面应用路由问题
historyApiFallback: truehistoryApiFallback的配置项详情见webpack官网
EsLint配置
安装
npm i eslint -D初始化npx eslint --init
webpack打包优化
- 技术迭代(
Node Npm Yarn),更新最新依赖 - 尽可能少的模块上应用
Loader Plugin尽可能精简并确保可靠性resolve参数合理配置,比如引入文件不写扩展名,使用extensions进行省略补写,如果配置的过多,打包时会按照扩展名进行遍历,alias可以对路径做别名,这样可以更快的定位。- 第三方引入库一般不会发生变化,所以可以单独进行打包,之后再打包就不需要重复打包。
DLLPlugin```javascript // webpack.dll.js 主要作用于对引入类库的打包,并生成映射json文件 module.exports = { entry: {
}, output: {vendors: ['react', 'react-dom', 'lodash']
path: path.resolve(__dirname, ‘../dll’), // 打包输出在dll文件夹下 library: ‘[name]’, // 挂载在全局上,向外暴漏库引用 }, plugin: [ // 使用webpack内置插件,对生成的库与对应路径下的json文件进行映射。filename: '[name].dll.js',
}) ] }new webpack.DllPlugin({name: '[name]',path: path.resolve(__dirname, '../dll/[name].manifest.json')
// webpack.prod.js 生产环境中,对映射文件进行查找。 // add-asset-html-webpack-plugin 在html挂载静态文件的插件 new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(dirname, ‘../dll/vendors.dll.js’) }), // 打包时进行关联查询,如果找到对应的manifest文件,不会再进行打包编译 new webpack.DllReferencePlugin({ manifest: path.resolve(dirname, ‘../dll/vendors.manifest.json’) })
// 业务情况: 如果对类库进行分类,打包多个manifest const plugins = []; // 读取dll文件夹下的文件 const files = fs.readdirSync(path.resolve(dirname, ‘../dll’)); files.forEach(file => { // 针对dll.js结尾的文件,挂载到html上 if(/.*.dll.js/.test(file)) { plugins.push(new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(dirname, ‘../dll’, file) })) }
// manifest.json 映射对应文件 if(/.*.manifest.json/.test(file)) { plugins.push(new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, ‘../dll’, file) })) } }); ```
- 控制包文件大小,可以使用
tree shaking,但是尽量避免不必要的插件和类库的引入。 webpack是通过node进行的,所以是单线程的。可以通过node中的多进程进行打包thread-loader parallel-webpack happypack,parallel-webpack是针对多个页面进行打包的,不需要等一个页面打包完,再打包另一个。- 合理使用
sourceMap。 - 结合
stats分析打包结果,通过线上或者本地的打包分析工具进行分析,针对性的进行优化。 - 开发环境内存编译。在使用
devServer进行运行项目时,项目编译后不会放在dist文件夹下,而是放在了内存中进行读取。 - 开发环境无用插件的剔除。
