2 webpack 进阶用法
Posted Indinity
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2 webpack 进阶用法相关的知识,希望对你有一定的参考价值。
极客时间课程 - 玩转webpack笔记,笔记未完成-更新中...
2.1 自动清理构建目录产物
通过npm scripts 清理构建目录(但是这样操作不优雅):
rm -rf ./dist && webpack rimraf ./dist && webpack
通过
clean-webpack-plugin
插件,默认会删除output指定的输出目录const { CleanWebpackPlugin } = require(\'clean-webpack-plugin\'); module.exports = {
entry: {
index: \'./src/index.js\',
app: \'./src/app.js\'
},
output: {
path: path.join(__dirname, \'dist\'),
filename: \'[name]_[chunkhash:8].js\'
},
mode: \'production\',
plugins: [
new CleanWebpackPlugin()
]
}
2.2 PostCSS插件autoprefixer自动补齐CSS3前缀
- CSS 兼容性:
- Trident(
-ms
),例如IE浏览器; - Geko(
-moz
),例如火狐浏览器; - Webkit(
-webkit
),例如谷歌浏览器; - Presto(
-o
),例如Opera浏览器;
- Trident(
- webpack中可以使用
autoprefixer
插件自动补齐CSS前缀; autoprefixer
是后置处理器,通常与postcss-loader
结合使用;\'use strict\'; const path = require(\'path\');
const webpack = require(\'webpack\');
const MiniCssExtractPlugin = require(\'mini-css-extract-plugin\');
module.exports = {
entry: {
index: \'./src/index.js\',
app: \'./src/app.js\'
},
output: {
path: path.join(__dirname, \'dist\'),
filename: \'[name]_[chunkhash:8].js\'
},
mode: \'production\',
module: {
rules: [
{
test: /.less$/, use: [MiniCssExtractPlugin.loader, \'css-loader\', \'less-loader\', {
loader: \'postcss-loader\',
options: {
postcssOptions: {
plugins: [
require(\'autoprefixer\')({
browsers: [\'last 2 version\', \'>1%\', \'ios 7\'] // 兼容用户使用率大于1%的最新两个ios 7支持的版本
})
]
}
}
}]
}
]
}
2.3 移动端CSS px 自动转换为 rem
CSS媒体查询实现响应式布局:
缺点是需要写多套适配样式代码;@media screen and (max-width: 980px) { .header {
height: 900px;
}
}
@media screen and (max-width: 480px) {
.header {
height: 400px;
}
}
@media screen and (max-width: 350px) {
.header {
height: 300px;
}
}rem
和px
的对比:rem
是相对单位,在页面渲染时根据跟元素的font-size
值计算;px
是绝对单位;
使用
px2rem-loader
- 将px转换为rem;
px转换成rem时,需要知道一个rem单位等于多少px,因此在页面打开的时候需要动态计算根元素的font-size值(这个计算可以使用淘宝的
lib-flexible
);
\'use strict\'; const path = require(\'path\');
const webpack = require(\'webpack\');
const MiniCssExtractPlugin = require(\'mini-css-extract-plugin\');
module.exports = {
entry: {
index: \'./src/index.js\',
app: \'./src/app.js\'
},
output: {
path: path.join(__dirname, \'dist\'),
filename: \'[name]_[chunkhash:8].js\'
},
mode: \'production\',
module: {
rules: [
{
test: /.less$/, use: [MiniCssExtractPlugin.loader, \'css-loader\', \'less-loader\', {
loader: \'postcss-loader\',
options: {
postcssOptions: {
plugins: [
require(\'autoprefixer\')({
browsers: [\'last 2 version\', \'>1%\', \'ios 7\'] // 兼容用户使用率大于1%的最新两个ios 7支持的版本
})
]
}
}
}]
},
{
loader: \'px2rem-loader\',
options: {
remUnit: 75, // 表示1个rem等于75px
remPrecission: 8, // 表示px转换成rem时保留的小数位数
}
}
]
}
2.4 静态文件内联
- 资源内联的意义:
- 代码层面:
- 页面框架的初始化脚本;
- 上报相关节点;
- CSS 内联避免页面闪动;
- 请求层面:减少HTTP网络请求数量
- 小图片或者字体内联(
url-loader
);
- 小图片或者字体内联(
- 代码层面:
html 内联
raw-loader
<script>${require(\'raw-loader!babel-loader!./index.html\')}</script> // 或
<%=require(\'raw-loader!./meta.html\')%>JS 内联
raw-loader
<script>${require(\'raw-loader!babel-loader!../node_modules/lib-flexible\')}</script> // 或
<script><%=require(\'raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js\')%></script>CSS 内联
- 方式一:借助
style-loader
; - 方法二:
html-inline-css-webpack-plugin
- 方式一:借助
2.5 多页面应用打包通用方法
- 多页面应用(MPA)概念:每一次页面跳转的时候,后台服务器都会给返回一个新的html文档,这种类型的网站也就是多页网站,也叫多页面应用。
- 多页面优势:
- 每个页面之间解耦;
- 多页面对SEO更加友好;
多页面打包基本思路:
- 每个页面对应一个entry,一个html-webpack-plugin;
打包通用方法:动态获取entry和设置html-webpack-plugin数量,即可以利用
glob.sync
库;entry: glob.sync(path.join(__dirname, \'./src/*/index.js\'))
举例:
const glob = require(\'glob\'); const
const setMPA = () => {
const entry = {};
const htmlWebpackPlugins = [];
const entryFiles = glob.sync(path.join(__dirname, \'./src/*/index.js\')));
Object.keys(entryFiles).map((index) => {
const entryFile = entryFiles[index];
const match = entryFile.match(/src\\(.*)\\/index\\.js/);
const pageName = match && match[1];
entry[pageName] = entryFile;
htmlWebpackPlugins.push(
new htmlWebpackPlugin({
template: path.join(__dirname, `src/${pageName}/index.html`),
filename: `${pageName}.html`,
chunks: [pageName],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
);
});
}
2.6 使用sourcemap
- source map:本质上是一个信息文件,里面储存着代码转换前后的对应位置信息。它记录了转换压缩后的代码所对应的转换前的源代码位置,是源代码和生产代码的映射。
source map作用:可以定位到源代码;
建议在开发环境开启souremap,但是在线上环境关闭,线上问题排查的时候,可以将sourcemap上传到错误监控系统。
source map 关键字:
module.exports = { devtool: \'eval\'
};
2.7 提取页面公共资源
2.7.1 基础库分离
方式一:CDN引入
- 思路:将react、react-dom基础包通过cdn引入,不打入bundle中。
- 方法:使用
html-webpack-externals-plugin
;
方式二:SplitChunksPlugin 进行公共脚本分离
chunks参数说明:
- async:异步引入的库进行分离(默认);
- inital:同步引入的库进行分离;
- all:所有引入的库进行分离(推荐);
cacheGroups参数
- test属性:可以配置将哪些库分离出来,例如:
- minChunks:设置需要满足的最小引用次数,若不满足引用次数则同样不会被提取出来;
minuSize:分离的包体积的大小;
2.8 Tree Shaking 的使用和原理分析
- Tree Shaking(摇树优化),一个模块可能有很多个方法,只要其中的某个方法是用到了,则整个文件都会背大包到bundle里面,tree shaking就是只把用到的方法打入bundle,没用到的方法会在uglify阶段被擦出掉;
- 使用:
- webpack默认支持,在
.babelrc
里设置modules: false
即可; - 在webpack4的production场景中默认是开启的;
- webpack默认支持,在
- tree shaking只支持ES6语法,对于node中CJS的方法是不支持的;
- 基础概念:DCE(Dead Code Elimination)
- 代码不会被执行到,不可到达;
- 代码执行的结果不会被用到;
- 代码只会影响死变量(即只写不读的变量);
- tree shaking的原理(基于DCE概念进行理解):
- 利用ES6模块的特点:
- 只能作为模块顶层的语句出现;
- import 的模块名只能是字符串常量;
- import binding 是 immutable的;
- tree shaking本质上还是对代码的静态分析,因此在编译阶段,代码是否有使用到是要确定下来的,在这个阶段,tree shaking会将未使用到的代码进行标记,最后在uglify阶段删除无用代码;
- 利用ES6模块的特点:
2.9 Scope Hoisting 使用和原理分析
- 不使用scope hoisting时构建生成的代码:
- 存在大量闭包代码,导致打包文件体积增大(模块越多越明显);
- 运行代码时创建的函数作用域变多,内存开销变大;
- webpack打包——模块转换分析
- 模块初始化函数
- 被webpack转换后的模块会带上一层包裹;
- import 会被转换成__webpack_require;
- export 也会进行相应的转换;
- 打包出来是一个IIFE(匿名闭包);
- modules 是一个数组,每一项是一个模块初始化函数;
- __webpack_require的工作:用来加载模块,返回module.exports
- 模块初始化函数
- scope hoisting 原理:将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突;
- 使用scope hoisting前后的对比:通过scope hoisting可以减少函数声明代码和内存开销;
scope hoisting 使用:
在webpack3需要手动引入插件:
plugins: [ new webpack.optimize.ModuleConcatenationPlugin()
]在webpack4中,mode为production时默认开启;
注意:编写的代码必须是ES6,不能是CJS语法;
2.10 代码分割和动态import
- 代码分割的适用场景:
- 抽离相同的代码到一个共享块;
- 脚本懒加载,使得初始化下载到代码更小;
- 懒加载JS的方式:
- CommonJS:require.ensure;
- ES6:动态import(目前还没有原生支持,需要使用babel进行转换)【本质上是利用了JSONP的技术进行实现的】;
动态import:
安装babel插件;
npm install @babel/plugin-syntax-dynamic-import --save-dev
配置.babelrc配置文件
{ "plugins": ["@babel/plugin-syntax-dynamic-import"],
// ...
}
2.11 在webpack中使用ESLint
- 行业内优秀的ESLint实现规范:
- Airbnb:eslint-config-airbnb、eslint-config-airbnb-base;
- 腾讯:eslint-config-alloy,eslint-config-ivweb
指定ESlint规范时遵循的三个原则:
- 不重复造轮子,基于eslint:recommand配置并改进;
- 能够帮助发现代码错误的规则,全部开启;
- 帮助保持团队代码的代码风格一致,而不是限制开发体验;
ESLint 规范落地方式:
与 CI/CD 集成:是在发布到环境/线上进行的检查;
本地开发阶段增加precommit钩子;
安装husky:
npm install husky --save-dev
增加npm script,通过lint-staged增量检查修改的文件;
"script": { "precommit": "lint-staged"
},
"lint-staged": {
"linters": {
"*.{js,scss}": ["eslint --fix", "git add"]
}
}
webpack 与 CI/CD 集成:使用eslint-loader,构建时检查JS规范【不建议老项目通过该方式新增检查,因为这种方式检查时针对这个歌项目进行检查的】;
eslint的配置文件可以是
.eslintrc.*
文件;
2.12 webpack打包和基础库
webpack 除了可以用来打包应用,也可以用来打包JS库;
实现一个大整数加法库的打包:
- 需要打包压缩版(适用于开发阶段)和非压缩版(线上打包);
- 支持AMD/CJS/ESM模块引入;
2.13 webpack 实现SSR打包
2.14 优化构建时命令行的显示日志
2.15 构建异常和中断处理
以上是关于2 webpack 进阶用法的主要内容,如果未能解决你的问题,请参考以下文章
我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情