相信很多人很难一下子理解官方文档的这段话:
官方介绍:webpack
compiler 能够识别遵循 ES2015 模块语法、CommonJS 或 AMD 规范编写的模块。但一些第三方库(或者老代码)可能会引用一些全局依赖(如jQuery
中的全局变量$
,即第三方库里没有导入 ,但把 ,但把 ,但把当成全局变量使用)。这些依赖也可能会创建一些需要导出的全局变量。这些 “broken modules(不符合规范的模块)” 就是 shimming(预置依赖) 发挥作用的地方。
本文尝试用大白话解释一下webpack里的shimming,shim意思是硬垫片,那就跟兼容有关系了(polyfill是软垫片)
使用场景:
有一些旧的库或代码,可能会多处使用某个全局变量,如使用jquery的全局变量$,这样就要求:全局变量$需要提前在浏览器通过script标签引入,否则可能会报错(webpack解决的痛点之一)。
shimming就是用来兼容这些旧的库的,让webpack也能对它们进行打包。打包时,把使用全局变量的方式,改为模块引入的方式。例如,把$认为是一个模块,而不是全局变量。
原理非常简单:
打包时,在旧的库里,插入导入该全局变量对应的模块的语句。本质就是给旧的库实现自动导入指定模块。注意:要同时安装该全局变量对应的模块化版本。
效果:
这样一些旧代码或者库,即使是以全局变量的方式使用某个库,也可以直接复制过来使用,同时无需修改旧代码,webpack帮你实现导入,把对旧代码的侵入性降到最低。
使用全局变量,和使用模块化二者在代码里主要区别是:有没有提前导入声明。模块化方式会在顶部导入再使用;全局变量方式会直接在代码里使用,没有导入语句。二者的其余代码可以一模一样。
配置:
// webpack.config.js
// 首先记得安装依赖:npm i jquery lodash -S
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.ProvidePlugin({
// 注意
// 并不是全局创建myLodash、$变量,而是识别业务代码里的这两个变量。
// 并在使用了myLodash、$变量的js文件里,自动导入对应的'lodash'、'jquery'模块
myLodash: 'lodash',
$: 'jquery'
})
],
}
// index.js
// 这样就可以在任意代码里使用 myLodash、$ 变量,无需提前导入
// 也就是可以直接把旧代码复制过来使用
myLodash.join(['hellow','hi'], ' ');
$('div')
细颗粒度shimming–更多自定义配置
上面实现shimming的webpack.ProvidePlugin插件,虽然可以实现把多个全局变量,改造成模块导入的方式使用(逻辑代码里像一个全局变量那样使用该依赖),但是webpack会改变被打包的库的一级作用域,如this字面量不再是window,而可能是module.exports。
而一些旧代码不仅使用全局变量,也会默认一级作用域下的this指向window。而经过webpack打包后,特别是经过转为 CommonJS 后,这个this会指向module.exports。
为了能自动导入依赖的同时,还能修改一级作用域下this指向,就需要使用imports-loader来实现:
更多imports-loader功能实现请查阅webpack文档
-
安装loader:
npm install imports-loader -D
-
对需要特殊处理的模块配置该loader即可
// webpack.config.js module.exports = { module: { rules: [ { // example.js这里可以是第三方库:使用了全局变量$,还使用this当window使用 test: require.resolve('example.js'), use: [ { loader: 'imports-loader', options: { imports: { // 给example.js导入jquery模块,命名为$ moduleName: 'jquery', name: '$', }, // 把this指向window wrapper: 'window', }, }, ], }, ], }, }; // example.js的全部代码Code,会被imports-loader转为: import $ from 'jquery'; (function () { // ... // Code // ... }.call(window));
自动全局导出
既然webpack可以实现自动导入,那么就会有自动导出。
全局exports就是帮你自动把旧代码的一级作用域下的变量导出,通过exports-loader来实现:
-
安装`npm install exports-loader --save-dev
-
使用
// 方式一:内联式,无需配置webpack // index.js import { addAlias } from 'exports-loader?type=commonjs&exports=multiple|funs.add|addAlias!./normal.js'; // normal.js 会被插入module.exports = {"addAlias": funs.add} // type取值为commonjs,exports只能是multiple(默认)、single // type的取值默认为module,即es6,对应的exports的值只能是named(默认)、defalult // import {num} from 'exports-loader?exports=named|num!./normal.js' =》export {num} // 方式二:配置loader module.exports = { module: { rules: [ { test: require.resolve('./normal.js'), loader: 'exports-loader', options: { type: 'commonjs', exports: 'multiple funs.add addAlias', }, }, ], }, }; // index.js import { addAlias } from './normal.js';