webpack学习笔记
- webpack学习笔记
1 | # 初始化项目 |
- 新建
webpack.config.js
- 文件为webpack打包配置文件
- 配置文件说明
- webpack.config.js
1 | const path = require('path') |
热加载
webpack --watch
webpack-dev-serve
node server.js
1 | const express = require('express') |
HMR 动态替换代码不刷新的时候需要
1 | if(module.hot) { |
babel
- 安装
babel-loader
新增webpack和babel的通信 - 安装
@babel/core
和@babel/preset-env
把es5转换为es6 - 安装
@babel/polyfill
并 主文件import '@babel/polyfill'
把没有的函数补充进去,但是会存在主文件打包出来很大,且会污染全局变量 - 之前用
import '@babel/polyfill'
- 之后用
1 | // import "core-js/stable" |
- 但是都可以,但是一定要配
corejs:3
1 | { |
- 如果是打包类库就不能这样配置babel
npm i @babel/runtime -S
babel-loader
里面要配置options
1 | "plugins": [["@babel/plugin-transform-runtime",{ |
如果配置了
corejs:2
,需要安装npm i @babel/runtime-corejs2 -S
且不需要在主要的js里面引用import '@babel/polyfill'
等,原来的会污染全局环境,但是这个runtime会以闭包的形式注入js里如果要配置更多的babel的话,可以新建
.babelrc
把options里面的对象放到
babelrc
就可以了
1 | { |
code splitting
需要配置用插件进行代码分割
1
2
3
4
5optimization: {
splitChunks:{
chunks: 'all'
}
},安装
npm i babel-plugin-dynamic-import-webpack -D
来对异步import
进行支持在
.babelrc
里配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
// 配置babel使按需加载
"presets": [
["@babel/preset-env",{
// // 目标浏览器是否需要babel转义
"targets": {
"chrome": "67"
},
// 按需加载的配置,一定要配置corejs:3不然报错
"useBuiltIns": "usage",
"corejs": 3,
}],
"@babel/preset-react"
],
"plugins": ["dynamic-import-webpack"]
}代码分割和webpack无关
webpack中实现代码分割,两种方式:
- 同步代码:只需要在webpack.common.js中做optimization的配置
- 一部代码(import):异步代码,无需做任何配置,会自动进行代码分割,放置到新的文件中
splitChunksPlugin 配置
做代码分割的时候给打包出来的起名字
1
() => import(/* webpackChunkName:"lodash" */ 'lodash')
为了支持这种魔法注释,需要替换掉原来babel插件
npm i @babel/plugin-syntax-dynamic-import -D
1 | "plugins": ["@babel/plugin-syntax-dynamic-import"] |
- 配置
splitChunks
有很多配置可选择
lazy loading
- 懒加载是js提出的一个概念,和webpack的关系不大,webpack只不过可以识别这种
() => import()
语法,然后对这个模块进行代码分割,返回的是一个promise chunk
就是每个代码分割出来的js文件
preloading/prefetching
- 使用magic注释的方式使用,但是可能会有一些兼容问题,在性能优化当中,缓存不是最重要的,因为缓存只是第二次打开的时候才有效果,而应该是code-coverage代码覆盖
1
() => import(/* webpackPrefetch:true */ 'lodash')
css的代码分割
如果不是入口文件的js
则走的是 chunkFilename那个入口
1
2
3
4
5output: {
filename: '[name].js',
chunkFilename: '[name].chunk.js'
path: path.resolve(__dirname,'../dist')
}先安装
npm i mini-css-extract-plugin -S
,然后在webpack里面配置,详情请看文档因为不支持模块热更新,所以一般在线上打包上使用
线上打包环境里面使用插件的loader替换掉style-loader
注意和tree shaking的冲突
可以在plugins里面的mini-css-extract-plugin插件里面配置导出的css名称
安装
npm i optimize-css-asstes-webpack-plugin -D
去做css的压缩还可配置多页面引用的css分割,在cacheGroups里面配置
打包缓存
对导出的值使用hash去命名,如果不更改源码,就不会更改hash,则浏览器还是会使用缓存
1
2
3
4output: [
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js'
]在webpack配置以下代码,可以让vendors和main.js之间的关联代码单独打包到runtime里面,防止重新打包的时候更改main.js和 vendor.js的hash值改变
1
2
3
4
5optimization : {
runtimeChunk: {
name:'runtime'
}
}
shimming 即webpack的垫片
在下面如果模块使用了$会自动去引入jquery
1
2
3
4
5
6
7const webpack = require('webpack)
plugins:[
new webpack.ProvidePlugin({
$: 'jquery'
})
]imports-loader
,让this总是指向windows
pwa
安装
npm i workbox-webpack-plugin -D
并在配置里配置这个插件1
2
3
4
5
6
7
8{
plugins: [
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
})
]
}并在代码里面写相关代码,详情参考插件使用文档
typescript
- 安装
ts-loader
并在webpack里面配置 - 新建tsconfig.json里配置更多相关信息
devserver的proxy代理
- 详细转发规则看文档,注意
historyApiFallback
,类似于解决vue路由history模式nginx重定向的问题
eslint 规范项目代码
安装
npm i eslint -D
配置
npx eslint --lint
npx eslint src
检测src下面的代码如果要规范react规范则需要配置eslint的配置,详情查看文档
安装
npm i babel-eslint -D
这种方式麻烦,另一种简单的方式是在vscode上安装eslint插件,他会根据你的配置文件自动的去提示你
安装
npm i eslint-loader -D
,并在配置上使用eslint-loader,overlay:true
可弹出层报错,详情可看相关文档
webpack性能优化
- 跟上技术的迭代(Node,Npm,Yarn)
- 在尽可能少的模块上应用loader
- Plugin 尽可能精简并确保可靠
- resolve参数合理配置,配置resolve可以使引入的后缀省略,及自动查找相应的后缀,详情需要看文档,一般不要配很多
- 使用
dllplugin
提高打包速度,配置webpack.dll.js
,并安装npm i add-asset-html-webpack-plugin -S
配置这个插件加载到html新增变量,目标:第三方模块只打包一次,1.第三方模块只打包一次2.我们引入第三方模块的时候,要去使用dll文件引入。即配置new webpack.DllPlugin()
和new webpack.DllReferencePlugin()
,详情看文档 - 控制包文件大小
- thread-loader,parallel-webpack,happypack多进程打包
- 合理使用sourceMap
- 结合stats分析打包结果
- 开发环境内存编译
- 开发环境无用插件剔除
多页面打包配置
- 使用多个entry配置和
htmlwebpackplugin
配合使用
自己编写loaders
- 用例:用try catch包装所有function做异常捕获
- 根据全局变量,替换中英文等多语言的替换
- 安装
npm i loader-utils -D
- 异步代码里面要用 this.async
- 可以配置resolveLoaders去配置要去使用的loader的目录文件
- 详情可看相关文档
1
2
3
4
5
6
7
8
9// replaceLoaders.js
const loaderUtils = require('loader-utils')
module.exports = function(source) {
console.log(this.query)
const options = loaderUtils.getOptions(this)
const result = source.replace('dell',options.name)
this.callback(null,result)
}
自己编写plugin
loader是用来处理模块,plugin是某些具体时刻上使用的
编写plugin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32// CopyrightWebpackPlugin.js
class CopyrightWebpackPlugin {
constructor(options) {
// console.log('插件被使用了')
// console.log(options)
}
apply(compiler) {
// 同步就用下面的写法
// compiler.hooks.compile.tap("CopyrightWebpackPlugin",(compilation) => {
// 不需要手动执行cb了
// })
// 使用emit时刻,因为是异步所以用tapAsync
compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin',(compilation,cb) => {
console.log(compilation.assets)
// 新增一个txt文件
compilation.assets['copyright.txt'] = {
// 内容是什么
source: function () {
return 'copy right dell lee'
},
// 大概的长度大小
size: function() {
return 21
}
}
cb()
})
}
}
module.exports = CopyrightWebpackPlugin然后在webpack配置里面引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const path = require('path')
const CopyRightWebpackPlugin = require('./plugins/copyright-webpack-plugin')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
plugins: [new CopyRightWebpackPlugin()],
output: {
path: path.resolve(__dirname,'dist'),
filename: '[name].js'
}
}