- entry
- 如果是单页面应用,那么入口只有一个;如果是多个页面的项目,那么通常是一个页面会对应一个构建入口 ```typescript module.exports = { entry: ‘./src/index.js’ }
上述配置等同于
module.exports = { entry: { main: ‘./src/index.js’ } }
- **output**
- webpack 的输出即指 webpack 最终构建出来的静态文件,webpack 构建生成的文件名、路径等都是可以配置的,在配置文件中使用 output 字段来进行设置
```typescript
module.exports = {
// ...
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
}
# 或者使用 entry 的名称
module.exports = {
entry: {
main: './src/index.js' // main 为 entry 的名称
},
output: {
// 使用 [name] 来引用 entry 名称,在这里即为 main
filename: '[name].js',
// 输出文件目录(将来所有资源输出的公共目录)
path: path.join(__dirname, 'dist'),
// 所有资源引入公共路径前缀 'imgs/a.png' ——> '/imgs/a.png'
publicPath: '/',
// 非入口chunk名称
chunkFilename: 'js/[name]_chunk.js',
// 整个库向外暴露的变量名
library: '[name]',
// 变量名添加到哪个上
libraryTarget: 'window|global|commonjs'
},
}
resolve
// 解析模块的规则 resolve: { // 配置解析路径的别名 alias: { $css: resolve(__dirname, 'src/css') }, // 配置省略文件路径的后缀名, 如果名字相同,则取第一个 extensions: ['.js', '.json', '.jsx', '.css'] }
loader
- 在前端构建中会遇见需要使用各式各样的文件,例如 css 代码,图片,模板代码等。webpack 中提供一种处理多种文件格式的机制,便是使用 loader。可以把 loader 理解为是一个转换器,负责把某种文件格式的内容转换成 webpack 可以支持打包的模块
- 在 module.rules 字段下来配置相关的规则,例如使用 Babel 来处理 .js 文件
- babel-loader 可以使用 babel 来将 ES6 代码转译为浏览器可以执行的的 ES5 代码
module: { // ... rules: [ { test: /\.jsx?/, // 匹配文件路径的正则表达式,通常我们都是匹配文件类型后缀 include: [ path.resolve(__dirname, 'src') // 指定哪些路径下的文件需要经过 loader 处理 ], use: { // 指定使用的 loader loader: 'babel-loader', // babel-loader 可以使用 babel 来将 ES6 代码转译为浏览器可以执行的的 ES5 代码 options: { presets: ['@babel/preset-env'], }, }, }, ], }
plugin
module.exports = { // …
plugins: [ new CopyPlugin([ { from: ‘src/public’, to: ‘public’ }, ]), ], };
- **区分环境**
- mode(webpack v4 引入的新概念,用于方便快捷地指定一些常用的默认优化配置)
- 取值:development、production、none
- 本地环境:
- 需要更快的构建速度
- 需要打印 debug 信息
- 需要 live reload 或 hot reload 功能
- 需要 sourcemap 方便定位问题
- 生产环境:
- 需要更小的包体积,代码压缩+tree-shaking
- 需要进行代码分割
- 需要压缩图片体积
```javascript
# 安装
npm install cross-env -D
# ./package.json配置
"scripts": {
"dev": "cross-env NODE_ENV=dev webpack serve --mode development",
"test": "cross-env NODE_ENV=test webpack --mode production",
"build": "cross-env NODE_ENV=prod webpack --mode production"
}
配置本地服务 webpack.config.js
const config = { devServer: { contentBase: path.resolve(__dirname, ‘public’), // 静态文件目录 compress: true, //是否启动压缩 gzip port: 5000, // 端口号 open:true, // 是否自动打开浏览器 hot:true, // 开启HMR clientLog: ‘none’, // 不显示启动服务器日志信息 quiet: true, // 除了一些基本启动信息以外,其他内容不显示 overlay: false, // 如果出错,不要全屏提示 // 服务器代理,解决开发环境跨域问题 proxy: { // 一旦devServer(5000)服务器接收到以/api/xx开头的请求,就会把请求发送到另外一个服务器3000 ‘/api’: { target: ‘http:localhost:3000’, // 发送请求,路径重写 pathRewrite: { ‘^/api’:’’ } } } } } module.exports = (env, argv) => { console.log(‘argv.mode=’,argv.mode) // 打印 mode(模式) 值 // 这里可以通过不同的模式修改 config 配置 return config }
- **optimization**
```javascript
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
splitChunks: {
chunks: 'all'
},
// 记录其他模块的hash打包成一个文件runtime
// 解决: 修改a文件导致b文件的contentHash发生变化
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: {
// 配置生产环境的压缩方案:压缩js和css
new TerserPlugin({
cache: true, // 开启缓存
parallel: true, // 开启多进程打包
sourceMap: true, // 启动source-map
})
}
}
}
- 引入CSS
- style-loader 就是将处理好的 css 通过 style 标签的形式添加到页面上(核心逻辑是通过动态添加 style 标签的方式,将样式引入页面)
- Loader 的执行顺序是固定从后往前,即按 css-loader —> style-loader 的顺序执行 ```javascript npm install style-loader -D
rules: [ { test: /.css$/, //匹配所有的 css 文件 use: [‘style-loader’,’css-loader’] } ]
- **CSS兼容性**
- [postcss-loader](https://link.juejin.cn/?target=https%3A%2F%2Fwebpack.docschina.org%2Floaders%2Fpostcss-loader%2F)自动添加 CSS3 部分属性的浏览器前缀
- 引入Less或Sass
```javascript
# 安装
npm install postcss postcss-loader postcss-preset-env -D
npm install sass-loader -D
# 淘宝镜像
npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
# 添加
rules: [
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
}
]
# 创建 postcss 配置文件 postcss.config.js
// postcss.config.js
module.exports = {
plugins: [require('postcss-preset-env')]
}
# 创建 postcss-preset-env 配置文件 .browserslistrc
# 换行相当于 and
last 2 versions # 回退两个浏览器版本
> 0.5% # 全球超过0.5%人使用的浏览器,可以通过 caniuse.com 查看不同浏览器不同版本占有率
IE 10 # 兼容IE 10
- 分离样式文件
- 通过 CSS 文件的形式引入到页面上,即link方式引入 ```javascript $ npm install mini-css-extract-plugin -D // 引入插件 const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)
const config = { module: { rules: [ { test: /.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件 use: [ // ‘style-loader’, MiniCssExtractPlugin.loader, // 添加 loader ‘css-loader’, ‘postcss-loader’, ‘sass-loader’, ] } ] }, plugins:[ // 配置插件 new MiniCssExtractPlugin({ // 添加插件 filename: ‘[name].[hash:8].css’ }) ] }
- **图片和字体文件**
| Loader | 说明 |
| --- | --- |
| file-loader | 解决图片引入问题,并将图片 copy 到指定目录,默认为 dist |
| url-loader | 解依赖 file-loader,当图片小于 limit 值的时候,会将图片转为 base64 编码,大于 limit 值的时候依然是使用 file-loader 进行拷贝 |
| img-loader | 压缩图片 |
```javascript
npm install file-loader -D
npm install url-loader -D
const config = {
module: {
rules: [
{
test: /\.(jpe?g|png|gif)$/i,
use:[
{
loader: 'file-loader',
options: {
name: '[name][hash:8].[ext]'
}
}
]
},
{
loader: 'file-loader',
options: {
name: '[name][hash:8].[ext]'
}
},
{
test: /\.(jpe?g|png|gif)$/i,
use:[
{
loader: 'url-loader',
options: {
name: '[name][hash:8].[ext]',
// 文件小于 50k 会转换为 base64,大于则拷贝文件
limit: 50 * 1024
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 匹配字体文件
use: [
{
loader: 'url-loader',
options: {
name: 'fonts/[name][hash:8].[ext]', // 体积大于 10KB 打包到 fonts 目录下
limit: 10 * 1024
}
}
]
}
]
}
}
- JS兼容性
- babel-loader 将 ES6 语法转化为 ES5
- babel的配置主要分为
presets
预设 和plugins
插件 - ES2018 新增了对象…展开,babel 提供
@babel/plugin-proposal-object-rest-spread
来转换 - ES2017新增了
async
和await
babel提供了@babel/plugin-transform-async-to-generator
用于将async/await
转换为generator
函数 - babel-polyfill:大浏览器中添加缺失的新特性,补齐API和标准对齐
- 缺点:全量引用所有的新特性垫片,这样引用的 polyfill 包会大增加构建的js体积大小,如果只用到了其中两三个新特性,那就存在很多冗余的垫片
- @babel/preset-env:预设除了包含所有稳定的转码插件,还可以根据我们设定的目标环境进行针对性转码,核心配置项有
targets
、useBuiltIns
、modules
、corejs
- targets:用于设置兼容到哪些目标浏览器,如果不配置,则尝试读取 package.json 和 .browserslistrc 中的配置, browserslist 的配置也同样作用于 autoprefixer、postcss等插件。 如果没有 targets 和 browserslist 配置,则转换所有的ES6语法为ES5版本
- useBuiltIns:取值有 usage/entry/false,默认为 false
- false:不使用 polyfill,只转换语法
- entry :会根据目标浏览器环境,引用未支持的所有的 polyfill,需要在入口文件引用 @babel/polyfill
- usage:会先分析代码中使用到的新特性,只为用到的新特性添加 polyfill,不需要手动添加 ;
- 缺点: 语法转换后,代码是会注入一些辅助函数,如果有很多个js文件,每个文件顶部都会注入相关辅助函数,有可能会注入导致辅助函数重复,最后用构建工具打包出来的产物会非常大
- corejs:取值为 2 或 3
- modules:模块语法转换,用于将当前ES6的模块语法转换为其它模块化的语法,取值
amd/umd/systemjs/commonjs/cjs/auto/false
,默认为auto
,会转换为commonjs
语法- 常见的模块化语法有两种:
ES6
的模块法语法用的是import
与export
;commonjs
模块化语法是require
与module.exports
- 一般建议设置为 false,不转换 es6 的模块语法,方便构建工具如
webpack、rollup
对模块进行静态分析,实现tree shaking
等优化措施
- 常见的模块化语法有两种:
- @babel/runtime:抽离公共辅助函数,为了自动使用
@babel/runtime
里的函数,还需要@babel/plugin-transform-runtime
插件 - 使用
core-js@3
来 polyfill,会全局引入缺少的 API - @babel/runtime-corejs3 避免污染全局环境,相当于
@babel/runtime
+ 不污染环境的core-js@3
预设和 - 插件的执行顺序
- 插件比预设先执行
- 插件执行顺序是插件数组从前向后执行
- 预设执行顺序是预设数组从后向前执行
```typescript // 普通的项目不怕污染全局环境可以用这个配置 npm install —save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime npm install —save @babel/runtime core-js@3@babel/preset-env @babel/preset-typescript @babel/preset-react @babel/preset-flow
// babel.config.json 配置文件 { “presets”: [ [ “@babel/preset-env”, { “useBuiltIns”: “usage”, “corejs”: 3 } ] ], “plugins”: [ [“@babel/plugin-transform-runtime”] ] }
```typescript
// 开发工具类库为了不污染全局环境可以用这个配置
npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm install --save @babel/runtime-corejs3
// babel.config.json 配置文件
{
"presets": [
[
"@babel/preset-env"
]
],
"plugins": [
[
"@babel/plugin-transform-runtime", {
"corejs": 3
}
]
]
}
SourceMap 配置选择
- SourceMap 是一种映射关系,当项目运行后,如果出现错误,可以利用 SourceMap 反向定位到源码位置
速度:eval > inline > cheap > … | 关键字 | 含义 | | —- | —- | | source-map | 产生.map 文件 | | eval | 使用 eval 包裹模块代码 | | cheap | 精确到行,不包含列信息(关于列信息的解释下面会有详细介绍)也不包含 loader 的 sourcemap | | module | 包含 loader 的 sourcemap(比如 jsx to js ,babel 的 sourcemap),否则无法定义源文件 | | inline | 将.map 作为 DataURI 嵌入,不单独生成.map 文件(内联,体积大,不考虑) |
推荐配置:
- 本地开发:eval-cheap-module-source-map
- 生产环境:(none)
const config = { entry: './src/index.js', // 打包入口地址 output: { filename: 'bundle.js', // 输出文件名 path: path.join(__dirname, 'dist'), // 输出文件目录 }, devtool: 'source-map' }
三种hash值
- hash :任何一个文件改动,整个项目的构建 hash 值都会改变
- chunkhash:文件的改动只会影响其所在 chunk 的 hash 值
- 打包后的hash值会根据入口文件的不用而不一样,当某个入口文件修改后重新打包,会导致本入口文件关联的所有文件的hash值都修改,但是不会影响到其他入口文件的hash值
- contenthash:每个文件都有单独的 hash 值,文件的改动只会影响自身的 hash 值
- 每个文件的hash值都是根据自身内容而生成,当某个文件内容修改时,打包后只会修改其本身的hash值,不会影响其他文件的hash值 | 占位符 | 解释 | | —- | —- | | ext | 文件后缀名 | | name | 文件名 | | path | 文件相对路径 | | folder | 文件所在文件夹 | | hash | 每次构建生成的唯一 hash 值 | | chunkhash | 根据chunk生成hash值 | | contenthash | 根据文件内容生成hash 值 |