webpack插件实现tree-shaking

367 阅读1分钟

tree-shaking主要实现的是按需加载

1.babel-plugin-import使用

babel中已经有了实现按需加载的包,babel-plugin-import,我们接下来看一下,在使用和不实用这个插件tree-shaking影响有多大

1.1.依赖

npm i webpack webpack-cli babel-plugin-import babel-loader @babel/core babel -D
npm i lodash -S

1.2.使用

  • webpack.config.js
const path = require('path');
module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader'
                }
            }
        ]
    }
}
  • src/index.js
import {flatten,concat} from 'lodash'
console.log(concat)
console.log(flatten)

未使用插件.png

打包编译需要的是485KiB

  • 使用babel-plugin-import
const path = require('path');
module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        plugins: [['import', {
                            libraryName: "lodash",
                            libraryDirectory: "",
                            camel2DashComponentName: false,  // default: true
                          }]]
                    }
                }
            }
        ]
    }
}

使用了插件.png

打包编译21.8KiB,相比485KiB小了不少,我们可以直观的看出tree-shaking在项目中的重要性,他决定着你的项目在上显示的大小

实现babel-import-plugin插件

astexplorer

  • 如果代码中在使用lodash时如下这种方式的引用也能达到目的
import flatten from 'lodash/flatten';
import concat from 'lodash/concat';

console.log(concat)
console.log(flatten)
  • 但是我们往往在开发中很少这么去写,太过于麻烦,我们希望保持原来的引用,达到上面的效果,所以对于我们插件来说,就是实现一个将import {concat} from 'lodash'转换成import concat from 'lodash/concat'的过程;

  • import {concat} from 'lodash'语法树

转译前语法树.png

  • import concat from 'lodash/concat'语法树

转译后语法树.png

const t = require('babel-types');
const visitor = {
    ImportDeclaration(path, state = { opts: {} }) {
        const { node } = path;
        const { opts } = state;
        const specifiers = node.specifiers;
        const source = node.source;
        if (opts.libraryName === source.value && !t.isImportDefaultSpecifier(specifiers[0])) {
            const defaultImportDeclaration= specifiers.map((specifier) => {
                const defaultSpecifier = t.importDefaultSpecifier(specifier.local)
                const importDeclaration = t.importDeclaration([defaultSpecifier], t.stringLiteral(`${source.value}/${specifier.local.name}`))
                return importDeclaration;
            })
            path.replaceWithMultiple(defaultImportDeclaration);
        }
    }
}
module.exports = function (babel) {
    return {
        visitor
    }
}