Webpack「v5.0」
概念 | webpack 中文文档 (docschina.org)
webpack是一个现代JavaScript应用程序的静态模块打包工具。当webpack处理应用程序时,它会在内部构建一个依赖图(dependency [/dɪˈpendənsi/] graph [/ɡræf/]),此依赖图会映射项目所需的每个模块,并生成一个或多个bundle /ˈbʌndl/ 包!
0. 预备知识
0.1 为啥要学webpack?
0.1.1 性能优化
见性能优化
之前我们在讲前端的时候,我们可以从以下几个方面,去提高页面第一次渲染的速度,以及运行时的性能:
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| /* 第一类:明显改善页面第一次加载的速度,减少白屏等待的时间 @1 使用骨架屏技术 + 服务器骨架屏:页面首屏内容是基于服务器渲染的 + 前端骨架屏:在真实内容渲染出来之前,我们给予相关位置用“色框”占位!(知乎?) @2 减少HTTP请求的次数和大小 + CSS/JS都要做合并压缩「尽可能合并为一个CSS和一个JS」 + 图片的合并压缩,例如:雪碧图(CSS Sprit) + 使用图片BASE64来加快图片的渲染! + 图片一定要做延迟加载 + 使用字体图片和矢量图,来代替位图!! + 音视频资源也要设置延迟加载「preload='none'」 + 数据的异步加载 + 开启服务器端的GZIP压缩,可以让访问的资源压缩60%+ @3 渲染页面中的优化 + 因为<script>会阻碍GUI渲染,所以:把其放在页面末尾,或者设置async/defer! + 因为<link>是异步加载CSS资源,所以建议放在页面开始,尽快获取样式资源!! + 不要使用import导入CSS,因为其会阻碍GUI的渲染! + 样式代码较少的情况下,使用<style>内嵌式,尤其是移动端开发! @4 在网络传输中的优化 + 减少cookie的内容大小「因为只要向服务器发送请求,不论服务器是否需要,总会在请求头中把cookie传递给服务器,如果cookie比较大,会让所有的请求都变慢」 + 资源分服务器部署「例如:web服务器、图片服务器、数据服务器」,这样可以降低服务器压力,提高服务器的并发,让资源能更加合理的被利用;但是也会导致DNS解析次数增加,此时可以基于DNS Prefetch来优化DNS解析!! + 基于HTTP/2.0来代替HTTP/1.1 + 基于新的二进制格式,让传输的内容更加丰富健壮 + header压缩,让传输速度更快(没改就不传) + 服务端自动推送,可以减少HTTP请求次数 + 多路复用,让传输速度更快,避免线头阻塞(UDP) + 基于Connection:keep-alive保持TCP通道长链接「HTTP/1.1版本会自动开启」 + 开启CDN「地域分布式服务器部署」 + 对于静态资源文件采取“强缓存和协商缓存”,对于不经常更新的数据请求,设置数据缓存! 第二类:提高页面运行时候的性能 @1 减少循环嵌套,降低时间复杂度;避免出现死循环/死递归; @2 使用事件委托来优化事件绑定,性能可以提高40%+ @3 减少DOM的回流(重排)和重绘 + 样式分离读写「渲染队列机制」 + 批量新增元素「字符串拼接、文档碎片」 + 基于transform/opacity等方式操作样式 + 把需要频繁修改样式的元素(例如:JS实现动画)脱离文档流 + ... @4 使用函数的防抖和节流处理高频触发操作 @5 合理使用闭包,避免内存泄漏,以及手动释放无用的内存! @6 减少页面冗余代码,提高代码的重复使用率「也就是尽可能的做封装处理」 @7 尽可能不要使用for/in循环,因为其消耗性能 @8 避免使用eval/with等消耗性能的代码 @9 能用CSS3处理的动画坚决不用JS「JS动画尽可能基于requestAnimationFrame来代替定时器」 @10 避免使用CSS表达式,因为其非常消耗性能 @11 CSS选择器的前缀不要过长「因为CSS选择器渲染是从右到左」 @12 减少table布局「table的渲染是比较消耗性能的」 ...... */
|
我们
0.1.2为了兼容、预编译、开发预览
平时开发的时候,一般会使用less/sass/stylus等CSS预编译器「需要基于vscode插件把其编译为CSS」;
会直接使用ES6的语法「需要基于babel把其转换为ES5,以此兼容IE」;
会给CSS3样式加很多的前缀,以此来处理兼容问题……
我们还会基于vscode中的liveserver插件,创建一个本地web服务器,来预览我们的项目…..
我们还要基于各种方案,来解决跨域访问的问题……
0.2 webpack可以帮我们干的事情
- 代码转换:TypeScript编译成JavaScript、LESS/SCSS编译成CSS、ES6/7编译为ES5、虚拟DOM编译为真实的DOM等等…
- 文件优化:压缩JS、CSS、HTML代码,压缩合并图片,图片BASE64等
- 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码等
- 模块合并:把模块分类合并成一个文件
- 自动刷新:创建本地Web服务器,监听本地源代码的变化,自动重新构建,刷新浏览器
- 代码校验:Eslint代码规范校验和检测、单元测试等
- 自动发布:自动构建出线上发布代码并传输给发布系统
- 跨域处理
- ……
除了webpack外的其他:
- grunt(淘汰)
- gulp(少了、老了)
- Parcel
- vite(底层原理是rollup,比webpack快,但兼容不好)
- rollup
- Turbopack(据说比webpack快700倍,比vite快10倍)
- ……
webpack问题
1.webpack打包原理
2.webpack的基础操作
安装
为防止全局安装webpack导致版本冲突,真实项目中以本地安装为主
弊端:本地安装就需要配置webpack命令
npm比较慢建议使用yarn、pnmp
1 2 3 4
| $ npm init -y $ npm install webpack webpack-cli --save-dev OR $ yarn add webpack webpack-cli -D
|
零配置使用
1 2 3 4 5 6
| /* * 默认会打包SRC目录中的JS文件(入口默认index.js) * 打包完成的目录默认是DIST/MAIN.JS * webpack默认支持CommonJS和ES6 Module的模块规范,依此进行依赖打包 */ $ npx webpack
|
自定义基础配置
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const path = require('path');
module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'bundle.[hash].js', path: path.resolve(__dirname, 'dist') } };
|
html-webpack-plugin
https://www.webpackjs.com/plugins/html-webpack-plugin/
$ yarn add html-webpack-plugin -D
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { ... plugins: [ new HtmlWebpackPlugin({ template: './public/index.html', filename: 'index.html', minify: true }) ] }
|
多入口 & 多出口
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
| const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlPlugins = ['index', 'login'].map(chunk => { return new HtmlWebpackPlugin({ template: `./public/${chunk}.html`, filename: `${chunk}.html`, chunks: [chunk], minify: true }); }); module.exports = { mode: 'production', entry: { index: "./src/index.js", login: "./src/login.js", }, output: { filename: "[name].[hash].js", path: path.resolve(__dirname, "dist") }, plugins: [ ...htmlPlugins ] };
|
clean-webpack-plugin
$ yarn add clean-webpack-plugin -D
1 2 3 4 5 6 7
| const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { ... plugins: [ new CleanWebpackPlugin() ] };
|
webpack-dev-server
https://webpack.js.org/configuration/dev-server/
$ yarn add webpack-dev-server -D
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 33 34 35 36 37 38 39 40 41 42 43 44
| "scripts": { "start": "webpack server", "build": "webpack" }
module.exports = { ... devServer: { host: '127.0.0.1', port: 3000, compress: true, open: true, hot: true, proxy: { "/jian": { target: "https://www.jianshu.com/asimov", changeOrigin: true, ws: true, pathRewrite: { "^/jian": "" } }, "/zhi": { target: "https://news-at.zhihu.com/api/4", changeOrigin: true, ws: true, pathRewrite: { "^/zhi": "" } } } } };
|
处理样式的loader「加载器」
$ yarn add css-loader style-loader less less-loader autoprefixer postcss-loader -D
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| module.exports = { ... module: { rules: [{ test: /\.(css|less)$/, use: [ "style-loader", "css-loader", "postcss-loader", "less-loader" ] }] } };
|
postcss.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| module.exports = { plugins: [ require('autoprefixer') ] };
{ loader: "postcss-loader", options: { postcssOptions: { plugins: [ require('autoprefixer') ] } } },
|
package.json OR .browserslistrc
1 2 3 4 5 6
| "browserslist": [ "> 1%", "last 2 versions", "not dead" ]
|
https://www.npmjs.com/package/mini-css-extract-plugin
$ yarn add mini-css-extract-plugin -D
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { ... plugins: [ ... new MiniCssExtractPlugin({ filename: 'main.[hash].css' }) ], ... module: { rules: [{ test: /\.(css|less)$/, use: [ MiniCssExtractPlugin.loader, ... ] }] } };
|
基于babel实现ES6的转换
$ yarn add babel babel-loader @babel/preset-env @babel/core -D
$ yarn add @babel/polyfill
1 2 3 4 5 6 7 8 9 10 11 12
| module.exports = { ... module: { rules: [{ test: /\.js$/, use: ['babel-loader'], include: path.resolve(__dirname, 'src'), exclude: /node_modules/ }] } };
|
babel.config.js
1 2 3 4 5
| module.exports = { presets: [ "@babel/preset-env" ] };
|
index.js
1
| import '@babel/polyfill';
|
设置优化项:压缩CSS/JS
$ yarn add css-minimizer-webpack-plugin terser-webpack-plugin -D
1 2 3 4 5 6 7 8 9 10 11
| const TerserPlugin = require('terser-webpack-plugin'); const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin'); module.exports = { optimization: { minimizer: [ new CssMinimizerWebpackPlugin(), new TerserPlugin() ] } };
|
设置解析器:配置别名
1 2 3 4 5 6 7
| module.exports = { resolve: { alias: { '@': path.resolve(__dirname, './src') } } };
|
图片的处理
$ yarn add file-loader url-loader -D
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
| module.exports = { ... module: { rules: [ ... { test: /\.(png|jpe?g|gif)$/i, type: 'javascript/auto', use: [{ loader: 'url-loader', options: { limit: 200 * 1024, esModule: false, name: 'images/[name].[hash].[ext]' } }] }] }, performance: { maxAssetSize: 100 * 1024 * 1024 * 1024, maxEntrypointSize: 100 * 1024 * 1024 * 1024 } };
|