webpack 配置
初始化webpack
通过npm i -g webpack webpack-cli下载webpack和webpack的命令行包。
js 配置
我们都知道es6语法在低版本浏览器内是不会被识别的, 另外有一些语法不会被浏览器支持。于是就有了
babel这个工具链: 将 ECMAScript 2015+ (又可称为ES6,ES7,ES8等)版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中
Babel 主要做了如下三件事:
- 语法转换
- 通过
Polyfill方式在目标环境中添加缺失的特性 (通过@babel/polyfill模块) - 源码转换, 比如 JSX 等
关于babel的相关插件和预设(.babelrc是babel的配置文件):
@babel/core: babel的核心功能, 没有我下面的插件都别想用@babel/preset-env: 提供一个预设环境(其实就是各种语法翻译包),是很多语法能够被转换成es5(箭头语法, let, const等) 但是有些es6后面的语法比如(includes等)是不会被翻译的. 另外babel的插件的使用都需要这个预设库 支持。@babel/preset-react: 对react jsx等语法的翻译处理,另外可以使用js可选链操作@babel/cli: 一个内置的 CLI 命令行工具,可通过命令行编译文件 比如:babel index.js -o ./dist/index.js@babel/polyfill: 包括core-js和一个自定义的regenerator runtime模块. 解决了有些浏览器或者低版本NodeJs下(inclues, promise, async, from等)语法不支持的问题@babel/plugin-transform-runtime: 防止打包后的文件中出现重复声明的函数, 它可以重复使用 Babel 注入的 helpers 函数,达到节省代码大小的目的@babel/runtime: @babel/plugin-transform-runtime这个插件需要@babel/runtime配合使用 供业务代码引入模块化的 Babel helpers 函数
babel的运作流程如下: source code -> @babel/core -> ast -> @babel/traverse 和 @babel/types -> ast -> @babel/generator -> output
但是babel会把esm的import语法变成CommonJS语法,这是不会被浏览器识别的。所以需要用打包工具处理
另外@babel/polyfill这个包文件很大. 一般会用 babel-polyfill 结合 @babel/preset-env + useBuiltins(entry) + preset-env targets 的方案 @babel/preset-env提供插件需要的预设环境, useBuiltins实现polyfill的按需引用 于是就有了下面的.babelrc配置
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
],
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
],
// 支持 import('xx.js').then(({default: file}) => {})语法
"dynamic-import-webpack"
]
}
先关的js处理在webpack配置如下:
const isProdMode = process.argv.indexOf("--mode=production") !== -1
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: "babel-loader",
options: {
cacheDirectory: true, // false是使用缓存, 所以就是对node_modules下的包进行缓存
cacheCompression: isProdMode // true是对每个Babel的输出将使用Gzip压缩, 开发环境当然不要压缩了
}
},
]
另外有时候在开发公共包的时候,团队协作可能就需要使用typescript了,typescript该怎么处理配置呢?
- webpack的配置文件下添加如下处理:
{
test: /\.ts$/,
use: "ts-loader",
exclude: /node_modules/,
},
- 添加ts-loader的配置文件
tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/", // 重定向输出目录。
"allowSyntheticDefaultImports" : true, // 允许从没有设置默认导出的模块中默认导入。
"module": "es6", // 指定生成哪个模块系统代码
"target": "es5", // 指定ECMAScript目标版本
"jsx": "react",
"sourceMap": true,
"moduleResolution": "node",
"allowJs": true //允许ts中引入js
},
"include": [
"mock/**/*",
"src/**/*",
"config/**/*",
".umirc.ts",
"typings.d.ts"
],
"exclude": [
"node_modules",
"lib",
"es",
"dist",
"typings",
"**/__test__",
"test",
"docs",
"tests"
]
}
样式配置
关于css的处理,一般要处理各种浏览器的css样式兼容性,支持scss等。下面是各种loader和插件的解释:
-
require("mini-css-extract-plugin").loder: 把css拆分出来用外链的形式引入css文件,该插件会将所有的css样式合并为一个css文件 -
style-loader: 把css插入到style中 -
css-loader: 对@import,url()进行处理 -
sass-loader: 把scss/sass 翻译成css -
postcss-loader: 使用postcss处理css -
postcss: 把css解析为一个抽象语法树, 调用插件处理抽象语法树并添加功能 -
autoprefixer: 添加前缀的,解决浏览器兼容问题. 添加webkit, mozilla前缀 -
postcss-plugin-px2rem: 根据跟节点fontsize设置,将px装成rem
在webpack中配置如下:
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const isProdMode = process.argv.indexOf("--mode=production") !== -1;
const isDevMode = process.argv.indexOf("--mode=development") !== -1;
const getStyleLoaders = (cssOptions, preProcessors) => {
const loaders = [
isDevMode && "style-loader",
isProdMode && MiniCssExtractPlugin.loader,
{
loader: require.resolve("css-loader"),
options: cssOptions,
},
{
loader: require.resolve("postcss-loader"),
options: {
postcssOptions: {
plugins: [
require("postcss-preset-env")({
autoprefixer: {
overrideBrowderslist: "andoroid >= 4.3",
},
stage: 3,
}),
require("postcss-plugin-px2rem")({
rootValue: 75,
minPixelValue: 2, // 设置要替换的最小像素值
}),
],
},
},
},
].filter(Boolean); // loader 是在数组的从后往前的顺序先后处理
if (preProcessors) {
loaders.push({
loader: require.resolve(preProcessors),
});
}
return loaders;
}
{
test: /\.css$/,
use: getStyleLoaders({
importLoaders: 1,
}),
},
{
test: /\.(scss|sass)$/,
exclude: /\.module\.(scss|sass)$/,
// importLoaders 选项告知走该loader之前需要走几个loader
// 为了防止经过css-loader处理时没有被sass-loader和postcss-loader处理
use: getStyleLoaders(
{
importLoaders: 2,
},
"sass-loader"
),
},
{
test: /\.module\.(scss|sass)$/,
use: getStyleLoaders({
importLoaders: 2,
sourceMap: isProdMode,
modules: {
localIdentName: '[local]_[hash:base64:6]'
},
}, 'sass-loader')
},
关于sourceMap
通过sourceMap可以查看报错是出自哪个源文件而不是打包后的文件哪里出错
不过使用sourceMap会使得打包速度变慢
另外默认在module: 'development'环境中sourceMap是默认开启的
下面是各种sourceMap的解释:
- devtool: 'inline-source-map' 将对应的map文件放到js文件中
- devtool: 'cheap-inline-source-map' 告诉你具体哪一行出问题,不会精确到那一列出问题
- devtool: 'eval' eval是打包速度最快的除了你设置
devtool:none - devtool: 'cheap-module-eval-source-map' 推荐development环境使用
- devtool: 'cheap-module-source-map' 推荐production环境使用
各种plugin
plugin 可以在webpack运行到某个时刻的时候帮你做一些事情,所以plugin有生命周期这个说法。
下面是一些常用的插件:
clean-webpack-plugin:
打包前清除上一次打包的文件
htmlWebpackPlugin:
打包结束后,自动生成一个html文件, 并把打包生成的js自动引入到这个html
MiniCssExtractPlugin:
把css拆分出来用外链的形式引入css文件,该插件会将所有的css样式合并为一个css文件
另外还可以对css进行代码分割
webpack-bundle-analyzer:
打包分析工具
HappyPack:
开启多进程Loader转换,不过还是别用,经常听到电脑风扇的转动声
HotModuleReplacementPlugin:
热更新的模块替换插件
copy-webpack-plugin:
拷贝静态资源
css-minimizer-webpack-plugin:
压缩css
terser-webpack-plugin:
压缩js
tree shaking
去除无用代码, 只支持ESM, 不支持commonJS的require的引入, 因为CommonJS定义的模块化规范,只有在执行代码后,才能动态确定依赖模块,所以不支持tree shaking。毕竟tree shaking的本质是在在编译生成AST以后进行无用代码去除 默认production模式开启tree shaking(由类似terser-webpack-plugin的插件做的处理) 如果想在开发环境中设置tree shaing, 需要这么配置:
// --webpack.config.js
optimization: {
usedExports: true,
}
// --package.json
'sideEffects': false
// 对所有模块做tree shaking, 不过一般我们不对css等进行tree shaking
// 所以要这么配置
"sideEffects": [
"*.css",
"*.scss",
"*.sass"
],
另外有时候我们的代码是作为公共库进行开发,可能在Node环境也要运行,而CommonJs又不支持 tree shaking. 所以一般我们会有下面的处理:
{
"name": "Library",
"main": "dist/index.cjs.js", // 打包出一份commonjs规范的bundle
"module": "dist/index.esm.js", // 打包出一份tree shaking后的ESM规范的bundle
}
代码分割(code splitting)
当重新载入文件的时候,因为代码分割到了不同文件, 只需要对变更代码的文件重新加载即可,所以提升了加载性能 在webpack中配置如下:
optimization: {
// 把runtime部分的代码抽离出来单独打包
// runtime部分的代码是管理各个模块的连接(模块之间的引用关系)
runtimeChunk: {
name: "runtime",
},
splitChunks: {
// 对同步代码和异步代码都进行代码分割
// 如果是async那就是只对异步代码做代码分割
chunks: "all",
minSize: 30000, // 表示抽取出来的文件在压缩前的最小大小,默认为30000
maxSize: 0, // 表示抽取出来的文件在压缩前的最大大小,默认为 0,表示不限制最大大小;
minChunks: 1, // 引入的模块最少引入次数,超过才打包
maxAsyncRequests: 6, // 同时加载的模块树
maxInitialRequests: 4,
automaticNameDelimiter: "~", // 组和文件名的连接符
cacheGroups: {
vendors: {
name: 'vendors', // 对这种打包文件放到vendors.js中
enforce: true, // 忽略默认参数
test: /[\\/]node_modules[\\/]/, // 打包的文件来自node_modules
priority: 20, // 不同组的优先级
reuseExistingChunk: true,
},
// MiniCssExtractPlugin会对生成的css进行代码分割
// 然后打包到styles.css的文件中
styles: {
name: 'styles',
test: /\.(scss|sass|css|less)$/,
chunks: 'all',
enforce: true,
priority: 10,
},
// 默认组
default: {
minChunks: 2,
priority: -20, // 同时满足多个组条件,谁的优先级高就用谁
reuseExistingChunk: true, // 如果一个模块已经被打包了,就用之前打包的模块做引用
},
},
},
},
最后
另外还需要对webpack进行
- 多环境配置
- 支持多页面入口打包配置
- 代码打包分析 这是我写的一个webpack配置