webpack是开发工具,面试考点重点在配置和使用,原理理解不需要太深。

一、基本配置

1、拆分配置和merge

将公共配置跟dev和prod的配置拆分,然后通过webpack-merge对配置进行整合。

2、启动本地服务

dev环境启动devserver配置。

3、处理ES6

使用babel-loader,针对对应目录的js进行代码转换

4、处理样式

使用postcss-loader、css-loader、style-loader等,这里有个考点是loader的执行顺序是从后往前执行。

5、处理图片

在dev环境直接使用file-loader进行图片的直接引用,prod环境会使用url-loader对小图片进行base64编码。

6、模块化

webpack天生支持模块化。

二、高级配置

1、配置多入口

entry设置多入口文件,在output输出文件使用[name]根据entry的key动态生成输出文件名

plugins要设置多个HtmlWebpackPlugin,根据多入口生成多个html文件,同时要设置chunks,来引入相应入口文件,如果不设置则会把全部入口文件都引入 。

2、每次打包清除dist原有文件

在plugins使用new CleanWebpackPlugin(),会默认清空 output.path 指定的文件夹内容。

3、抽离css文件

mini-css-extract-plugin使用这个插件的loder来替换style-loader。

在plugins里面配置mini-css-extract-plugin的filename,用于设置存放抽离的css具体目录和名字。

使用webpack的optimization的minimizer加入 terser-webpack-plugin 和 optimize-css-assets-webpack 插件来压缩抽离的css。

4、抽离公共代码

使用webpack的optimization的splitChunks

optimization:{
  splitChunks:{
    chunks: 'all',
    // 缓存分组
    cacheGroups:{
      //第三方模块
      vendor:{
        name:'vendor', // chunk 名称
        priority:1, // 权限最高,优先抽离,重要!!
        test:/node_modules/, // 匹配目录规则
        minSize:0, // 大小限制
        minChunks:1, // 最少复用过几次
      }
      //公共模块
      common:{
        name:'common', // chunk 名称
        priority:0, // 优先级
        minSize:0, // 公共模块的大小限制
        minChunks:2, // 公共模块最少复用过几次
      }
    }
  }    
}

5、异步加载js

import语法webpack天生支持异步加载。

异步加载会产生单独的chunk。

6、处理JSX

安装配置 @babel/preset-react 即可。

7、处理VUE文件

安装配置 vue-loader 即可

三、优化构建速度

1、优化 babel-loader(可用于生产)

{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'], // 开启缓存
include: path.resolve(__dirname, 'src'), // 明确范围
// 排除范围,include 和 exclude 两者选一个即可
// exclude: path.resolve(__dirname, 'node_modules')
}

开启缓存后,es6没有改的会使用缓存不会重新编译

设置使用范围可以缩小编译量

2、IgnorePlugin(可用于生产)

忽略正则匹配的 js

3、noParse(可用于生产)

不去解析正则匹配的库的依赖

4、happyPack(可用于生产)

JS单线程,使用happyPack开启多进程打包,提高构建速度,特别是多核CPU。

5、ParallelUglifyPlugin(只用在生产)

webpack内置Uglify工具压缩JS,开启多进程压缩JS,原理和happyPack相同。

new ParallelUglifyPlugin({
// 传递给UglifyJS的参数,值不过开启了多进程压缩
uglifyJS:{
output:{
beautify:false, // 最紧凑的输出
comments:false, // 删除所有注释
},
compress:{
// 删除所有的 console 语句,可以兼容ie
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
}
}
}) 

关于开启多进程要按需使用,

项目大,打包速度慢,开启多进程提高速度

项目小,打包速度快,开启多进程反而降低速度

6、自动刷新(不可用于生产)

{
watch:true, // 开启监听,默认 false
// 注意,开启监听之后,webpack-dev-server 会自动开启刷新浏览器
// 监听配置
watchOptions:{
ignored: /node_modules/, // 忽略内容
// 监听到变化发生后等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
aggregateTimeout:300, // 默认为300ms
// 判断文件是否发生变化时通过不停的去询问系统指定文件有没变化实现的
poll:1000, // 默认每隔1000ms询问一次
}
} 

以上配置一般开发不会配置到,因为开启webpack-dev-server会自动带上自动刷新的配置。以上配置是为了理解自动刷新有哪些内容。

7、热更新(不可用于生产)

自动刷新:整个网页全部刷新,速度较慢,状态会丢失

热更新:新代码生效,网页不刷新,状态不丢失

热更新使用插件:HotModuleReplacementPlugin

