- 常见的loader
- 常见的pulgins
- Loader和Plugin的区别
- webpack构建流程简单说一下
- source map是什么?生产环境怎么用?
- 文件监听原理
- 说一下webpack热更新原理
- 手写Loader和Plugin
- webpack打包的文件内容过大怎么解决
- webpack如何提高构建速度
- webpack中module,chunk,bundle
- webpack中hash,contentHash,chunkHash
- webpackPrefetch/webpackPreload
- webpack和grunt,glup
- webpack和rollup
- babel原理以及冗余代码怎么解决
- babel-core/babel-runtime/babel-polyfill
- npm打包需要注意些啥
- webpack tree-shaking以及Scope Hositing
常见的loader
- raw-loader:加载原始文件
- file-loader: 加载文件
- url-loader: 加载文件,有个阈值,小于阈值采用base64编码形式
- sass-loader: 将sass,scss文件转化为css
- css-loader: 加载css,支持模块化
- style-loader: 将css注入到js中,通过dom加载css
- vue-loader: 加载.vue单文件
- babel-loader: 把es6转化为es5
- eslint-loader: 检查eslint
- cache-loader: 可以在一些性能消耗比较大的loader中添加,目的是将结果缓存到磁盘中
- json-loader: 加载json文件
- image-loader:加载图片
常见的pulgins
- define-plugin: 指定环境变量,不过webpack4之后加了mode就会默认指定环境变量
- html-webpack-plugin: 构建html
- clean-webpack-plugin: 清除输出目录
- ignore-plugin: 忽略部分文件
- min-css-extract-plugin: css文件分离,把css单独提取为一个文件,支持按需加载
- css-minimizer-webpack-plugin: 压缩和优化css,但是不分离css
- dll-plugin和dll-refer-plugin:生成动态链接库,加快构建速度(这个我在webpack4里面试了下,发现构建并没有提升多少,然后我就查找vue-cli的changed log,发现它们特把dll配置去掉了,给的理由是webpack4性能已经不需要dll了)
- terser-webpack-plugin: 支持es6压缩
- uglifyjs-webpack-plugin: 支持js压缩
webapck-bundle-analyser: 分析打包的大小
Loader和Plugin的区别
loader的本质就是函数,用来处理输入,然后输出,由于webpack只认识js代码,所以loader就成了中间的翻译官,对其他的类型进行转义的预处理工作
- Plugin是基于webpack事件流Tabable,插件可以扩展webpack的功能,在webpack的运行周期中会广播出来很多事件,Plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果
- Loader在module.rules中配置,作为模块的解析规则,类型为数组,每一项都是一个Object,内部包含了test,loader,options等属性
Plugin是单独配置的,类型为数组,每一项为Plugin的实例
webpack构建流程简单说一下
初始化参数,从配置参数和Shell语句(或者js接口)中读取参数,得出最终的参数
- 开始编译:用上一步得到的参数初始化一个Complier对象,加载所以配置的插件,执行run方法开始编译
- 确定入口:根据配置的entry找到所有的入口文件
- 模块编译:从入口文件出发,调用所有配置的laoder对模块及进行翻译,找找出该模块的依赖模块,再递归本步骤直到所有的入口依赖的文件都经过了本步骤的处理
- 完成模块编译:在经过上一步Loader翻译完所有模块后,得到每个模块被翻译后的最后在那个内容以及它们之间的依赖关系
- 输出资源:根据入口和模块之间的依赖关系,组装成一个一个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加载到输出列表,这步是修改输出内容的最后机会
- 输出完成:再确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写到文件系统中
- 在以上过程中,webpack会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,丙炔插件可以调用Webpack提供的API改变webpack的运行结果
- 简单点
- 初始化:启动构建,读取和合并参数,加载Plugin,实例化Complier
- 编译: 从Entry出发,针对每个Modle调用相应的Loader去翻译文件,再找到改module的依赖Module,递归进行编译处理
- 输出:将编译后的module组合成Chunk,将Chunk转化为文件,输出到文件系统中
source map是什么?生产环境怎么用?
- source map是将编译,打包,压缩后的代码映射会源代码的过程,打包压缩后的代码不具备良好的可读性,想要调试源码就需要source map
- map文件只要不打开开发者工具,浏览器是不会加载的、
- 在bundle文件末尾加上 //# sourceURL=/path/to/file.js.map
- eval: 不单独生成map文件,打包后的模块都通过eval执行
- cheap: map只显示行,不显示列,单独生成map文件
- inline: 映射文件以base64编码格式添加在bundle后面,不单独生成map文件
- module: 增加对Loader source map和第三方映射
一般开发环境: cheap-module-eval-source-map
一般生产环境:cheap-soource-map
文件监听原理
- 在源码发生变化时,自动重新构建出新的输出文件
- 开启模式有两种,一个是启动webpack命令时带上—watch参数,一种是在config中添加watch:true
- 缺点:每次都需要手动刷新浏览器
轮询判断文件的最后编辑事件是否改变,如果某个文件发生了变化,并不会立刻告诉监听者,而是先缓存气力啊
说一下webpack热更新原理
webpack热更新,HMR,这个机制可以实现不用刷新浏览器而将变更的模块替换掉旧的模块
手写Loader和Plugin
loader步骤:
需要把loader路径添加到resolveLoader的里面去
然后遵循loader标准,返回一串js的代码webpack打包的文件内容过大怎么解决
- terfer压缩is
- css-min压缩css
- 公共包通过splitChunk提取出来
- 将css单独提取出来
- tree-shaking去除无用代码
-
webpack如何提高构建速度
从搜索角度,解析角度,压缩时间,第二次打包角度四个维度提高构建速度
使用最新版本
- 通过添加cache优化二次打包速度
- terser使用多进程压缩
- exclude/include做排除工作
- resolve的配置,优化搜索时间
- rsesolve.module配置webpack去那个依赖文件下查找第三方模块
- resolve.alias 配置别名,减少递归时的查询时间
- resolve.extensions 在带入文件不带后缀的时,尝试匹配后缀顺序
- resolve.mainFields 有些第三方把在导出的时候提供好几种格式,这个用来控制优先级
- module.noParse 忽略某些文件
- babel-loader开启缓存
- webpack-dev-server/webpack-dev-middleware采用内存存储
- thread-loader: 多进程
- 合理使用soucemap
- 合理使用externals排除第三方依赖
- resolveloader配置Loader的路径
- externals:排除通过html引入的包
https://www.webpackjs.com/guides/build-performance/
// 编译代码的基础配置
module.exports = {
// ...
module: {
// 项目中使用的 jquery 并没有采用模块化标准,webpack 忽略它
noParse: /jquery/,
rules: [
{
// 这里编译 js、jsx
// 注意:如果项目源码中没有 jsx 文件就不要写 /\.jsx?$/,提升正则表达式性能
test: /\.(js|jsx)$/,
// babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
use: ['babel-loader?cacheDirectory'],
// 排除 node_modules 目录下的文件
// node_modules 目录下的文件都是采用的 ES5 语法,没必要再通过 Babel 去转换
exclude: /node_modules/,
},
]
},
resolve: {
// 设置模块导入规则,import/require时会直接在这些目录找文件
// 可以指明存放第三方模块的绝对路径,以减少寻找
modules: [
path.resolve(`${project}/client/components`),
path.resolve('h5_commonr/components'),
'node_modules'
],
// import导入时省略后缀
// 注意:尽可能的减少后缀尝试的可能性
extensions: ['.js', '.jsx', '.react.js', '.css', '.json'],
// import导入时别名,减少耗时的递归解析操作
alias: {
'@compontents': path.resolve(`${project}/compontents`),
}
},
};
webpack中module,chunk,bundle
我们写的index.js或者css文件就是一个module,webpack处理时就是chunk,每一个entry入口生成的output都属于同一个chunk,也就是每个入口就是一个chunk,经过webpack处理之后的文件就是bundle文件。
一般情况下,一个chunk对应一个bundle,但是如果把css单独提取出来,一个chunk就会对应index.bundle.js和index.bundle.css两个bundle文件。
webpack中hash,contentHash,chunkHash
- hash: hash是针对整个目录,目录中任何一个文件发生改变,bundle的hash就会发生改变
- chunkHash: 同一个入口文件(chunk)发生改变之后,整个chunk的hash就会发生改变
- contentHash:是根据文件内容的,文件内容发生改变之后,文件对应的hash也就发生改变,有利于CDN缓存
webpackPrefetch/webpackPreload
webpackPreload是跟父资源同步加载
webpackPrefetch等浏览器空闲的时候下载
webpack和grunt,glup
- grunt是基于任务
- glup是基于流
- 原理是找到一个文件,然后通过链式调用,更新流上的数据,整个链式调用任务形成一个流,多个任务构建就构成了web的构建流程
- webpack是基于入口,递归找到入口的依赖文件,然后用不同的Loader解析文件,用plugin来扩展webpack功能
- grunt和glup是将前端任务分成多个task,并合理控制task之间的调用关系完成构建流程
- webpack是根据入口文件,并需要清楚的知道什么样的类型文件用什么loader做处理
webpack和rollup
rollup使用基础库,比如vue
webpack使用于大型项目
parcel适用于简单的实验性项目,配置项不太自由
babel原理以及冗余代码怎么解决
https://juejin.cn/post/6844903760603398151#heading-15 babel原理
babel会将js代码经过词法编译和语法编译,然后通过transform转化为ast树,然后在生成最终代码
在整个过程中,babel-core不做任何转换es6语法的操作,babel是交给babel/preset-env去做这件事件
- babel-parse: 负责将js代码转换成AST树,会经过词法分析转换成tokens,然后遍历tokens进行语法分析生成AST树
- babel-transfer负责提供深度遍历 的能力,也就是提供各种vistors,真正的转换工作是交给各个插件去做的
- babel-generator:将上述两部的AST树生成js代码
- babel-transform-runtime: 作为dev dependencies,把es6的语法转换下,需要结合babel-runtime使用
- babel-runtime: 需要作为开发阶段的依赖,把babel转换的多余代码,单独提取到一个包,然后通过import的方式引入,不需要每个文件都添加相同的多余代码
babel7: preset-env添加es6的转换插件 plugins:@babel-transform-runtime 转换运行时的es6代码
babel-core/babel-runtime/babel-polyfill
babel-polyfill已经在babel7.4以上版本去掉了
babel-runtime解决两个问题:
复用辅助函数,减少打包的体积
解决babel-polyfill对api层面接口的转义,避免的接口污染plugins: [
["@babel/plugin-transform-runtime", {
"corejs": 3 // 可选 false | 2 | 3
}]
]
npm打包需要注意些啥
CommonJS模块化规范的解决方案: 设置output.libraryTarget=’commonjs2’使输出的代码符合CommonJS2 模块化规范,以供给其它模块导入使用
- 输出ES5代码的解决方案:使用babel-loader把 ES6 代码转换成 ES5 的代码。再通过开启devtool: ‘source-map’输出SourceMap以发布调试。
- Npm包大小尽量小的解决方案:Babel 在把 ES6 代码转换成 ES5 代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改.babelrc文件,为其加入transform-runtime插件
- 不能将依赖模块打包到NPM模块中的解决方案:使用externals配置项来告诉webpack哪些模块不需要打包。
对于依赖的资源文件打包的解决方案:通过css-loader和extract-text-webpack-plugin来实现
webpack tree-shaking以及Scope Hositing
这两个功能都是依赖ES6 Import的静态编译。
- JS引擎在执行Js代码的时候,会先经过词法分析,语法分析,生成语法树。然后分配内存,然后在执行代码。所以在执行代码之前,js就已经对import进行了编译。
- 不像require动态编译的,只有在执行的时候才知道依赖的包
- webpack就是借助这一特性,就可以对文件行进静态分析了
- webpack4以后mode为production就自动配置了
https://juejin.cn/post/6844904007362674701#heading-20