我的前端之路笔记
cdn资源 cdn资源
webpack笔记
解决作用域问题 快速执行函数
;(function(){ ..... })
解决代码拆分问题 node commonjs 模块化
解决浏览器支持问题 requirejs
想要主js调用别的js要在主js前引入
hello.js export hello()
main.js hello()
import hello.js import main.js
安装webpack
先安装node
然后安装webpack webpack-cli 全局安装(不推荐,会锁定版本)
npm install webpack webpack-cli --global
本地安装
1npm init 2npm install webpack webpack-cli --save-dev 3
cmd cls清屏
webpack打包
webpack
webpack --stats detailed 查看详细打包信息
npx牛逼
配置入口文件(指令不如文件配置不可保存)
webpack --entry xxx
加 --mode production生产环境
配置出口
output
文件配置
webpack.config.js文件
绝对路径 使用 require('path')
1path.resolve(__dirname,'xxx') 2 3module.exports = { 4 entry:'', 5 6 output:{ 7 filename: '', 8 path: '结对路径'' 9 } 10 11 12}
自动引入资源
插件-html-webpack-plugin
npm install html-webpack-plugin
引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
在根{}下
1 2plugins:[ 3 new HtmlWebpackPlugin() 4] 5 6配置HtmlWebpackPlugin 7new HtmlWebpackPlugin({ 8 template: './index.html', 模板文件 9 filename: 'app.html', 生成文件名 10 inject: 'body' 在哪个标签引入 11}) 12
清理dist(清理旧的打包)
在output选项里面
1output:{ 2 filename: '', 3 path: '结对路径'', 4 clean: true 5 }
搭建开发环境
mode选项
定位错误
更好显示代码定位错误
devtool: 'inline-source-map',
监听代码变化
webpack --watch
使用 webpack-dev-server
npm install webpack-dev-server
加-D 在本地开发环境运行
在 配置文件中
1devServer: { 2 devServer: { 3 static: './dist' //注意这里的./dist是路径 4 } 5}
在控制台 webpack-dev-server
资源模块
1module: { 2 rules: [ 3 { 4 test: /\.png$/, 5 type: 'asset/resource' 6 } 7 ] 8 }
在js 文件中引入
1import imgsrc from './assets/img-1.png' 2 3const img = document.createElement('img') 创建一个照片元素 4img.src = imgsrc 添加路径 5document.body.appendChild(img) 将照片添加进页面
webpack-dev-server --open 加--open 默认打开
在output中定义导出路径以及名字
1output: { 2 filename: 'bundle.js', 3 path: path.resolve(__dirname,'./dist'), 4 clean: true, 5 assetModuleFilename: 'images/test.png' 6 },
assetModuleFilename: 'images/[contenthash].png' [contenthash]可自动根据hash来生成文件名
assetModuleFilename: 'images/[contenthash][ext]' [contenthash]可自动根据hash来生成文件名以及扩展名
若在module rules generator配置 则generator高于output
inline配置资源 使图片变成base64资源
使图片变成base64资源
1test: /\.svg$/, 2type: 'asset/inline'
配置source
1test: /\.txt$/, 2type: 'asset/source
配置asset
1test: /\.jpg$/, 2type: 'asset'
自动选择url还是文件base64 一般小于8k会生成base64
可通过追加 parser 来控制
1test: /\.jpg$/, 2type: 'asset', 3parser: { 4 dataUrlCondition: { 5 maxSize: 4*1024 //默认大小4*1024 6 } 7}
loader使用
安装css-loader以及style-loader
执行
1npm install css-loader -D 2 3npm install style-loader -D
配置
1{ 2 test: /\.css$/, 3 use: ['style-loader','css-loader'] 4}
在index.js引入 import './style.css'
先执行css-loader再执行style-loader
安装配置less-loader css-loader
npm install less-loader less -D
配置
1{ 2 test: /\.(css|less)$/, 3 use: ['style-loader','css-loader','less-loader'] 4}
在index.js引入 import './style.less'
抽离和压缩css
安装插件
npm install mini-css-extract-plugin -D
在webpack.js引入
const MiniCssExtract = require('mini-css-extract-plugin')
在plugins中添加
new MiniCssExtract()
配置
1{ 2 test: /\.(css|less)$/, 3 use: [MiniCssExtract.loader','css-loader','less-loader'] 4}
更换style-loader为MiniCssExtract.loader
style-loader作用是将css连接到页面 而为了抽离改为MiniCssExtract.loader
自定义生成的文件名
new MiniCssExtract({ filename: 'styles/[contenthash].css' })
压缩
安装插件
npm install css-minimizer-webpack-plugin -D
引入
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
在优化配置中配置
webpack配置根级
1optimization: { 2 minimizer: [ 3 new CssMinimizerPlugin() 4 ] 5}
注意配置此项之后 代码压缩会失效 需要单独配置terser
且mode更换为生产环境
mode: 'production',
加载images图像
图片优先级
.block-bg{ background-image: url(./assets/webpack-logo.svg) !important; }
!important 使优先级最高
加载字体
配置webpack
1{ 2 test: /\.(woff|woff2|eot|ttf|otf)$/, 3 type: 'asset/resource' 4}
在css文件引入字体文件
1@font-face { 2 font-family: 'iconfont'; 3 src: url('./assets/iconfont.ttf'); 4} 5 6.icon{ 7 font-family: 'iconfont'; 8 font-size: 30px; 9}
在index.js引入字体
1const span = document.createElement('span') 2 3span.classList.add('icon') 4span.innerHTML = '' 5document.body.appendChild(span)
加载数据 csv-loader xml-loader
安装
npm install csv-loader xml-loader -D
配置
1{ 2 test: /\.(csv|tsv)$/, 3 usr: 'csv-loader' 4}, 5 6{ 7 test: /\.xml$/, 8 usr: 'xml-loader' 9}
引入数据在index.js
1import Data from './assets/data.xml' 2import Notes from './assets/data.csv'
xml转成js对象
csv转换为数组
自定义JSON的parser 例如toml yaml json5
安装
npm install toml yaml json5 -D
配置webpack
1const toml = require('toml') 2const yaml =require('yaml') 3const json5 = require('json5')
1{ 2 test: /\.toml$/, 3 type: 'json', 4 parser: { 5 parse: toml.parse 6 } 7}, 8 9{ 10 test: /\.yaml$/, 11 type: 'json', 12 parser: { 13 parse: yaml.parse 14 } 15}, 16 17{ 18 test: /\.json5$/, 19 type: 'json', 20 parser: { 21 parse: json5.parse 22 } 23}
使用文件
babel-loader
将es6转化为es5
babel-loader:在webpack解析es6 @babel/core:babel核心模块 @babel/preset-env:babel预定,一组babel插件的集合
安装
npm install -D babel-loader @babel/core @babel/preset-env
配置
{ test: /.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }
解决报错regeneratorRuntime
原因 babel生产用于兼容async/await
安装插件 @babel/runtime
npm install @babel/runtime -D
安装插件 @babel/plugin-transform-runtime
npm install @babel/plugin-transform-runtime -D
配置
1{ 2 test: /\.js$/, 3 exclude: /node_modules/, 4 use: { 5 loader: 'babel-loader', 6 options: { 7 presets: ['@babel/preset-env'], 8 plugins: [ 9 ['@babel/plugin-transform-runtime'] 10 ] 11 } 12 } 13} 14
分离代码
如果有多个入口文件
1entry: { 2 index: './src/index.js', 3 another: './src/another-module.js' 4}, 5 6出口的filename: '[name].bundle.js',
但是这样会导致重复打包
避免重复
方案一 共享
1entry: { 2 index: { 3 import: './src/index.js', 4 dependOn: 'shared' 5 }, 6 another: { 7 import: './src/another-module.js', 8 dependOn: 'shared' 9 }, 10 11 shared: 'lodash' 12}, 13
方案二 配置splitChunks
optimization: { splitChunks: { chunks: 'all' } }
动态(异步)导入
如下
1function getComponent() { 2 return import('lodash') 3 .then(({default: _})=>{ 4 const element = document.createElement('div') 5 element.innerHTML = _.join(['hello','webpack'],' ') 6 return element 7 }) 8} 9 10getComponent().then((element)=>{ 11 document.body.appendChild(element) 12}) 13 14 15const button = document.createElement('button') 16
懒加载
1button.textContent = '点击加法运算' 2button.addEventListener('click',()=>{ 3 import(/* webpackChunkName: 'math' */'./math').then(({add})=>{ 4 console.log(add(4,5)) 5 }) 6}) 7 8document.body.appendChild(button)
import(/* webpackChunkName: 'math' */'./math')魔法注释 可以设置打包文件名
预加载预获取
prefetch 浏览器空闲时加载
import(/* webpackPrefetch: true */
preload 类似懒加载
import(/* webpackPreload: true */
缓存
输出文件名
filename: '[name].[contenthash].js',
缓存第三方库
1optimization: { 2 minimizer: [ 3 new CssMinimizerPlugin() 4 ], 5 6 splitChunks: { 7 cacheGroups: { 8 vendor: { 9 test: /[\\/]node_modules[\\/]/, 10 name: 'vendors', 11 chunks: 'all' 12 } 13 } 14 } 15} 16
js放到一个文件夹
output: { filename: 'scripts/[name].[contenthash].js', ....
开发配置
公共路径
在output中 加入publicPath: 'http://localhost:8080/'
环境变量
module.exports = (env) => { console.log(env) return { webpack配置项 可通过env参数配置 } }
比如
mode: env.production ? 'production' :'development'
webpack --env production
可以传参 a = 1
压缩代码 使用terser-webpack-plugin -D
npm install terser-webpack-plugin -D
使用
1optimization: { 2 minimizer: [ 3 new CssMinimizerPlugin(), 4 new TerserPlugin() 5 ], 6...
拆分配置文件
开发环境和生产环境
开发环境
项目根目录新建webpack.config.dev.js 开发环境
修改mode为开发环境
去掉压缩代码以及公共路径或包括缓存
启动
webpack -c ./config/webpack.config.dev.js
-c可用 -config替换
注意生成的文件的路径
生产环境
在config目录下新建 webpack.config.prod.js文件
修改mode为生产环境
删除调试 devtool dev-server
启动
webpack -c ./config/webpack.config.prod.js
额外webpack serve (webpack-dev-server)
可通过 webpack serve -c ./config/webpack.config.dev.js
npm 脚本
在项目根目录下 package.json
1{ 2 "scripts": { 3 "start": "npx webpack server -c ./config/webpack.config.dev.js", 4 "build": "npx webpack -c ./config/webpack.config.prod.js" 5 } 6}
忽略性能优化提示
在webpack配置根{}下
performance: { hints: false }
提取公共配置
项目根目录创建webpack.config.common.js文件
去除掉 dev prod中相同配置
合并配置文件 使用webpack-merge
安装
npm install webpack-merge -D
config目录下创建 wenpacj.config.js
1const { merge } = require('webpack-merge') 2 3const commonConfig = require('./webpack.config.common') 4const productionConfig = require('./webpack.config.prod') 5const developmentConfig = require('./webpack.config.dev') 6 7module.exports = (env) => { 8 switch (true) { //可定义key-value判断 9 case env.development: 10 return merge(commonConfig,developmentConfig) 11 12 case env.production: 13 return merge(commonConfig,productionConfig) 14 15 default: 16 return new Error('No matching configuration was found') 17 } 18} 19 20
source-map
新建目录 npm init 初始化
安装 npm install webpack webpack-cli webpack-dev-server html-webpack-plugin -D
默认devtool为eval
'source-map'
会生产main.js.map 且生产的main.js注释里会显示sourceUrl main.js.map(显示行列) 且关联 能找到代码问题
'hidden-source-map'
会生产main.js.map 且生产的main.js注释里不会显示sourceUrl main.js.map 且不关联 不能直接找到代码问题
'inline-source-map'
不会生产main.js.map 但生产的main.js注释里会显示sourceUrl main.js.map 且关联 能找到代码问题
'eval-source-map'
不会生成sourcemap文件 而是放到了eval后面 能找到代码问题
'cheap-source-map'
生成map文件 mappings带有行数不带列 能找到代码问题
'cheap-module-source-map' 推荐开发环境
生成map文件 mappings带有行数不带列 带有module的 能找到代码问题
webpack-server 配置
1devServer: { 2 static: path.resolve(__dirname, './dist'), 3 compress: true, //代码压缩 增加gzip 4 port: 3000, //端口号 5 host: '0.0.0.0', //局域网下可访问 6 7 headers: { 8 'X-Access-Token': 'abc123' 9 }, 10 11 proxy: { //代理配置 12 '/api': 'http://localhost:9000' 13 }, 14 15 // https: true, //开启https 16 // { 17 // cacert: './server/pem', 18 // pfx: './server.pfx', 19 // key: './server.key', 20 // cert: './server.crt', 21 // passphrase: 'webpack-dev-server', 22 // requestCert: true 23 // } 24 25 26 27 http2: true, //开启 http2 https默认自签名 28 29 historyApiFallback: true //历史路径 30 31}
模块热替换和热加载
热替换
hmr在webpack5不需要再繁琐配置 疫情开箱即用
1devServer: { 2 hot: true 3}
修改js热更新
在app.js
1if(module.hot){ 2 module.hot.accept('./input.js', () => { 3 4 }) 5}
热加载
1devServer: { 2 liveReload: true 3}
代码规范 eslint
安装
npm i eslint -D
eslint ./src
项目使用
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2 3module.exports = { 4 mode: 'development', 5 6 entry: './src/app.js', 7 8 module: { 9 rules: [ 10 { 11 test: /\.js$/, 12 usr: ['babel-loader','eslint-loader'] //先eslint-loader' 13 }, 14 ], 15 }, 16 17 plugins: [ 18 new HtmlWebpackPlugin() 19 ] 20}; 21 22
开启后可关闭报错
1devServer: { 2 client: { 3 overlay: false //报错覆盖层 4 } 5} 6
Githooks--Husky
目的 提交之前检测代码
基本原理
.git/hooks/pre-commit文件
文件内容
1eslint ./src 2或者 3npx eslint ./src
自定义
新建目录 .mygithooks
文件 .mygithooks/pre-commit 内容一样
git 配置
git config core.hooksPath .mygithoosk
Husky
1npm husky install -D 2 3huxky install 4 5 6package.json 7 "main": "index.js", 8 "scripts": { 9 "prepare": "husky install" 10 },
在./husky目录下 新增pre-commit文件
记得给予 pre-commit 文件权限 (+x)
写入 npx eslint .src
执行
git add.
git commit -m 'xxx'
如果代码出错会提示
探索webpack原理
解析绝对目录
别名配置
用@ 指向 src
webpack.js
1resolve: { 2 alias: { 3 '@': path.resolve(__dirname,'./src') 4 } 5}
优先级配置 默认 js>json
配置
1resolve: { 2 alias: { 3 '@': path.resolve(__dirname,'./src') 4 }, 5 extensions: ['.json','.js','vue'] 6} 7
配置外部资源引入(链接引入)
方式一
wepack配置文件
1externals: { 2 jquery: 'jQuery' 3}
在html模板文件里面加入
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
方式二
wepack配置文件
1externalsType: 'script', //暴露为script标签 2externals: { 3 jquery: [ 4 'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js', //链接 5 '$' //暴露标签 6 ] 7}
依赖图
安装
npm i webpack-bundle-analyzer -D
引入
1const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') 2 3plugins: [ 4 new BundleAnalyzerPlugin() 5 ]
启动webpack serve就会弹出
PostCSS和CSS模块 处理浏览器css兼容问题
安装
npm i postcss-loader -D
npm i autoprefixer -D
配置 webpack
1module: { 2 rules: [ 3 { 4 test: /\.css$/, 5 use: ['style-loader', 'css-loader', 'postcss-loader'] 6 } 7 ] 8 }
配置 postcss.config.js
在根目录下创建文件
1module.exports = { 2 plugins: [ 3 require('autoprefixer') 4 ] 5} 6
使用
在package.json目录下 根{}
1"browserslist": [ 2 "> 1%", //全球浏览器使用率要大于1% 3 "last 2 versions" //每个浏览器的最近两个版本 4 ]
插件 postcss-nested
支持比如 body下包括div的 这种
安装
npm i postcss-nested -D
配置postcss.config.js
1module.exports = { 2 plugins: [ 3 require('autoprefixer'), 4 require('postcss-nested') 5 ] 6}
开启css模块化
1use: ['style-loader', 2 { 3 loader: 'css-loader', 4 options: { 5 modules: true //开启css模块化 6 } 7 } 8, 'postcss-loader'], 9exclude: [path.resolve(__dirname,'..','node_modules')] //排除外部modules
可设置两个配置 一个全局一个局部
如下 在webpack配置
全局配置
1{ 2 test: new RegExp(`^(?!.*\\.global).*\\css`), 3 use: ['style-loader', 4 { 5 loader: 'css-loader', 6 options: { 7 modules: true //开启css模块化 8 } 9 } 10 , 'postcss-loader'], 11 exclude: [path.resolve(__dirname,'..','node_modules')] //排除外部modules 12}
局部配置
1{ 2 test: new RegExp(`^(.*\\.global).*\\css`), 3 use: [ 4 { 5 loader: 'style-loader' 6 }, 7 { 8 loader: 'css-loader' 9 }, 10 { 11 loader: 'postcss-loader' 12 } 13 ], 14 exclude: [path.resolve(__dirname,'..','node_modules')] //排除外部modules 15}
WebWorks
创建一个worker const worker = new Worker(new URL('./work.js',import.meta.url))
接收主线程信息 self.onmessage = () => {
}
主线程接收信息 worker.onmessage = (message) => { console.log(message) }
向主线程发送信息 self.postMessage({ answer: 1111 })
主线程发送信息 worker.postMessage({ question: 'hi,那边的worker线程,请告诉我今天的幸运数字是多少?' })
集成typescript
安装
npm i typescript ts-loader -D
配置 webpack
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const path = require('path') 3module.exports = { 4 mode: 'development', 5 entry: './src/app.ts', 6 devtool: 'inline-source-map', 7 module: { 8 rules: [ 9 { 10 test: /\.ts$/, 11 use: 'ts-loader', 12 exclude: /node_modules/ 13 } 14 ] 15 }, 16 17 18 resolve: { 19 extensions: ['.ts', '.js'] //设置优先ts扩展名 20 }, 21 22 output: { 23 filename: 'bundle.js', 24 path: path.resolve(__dirname, './dist') 25 }, 26 27 plugins: [ 28 new HtmlWebpackPlugin() 29 ] 30 31}
初始化ts配置文件
tsc --init
修改ts配置
rootDir: "./src"
outDir: "./dist"
ts 使用模块
网址 https://www.typescriptlang.org/dt/search?search=
查询需求模块安装
entry 配置
配置一
1entry: [ 2 './src/app.js', 3 './src/app2.js' 4 ]
配置二
1// entry: [ 2 // './src/app.js', 3 // './src/app2.js', 4 // 'lodash' 5 // ], 6 7 entry: { 8 main: ['./src/app2.js', './src/app.js'], 9 lodash: 'lodash' 10 },
配置三
1entry: { 2 main: { 3 import: ['./src/app2.js', './src/app.js'], 4 dependOn: 'lodash' //依赖 5 }, 6 lodash: 'lodash' 7 },
index.html模板配置
配置一 基础配置
webpack
1 plugins: [ 2 new HtmlWebpackPlugin({ 3 title: '多页面应用', //参数 4 template: './index.html', 5 inject: 'body', //引入js的地方 6 chunks: ['main'] //规定引入的js 7 }) 8 ]
1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <title><%= htmlWebpackPlugin.options.title %></title> 6</head> 7<body> 8 9</body> 10</html>
配置二
多页面配置
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2 3module.exports = { 4 mode: 'development', 5 6 // entry: [ 7 // './src/app.js', 8 // './src/app2.js', 9 // 'lodash' 10 // ], 11 12 entry: { 13 main: { 14 import: ['./src/app2.js', './src/app.js'], 15 dependOn: 'lodash', 16 filename: 'chanel1/[name].js' 17 }, 18 main2: { 19 import: './src/app3.js', 20 dependOn: 'lodash', 21 filename: 'chanel2/[name].js' 22 }, 23 lodash: { 24 import: 'lodash', 25 filename: 'common/[name].js' 26 } 27 }, 28 29 output: { 30 clean: true 31 }, 32 33 34 plugins: [ 35 new HtmlWebpackPlugin({ 36 title: '多页面应用', 37 template: './index.html', 38 inject: 'body', 39 filename: 'chanel1/index.html', 40 chunks: ['main', 'lodash'], 41 publicPath: 'http://www.b.com' 42 }), 43 44 new HtmlWebpackPlugin({ 45 template: './index2.html', 46 inject: 'body', 47 filename: 'chanel2/index2.html', 48 chunks: ['main2', 'lodash'], 49 publicPath: 'http://www.a.com' 50 }) 51 ] 52 53 54} 55
Tree Shaking
移除未使用模块
配置一 usedExports
es2015特性
但是无法额外模块
配置webpack
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2 3module.exports = { 4 mode: 'production', 5 entry: './src/app.js', 6 plugins: [ 7 new HtmlWebpackPlugin() 8 ], 9 10 optimization: { 11 usedExports :true //此处开启 12 } 13} 14
配置二 sideEffects
在packages.json配置
1{ 2 "sideEffects": true, //true都加载 false都不加载 3 "sideEffects": ["*.css"], //对于所有的css文件都加载,其它不加载 4 "sideEffects": ["*.css", "*.global.js"],//对于所有的css文件以及.global.js文件都加载,其它不加载 5}
离线环境下运行
非离线环境下运行
打包完成
安装 http-server
npm i http-server -D
配置 packages.json
1"scripts": { 2 "start": "http-server dist" 3 },
使webpack serve 运行时变动打包而不是内存
webpack 配置
1devServer: { 2 devMiddleware: { 3 writeToDisk: true 4 } 5 }
添加workbox 实现pwa
安装
npm i workbox-webpack-plugin -D
配置
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const WorkboxPlugin = require('workbox-webpack-plugin') 3 4module.exports = { 5 mode: 'production', 6 entry: './src/app.js', 7 plugins: [ 8 new HtmlWebpackPlugin(), 9 new WorkboxPlugin.GenerateSW({ 10 clientsClaim: true, 11 skipWaiting: true 12 }) 13 ], 14
浏览器注册
入口文件 app.js
1if ('serviceWorker' in navigator) { //浏览器是否支持 2 window.addEventListener('load', () => { 3 navigator.serviceWorker.register('/service-worker.js') 4 .then(registration => { 5 console.log("SW 注册成功") 6 console.log(registration) 7 }) 8 .catch(registrationError => { 9 console.log("SW 注册失败", registrationError) 10 }) 11 }) 12}
shimming 全局变量
webpack配置
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const webpack = require('webpack') 3 4module.exports = { 5 mode: 'development', 6 entry: './src/index.js', 7 plugins:[ 8 new HtmlWebpackPlugin(), 9 new webpack.ProvidePlugin({ 10 _: 'lodash' 11 }) 12 ] 13} 14
使用 index.js
1// import _ from 'lodash' //无需引入 2 3console.log(_.join(['hello', 'webpack'], ' ')) 4
细颗粒度 shimming
this问题 imports-loader
安装
npm i imports-loader -D
配置webpack
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const webpack = require('webpack') 3 4module.exports = { 5 mode: 'development', 6 entry: './src/index.js', 7 plugins:[ 8 new HtmlWebpackPlugin(), 9 new webpack.ProvidePlugin({ 10 _: 'lodash' 11 }) 12 ], 13 module: { 14 rules: [ 15 { 16 test: require.resolve('./src/index.js'), 17 use: 'imports-loader?wrapper=window' //让包里的this指向window 18 } 19 ] 20 } 21} 22
全局exports
插件 exports-loader
npm i exports-loader -D
使用 webpack配置
1module: { 2 rules: [ 3 { 4 test: require.resolve('./src/index.js'), 5 use: 'imports-loader?wrapper=window' 6 }, 7 { 8 test: require.resolve('./src/global.js'), 9 use: 'exports-loader?type=commonjs&exports=file,multiple|helpers.parse|parse' //相当于暴露parse:helper.parse 10 } 11 ] 12 }
polyfills 垫片
简单原理
不能这样引入
安装 @babel/polyfill
npm i @babel/polyfill -D
1import '@babel/polyfill' //垫片 这样导入 X 2 3console.log(Array.from([1, 2, 3], x => x + x))
进一步优化
安装babel环境
npm i babel-loader @babel/core @babel/preset-env -D
配置webpack
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2 3module.exports = { 4 mode: 'development', 5 entry: './src/index.js', 6 7 module: { 8 rules: [ 9 { 10 test: /\.js$/, 11 exclude: /node_modules/, 12 use: { 13 loader: 'babel-loader', 14 options: { 15 presets: [ 16 [ 17 '@babel/preset-env', 18 { 19 targets: [ 20 'last 1 version', //浏览器最新的一个版本 21 '> 1%' //代码使用超过1% 22 ], 23 useBuiltIns: 'usage', 24 corejs: 3 25 } 26 ] 27 ] 28 } 29 } 30 } 31 ] 32 } 33 34} 35
额外安装
npm install --save core-js@3
library
打包配置为不同模块
1const path = require('path') 2 3module.exports = { 4 mode: 'production', 5 entry: './src/index.js', 6 experiments: { 7 outputModule:true // module时候开启此配置 8 }, 9 output: { 10 path: path.resolve(__dirname, 'dist'), 11 filename: 'mylib.js', 12 library: { 13 // name: 'mylib', // module时候取消此配置 14 type: 'module' // window/commonjs/module 15 } 16 } 17}
打包为通用模块
1const path = require('path') 2 3module.exports = { 4 mode: 'production', 5 entry: './src/index.js', 6 // experiments: { 7 // outputModule:true // module时候开启此配置 8 // }, 9 output: { 10 path: path.resolve(__dirname, 'dist'), 11 filename: 'mylib.js', 12 library: { 13 // name: 'mylib', // module时候取消此配置 14 type: 'umd' // window/commonjs/module/umd 15 }, 16 globalObject: 'globalThis' //全局this代替self 17 } 18} 19
构建小轮子
配置
1const path = require('path') 2 3module.exports = { 4 mode: 'production', 5 entry: './src/index.js', 6 output: { 7 path: path.resolve(__dirname, 'dist'), 8 filename: 'webpack-numbers.js', 9 library: { 10 name: 'webpackNumbers', 11 type: 'umd' 12 }, 13 globalObject: 'globalThis' 14 }, 15 externals: { //优化依赖 16 lodash: { 17 commonjs: 'lodash', 18 commonjs2: 'lodash', 19 amd: 'lodash', 20 root: '_' 21 } 22 } 23 24} 25
发布为 npm-package
执行
npm config get registry
确保为
如果不是 切换 npm set registry "https://registry.npmjs.org/"
npm adduser 添加用户
npm publish 发布
模块联邦 多项目共享模块
使用webpack 的 ModuleFederationPlugin
先准备好两个模块
模块nav
组件js Header.js
1const Header = () =>{ 2 const header = document.createElement('h1') 3 header.textContent = '公共头部内容' 4 return header 5} 6 7export default Header 8
webpack配置项
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const { ModuleFederationPlugin } = require('webpack').container 3 4 5module.exports = { 6 mode: 'production', 7 entry: './src/index.js', 8 plugins: [ 9 new HtmlWebpackPlugin(), 10 11 new ModuleFederationPlugin({ 12 name: 'nav', //模块名 13 filename: 'remoteEntry.js', //模块文件名 14 remotes: {}, //引入的模块 15 exposes: { //导出的模块 16 './Header': './src/Header.js' //模块路径 17 }, 18 shared: {} //共享包 19 }) 20 ] 21 22} 23
模块home引入 nav
webpack配置项
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const { ModuleFederationPlugin } = require('webpack').container 3 4module.exports = { 5 mode: 'production', 6 entry: './src/index.js', 7 plugins: [ 8 new HtmlWebpackPlugin(), 9 10 new ModuleFederationPlugin({ 11 name: 'home', //模块名 12 filename: 'remoteEntry.js', //模块文件名 13 remotes: { //引入的模块 14 nav: 'nav@http://localhost:3003/remoteEntry.js' //网络位置 15 }, 16 exposes: {}, //导出的模块 17 shared: {} //共享包 18 }) 19 ] 20} 21
使用nav下的Header
异步加载
1import HomeList from "./HomeList"; 2 3import('nav/Header').then((Header)=>{ 4 const body = document.createElement('div') 5 body.appendChild(Header.default()) 6 document.body.appendChild(body) 7 document.body.innerHTML += HomeList(5) 8}) 9 10 11
模块 search 引入两个资源
暴露 home的homeList组件
1new ModuleFederationPlugin({ 2 name: 'home', 3 filename: 'remoteEntry.js', 4 remotes: { 5 nav: 'nav@http://localhost:3003/remoteEntry.js' 6 }, 7 exposes: { 8 './HomeList': './src/HomeList.js' 9 }, 10 shared: {} 11 })
在webpack配置项引入
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const { ModuleFederationPlugin } = require('webpack').container 3 4module.exports = { 5 mode: 'production', 6 entry: './src/index.js', 7 plugins: [ 8 new HtmlWebpackPlugin(), 9 10 new ModuleFederationPlugin({ 11 name: 'search', 12 filename: 'remoteEntry.js', 13 remotes: { 14 nav: 'nav@http://localhost:3003/remoteEntry.js', 15 home: 'home@http://localhost:3001/remoteEntry.js' 16 } 17 }) 18 ] 19} 20 21
在 search 中引入 index.js
1Promise.all([import('nav/Header'),import('home/HomeList')]) 2 .then(([ 3 { 4 default: Header 5 }, 6 { 7 default: HomeList 8 } 9 ]) => { 10 document.body.appendChild(Header()) 11 document.body.innerHTML += HomeList(3) 12 })
Promise.all() 可执行多个异步
优化
使用最新版本
webpack 以及 nodejs最新版本
内置优化
将loader应用于最少数量的必要模块
解析必要的 提高打包速度
1{ 2 test: /\.js$/, 3 include: 'xxxxxx', 4 loader: 'xxx' 5}
能不用loader和plugin就不用 引导
解析
减少 resolve,modules,resolve.extensions,resolve.mainFiles,resolve.descriptionFiles中的条目数量 来减少系统文件调用次数
如果 不使用 symlinks 设置resolve.symlinks: false
如果自定义resolve plugin规则 并且没有指定 context,可以设置resolve.cacheWithContext:false
小即快
使用更少或者更小的library
在多页面应用使用splitChunksPlugin 并且开启async
移除未引用代码
只编译当前正在开发的代码
持久化缓存
在webpack配置中使用cache选项 使用package.json中的 "postinstall" 清除缓存目录
将cache类型设置为内存或者文件系统 memory 选项很简单 它告诉webpack在内存中存储缓存
cache: { type: 'memory' }
自定义plugin/loader
对它们概要分析 以免在此处引入性能问题
权衡progress plugin的利弊
通用构建优化 dll
把包生成dll
1const path = require('path') 2const webpack = require('webpack') 3 4module.exports = { 5 mode: 'production', 6 entry: { 7 jquery: ['jquery'] 8 }, 9 output: { 10 filename: '[name].js', 11 path: path.resolve(__dirname, 'dll'), 12 library: '[name]_[hash]' 13 }, 14 plugins: [ 15 new webpack.DllPlugin({ 16 name: '[name]_[hash]', 17 path: path.resolve(__dirname, 'dll/manifest.json') 18 }) 19 ] 20} 21
引入
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const webpack = require('webpack') 3const path = require('path') 4 5module.exports = { 6 mode: 'production', 7 entry: './src/index.js', 8 plugins: [ 9 new HtmlWebpackPlugin(), 10 new webpack.DllReferencePlugin({ 11 manifest: path.resolve(__dirname, './dll/manifest.json') 12 }) 13 ] 14} 15
此时并不能使用
额外配置
1const HtmlWebpackPlugin = require('html-webpack-plugin') 2const webpack = require('webpack') 3const path = require('path') 4const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin') 5 6module.exports = { 7 mode: 'production', 8 entry: './src/index.js', 9 plugins: [ 10 new HtmlWebpackPlugin(), 11 new webpack.DllReferencePlugin({ 12 manifest: path.resolve(__dirname, './dll/manifest.json') 13 }) 14 , 15 new AddAssetHtmlPlugin({ 16 filePath: path.resolve(__dirname, './dll/jquery.js'), 17 publicPath: './' 18 }) 19 ] 20} 21
worker pool
注意 多个loader 从下到上运行
使用 thread-loader
用于非常耗时的loader
因为worker也会消耗资源
1// const HtmlWebpackPlugin = require('html-webpack-plugin') 2 3module.exports = { 4 mode: 'development', 5 entry: './src/index.js', 6 7 module: { 8 rules:[ 9 { 10 test: /\.js$/, 11 exclude: /node_modules/, 12 use: [ 13 { 14 loader: 'babel-loader', 15 options:{ 16 presets: ['@babel/preset-env'] 17 } 18 }, 19 { 20 loader: 'thread-loader', 21 options: { 22 workers: 2 23 } 24 } 25 ] 26 } 27 ] 28 } 29 30 31} 32 33
开发环境提升构建性能
使用webpack的 watch mode
监听过多导致的cpu负载
可用watchOptions.poll来增加轮询的时间间隔
在内存中编译
webpack-dev-server
webpack-hot-middleware
webpack-dev-middleware
stats.toJson加速
devtool
eval性能最好 但无法转译
cheap-source-map 稍差的map 但性能不错
eval-source-map 增量编译
多数情况为 eval-cheap-module-source-map
避免使用生产环境的工具
比如
TerserPlugin 压缩和混淆
[fullhash]/[chunkhasn]/[contenthash]
AggressiveSplittingPlugin
AggressiveMergingPlugin
ModuleConcatenationPlugin
最小化 entry chunk
optimization: { runtimeChunk: true }
避免额外优化步骤
1optimization: { 2 removeAvailableModules: false, 3 removeEmptyChunks: false, 4 splitChunks: false 5}
输出结果不要带路径信息
1output: { 2 pathinfo: false 3}
nodejs版本
v8.9.10-v9.11.1存在性能回退
ts-loader
加
1use: [ 2 { 3 loader: 'ts-loader', 4 options: { 5 transpileOnly: true 6 } 7 } 8]
生产环境提升构建性能
不启用 SourceMap