在entry入口中要新增两行配置:

entry:{
index:[
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/dev-server',
path.join(srcPath,'index.js')
]
}

然后在plugin中使用HotModuleReplacementPlugin插件。

最后在devServer中增加一个属性:hot:true,开启热更新。

配置热更新后,需要增加需要热更新模块的逻辑代码,如下

if(module.hot){
moudule.hot.accept(['./math'],()=>{
// 热更新模块里面的回调函数
})
}

  

8、DllPlugin 动态链接库(不可用于生产)

使用前提:

前端框架如vue、react体积大,构建慢,比较稳定,不常升级。

同一个版本只构建一次即可,不用每次都重新构建。

webpack已内置DllPlugin支持。

使用过程:

DllPlugin先打包出dll文件

DllReferencePlugin再使用dll文件

四、优化产出代码

优化方向:体积更小,合理分包,不重复加载,速度更快,内存使用更少

1、小图片Base64编码

2、bundle加hash

3、懒加载

4、提取公共代码

5、IngorePlugin

6、使用CDN加速:

  在output设置publicPath,会修改所有js跟css文件添加cdn前缀

  在url-loader设置publicPath,可以修改图片的cdn前缀

7、使用production

开启production模式跟development相比的差异点:

(1)自动开启代码压缩

(2)Vue React等会自动删掉调试代码(如开发环境的warning)

(3)自动启动Tree-Shaking,必须用ES6 Module 才能让tree-shaking生效,commonjs就不行

  ES6 Module 静态引入,编译时引入

  Commonjs动态引入,执行时引入

  只有静态引入才能进行静态分析,实现Tree-Shaking

8、Scope Hosting

开启之后,会将多个引用文件合并成一个函数,减少函数作用域,代码体积更小,代码可读性更好

如何配置:

引用ModuleConcatenationPlugin插件,使用插件即可开启。

针对npm中第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件

resolve:{
mainFields: ['jsnext:main','brower','main']
}

  

五、babel

基本配置:.babelrc文件

plugins是写如何翻译语法的插件,presets是集合多个插件整合的一个包。

{
"presets":[
["@babel/preset-env"]
],
"plugins":[]
}

babel-polyfill:兼容低版本浏览器,重写低版本没有的新语法。

core-js和regenerator两个库可以满足最大多数新语法的polyfill,babel-polyfill就是前两个库的集合。

babel 7.4之后已经弃用了babel-polyfill,而推荐直接使用core-js和regenerator。

直接使用的弊端是由于重写全局方法会污染全局环境。

babel-runtime:

使用runtime配置,可以使polyfill重写的方法不污染全局环境,相当于重命名方法,代码也是自动替换成新名称的方法。

面试真题:

1、前端代码为什么要进行构建和打包?

代码层面:

  (1)体积更小(Tree-Shaking、压缩、合并),加载更快

  (2)编辑高级语言或语法(TS、ES6+、模块化,scss)

  (3)兼容性和错误检查(Polyfill、postcss、eslint)

研发流程:

  (1)统一、高效的开发环境

  (2)统一的构建流程和产出标准

  (3)集成公司构建规范(提测、上线等)

2、module chunk bundle 分别什么意思,有何区别?

module - 各个源码文件,webpack 中一切皆模块

chunk - 多模块合并成的,如 entry import() splitChunk

bundle - 最终的输出文件

3、loader和plugin的区别?

loader模块转换器,如 less 转 css。说出一些常用的loader

plugin扩展插件,如 HtmlWebpackPlugin。说出一些常用的plugin

4、babel和webpack的区别?

babel是JS新语法编译工具,不关系模块化

webpack是打包构建工具,是多个 loader plugin 的集合

5、如何产出一个 lib

output:{
// lib的文件名
filename: 'lodash.js',
// 输出 lib 到dist 目录下
path: distPath,
// lib 的全局变量名
library: 'lodash'
}

  

6、webpack如何实现懒加载?

import()语法实现

结合 Vue React 异步组件,结合 Vue-router React-router 异步加载路由来简要说下。

7、webpack常见性能优化?

将上面的三、四的优化内容根据实际效果讲下

8、babel-runtime和babel-polyfill的区别?

babel-polyfill会污染全局

babel-runtime不会污染全局

产出第三方 lib 要用 babel-runtime

9、为何Proxy 不能被 polyfill?

Class 可以用 function 模拟, Promise 可以用 callback 来模拟

但是Proxy的功能用Object.defineProperty 无法模拟

发表回复