常见的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的运行结果
    1. 简单点
  1. 初始化:启动构建,读取和合并参数,加载Plugin,实例化Complier
  2. 编译: 从Entry出发,针对每个Modle调用相应的Loader去翻译文件,再找到改module的依赖Module,递归进行编译处理
  3. 输出:将编译后的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去除无用代码
  • external加CDN

    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/

  1. // 编译代码的基础配置
  2. module.exports = {
  3. // ...
  4. module: {
  5. // 项目中使用的 jquery 并没有采用模块化标准,webpack 忽略它
  6. noParse: /jquery/,
  7. rules: [
  8. {
  9. // 这里编译 js、jsx
  10. // 注意:如果项目源码中没有 jsx 文件就不要写 /\.jsx?$/,提升正则表达式性能
  11. test: /\.(js|jsx)$/,
  12. // babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
  13. use: ['babel-loader?cacheDirectory'],
  14. // 排除 node_modules 目录下的文件
  15. // node_modules 目录下的文件都是采用的 ES5 语法,没必要再通过 Babel 去转换
  16. exclude: /node_modules/,
  17. },
  18. ]
  19. },
  20. resolve: {
  21. // 设置模块导入规则,import/require时会直接在这些目录找文件
  22. // 可以指明存放第三方模块的绝对路径,以减少寻找
  23. modules: [
  24. path.resolve(`${project}/client/components`),
  25. path.resolve('h5_commonr/components'),
  26. 'node_modules'
  27. ],
  28. // import导入时省略后缀
  29. // 注意:尽可能的减少后缀尝试的可能性
  30. extensions: ['.js', '.jsx', '.react.js', '.css', '.json'],
  31. // import导入时别名,减少耗时的递归解析操作
  32. alias: {
  33. '@compontents': path.resolve(`${project}/compontents`),
  34. }
  35. },
  36. };

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层面接口的转义,避免的接口污染

    1. plugins: [
    2. ["@babel/plugin-transform-runtime", {
    3. "corejs": 3 // 可选 false | 2 | 3
    4. }]
    5. ]

    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

https://juejin.cn/post/6844904094281236487#heading-9

https://juejin.cn/post/6844904036936712200 webpack配置