Jacleklm's Blog

webpack小结

2019/10/01

基本篇

对webpack的看法

webpack是一个模块打包工具。可以用webpack管理我们的模块依赖,并编绎输出模块们所需的静态文件。它能够很好地管理、打包Web开发中所用到的HTML、Javascript、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后 生成了优化且合并后的静态资源

entry && output

loader

loader其实就是一个打包的方案,它知道对于某些特定的文件,webpack应该如何去打包。写在配置文件的 module 项中定义rules。

常用的loader

  • 打包图片:file-loader,url-loader(会把图片打包成base64字符串直接放在打包好的js文件中。这样能使得html打开时候少点http请求,但是只适合于图片不要太大的情况,否则js文件太大也不好。所以可以加limit配置项)
  • 打包样式文件:style-loader(把打包好的css挂载在html的<head>),css-loader(分析各css文件的关系,合并成一个css文件)。如果是sass或stylus文件,还需要借助sass-loader,node-sass和stylus-loader。自动添加产商前缀(提高css兼容性)的postcss-loader

PS:use中的loader的执行顺序的是从下到上,从右到左,所以顺序不能弄错

plugins

plugin可以在webpack运行到某个时刻的时候,帮我们做一些事情(有点像生命周期函数)。写在配置文件的 plugins 项中

常用的plugins

  • html-webpack-plugin。会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html文件中
  • clean-webpack-plugin。不是官网写的插件。会在打包开始前,把原来的打包目录删除

SourceMap

当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置(只是显示在打包后的文件中的错误的位置),用SouceMap即可通过映射关系能知道该错误在源代码中的位置

使用方法

在development模式下默认SourceMap已经被配置进去了,若想关闭可用 devtool: none

devtool配置项的几种写法

  • cheap:使打包性能提升,sourcemap提示错误只提示到行不提示列
  • inline:把映射关系文件放在打包好的js文件中而不是生成一个单独的文件
  • module:使得cheap-sourcemap不仅管打包好的文件和源js的关系,还会管loader、第三方模块等的关系
  • eval:是最快的,会以eval的方式检查

devtool最配置

在development下,用 cheap-module-eval-source-map
在production下,用 cheap-module-source-map

webpack-dev-server

为我们提供了一个简单的 web 服务器,并且能够实时重新加载;不会生成打包好的目录,是把打包好的文件存在内存中,提高效率
使用方法:安装,添加命令,写 devServer 项

proxy转发

proxy配置项,其实用 Vue-cli 和 create-react-app 构建的项目的proxy功能本质就是这个配置项

模块热替换

Hot Module Replacement,HMR。

功能

当源文件的发生改变的时候,理论上webpack-dev-server会帮我们打包更新,但是这个过程会刷新页面,使得页面上已有的样式/数据被刷新了。用HMR能实现在运行时更新各种模块,而无需进行完全刷新

原理

使用

  1. 更新 webpack-dev-server 的配置:devSever配置项中写 hot: true
  2. 在plugins中实例化HMR插件

对js文件的HMR功能需要手动多写一段代码

1
2
3
4
5
if (module.hot) { // 注意
module.hot.accept('./number.js', () => {
// 一些操作
})
}

css-loader 中内置了if(module.hot)...这样的代码,所以我们手写

Babel

安装 babel-loader(其实只是打通webpack和babel的一个通道),@babel/core,@babel/preset-env(这个才是翻译器)@babel/polyfill(添加ES6中的新函数新变量,eg. Promise和Map)
具体配置方法见Babel官方文档

@babel/polyfill

  • 默认情况下polyfill会把所有新变量和函数都添加进打包好的文件,文件会变得较大,可写 useBuiltIns实现按需添加;可写target设定在目标浏览器,会自己判断是否添加这些新变量。(当options太长的时候可以直接把该对象放在根目录下的.babelrc )
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    options: {
    presets: [["@babel/preset-env", {
    targets: {
    edge: "17",
    firefox: "60",
    chrome: "67",
    safari: "11.1",
    },
    useBuiltIns: 'usage' }]]
    }
  • 此外,polyfill添加新变量是以全局变量的形式,会污染全局环境,当我们在打包UI组件库/类库的时候,就不能用这种配置方式。应安装@babel/plugin-transform-runtime,@babel/runtime 。配置方法见文档

