Skip to content

Webpack 进阶功能 #123

@coconilu

Description

@coconilu

tree shaking

tree shaking指的是移除JavaScript上下文中的未引用代码(dead-code),它依赖于ES2015模块系统中的静态结构特性。

JS里有一个古老的继承方式,叫mixin(混入),像Express、VueJS都在使用这种方式去为一个类实例的原型增加属性、方法。思路一般是创建一个普通对象来存储希望加入类实例的原型的属性和方法,然后通过Object.defineProperty接口把这些属性和方法添加到类实例的原型上。但是这种方法有一个弊端,就是你的编辑器无法识别这个普通对象是否存在未引用代码。

这里需要引入一个概念:副作用。如果一个文件里有一些变量、方法会被隐式调用,那么这个文件就是有副作用的;如果一个文件中的变量、方法除了被显式调用外,其它的也不会被隐式调用,那么这个文件就是无副作用的。

一般在package.json的sideEffects属性设置false,那么就是声明所有文件都是无副作用的;还可以给sideEffects属性添加一个数组,说明哪些文件是有副作用的,支持文件的相对路径、绝对路径和glob模式。

应用:对于像lodash这样的工具函数库,使用tree shaking真的很合适,打包JS代码的时候仅会引入依赖的工具函数就可以了,不必像以前一样引入整个大库。

glob 模式是指 shell 所使用的简化了的正则表达式

shimming

使用场景如下:

  1. 一些旧的第三方库可能会引用一些全局变量(例如jQuery$
  2. 旧版本的浏览器可能不支持最新的JS特性,我们可以给浏览器打补丁,让它支持新特性

首先,我们可以借助ProvidePlugin插件告知webpack,如果遇到某个全局变量的时候,把对应的包引入进来(模块化)。例如项目中需要lodash库的时候,我们不需要全局注册它,只需要在webpack.config.js文件中说明:

plugins: [
  new webpack.ProvidePlugin({
    _: 'lodash'
  })
]

然后在引用它的地方直接使用_就可以了,webpack会在打包的时候帮我们import这个库。

对于给浏览器打补丁,可以借助babel-polyfill这个库,不过为了更灵活(比如在新浏览器中,我们就不需要引入polyfill了),我们可以把polyfill的执行单独放在一个模块中,然后在浏览器中执行一段代码用以检测是否需要引入polyfill。

总结:

  1. webpack可以把某个全局变量导入到需要引用它的模块中
  2. webpack还可以让一个模块中的this指向全局变量(比如window),需要借助imports-loader
  3. webpack还可以把一个模块中的一些变量设置为全局变量,自动导入所有的模块中,需要借助exports-loader

代码分离和懒加载

这是webpack的一个亮点——组合即创新——通过JSONP和Promise来加载异步模块。

高效使用浏览器缓存

如果不了解浏览器的缓存机制,推荐阅读这里——浏览器缓存机制。webpack可以确保编译生成的文件能够被客户端缓存,且在文件内容变化后,能够请求到新的文件。

有如下建议:

  1. 给输出文件名添加hash值
  2. 使用CommonsChunkPlugin提取boilerplate(样板)和manifest到一个单独的文件
  3. 把第三方库提取到一个单独的vendor chunk文件中,因为它们几乎不做任何修改

为了不让vendor chunk随着自身module.id的修改而变化,我们可以借助HashedModuleIdsPlugin插件去改变这一行为。

用函数代替原来的module.exports对象

为了更好接收命令行传来的环境变量,我们可以把webpack.config.js的导出对象转换成一个函数,函数的第一个入参就是env对象,它包含了命令行传入的环境变量。

创建library

除了打包应用程序代码,webpack 还可以用于打包 JavaScript library。

当我写好自己的库的时候,需要注意两点:

  1. 依赖的第三方库是否需要打包进来
  2. 选用的导出方式

外部化第三方依赖库,可以在配置文件里添加externals字段,webpack将不会把第三方库打包到输出文件里。

为了让library可以兼容不同的环境,比如 CommonJS,AMD,Node.js 或者作为一个全局变量,可以在配置文件的output里添加library字段,还以通过libraryTarget设置来兼容不同的环境。

libraryTarget有如下选项:

  1. var(默认),暴露为一个变量
  2. this,入口起点的返回值将分配给 this 的一个属性
  3. window,入口起点的返回值将使用 output.library 中定义的值,分配给 window 对象的这个属性下
  4. global, 入口起点的返回值将使用 output.library 中定义的值,分配给 global 对象的这个属性下
  5. commonjs,入口起点的返回值将使用 output.library 中定义的值,分配给 exports 对象

分析输出结果

官方有导出输出结果的工具:

webpack --profile --json > stats.json

参考

webpack指南
tree shaking
output.libraryTarget

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions