• 技术文章 >web前端 >js教程

    使用webpack模块给Library打包原理及实现

    亚连亚连2018-05-31 13:53:28原创1105
    这篇文章主要介绍了webpack组织模块打包Library的原理及实现,现在分享给大家,也给大家做个参考。

    之前一篇文章分析了Webpack打包JS模块的基本原理,所介绍的案例是最常见的一种情况,即多个JS模块和一个入口模块,打包成一个bundle文件,可以直接被浏览器或者其它JavaScript引擎执行,相当于直接编译生成一个完整的可执行的文件。不过还有一种很常见的情况,就是我们要构建发布一个JavaScript的库,比如你在npm社区发布自己的库,这时Webpack就需要相应的配置,编译生成的代码也会略有不同。

    和之前一篇文章一样,本文主要分析的是Webpack的生成代码,并结合它来说明编译库时Webpack那些关于library的配置选项的具体作用,相应的官方文档在这里。

    编写JS的库

    我们还是从简单的案例开始,我们随便编写一个简单的库util.js:

    import $ from 'jquery'
    
    function sayHello() {
     console.log("Hello");
    }
    
    function hideImages() {
     $('img').hide();
    }
    
    export default {
     sayHello: sayHello,
     hideImages: hideImages
    }

    提供了两个函数,当然它们之间毫无关系,也实际上没有任何卵用,纯粹是仅供教学参考。。。

    接下来写Webpack的配置:

    // 入口文件
    entry: {
     util: './util.js',
    }
    
    // 输出文件
    output: {
     path: './dist',
     filename: '[name].dist.js'
    }

    但仅仅这样是不够的,这样输出的文件就是一个立即执行的函数,最后会返回util.js的exports,参照上一篇文章中分析,最后生成的bundle代码结构大致是这样的:

    (function(modules) {
     var installedModules = {};
     
     function webpack_require(moduleId) {
       // ...
     }
    
     return webpack_require('./util.js');
    }) ({
     './util.js': generated_util,
     '/path/to/jquery.js': generated_jquery
    });

    它如果执行完,那就结束了,将util.js的export部分返回而已,而我们需要的是将这个返回值交给编译后的文件的module.export,这样编译后的文件就成了一个可以被别人import的库。所以我们希望得到的编译文件应该是这样的:

    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
    });

    要得到这样的结果,Webpack配置output部分需要加入library信息:

    // 入口文件
    output: {
     path: './dist',
     filename: '[name].dist.js',
    
     library: 'util',
     libraryTarget: commonjs2
    }

    这里最重要的就是libraryTarget,我们现在采用commonjs2的格式,就会得到上面的编译结果,也就是说Webpack会library把最后的输出以CommonJS的形式export出来,这样就实现了一个库的发布。

    其它发布格式

    除了commonjs2,libraryTarget还有其它选项:

    var (默认值,发布为全局变量)
    commonjs
    commonjs2
    amd
    umd

    使用不同的选项,编译出来的文件就能够在不同JavaScript执行环境中使用。在这里我们直接看万金油umd格式的输出是怎么样的:

    (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
     });
    }

    比之前的commonjs2的情况要复杂得多,因为它需要处理各种不同的case,但其实后面的部分都是一样的,最重要的就是最前面的几行,这是umd模块的标准写法。它运行传入的factory函数,实际上就是加载模块的函数,然后把返回的结果根据不同的运行环境交给相应的对象。例如var,那就会把结果设置为一个全局变量,这用于浏览器通过<script>标签直接导入该JS文件;如果是CommonJS,它则交给exports对象;如果是AMD环境,它也是用标准的AMD的写法。这样这个发布出来的JS库就可以在任意的环境中都能被其他人使用。

    targetExport控制输出内容

    如果用umd格式打包,可能会有一个坑需要注意,如果你的库的源代码是用ES6格式export default来输出的,正如上面的例子util.js,你直接把编译后的JS库文件放到浏览器中使用,可以是<script>,或者RequireJS,可能得不到你想要的结果。这是因为你的JS文件返回给你的对象是这样的:

    {
     'default': {
      sayHello: sayHello,
      hideImages: hideImages
     }
    }

    而不是你所期望的:

    {
     sayHello: sayHello,
     hideImages: hideImages
    }

    不仅是浏览器,在不支持ES6的模块系统中同样会出这个问题,就是因为它们并不认识default。所以你编译后的JS文件实际上应当只输出default,这就需要在Webpack配置里用targetExport来控制:

    library: 'util',
    libraryTarget: umd,
    targetExport: 'default'

    这样上面的模块加载函数factory会在返回值后面加一个['default'],这样就只返回exports的default部分。

    这个坑在umd格式下其实还是挺容易踩到的,例如你发布一个Vue组件,.vue文件中的JavaScript部分一般都是把Component对象以export default的格式导出的,就像这样:

    export default {
     name: 'xxx',
     data: {
      return // ...
     },
     props: {
      // ...
     }
     methods: {
      // ...
     }
    }

    如果你把编译后的JS文件直接放在浏览器里运行,并且用CDN的方式通过<script>加载Vue,你会发现Vue无法识别这个Component,因为你得到的这个对象多了一层不必要的default。

    你可能会问如果我把输出内容改成了default,会不会影响这个模块在ES6环境下的使用?一般来说是不会的。之前一篇文章里已经谈到,Webpack的生成代码在引入一个模块时,会通过一个叫__esModule的值来设置和判断它是不是ES6格式的export,现在如果只导出default部分,那么这个对象是被视为非ES6的,因为它不含__esModule。这样其它模块通过import来引入这个模块时,会引入整个对象,这实际上变相地就等价于只引入原模块的export default部分。

    当然以上讨论的前提是,你所有需要export的内容全部都在export default里。如果你既有default,又有正常的export,那编译后的文件只导出default部分显然是不行的。

    上面是我整理给大家的,希望今后会对大家有帮助。

    相关文章:

    200行代码实现blockchain 区块链实例详解

    vue使用facebook twitter分享示例

    react以create-react-app为基础创建项目

    以上就是使用webpack模块给Library打包原理及实现的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:Library webpack web
    上一篇:在webpack中如何使用external模块 下一篇:深入讲解webpack模块的基本原理
    20期PHP线上班

    相关文章推荐

    • 【活动】充值PHP中文网VIP即送云服务器• node.js gm是什么• 进程和线程如何理解?Node.js中的进程和线程是怎样的?• 详细介绍JavaScript中Promise的基本概念及使用方法• node的异步机制是基于什么• node.js后台框架有哪些
    1/1

    PHP中文网