webpack5学习笔记

我的前端之路笔记

cdn资源 cdn资源

webpack官网

webpack插件

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

确保为

"https://registry.npmjs.org/"

如果不是 切换 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

点赞
收藏

评论区

加载中...

相关推荐

React系列-轻松学会Hooks(中)

我的前端学习笔记📒最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读我的前端学习笔记📒(https://www.yuque.com/wanggangfeng

写C端,如何优雅的处理多个弹框的显示?(附带源码)

我的前端学习笔记📒最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读我的前端学习笔记📒(https://www.yuque.com/wanggangfeng

性能优化之防抖和节流

我的前端学习笔记📒最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读我的前端学习笔记📒(https://www.yuque.com/wanggangfeng

React系列-自定义Hooks很简单(下)

我的前端学习笔记📒最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读我的前端学习笔记📒(https://www.yuque.com/wanggangfeng

Redux系列之分析中间件原理(附经验分享)

我的前端学习笔记📒最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读我的前端学习笔记📒(https://www.yuque.com/wanggangfeng

JavaScript进阶之实现拖拽(上)

我的前端学习笔记📒最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读我的前端学习笔记📒(https://www.yuque.com/wanggangfeng