压缩可以移除模块内部的无效代码 tree shaking 可以移除模块之间的无效代码

src=http___5b0988e595225.cdn.sohucs.com_images_20180812_0190a32820364660963273a4e3627f7a.gif&refer=http___5b0988e595225.cdn.sohucs.gif

背景

某个模块导出的代码不一定被用到

  1. //myMath.js
  2. export function add(a,b){
  3. console.log('add');
  4. return a+b;
  5. }
  6. export function sub(a,b){
  7. conosle.log('sub');
  8. return a=-b;
  9. }
//index.js

import {add} from './myMath'

conosle.log(add(1,2));

tree shaking 用于移除掉不会用到的**

使用

webpack2.0就已经开始支持tree shaking

只要是生产环境,tree shaking 自动开启

原理

webpack会从入口模块出发寻找依赖关系

当解析一个模块的时候,webpack会根据es6的模块导入语句来判断,该模块依赖了另一个模块的哪导出

webpack之所以选择es6模块导入语句,是因为es6模块有一下特点:

  1. 导入导出语句只能是顶层语句
  2. import 的模块名只能是字符串常量
  3. import 绑定的变量是不可变的

这些特征非常有利于分析出稳定的依赖

在具体分析依赖的时候,webpack坚持的原则是: 保证代码正常运行,然后再尽量tree shaking

所以,如果你依赖的是一个导出的对象,由于js语言的动态特性,以及webpack还不够智能,为了保证代码正常运行,它不会移除对象中的任何信息

因此,我们在写代码的时候,尽量:

  • 使用export xxx 导出,而不是export default {xxx} 导出
  • 使用iimport {xxx} fromz "xxx"import * as xxx from 'xxx' 导入,而不是使用import xxx from 'xxxx'

依赖分析完毕以后,,webpack会根据每个模块每个导出是否被使用,标记其他导出为dead code ,然后交给代码压缩工具处理

代码压缩工具最终移除掉那些dead code 代码

使用第三方库

某些第三方库可能使用的是commonjs 的方式导出的,比如lodash

又或者没有提供普通的ES6导出方式

对于这些库, tree shking 是无法发挥作用的

因此要寻找这些库的es6的版本,好在很多流行但没有使用es6的第三方库,都发布了它的es6版本,比如lodash-es

作用于分析

tree shaking 本身并没有完善的作用域分析,可能导致在一些dead code 函数中的依赖仍然会被视为依赖插件webpack-deep-scope-plugin (这个库是个人开发,可能小的问题比较多,所以要慎重使用 提供了作用域分析,可以解决这些问题

副作用问题

webpack在tree shaking的使用,有一个原则:一定要保证代码的正确运行

在满足该原则的基础上,再来决定如何tree shaking

因此,当webpack无法确定某个模块是否有副作用的时候,往往将其视为有副作用

因此,某些情况可能不是我们想要的

//common.js

var m=Math.random();

//index.js

import "./common.js"

虽然我们根本没有使用common.js的导出,但是webpack 担心common.js有副作用,如果去掉会影响某些功能

如果要解决该问题,就需要标记该文件没有副作用

package.json中加入sideEffects

{
  "sideEffests":false 该模块是没有副作用的
}

有两种配置方式:

  • false: 当前工程中,所有的模块都没有副作用,注意:这种写法会影响到某些css文件的导入
  • 数组: 设置哪些文件时有副作用的,例如:["!src/common.js"],表示只要不是src/common.js的文件,都有副作用

    这种方式我们一般不处理,通常一些第三方库在他们自己的package.json中标注

css tree shaking

webpack 无法对css 完成tree shaking,因为csses6没有半毛钱关系

因此对,csstree shaking需要其他的插件完成

例如: purgecss-webpack-plugin

purgecss-webapck-plugincss module 无能为力

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const DeepScope = require("webpack-deep-scope-plugin").default;
const MiniCss = require("mini-css-extract-plugin");
const Purgecss = require("purgecss-webpack-plugin");
const path = require("path");
const globAll = require("glob-all");
const srcAbs = path.resolve(__dirname, "src"); //得到src的绝对路径
const htmlPath = path.resolve(__dirname, "public/index.html");
const paths = globAll.sync([`${srcAbs}**/*.js`, htmlPath]);

module.exports = {
  mode: "production",
  module: {
    rules: [{ test: /\.css$/, use: [MiniCss.loader, "css-loader"] }]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new DeepScope(),
    new MiniCss(),
    new Purgecss({
      paths
    })
  ]
};