高级概念

Tree Shaking

Code Splitting

CSS文件的代码分割

  • 一般情况下,css会被打包在js文件中,如果想把css文件单独打包,需要用 MiniCssExtractPlugin (该插件不支持HMR,一般只在生产环境下使用,详细用法见笔记)
  • css 压缩用 optimize-css-assets-webpack-plugin

Chunks:打包生成的每个文件都是一个chunk,打包生成很多个js文件,就有很多个chunk
output的chunkFilename配置项:如果一个打包后的文件会直接被html引用,那就是走filename,否则就是chunkFilename

Lazy loading

Shimming

应用场景:由于webpack具有模块化开发的理念,所以当index.js中有引入jQuery,而有个模块用了$变量却没import jQuery的时候,打包的时候这个模块的$还是会翻译不出,手动往每个用了$的模块去import却很麻烦。故可用shimming。
解决方法
在common中require(‘webpack’),然后在plugin中写入new webpack.ProvidePlugin({ $: ‘jquery’ })
当发现模块中有 $ 时,就会自动给这个模块引入jQuery

打包分析

当我们用webpack打包之后,可以用打包分析的工具进行分析,看打包是否合理。看代码分割是否合理

使用

文档提示加命令,会在打包过程中生成json文件描述打包过程,把这个文件传到网站即可;也可以用官方文档建议的方法

preloading & prefetching

怎么看页面的代码使用率? 用coverage
webpack希望我们多写异步加载的代码(多用懒加载),能提高页面的代码使用率,eg,把具体代码写在一个js文件中,然后在index.js中写。(所以代码分割默认是async)

1
2
3
4
5
document.addEventListener('click', () => {
import('./click.js').then(({default: func}) => {
func();
})
})

但是等到用户点击的时候再加载代码,加载时间可能也比较长,这时就可用preloading、prefetching:实现在页面加加载完之后,在接下来的空闲时间偷偷地把异步操作的代码下载好,这样真正用的时候只需要加载缓存即可(这个其实是prefetching;preloading是会和主代码一起加载)。
使用方法是在import语句中写入魔法注释即可 /* webpackPrefetch: true */

提升webpack打包速度的方法

原理篇

如何编写一个plugin

如何编写一个loader

Bundler源码编写

Vue Cli3

如果想改Vue项目中webpack配置,是直接建立vue.config.js然后按文档简单粗暴地去配置就可以了,对新手十分友好,vue-cli会在底层自己转变成webpack配置并实施

参考资料
慕课网-从基础到实战 手把手带你掌握新版Webpack4.0
webpack官方文档

CATALOG
  1. 1. 基本篇
    1. 1.1. 对webpack的看法
    2. 1.2. entry && output
    3. 1.3. loader
      1. 1.3.1. 常用的loader
    4. 1.4. plugins
      1. 1.4.1. 常用的plugins
    5. 1.5. SourceMap
      1. 1.5.1. 使用方法
      2. 1.5.2. devtool配置项的几种写法
      3. 1.5.3. devtool最配置
    6. 1.6. webpack-dev-server
      1. 1.6.1. proxy转发
      2. 1.6.2. 模块热替换
        1. 1.6.2.1. 功能
        2. 1.6.2.2. 原理
        3. 1.6.2.3. 使用
    7. 1.7. Babel
      1. 1.7.1. @babel/polyfill
  2. 2. 高级概念
    1. 2.1. Tree Shaking
    2. 2.2. Code Splitting
      1. 2.2.1. CSS文件的代码分割
    3. 2.3. Lazy loading
    4. 2.4. Shimming
    5. 2.5. 打包分析
      1. 2.5.1. 使用
    6. 2.6. preloading & prefetching
    7. 2.7. 提升webpack打包速度的方法
  3. 3. 原理篇
    1. 3.1. 如何编写一个plugin
    2. 3.2. 如何编写一个loader
    3. 3.3. Bundler源码编写
    4. 3.4. Vue Cli3