Principles and implementation of packaging Library in the webpack organization Module
The previous article analyzed the basic principles of Webpack packaging JS modules. The case described here is the most common case: Multiple JS modules and one entry module, package into a bundle file that can be directly executed by browsers or other JavaScript Engines. This is equivalent to directly compiling and generating a complete executable file. However, there is also a common situation where we want to build and release a JavaScript library. For example, if you release your own library in the npm community, then the corresponding configuration is required for Webpack, the compiled code is slightly different.
As in the previous article, this article mainly analyzes the code generated by Webpack and describes the specific functions of Webpack configuration options on library during library compilation, the corresponding official documentation is here.
Compile a JS Library
Starting from a simple case, we can write a simple library util. js:
import $ from 'jquery'function sayHello() { console.log("Hello");}function hideImages() { $('img').hide();}export default { sayHello: sayHello, hideImages: hideImages}
Two functions are provided. Of course, they are irrelevant and actually useless. They are purely for reference only...
Next, write the Webpack Configuration:
// Entry file entry: {util :'. /util. js',} // output file output: {path :'. /dist ', filename:' [name]. dist. js '}
However, this is not enough. The output file is a function that is executed immediately, and util is returned. js exports. According to the analysis in the previous article, the final bundle code structure is roughly as follows:
(function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require('./util.js');}) ({ './util.js': generated_util, '/path/to/jquery.js': generated_jquery});
If the execution is complete, it will end. js export only partially returns, but what we need is to hand over this return value to the module of the compiled file. in this way, the compiled file becomes a library that can be imported by others. Therefore, we hope to get the compilation file as follows:
module.exports = (function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require('./util.js');}) ({ './util.js': generated_util, '/path/to/jquery.js': generated_jquery});
To get this result, add the library information to the output section of Webpack Configuration:
// Input file output: {path: './dist', filename:' [name]. dist. js', library: 'util', libraryTarget: commonjs2}
The most important thing here is libraryTarget. Now we use the commonjs2 format to get the above compilation result. That is to say, Webpack will export the final output in the form of CommonJS, in this way, a library is released.
Other release formats
In addition to commonjs2, libraryTarget has other options:
Var (default value, published as a global variable) commonjscommonjs2amdumd
With different options, compiled files can be used in different JavaScript Execution environments. Here, let's look at the output of the umd format:
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') // commonjs2 module.exports = factory(); else if(typeof define === 'function' && define.amd) define("util", [], factory); // amd else if(typeof exports === 'object') exports["util"] = factory(); // commonjs else root["util"] = factory(); // var}) (window, function() { return (function(modules) { var installedModules = {}; function webpack_require(moduleId) { // ... } return webpack_require('./util.js'); }) ({ './util.js': generated_util, '/path/to/jquery.js': generated_jquery });}
It is much more complicated than the previous commonjs2 situation, because it needs to deal with different cases, but in fact the latter part is the same. The most important thing is the first few rows, this is the standard description of the umd module. It runs the passed factory function. In fact, it loads the module function and then delivers the returned result to the corresponding object according to different runtime environments. For example, var sets the result as a global variable, which is used by the browser to directly import the JS file through the <script> label; if it is CommonJS, it is handed to the exports object; in the AMD environment, it is also written in the standard AMD format. In this way, the released JS library can be used by others in any environment.
TargetExport control output content
If you use the umd format for packaging, there may be a pitfall. If your library's source code is output in the ES6 format export default, as in the above example util. js, you directly put the compiled JS library file in the browser for use, can be <script>, or RequireJS, may not get the result you want. This is because the object your JS file returns to you is like this:
{ 'default': { sayHello: sayHello, hideImages: hideImages }}
Instead of what you expect:
{ sayHello: sayHello, hideImages: hideImages}
This problem also occurs in module systems that do not support ES6, because they do not know default. Therefore, the compiled JS file should actually only output default, which needs to be controlled by targetExport in the Webpack Configuration:
Library: 'util ',
LibraryTarget: umd,
TargetExport: 'default'
In this way, the above module will add a ['default'] after the return value, so that only the default part of exports will be returned.
This pitfall is easy to step on in the umd format. For example, you release a Vue component ,. the JavaScript section of the vue file generally exports the Component object in the format of export default, as shown in the following figure:
export default { name: 'xxx', data: { return // ... }, props: { // ... } methods: { // ... }}
If you put the compiled JS file directly in the browser and load Vue through <script> using CDN, you will find that Vue cannot identify this Component, because the object you get has an additional layer of unnecessary default.
You may ask if I change the output content to default, will it affect the use of this module in the ES6 environment? Generally, this is not true. As mentioned in the previous article, when introducing a module, the Webpack Generation Code sets and determines whether it is an export in ES6 format by a value named _ esModule, now, if only the default part is exported, this object is considered non-ES6 because it does not include _ esModule. In this way, when other modules introduce this module through import, the entire object will be introduced, which is equivalent to introducing only the export default part of the original module in disguise.
Of course, the premise of the above discussion is that all the content you need to export is in export default. If you have both default and normal export, it is obviously not feasible to export only the default part of the compiled file.
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.