老记不住的Webpack知识点汇总(上)
前言
自从学习Webpack
后,Webpack的知识点总是老忘,而且有些知识点很难用言语给描述出来,没办法讲给别人听,秉着好记性不如烂笔头的原则,我还是总结下来吧,便于后期回顾。
Webpack的loader和plugin
很多人都会有这样的一个疑问,包括面试中,面试官也会常问,Webpack中的loader和plugin
有什么区别,下面,我们来看看,他们到底有什么区别。
如何配置使用
//webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
devtool: false,
// entry: './src/index.js',
entry: {
index: {
import: './src/index.js',
dependOn: 'shared'
},
main: {
import: './src/main.js',
dependOn: 'shared'
},
shared: ['axios']
},
output: {
path: path.resolve(__dirname, './build'),
// placeholder
filename: '[name]-bundle.js',
clean: true
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: "babel-loader",
}
},
{
test: /\.ts$/,
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
可以看到,配置用法上,其实看不出什么区别,但是我们可以从配置的插件入手,观看区别,我们先看babel-loader
有什么作用,babel-loader
是将部门浏览器不支持的语法和API转换成所有浏览器都支持的语法、polyfill的作用,那么它是如何做到这个的呢?是不是将我们需要打包的代码进行转换,然后输出到打包文件,我们仔细想一想,less-loader、css-loader、style-loader这种我们常用的loader是不是都是将目标代码进行转换,转换成我们所需要的代码。
经过上边的思路,那我们就总结一下loader的作用。
loader的作用
:loader 通常指打包的方案,即按什么方式来处理打包,打包的时候它可以拿到模块源代码,经过特定 loader 的转换后返回新的结果。执行时机是打包的时机。
那么plugin呢?
我们仔细想一想,我们一般使用plugin做什么?是不是拓展一些Webpack没有的功能?,就像上边代码中写的HtmlWebpackPlugin,它的作用是在打包目录文件中生成html文件,他是不是拓展了Webpack在打包过程中无法生成html文件的功能?那我们不就可以总结一下了嘛。
Webpack的作用
:plugin的作用就是拓展Webpack的功能,当我们的Webpack运行到某一个时刻的时候,就会触发plugin,帮助Webpack做一些拓展功能,plugin的执行时机在Webpack运行的全过程都有可能执行,Webpack在运行的过程中,会广播出很多Webpack的生命周期,plugin会监听这些生命周期,在合适的时机通过 Webpack 提供的 API 改变输出结果。
官方的解释是:插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。
source-map
const path = require('path')
module.exports = {
mode: 'production',
devtool: 'nosources-source-map',
// 常见的值:
// 1.false
// 2.none => production
// 3.eval => development
// 4.source-map => production
// 不常见的值:
// 1.eval-source-map: 添加到eval函数的后面
// 2.inline-source-map: 添加到文件的后面
// 3.cheap-source-map(dev环境): 低开销, 更加高效
// 4.cheap-module-source-map: 和cheap-source-map比如相似, 但是对来自loader的source-map处理的更好
// 5.hidden-source-map: 会生成sourcemap文件, 但是不会对source-map文件进行引用
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader'
}
}
]
}
}
babel配置
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js',
// 重新打包时, 先将之前打包的文件夹删除掉
clean: true
},
resolve: {
extensions: ['.js', '.json', '.wasm', '.jsx', '.ts']
},
module: {
rules: [
// 针对jsx?代码进行babel处理
{
test: /\.jsx?$/, // x?: 0或者1个x
// exclude: /node_modules/,
use: {
loader: "babel-loader",
// options: {
//指定babel插件转换
// // plugins: [
// // "@babel/plugin-transform-arrow-functions",
// // "@babel/plugin-transform-block-scoping"
// // ]
//使用预设
// presets: [
// ["@babel/preset-env", {
// // 在开发中针对babel的浏览器兼容查询使用browserslist工具, 而不是设置target
// // 因为browserslist工具, 可以在多个前端工具之间进行共享浏览器兼容性(postcss/babel)
// // targets: ">5%"
// }]
// ]
// }
}
},
{
test: /\.ts$/,
// use: "ts-loader"
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
babel-loader的配置可以配置具体的插件进行代码转换,也可以配置预设,让babel检测那些代码需要转换,从而自动转换。
使用预设时,我们需要在babel.config.js文件中进行配置,给preset-env配置一些属性:
useBuiltIns:设置以什么样的方式来使用polyfill;
corejs:设置corejs的版本,目前使用较多的是3.x的版本,比如我使用的是3.8.x的版本;
另外corejs可以设置是否对提议阶段的特性进行支持;
设置 proposals属性为true即可;
//babel.config.js
module.exports = {
// plugins: [
// "@babel/plugin-transform-arrow-functions",
// "@babel/plugin-transform-block-scoping"
// ]
presets: [
["@babel/preset-env", {
// 在开发中针对babel的浏览器兼容查询使用browserslist工具, 而不是设置target
// 因为browserslist工具, 可以在多个前端工具之间进行共享浏览器兼容性(postcss/babel)
// targets: ">5%"
// corejs: 3,
// // false: 不使用polyfill进行填充
// useBuiltIns: "entry"
}],
["@babel/preset-react"],
["@babel/preset-typescript", {
corejs: 3,
useBuiltIns: "usage"
}]
]
}
useBuiltIns属性有三个常见的值
false
打包后的文件不使用polyfill来进行适配; 并且这个时候是不需要设置corejs属性的;
usage
会根据源代码中出现的语言特性,自动检测所需要的polyfill; 这样可以确保最终包里的polyfill数量的最小化,打包的包相对会小一些; 可以设置corejs属性来确定使用的corejs的版本;
entry
如果我们依赖的某一个库本身使用了某些polyfill的特性,但是因为我们使用的是usage,所以之后用户浏览器可能会报错; 所以,如果你担心出现这种情况,可以使用 entry; 并且需要在入口文件中添加 import 'core-js/stable'; import 'regenerator-runtime/runtime'
; 这样做会根据 browserslist 目标导入所有的polyfill,但是对应的包也会变大;
Webpack服务器
为什么需要搭建Webpack服务器?
目前我们开发的代码,为了运行需要有两个操作:
操作一:npm run build,编译相关的代码;
操作二:通过live server或者直接通过浏览器,打开index.html代码,查看效果; 这个过程经常操作会影响我们的开发效率,我们希望可以做到,当文件发生变化时,可以自动的完成 编译 和 展示; 为了完成自动编译,webpack提供了几种可选的方式:
- webpack watch mode;
- webpack-dev-server(常用);
- webpack-dev-middleware;
webpack-dev-server
上面的方式可以监听到文件的变化,但是事实上它本身是没有自动刷新浏览器的功能的:
当然,目前我们可以在VSCode中使用live-server来完成这样的功能;
但是,我们希望在不使用live-server的情况下,可以具备live reloading(实时重新加载)的功能;这就是热重载。
webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中:
Webpack性能优化-分包
webpack分包主要分为三种
- 多入口分包
- 动态导入模块
- 自定义分包。
多入口分包就是定义多个入口,打包成多个文件。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
devtool: false,
// entry: './src/index.js',
entry: {
//定义多个入口文件
index: {
import: './src/index.js',
dependOn: 'shared'
},
main: {
import: './src/main.js',
dependOn: 'shared'
},
shared: ['axios']
},
output: {
path: path.resolve(__dirname, './build'),
// placeholder
filename: '[name]-bundle.js',
clean: true
},
resolve: {
extensions: ['.js', '.json', '.wasm', '.jsx', '.ts']
},
devServer: {
static: ['public', 'content'],
port: 3000,
compress: true,
proxy: {
'/api': {
target: 'http://localhost:9000',
pathRewrite: {
'^/api': ''
},
changeOrigin: true
}
},
historyApiFallback: true
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: "babel-loader",
}
},
{
test: /\.ts$/,
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
动态导入就是通过import函数来动态导入文件。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
devtool: false,
// entry: './src/index.js',
entry: './src/main.js',
output: {
clean: true,
path: path.resolve(__dirname, './build'),
// placeholder
filename: '[name]-bundle.js',
// 单独针对分包的文件进行命名
chunkFilename: '[name]_chunk.js'
},
resolve: {
extensions: ['.js', '.json', '.wasm', '.jsx', '.ts']
},
devServer: {
static: ['public', 'content'],
port: 3000,
compress: true,
proxy: {
'/api': {
target: 'http://localhost:9000',
pathRewrite: {
'^/api': ''
},
changeOrigin: true
}
},
historyApiFallback: true
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: "babel-loader",
}
},
{
test: /\.ts$/,
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
自定义分包
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
mode: 'production',
devtool: false,
// entry: './src/index.js',
entry: './src/main.js',
output: {
clean: true,
path: path.resolve(__dirname, './build'),
// placeholder
filename: '[name]-bundle.js',
// 单独针对分包的文件进行命名
chunkFilename: '[name]_chunk.js',
// publicPath: 'http://coderwhycdn.com/'
},
resolve: {
extensions: ['.js', '.json', '.wasm', '.jsx', '.ts']
},
devServer: {
static: ['public', 'content'],
port: 3000,
compress: true,
proxy: {
'/api': {
target: 'http://localhost:9000',
pathRewrite: {
'^/api': ''
},
changeOrigin: true
}
},
historyApiFallback: true
},
// 优化配置
optimization: {
// 设置生成的chunkId的算法
// development: named
// production: deterministic(确定性)
// webpack4中使用: natural
chunkIds: 'deterministic',
// runtime的代码是否抽取到单独的包中(早Vue2脚手架中)
runtimeChunk: {
name: "runtime"
},
// 分包插件: SplitChunksPlugin
splitChunks: {
chunks: "all",
// 当一个包大于指定的大小时, 继续进行拆包
// maxSize: 20000,
// // 将包拆分成不小于minSize的包
// minSize: 10000,
minSize: 10,
// 自己对需要进行拆包的内容进行分包
cacheGroups: {
utils: {
test: /utils/,
filename: "[id]_utils.js"
},
vendors: {
// /node_modules/
// window上面 /\
// mac上面 /
test: /[\\/]node_modules[\\/]/,
filename: "[id]_vendors.js"
}
}
},
// 代码优化: TerserPlugin => 让代码更加简单 => Terser
minimizer: [
// JS代码简化
new TerserPlugin({
extractComments: false
})
// CSS代码简化
]
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: "babel-loader",
}
},
{
test: /\.ts$/,
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
}
转载自:https://juejin.cn/post/7267091810380103740