夫天地者,万物之逆旅;光阴者,百代之过客。而浮生若梦,为欢几何?
手撕webpack:创建自己的 library

什么是 library?

webpack 除了打包应用程序代码,还可以用于打包 JavaScript library。我们在使用 vue 、element 等前端框架(library)时,可以通过 ES6 Modules import 导入使用,也可以通过 script 引入组件库,更或是使用 CommonJS,要支持这么多的使用方式,需要熟悉使用 output.library 和 output.libraryTarget 两个选型的使用。

搭建自己 library 的基础代码

假设我们正在编写一个校验字符串是否为空的 library,如果输入的字符串为空返回 true,反之返回 false。基本的项目结构如下:

src/index.js

function isEmpty(str) {
  if(str==null || typeof str=='undefined' || str.trim()=="") {
    return true
  } else {
    return false
  }
}

export {isEmpty}

理想中 library 在使用时为了避免和其他 library 混淆,一般会起一个自己的独有名称,这里我们使用 MyLibrary ,使用方式如下:

// ES2015 模块引入
import * as MyLibrary 'isEmpty';

// CommonJS 模块引入
var MyLibrary = require('MyLibrary');

var name = "暗夜余晖";

// ES2015 和 CommonJS 模块调用
MyLibrary .isEmpty(name );


// AMD 模块引入
require(['MyLibrary'], function ( MyLibrary) {
  var name = "暗夜余晖";
  // AMD 模块调用
  MyLibrary.isEmpty(name );
  
});

要实现以上目标,library 应该如何打包呢?

打包适用于 script 标签引入的组件库

用户可以通过 script 标签来加载和使用此 library:

<!doctype html>
<html>

  <script src="./dist/isEmpty.js"></script>
  <script>
      var name = "暗夜余晖";
     
    MyLibrary.isEmpty(name );
    
  </script>
</html>

webpack 的配置:

const path = require("path")

module.exports = {
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'isEmpty.js',
    path: path.resolve(__dirname, 'dist'),
    library: 'MyLibrary',
    libraryTarget: "var"
  }
}

library 配置我们的库名称,这里是 "MyLibrary",libraryTarget 配置如何对外暴露 library ,不写的时候默认值是 "var"。

libraryTarget: "var"  表示当 library 加载完成,入口起点的返回值将分配给一个变量,向下面这样:

var MyLibrary = _entry_return_;

来看看实际打包后的输出结果(只截取开头一小段):

暴露为 window 对象的属性

上面打包出的文件,isEmpty 方法被使用时需要增加 MyLibrary 前缀,即 库名.函数名 的格式,有时我们的函数名可能是一个独一无二的名称,更期望简洁方便的直接使用函数名的方式调用,类似下面这样:

<!doctype html>
<html>

  <script src="./dist/isEmpty.js"></script>
  <script>
    var name = "暗夜余晖";
    // 直接使用函数名
    isEmpty(name);
    // 或是
    window.isEmpty(name);
  </script>
</html>

想打出这种 library  也很简单,把 output.library 的值配置成空,  output.libraryTarget 值改成 "window" 就可以了:

const path = require("path")

module.exports = {
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'isEmpty.js',
    path: path.resolve(__dirname, 'dist'),
    library: '',
    libraryTarget: "window"
  }
}

打包后的 bundle 代码就不贴了,知道它的原理就可以了:

libraryTarget: "window" 会将入口起点的返回值赋值给 window 对象的某个属性,这个属性取决于 library 的值。如果不设置 output.library 将导致由入口起点返回的所有属性,都会被赋值给给定的对象,这里并不会检查现有的属性名是否存在。

打包为模块

打包后的 bundle 能与各种模块系统(commonjs2、ES2015、AMD )兼容。

打包为 CommonJS 模块

用户可以使用 require 引入模块。

// CommonJS 模块引入
var MyLibrary = require('MyLibrary');

var name = "暗夜余晖";

// CommonJS 模块调用
MyLibrary .isEmpty(name );

使用 libraryTarget: "commonjs2" , 入口起点的返回值将分配给 module.exports 对象,这个名称也意味着模块用于 CommonJS 环境。使用  libraryTarget: "commonjs2"  时 output.library 将被忽略,所以我们可以不配置这个属性的值。

const path = require("path")

module.exports = {
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'isEmpty.js',
    path: path.resolve(__dirname, 'dist'),
    libraryTarget: "commonjs2"
  }
}

打包后的 bundle 结果:

打包为兼容全部环境的 library

libraryTarget: "umd" - 将你的 library 暴露为所有的模块定义下都可运行的方式。它将在 CommonJS, AMD 环境下运行,或将模块导出到 global 下的变量。

const path = require("path")

module.exports = {
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'isEmpty.js',
    path: path.resolve(__dirname, 'dist'),
    library: 'MyLibrary',
    libraryTarget: "umd"
  }
}

打包后的 bundle 结果:

作者:暗夜余晖

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

0

支持

0

反对

posted @2020-5-14  拜读(403)

评论列表

#1楼 2020-3-26 218.93.213.190
您好~ 我是csdn 运营 因看到你的精彩内容,诚邀您的文章授权到您的CSDN个人博客,让更多的用户分享您的内容,同时也是您的内容多了一个平台分发引流,不知您是否愿意?当然,我们也会针对同步后的优质博文进行CSDN首页及APP端的推荐,期待您的入驻。https://t.csdnimg.cn/EHP7

评论内容:



喜欢请打赏

支付宝 微信

请放心支付