7. 重学webpack——如何打包一个库

推荐:《webpack学完这些就够了》

《webpack学完这些就够了》该专题主要讲述webpack4.x从入门到成仙的学习笔记,配置和原理应有尽有。现在由于webpack5.0的诞生,打算从现在开始从0开始写一遍webpack5.0的学习笔记,与大家一起分享学习。

以下是本节正文:

在工作中,经常会用到一些外部依赖的库。通常有以下的引用形式

// 直接通过 cdn 引用,然后在全局直接使用 React 即可
1<script src="https://unpkg.com/react@16.12.0/umd/react.development.js" />

// 通过 cmd 的形式引入
2const React = require('React')

// import 的方式最终会被转换为通过 cmd 的形式引入
3const React = import('React')

对于一些开发者来说,使用 webapck 的大部分场景可能就是用来对项目进行打包(打包生成 css、js、image、font 等资源然后通过 Html 引入即可完成页面的渲染),不会涉及到库的打包。打包一个库不同的地方是需要将你的库向外部暴露一个变量,这样别人才能使用你的库。例如前面通过 cdn 引入的 React 库,它会暴露 React 变量,如下所示

在这里插入图片描述

通过执行一个函数返回一个对象然后挂载到 global.React 上,这样就可以在全局使用了。

上面只是向外暴露你的库中的一种方式,在浏览器环境中暴露一个变量,具体需要根据你的场景来定,例如你可能是一个包,通过 cmd 的方式引入。

这里如何将你的库向外暴露一个变量的方式主要是通过配合下面三个配置项来达到的。

output.library
output.libraryExport
output.libraryTarget
假设你写了一个库,并且导出的内容如下

function a() {}
function b() {}
module.exports = { func: { a: a, b: b } }

假设经过 webapck 编译后,module.exports 导出的对象会被挂到一个叫 entry_return 的变量下。webapck 官网给出的解释如下:

Note that_entry_return_is the value returned by the entry point. In the bundle itself, it is the output of the function that is generated by webpack from the entry point.

library
可以用它来指定导出的包名,如下

module.exports = {
  //...
  output: {
    library: 'MyLibrary',
    // libraryTarget: 'var'
  }
};

它的用途需要根据 libraryTarget 来确定,默认情况下 libraryTarget 的值为 var,上面的代码经过编译后如下。

var MyLibrary = _entry_return_; // _entry_return_ 代表 webapck 编译所返回的产物

需要注意的是,如果 entry 的入口指定为一个数组,那么只有数组最后一项会被暴露出来

假设你是通过 script 标签将上去资源引人到页面中,那么上面定义了一个 MyLibrary 变量到全局,然后就可以在全局直接使用该库了。至于模块化引入的方式参考后面说到的 libraryTarget 。

libraryExport
用它来指定你需要导出的模块内容。

可以使用 libraryExport 来指定具体需要导出的部分,看

module.exports = {
  //...
  output: {
    library: 'MyLibrary',
    libraryExport: ['func', 'a']
    libraryTarget: 'var'
  }
};

经过编译,它最终是这样:

var MyLibrary = _entry_return_.func.a; // 导出 a 函数赋值给 MyLibrary

它可以有四种值:

1、libraryExport: undefined 不设置或者 libraryTarget 的值为 ‘’,它的值为 undefined
2、libraryExport: 'default'
3、libraryExport: 'MyModule' 使用自定义 name 导出
4、libraryExport: ['MyModule', 'MySubModule']使用多个自定义 name 导出
上面四种情况编译后得到的编译结果分别如下

1var MyLibrary = _entry_return_ // 会暴露整个打包后得到的 bundle 中导出的内容
2var MyLibrary = _entry_return_.default
3var MyLibrary = _entry_return_.MyModule
4var MyLibrary = _entry_return_.MyModule.MySubModule 

libraryTarget
它定义了 webpack 打包后的 bundle 中导出的内容如何被暴露,它的值有很多种,我们一种一种来看,假设 webpack 配置如下,后面的 都以这个为基础,我们通过改变 libraryTarget 的值来举

module.exports = {
  //...
  output: {
    library: 'MyLibrary',
    libraryTarget: 'var'
  }
};

libraryTarget: 'var':默认值。通过 var 暴露 MyLibrary 变量。编译后如下:

var MyLibrary = _entry_return_; 

libraryTarget: 'assign':复制操作,这可能会导致全局污染。编译后如下:

MyLibrary = _entry_return_; 

如果前面 library 是空字符串类型或没有指定 library 那么 _entry_return_中所有的值都会被复制到 libraryTarget 给的对象(通常为 window、global、exports 下面会解释)中。过程如下

(function(e, a) { 
 for(var i in a) { 
  e[i] = a[i];
 } 
}(output.libraryTarget, _entry_return_)); 

相反如果library 给定了值,libraryTarget也是一个对象, 那么以library 的值为key_entry_return_(这里没有管 libraryExport 值的情况,只是举 ) 为 value,定义到libraryTarget给定的对象上去,通常有下面几个值。

libraryTarget: 'window':定义到 window 对象上,浏览器环境有效。 编译后如下:

window['MyLibrary'] = _entry_return_;

libraryTarget: 'global':同理。编译后如下

global['MyLibrary'] = _entry_return_; 

libraryTarget: 'commonjs':在 commonjs 环境中, entry_return 的值会被赋值给 exports 对象。编译后如下

exports['MyLibrary'] = _entry_return_; 

前面列出的情况除了 commonjs 大多适用于浏览器环境,通过 script 标签引入比较合适。还有一些是跟模块化相关的内容。通过不同的模块化规范导出。这在发布 npm 包的时候很有用。

libraryTarget: 'commonjs2':这里忽略了 library 的值,因为这种情况不需要。编译后如下
module.exports = entry_return;
libraryTarget: ‘amd’:通过 AMD 规范导出。不做介绍。
libraryTarget: ‘umd’:这是一个万能的配置(常用),可以兼容 CommonJS, AMD 和 全局变量,它编译后的代码如下。

(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if(typeof define === 'function' && define.amd)
    define([], factory);
  else if(typeof exports === 'object')
    exports['MyLibrary'] = factory();
  else
    root['MyLibrary'] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
  return _entry_return_;
});

如果不指定 library 那么会被定义到全局,过程如下:

(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if(typeof define === 'function' && define.amd)
    define([], factory);
  else {
    var a = factory();
    for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
  }
})(typeof self !== 'undefined' ? self : this, function() {
  return _entry_return_;
}); 

本文来源:https://zhuanlan.zhihu.com/p/108873701

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值