lodash-es、babel-plugin-loadsh、lodash-webpack-plugin 的区别

2,795 阅读2分钟

前言

我们知道 lodash 是一个非常常见的工具包,但在实际项目中,我们并没有使用 lodash 的提供的所有方法,因此我们会通过按需引入的方式去使用 lodash,进而减少产物的体积。

lodash

在使用 lodash 时,如果需要按需引入,我们需要手动的进行按需,如:

import head from 'lodash/head';

lodash-es

lodash-es 是 esm 的版本,得益于 esm 的静态分析带来的 tree-shaking 的能力,如

import { head } from 'lodash-es'

对于新项目而言,建议使用 lodash-es 替代 lodash

babel-plugin-lodash

对于旧项目而言,使用 lodash-es 替换 lodash 的成本太高了,因此我们可以通过 babel-plugin-lodash 去做转化。

通过查阅源码我们发现 babel-plugin-lodash 的主要是将引用路径进行替换,已达到瘦身的效果

image.png

import { pick } from 'lodash'
// 通过 babel-plugin-lodash 会转化为
import pick from 'lodash/pick'

举个🌰

// 源代码
import { pick } from 'lodash';
const d = pick({a: 1, b: 2}, 'a');
console.log(`d===>`, d);
// babel.config.js
module.exports = {
  presets: ["@babel/preset-env"],
  plugins: ["babel-plugin-lodash"]
};

通过执行 babel src -d dist, 可以得到转化结果:

"use strict";

var _pick2 = _interopRequireDefault(require("lodash/pick"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
var d = (0, _pick2["default"])({
  a: 1,
  b: 2
}, 'a');
console.log("d===>", d);

但体积还是特别大,例如

import map from 'lodash/map'

它会打包多个 lodash 模块,原因是 map 模块里面内联了其他模块

image.png

lodash-webpack-plugin

通过查阅源码我们可以发现,lodash-webpack-plugin会根据 Map 的 对应关系 ( github.com/lodash/loda…)去对一些模块做替换操作。

企业微信截图_18852150-6eb1-4fcd-9a1f-80857d7427f4.png

可以看到,lodash-webpack-plugin 会对模块做一些替换操作,例如 clamp 模块会依赖 toNumber 进行参数处理,但是使用 lodash-webpack-plugin 后,会把 toNumber 替换成 identity,导致 clamp 不再支持字符串参数。如果传入的是字符串,返回的结果将发生变化。 因此如果使用的是 lodash-webpack-plugin 会有一些坑点,如影响第三方模块的行为、影响 lodash 自身模块的行为 造成的后果是出现 bug 比较难定位到问题

举个🌰:

// 源代码
// 主要验证 lodash-webpack-plugin
// 相关链接 https://github.com/lodash/lodash-webpack-plugin
import { clamp } from 'lodash';

/**
 * clamp函数,包含number(需要判断的值)upper(上边界)lower(下边界)三个参数。如果number超出上边界或下边界,会返回距离number差值最小的边界值。如果包含在两个边界值中,则返回number
 */
console.log(`a====>`, clamp(10, 5, 20)); // 10
console.log(`b====>`, clamp('123', '5', '20')); // 20

console.log(`c====>`, clamp('123', '1', '20')); // 20
console.log(`d====>`, clamp(123, 1, 20)); // 20

webpack 配置

const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
const path = require('path');
module.exports = {
    devtool: false,
    mode: 'development',
    entry: './src/index.js',
    output: {
        path: path.join(__dirname, './dist'),
        filename: '[name].js',
        libraryTarget: 'commonjs',
    },
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                plugins: ['lodash'],
                presets: ['@babel/preset-env']
              }
            }
          }]
    },
    plugins: [
        new LodashModuleReplacementPlugin,
    ],
};

输出的代码:

企业微信截图_5540124b-2a40-487f-937c-95ae4404418c.png

image.png

image.png

总结

对于新项目而言,采用 lodash-es 是一个不错的方案,对于旧项目而言,babel-plugin-loadsh 要比 lodash-webpack-plugin 可靠。

参考文章