4-11 shimming 的作用

1. 简介

webpack 编译器(compiler)能够识别遵循 ES2015 模块语法、CommonJS 或 AMD 规范编写的模块。然而,一些第三方的库(library)可能会引用一些全局依赖(例如 jQuery 中的 $)。这些库也可能创建一些需要被导出的全局变量。这些“不符合规范的模块”就是 shimming 发挥作用的地方。
shimming 另外一个使用场景就是,当你希望 polyfill 浏览器功能以支持更多用户时。在这种情况下,你可能只想要将这些 polyfills 提供给到需要修补(patch)的浏览器(也就是实现按需加载)。

2. 处理遗留模块

这块以前比较多见,但随着各个库的规范升级,现在用的比较少了。我们来看一下。

2.1 shimming 全局变量

我们来看一个简单的例子:

// index.js
import { ui } from './ui';

ui();

// ui.js
import $ from 'jquery';

export function ui() {
    $('body').css('background', 'green');
}

我们使用一个 ui 库,提供了一个方法 ui,依赖 jquery 实现。打包后,打开页面:


4-11 shimming 的作用_第1张图片
image.png

可以看到正常输出。
现在我们试着调整 ui, 去掉对 jquery 的引用,在 index 将其引入会如何呢?

// index.js
import $ from 'jquery';
import { ui } from './ui';

ui();
// ui.js

export function ui() {
    $('body').css('background', 'green');
}

打包:

4-11 shimming 的作用_第2张图片
image.png

发现即使在入口 index 引入 jquery,但是 ui 找不到该变量,这还是因为模块引入变量的作用范围是模块内,正确的用法是哪里使用,就在哪里引用(虽然webpack 底层只会对相同模块加载一次,但是引用标识必须是多次的)。可是对一些老的三方库,并没有引用 jquery,怎么办呢,他们默认 jquery 是全局变量可以直接引用。
要解决这个问题,我们把 jquery 作为我们应用程序中的一个全局变量就可以了。要实现这些,我们需要使用 ProvidePlugin 插件。

使用 ProvidePlugin 后,能够在通过 webpack 编译的每个模块中,通过访问一个变量来获取到 package 包。如果 webpack 知道这个变量在某个模块中被使用了,那么 webpack 将在最终 bundle 中引入我们给定的 package。
我们恢复 index.js, 然后修改 webpack.common.js

    plugins: [
        ...
        new webpack.ProvidePlugin({
            $: 'jquery'
        })
    ],

打包,


4-11 shimming 的作用_第3张图片
image.png

本质上,我们所做的,就是告诉 webpack,如果你遇到了至少一处用到 lodash 变量的模块实例,那请你将 lodash package 包引入进来,并将其提供给需要用到它的模块。
我们还可以使用 ProvidePlugin 暴露某个模块中单个导出值,只需通过一个“数组路径”进行配置(例如 [module, child, ...children?])
如下:

    plugins: [
        ...
        new webpack.ProvidePlugin({
            $: 'jquery',
            _join: ['lodash', 'join']
        })
    ],

告诉我们如果用到_join 的地方,实际上是使用 lodash.join 方法。

// ui.js

export function ui() {
    $('body').css('background', _join(['dark', 'green'], ''));
}

打包后如下:

4-11 shimming 的作用_第4张图片
image.png

这样就能很好的与 tree shaking 配合,将 lodash 库中的其他没用到的部分去除。

2.2 细粒度 shimming

一些传统的模块依赖的 this 指向的是 window 对象。我们来看一下 webpack 模块中打印 this 指向哪里:

// index.js
console.log(this);
this.alert('hi');
4-11 shimming 的作用_第5张图片
image.png

本来模块设想运行在 window 下,如果当模块运行在 CommonJS 环境下这将会变成一个问题,也就是说此时的 this 指向的是 module.exports。在这个例子中,可以通过使用 imports-loader 覆写 this

rules: [
            ...
            {
                test: require.resolve('../src/index.js'),
                use: 'imports-loader?this=>window'
            }
        ]

安装 imports-loader, 然后打包后如下:


4-11 shimming 的作用_第6张图片
image.png

2.3 全局 exports

让我们假设,某个库(library)创建出一个全局变量,它期望用户使用这个变量。

// index.js
import { file, parse } from './global.js';

console.log(file);
parse();
// global.js
const file = 'blah.txt';
const helpers = {
    test: function() { console.log('test something'); },
    parse: function() { console.log('parse something'); }
}

你可能从来没有在自己的源码中做过这些事情,但是你也许遇到过一个老旧的库(library),和上面所展示的代码类似。在这个用例中,我们可以使用 exports-loader,将一个全局变量作为一个普通的模块来导出。例如,为了将 file 导出为 file 以及将 helpers.parse 导出为 parse,做如下调整:

rules: [
            ...
            {
                test: require.resolve('../src/global.js'),
                use: 'exports-loader?file,parse=helpers.parse'
            }
        ]

打包后:


4-11 shimming 的作用_第7张图片
image.png

3. polyfills

除了处理那些遗留的 package 包,shimming 的另一个作用就是处理 polyfills。有很多方法来载入 polyfills。例如,要引入 babel-polyfill 我们只需要如下操作:

npm install --save babel-polyfill
// index.js
import 'babel-polyfill';

请注意,我们没有将 import 绑定到变量。这是因为只需在基础代码(code base)之外,再额外执行 polyfills,这样我们就可以假定代码中已经具有某些原生功能。
让我们把 import 放入一个新文件,并加入 whatwg-fetch polyfill:

npm install --save whatwg-fetch
// pollyfills.js
import 'babel-polyfill';
import 'whatwg-fetch';

配置修改如下:

    entry: {
        polyfills: './src/polyfills.js',
            index: "./src/index.js"
    },
    output: {
        path: path.resolve(__dirname, '../dist'),
        filename: "[name].bundle.js",
    },

打包后正常运行:


4-11 shimming 的作用_第8张图片
image.png

当我们开始执行构建时,polyfills.bundle.js 文件将会被载入到浏览器中,然后所有代码将正确无误的在浏览器中执行。请注意,以上的这些设定可能还会有所改进,我们只是对于如何解决「将 polyfills 提供给那些需要引入它的用户」这个问题,向你提供一个很棒的想法。

4. 小结

shimming 说到底视为了解决兼容问题,对旧的库或者浏览器进行兼容。shim 是一个库(library),它将一个新的 API 引入到一个旧的环境中,而且仅靠旧的环境中已有的手段实现。polyfill 就是一个用在浏览器 API 上的 shim。我们通常的做法是先检查当前浏览器是否支持某个 API,如果不支持的话就加载对应的 polyfill。然后新旧浏览器就都可以使用这个 API 了。

参考

https://www.webpackjs.com/guides/shimming/
https://webpack.js.org/guides/shimming/

你可能感兴趣的:(4-11 shimming 的作用)