网页
综合
更新时间 2026-11-14 09:19:31
Shimming | webpack
自动蜘蛛
webpack.js.org
正文约 6000 字
The webpack compiler can understand modules written as ES2015 modules, CommonJS or AMD. However, some third party libraries may expect global dependencies (e.g. $ for jQuery). The libraries might also create globals which need to be exported. These "broken modules" are one instance where shimming comes into play.
We don't recommend using globals! The whole concept behind webpack is to allow more modular front-end development. This means writing isolated modules that are well contained and do not rely on hidden dependencies (e.g. globals). Please use these features only when necessary.
Another instance where shimming can be useful is when you want to polyfill browser functionality to support more users. In this case, you may only want to deliver those polyfills to the browsers that need patching (i.e. load them on demand).
The following article will walk through both of these use cases.
For simplicity, this guide stems from the examples in Getting Started. Please make sure you are familiar with the setup there before moving on.
Let's start with the first use case of shimming global variables. Before we do anything let's take another look at our project:
Remember that lodash package we were using? For demonstration purposes, let's say we wanted to instead provide this as a global throughout our application. To do this, we can use ProvidePlugin.
The ProvidePlugin makes a package available as a variable in every module compiled through webpack. If webpack sees that variable used, it will include the given package in the final bundle. Let's go ahead by removing the import statement for lodash and instead provide it via the plugin:
What we've essentially done here is tell webpack...
If you encounter at least one instance of the variable _, include the lodash package and provide it to the modules that need it.
If we run a build, we should still see the same output:
We can also use the ProvidePlugin to expose a single export of a module by configuring it with an "array path" (e.g. [module, child, ...children?]). So let's imagine we only wanted to provide the join method from lodash wherever it's invoked:
This would go nicely with Tree Shaking as the rest of the lodash library should get dropped.
Some legacy modules rely on this being the window object. Let's update our index.js so this is the case:
This becomes a problem when the module is executed in a CommonJS context, where the module's own this is module.exports rather than the global object. Wrapping the module in a function called with the global object fixes that, and it needs no loader: NormalModule's processResult hook hands a plugin what the loaders produced and takes back a replacement, so putting code around a file before webpack parses it is a plugin short enough to keep in the configuration.
webpack's add-imports example is that plugin. It covers the wrapper above, a side-effect import the file never names, and a real binding.
The wrapper is for scripts only. import and export may appear only at the top level, so wrapping an ES module in a function is a syntax error — and an ES module's own this is undefined anyway, never module.exports.
A name the module only reads is ProvidePlugin's job, since it binds the name where the module reads it without touching the source.
Let's say a library creates a global variable that it expects its consumers to use. We can add a small module to our setup to demonstrate this:
Now, while you'd likely never do this in your own source code, you may encounter a dated library you'd like to use that contains similar code to what's shown above. A file that exports nothing needs the exports appending to it, which is the same hook from the other end of the file: webpack's add-exports example is the plugin for it.
For globals.js above, appending module.exports = { file, parse: helpers.parse }; exports file as file and helpers.parse as parse. From within our entry script (i.e. src/index.js), we could then use const { file, parse } = require('./globals.js'); and all should work smoothly.
Which format to append is the file's own, and the two are not equivalent. module.exports = … keeps a script the script it is, which is what globals.js above wants. export { … } is what makes a file an ES module, exactly as it would be in the source, so those exports can be analyzed and tree-shaken — prefer it wherever the file tolerates it. The example shows both side by side.
Almost everything we've discussed thus far has been in relation to handling legacy packages. Let's move on to our second topic: polyfills.
There's a lot of ways to load polyfills. For example, to include the babel-polyfill we might:
and import it so as to include it in our main bundle:
Note that we aren't binding the import to a variable. This is because polyfills run on their own, prior to the rest of the code base, allowing us to then assume certain native functionality exists.
Note that this approach prioritizes correctness over bundle size. To be safe and robust, polyfills/shims must run before all other code, and thus either need to load synchronously, or, all app code needs to load after all polyfills/shims load. There are many misconceptions in the community, as well, that modern browsers "don't need" polyfills, or that polyfills/shims merely serve to add missing features - in fact, they often repair broken implementations, even in the most modern of browsers. The best practice thus remains to unconditionally and synchronously load all polyfills/shims, despite the bundle size cost this incurs.
If you feel that you have mitigated these concerns and wish to incur the risk of brokenness, here's one way you might do it: Let's move our import to a new file and add the whatwg-fetch polyfill:
With that in place, we can add the logic to conditionally load our new polyfills.bundle.js file. How you make this decision depends on the technologies and browsers you need to support. We'll do some testing to determine whether our polyfills are needed:
Now we can f
本页为82搜索生成的网页快照,内容来自站内索引;演示数据的原文链接仅作示例。