第三章:webpack进阶用法
https://github.com/cpselvis/geektime-webpack-course
1. 自动清理构建目录产物
在之前的示例中,每次构建之后,会在 dist 目录下新增文件,长期这样之后,会积攒许多的无用文件。
可以通过手动删除构建目录
通过 npm scripts 删除构建目录(不够优雅)
rm -rf ./dist && webpack
rimraf ./dist && webpack
使用 clean-webpack-plugin
该插件默认会删除 output 指定的输出目录
安装依赖: npm i clean-webpack-plugin -D
配置 webpack.config.js:
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
..
module.exports = {
plugins: [
new CleanWebpackPlugin()
]
}2. 自动补全 CSS3 前缀
浏览器渲染引擎(内核) 划分: 参考资料
浏览器
内核
前缀
IE
Trident
-ms
Firefox
Geko
-moz
Chrome
webkit
-webkit
Opera
Presto
-o
使用 autoprefixer 插件,可以给某些属性(如 display: flex) 添加相应的浏览器前缀,使得浏览器支持某些属性。
安装依赖: npm i postcss-loader autoprefixer -D
配置 webpack.config.js
3. 移动端 CSS px 转 rem
安装依赖: npm i px2rem-loader -D
配置 webpack.config.js
此时,就能把所有引入的 css 中的像素值,转换为 rem 的值
注意,在测试的过程中,发现好像不能解析 less 语法,会报代码错误,有机会可以再次测试
此刻在 html 页面中引入 淘宝 flexible库 即可实现,移动端自适应问题。因为 屏幕大小变化时, flexible 库使得 rem 的值发生变化,而我们的样式经过 px2rem-loader 之后,所有的尺寸单位都是 rem 了,自然会做自适应。
4. 静态资源内联
对于有一些资源,如埋点js,flexible js 等,我们希望这些 js 首先被加载,而不是夹杂在 bundle.js 中,从而更快的得到加载。
另外,如果资源内联 html 模板中,那么会跟随 html 请求一并回来,减少 http 请求数量.
html片段(如 meta 信息) 和 js 内联: 使用 raw-loader
raw-loader 实质上就是将文件内容读取出来,填充到 html 模板对应的位置。
css 内联 的两种方式:
将 css 的样式内联到 html 中:
style-loader
使用 html-inline-css-webpack-plugin (推荐)
将打包出来的 css 插入到 html 对应的位置。
安装依赖: npm i raw-loader@0.5.1 -D
在 html 模板中使用:
5. 通用的多页面应用打包方案
每一次页面跳转的时候,后台服务器都会返回一个新的 html 文档,这种叫做多页面应用。
思路就是每个页面对应一个 entry,打包出一个 js 文件。同时有一个 html-webpack-plugin 生成对应的页面。
示例:
于是我们可以约定如下的数据结构:
每个目录对应一个页面,目录中的 index.js 对应 entry, index.html 作为页面模板。
利用 glob.sync 读取目录: entry: glob.sync(path.join(__dirname, './src/*/index.js'))
安装依赖: npm i glob -D
6. source map
作用: 将压缩后的代码,通过 source map 定位到源代码。
开发环境开启,生产环境关闭,线上排查问题的时候可以将 source map 上传到错误监控系统。
webpack 中的 source map 类型:
类型
说明
eval
使用 eval 包括模块代码
source-map
产生 .map 文件
cheap
不包含列信息
inline
将 .map 作为 DataUrl 嵌入,不单独生成 .map 文件
module
包含 loader 的 source map
设置 webpack.config.js:
以上的测试在 mode 为 production 的时候不起效果,应该是 debugger 被移除了吧。
经过测试发现:
以上代码,在 development 情况下,打包出来的 index.js 为 900多k,在 production 的情况下,打包出来为 128k.
7. 提取页面公共资源
思路: 将 react 和 react-dom 等基础包通过 cdn 引入,而不打入 bundle 中。
7.1 使用 html-webpack-extrnals-plugin
安装依赖: npm - html-webpack-extrnals-plugin
设置 webpack.config.js
通过设置以上代码,打包出来的结果就不会包含 react, react-dom,体积大大减小。
7.2 使用 SplitChunksPlugin
通过该插件进行公共脚本分离,该插件是 webpack4 内置的,用于替代原来的 CommonChunksPlugin 插件。
chunks 参数说明:
async 异步引入的库进行分离(例如 动态 import,引入 react)
initial 同步引入的库进行分离
all 所有引入的库都会进行分离 (推荐)
minChunks: 某个公共模块被引用的次数,如为 2 ,则如果该模块被引用 >= 2, 则会被认为 minChunks.
设置 webpack.config.js
以上公共代码,要在页面中看到效果,也要添到 HtmlWebpackPlugin 中的 chunks 数组中。
8. Tree Sharking
概念:一个模块可能有很多的方法,只要其中某个方法被使用到了(甚至只是引入某个函数,而函数没有调用),则整个文件都会被打到 bundle 中,tree sharking 就是只把用到的方法打入 bundle, 没用到的方法会在 uglify 阶段被擦除掉。
要求:
必须使用 es6 的 import / export 语法,不能使用 cjs 中的 module.exports / require 语法。
引入的模块代码不能有 副作用
因为 import 只能在代码顶层出现(不能在 if 中),因此在静态分析阶段,就能够知道那些方法被用到了,而不需要在执行阶段才能判定。
在 webpack 生产环境的情况下,(mode='production') 的情况下,会默认开启 tree sharking.
9. Scope Hoisting
编译之后的代码:
可以看到以上两个模块,都被函数包裹着:
会导致: 1. 大量闭包函数包裹着代码,导致代码体积增大(模块越多越明显) 2. 运行代码时创建的函数作用域变多,内存消耗变大
scope hoisting 原理: 将所有模块的代码按照 引用顺序 放在一个函数作用域里面,然后适当的重命名一些变量以防止变量名冲突。 要求: 必须是 es6 语法, cjs 语法不支持。
设置 webpack.config.js
编译结果:
结果同上,最终两个模块会在一个函数包裹里面,这样就减少了闭包数量。
注意: 有些模块是经过 splitChunksPlugin 之后,可能依然会被函数包裹
10. 代码分割和动态 import
对于大的 WEB 应用来说,将所有的代码都放在一个文件中,显然是不够有效的。特别是你的某些代码块是在某些特殊的时候才会被使用到。
webpack 有一个功能就是将你的代码库分割成 chunks, 当代码运行到需要他们的时候再进行加载。
适用场景: 1. 抽离相同的代码到一个共享块 2. 脚本懒加载,使得初始化下载的代码更小
懒加载 JS 的两种方式: CommonJS: require.ensure ES6: 动态 import (没有原生支持,需要 babel 转换)
安装依赖: npm i @babel/plugin-syntax-dynamic-import -D
设置 .babelrc:
当 webpack 打包发现代码里有动态 import() 的语法,就会 将被引入的组件单独打包出js ,当点击时,才会加载该组件的js。
11. Webpack 集成 Eslint
在 CI/CD 中集成 eslint
在 webpack 中集成 eslint
安装依赖:npm i eslint eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y -D
npm i eslint-loader -D: 用于配合 babel-loader, 解析 js 文件
npm i babel-eslint -D: eslint 的解析器
npm install eslint-config-airbnb -D: eslint 的基础规则
设置 webpack.config.js
设置 .eslintrc.js
12. webpack 打包组件和库
要求: 1. 需要打包压缩版和非压缩版本 2. 支持 AMD/CMD/ESM 模块引入,同时支持 script 的方式引入
如何将库暴露出去:
library: 指定库的全局变量,如 react 库在全局的变量是 React libraryTarget: 支持库的引入方式 libraryExport: "default" // 设置为 default
webpack.config.js
如何针对 .min 进行压缩:
先将 mode 设置为 none
设置 webpack.config.js
如何针对不同环境引入不同的包?
设置 package.json 中 main 字段为 index.js, 同时该 index.js 的内容为:
13. webpack 实现 SSR 打包 (上)
SSR 优势: 1. SEO 友好 2. 减少白屏时间
实现思路: 服务端:
使用 react-dom/server 的 renderToString 方法将 React 组件渲染成字符串
服务端路由返回对应的模板
客户端:
打包出针对服务端的组件
服务端的代码:
客户端打包入口文件的代码:
修改 webpack.ssr.js
webpack ssr 打包存在的一些问题: 1. 浏览器中的全局变量(nodejs 没有 window 和 document) 1. 组件适配,将不兼容的组件根据打包环境进行适配 2. 请求适配,将 fetch, ajax 请求改为 axios 或者 isomorphic -fetch 2. 样式问题(nodejs 无法解析 css) 1. 方案一: 服务端打包通过 ignore-loader 忽略 css 的解析 2. 方案二:将 style-loader 替换为 isomorphic-style-loader(但是必须采用 css in js 的语法)
报错: 1. window is undefined
解决办法: 在 app.js 添加以下代码:
14. webpack 实现 SSR 打包 (下)
13 节中,我们针对 css 可以采用忽略 css 的做法,但是应该怎么引入 css 呢?
因为在打包出客户端模板时,css 被打包且内联进模板中了 ,因此我们需要的是将打包后的客户端模板作为基础的壳子,将服务端渲染的结果放进壳子中。
同理,我们可以在初始的模板中,添加数据的 PLACEHOLDER:
15. 优化构建时的命令行显示日志
统计信息 stats
preset
alternative
Description
errors-only
none
只在发生错误时输出
minimal
none
只在发生错误或有新的编译时输出
none
false
没有输出
normal
true
标准输出
verbose
none
全部输出
设置 webpack.config.js
以上设置,只会在 webpack 编译出错的时候才会打印错误信息,对于成功的情况,不会打印任何信息,因此也是不太友好。
所以,可以使用 friendly-errors-webpack-plugin 插件,对于成功,警告,错误都有对应的错误提醒。
安装依赖: npm i friendly-errors-webpack-plugin -D
设置 webpack.config.js
16. 构建异常和中断处理
在每次构建完成之后,输入 echo $? 可以获取错误码的信息。如果错误码不为 0 , 则代表当前构建是失败的。
Node.js 中的 process.exit 规范 1. 0 表示成功完成,回调函数中,err 为 null 2. 非0 表示执行失败,回调函数中,err 不为 null,err.code 为传给 exit的数字
compiler 在每次构建结束后会触发 done 这个 hook:
此时,如果你构建发生错误(例如引用了一个不存在的模块),就会打印 build error 这个错误信息。
Last updated
Was this helpful?