通过webpack创建并打包js库到npm仓库

1.创建项目并进行基本配置

webpack配置文件: webpack.build.js

javascript 复制代码
const path = require('path');
  
module.exports = {
    mode:'development',
    entry:'./src/webpack-numbers.js',
    output: {
      filename: 'webpack-numbers.js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    },
  };

package.json:

javascript 复制代码
script:{
 "buildJs": "webpack --config ./webpack.build.js",
"dev": "set NODE_ENV=development && webpack serve --open --config ./webpack.dev.js --mode=development",
}

2.暴露库:需要通过 output.library 配置项暴露从入口导出的内容

注意:如果是暴露为ES Module时,不用同时设置name属性,build时会报错

javascript 复制代码
    //   暴露库
      library: {
        //   library.type设置为module时不能设置同时设置name
        type: 'module',
        },
    },

3.使用库

可以通过 script 标签使用,不演示

4.运行在 ES6、CommonJS、AMD、Node.js 等环境中

以上都只能通过script 标签使用,但是希望它能够兼容不同的环境

解决:更新 output.library 配置项,将其 type 设置为 'umd'

javascript 复制代码
output: {
     path: path.resolve(__dirname, 'dist'),
     filename: 'webpack-numbers.js',
    library: {
      name: 'webpackNumbers',
      type: 'umd',
    },
   },

我这里是将其导出为ES Module:

注意:

暴露为ES Module时,不能同时设置name属性;

且需要在output同级设置experiments.outputModule为true

javascript 复制代码
    mode:'development',
    entry:'./src/webpack-numbers.js',
    output: {
      filename: 'webpack-numbers.js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    //   暴露库
      library: {
        //   library.type设置为module时不能设置同时设置name
        type: 'module',
        },
    },
    // 设置 type: 'module'时,必须加入以下配置
    experiments: { 
        outputModule: true,
    },

5.library的type字段详解

library 可接受的数据类型是 string | string\[\] | object。string 是 object 类型的简写形式,当值为 object 类型时,object 中能包含的属性有 name、type、export、auxiliaryComment 和 umdNamedDefine。本文将重点放在 type 字段上,它决定如何公开当前库,取值基本固定,name 字段可以是任何字符串,它用来指定库的名称。

  1. library.type = var(默认值),{type: 'var', name: 'MyLibrary'}:通过MyLibrary能访问到add函数,但不能保证MyLibrary在全局变量上
  2. library.type = window ,{type: 'window', name: 'MyLibrary'}:通过window.MyLibrary能访问到add函数。
  3. library.type = module,将 library 的值改成 {type: 'module'}, 此时还要 experiments.outputModule 设置为 true。此时不存在闭包,并且能用 es modules 将库导入。
  4. library.type = this,将 library 的值改成 {type: 'this', name: 'MyLibrary'},通过 this.MyLibrary 能访问到 add 函数;
  5. 将 library 的值改成 {type: 'self', name: 'MyLibrary'},此时通过 self.MyLibrary 可访问到 add 函数,在浏览器环境的全局上下文中 self 等于 window;
  6. 将 library 的值改成 {type: 'global', name: 'MyLibrary'},此时 MyLibrary 会被分配到全局对象,全局对象会根据target值的不同而不同,全部对象可能的值是 self、global 或 globalThis。当 target 的值为 web(默认值),此时的打包结果与 library.type = self 结果一样。
  7. library.type = commonjs,将 library 的值改成 {type: 'commonjs', name: 'MyLibrary'},
  8. 将 library 的值改成 {type: 'commonjs2', name: 'MyLibrary'},CommonJs 规范只定义了 exports ,但是 module.exports 被 node.js 和一些其他实现 CommonJs 规范的模块系统所使用,commonjs 表示纯 CommonJs,commonjs2 在 CommonJs 的基础上增加了 module.exports。

webpack output.library的16 种取值方法示例_output.library.type-CSDN博客

7.使用命令npm run buildJs打包时报错

bash 复制代码
[webpack-cli] Error: Library name must be unset. Common configuration options that specific library names are 'output.library[.name]', 'entry.xyz.library[.name]', 'ModuleFederationPlugin.name' and 'ModuleFederationPlugin.library[.name]'.   

webpack-cli Error: Library name must be unset. Common configuration options that specific library names are 'output.library.name', 'entry.xyz.library.name', 'ModuleFederationPlugin.name' and 'ModuleFederationPlugin.library.name'.

解决:library.type设置为module时不能设置同时设置name

javascript 复制代码
    //   暴露库
      library: {
        //   library.type设置为module时不能设置同时设置name
        //name:'tools',
        type: 'module',
        },

6.外部化 lodash

如果工具包中将lodash也打包进去包体积会很大,所以可以通过设置externals属性将lodash包进行隔离不打包,但是这要求使用工具包的项目环境下安装了lodash这个被隔离的包。

javascript 复制代码
externals: {
     lodash: {
       commonjs: 'lodash',
       commonjs2: 'lodash',
       amd: 'lodash',
       root: '_',
     },
   },

7.问题:导出为ES6报错:

ERROR in external {"commonjs":"lodash","commonjs2":"lodash","amd":"lodash","root":"_"}

Cannot read properties of undefined (reading 'length')

TypeError: Cannot read properties of undefined (reading 'length')

解决:配置ES6的外置依赖库 module: 'lodash'

javascript 复制代码
    // 依赖工具库外置:要求引用的项目本身有这个工具库
    externals: {
     lodash: {
       commonjs: 'lodash',
       commonjs2: 'lodash',
       amd: 'lodash',
    //    配置ES module 
       module: 'lodash',
       root: '_',
     },

8.问题二:webpack 外部化lodash后报错,reduce()方法找不到

将lodash外部化后,工具类中的引入lodash就不能再使用了,否则就会报错。注释掉引用即可用

javascript 复制代码
// 注意外部化的时候,这里引入就不能直接引入了
// import _ from 'lodash';
import numRef from './data/ref.json';

export function numToWord(num) {
  return _.reduce(
    numRef,
    (accum, ref) => {
      return ref.num === num ? ref.word : accum;
    },
    ''
  );
}

export function wordToNum(word) {
  return _.reduce(
    numRef,
    (accum, ref) => {
      return ref.word === word && word.toLowerCase() ? ref.num : accum;
    },
    -1
  );
}

9.外部化的限制

对于想要实现从一个依赖中调用多个文件的那些库,无法通过在 externals 中指定整个 library 的方式将它们从 bundle 中排除,而是需要逐个或者使用正则表达式排除它们。

javascript 复制代码
module.exports = {
  //...
  externals: [
    'library/one',
    'library/two',
    // 匹配以 "library/" 开始的所有依赖
    /^library\/.+$/,
  ],
};

10.优化生产环境下的输出结果

为了优化生产环境下的输出结果。那么,我们还需要将生成 bundle 的文件路径,添加到 package.json 中的 main 字段中。

javascript 复制代码
{
  ...
  "main": "dist/webpack-numbers.js",
  ...
}

或者,按照这个 指南 将其添加为标准模块

javascript 复制代码
{
  ...
  "module": "src/webpack-numbers.js",
  ...
}

另外,为了暴露和库关联的样式表,你应该使用 MiniCssExtractPlugin。然后,用户可以像使用其他样式表一样使用和加载这些样式表。

11.发布到npm仓库

可以 将其发布为一个 npm 包,并且在 unpkg.com 找到它,并分发给你的用户。

进入打包好后的文件夹dist,然后npm init 创建package.json文件,并且设置基本信息

javascript 复制代码
{
  "name": "webpack-numbers-lmf",
  "version": "1.0.4",
  "description": "修改",
  "main": "webpack-numbers.js",
  "private": false,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "limingfang",
  "license": "ISC"
}

npm login登录npm仓库

命令行输入用户名和密码后npm publish发布版本即可

12.使用:

javascript 复制代码
npm i webpack-numbers-lmf lodash -S
javascript 复制代码
import { numToWord,wordToNum } from "webpack-numbers-lmf"
console.log(numToWord(3), wordToNum("Ten") );

13.完整配置代码

webpack.build.js:

javascript 复制代码
//webpack.build.js
const path = require('path');
  
module.exports = {
    mode:'development',
    entry:'./src/webpack-numbers.js',
    output: {
      filename: 'webpack-numbers.js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    //   暴露库
      library: {
        //   library.type设置为module时不能设置同时设置name
        type: 'module',
        },
    },
    // 设置 type: 'module'时,必须加入以下配置
    experiments: { 
        outputModule: true,
    },
    // 依赖工具库外置:要求引用的项目本身有这个工具库
    externals: {
     lodash: {
       commonjs: 'lodash',
       commonjs2: 'lodash',
       amd: 'lodash',
    //    配置ES module 
       module: 'lodash',
       root: '_',
     },
   },
  };
javascript 复制代码
//package.json
{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "private": false,
  "main": "dist/webpack-numbers.js",
  "module": "src/webpack-numbers.js",
  "scripts": {
    "buildJs": "webpack --config ./webpack.build.js",
    "dev": "set NODE_ENV=development && webpack serve --open --config ./webpack.dev.js --mode=development",
    "prod": "set NODE_ENV=production && webpack serve --open --config ./webpack.prod.js --mode=production"
  },
...
}
javascript 复制代码
//webpack-numbers.js
// 注意外部化的时候,这里引入就不能直接引入了
// import _ from 'lodash';
import numRef from './data/ref.json';

export function numToWord(num) {
  return _.reduce(
    numRef,
    (accum, ref) => {
      return ref.num === num ? ref.word : accum;
    },
    ''
  );
}

export function wordToNum(word) {
  return _.reduce(
    numRef,
    (accum, ref) => {
      return ref.word === word && word.toLowerCase() ? ref.num : accum;
    },
    -1
  );
}

dist里的package.json

javascript 复制代码
{
  "name": "webpack-numbers-lmf",
  "version": "1.0.4",
  "description": "修改",
  "main": "webpack-numbers.js",
  "private": false,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "xxx",
  "license": "ISC"
}

使用:

javascript 复制代码
//index.js
import _ from 'lodash';
import { numToWord,wordToNum } from "webpack-numbers-lmf"

function component() {
  console.log(numToWord(3), wordToNum("Ten") );
  return element;
}

document.body.appendChild(component());
相关推荐
晴天161 天前
peerDependencies 全面解析:前端依赖生态的核心机制与实战指南
前端·npm
天道kabuto2 天前
Webpack 迁 Vite 踩坑记:一个让人抓狂的 Sourcemap 报错怎么破?
webpack·vite
梨想橙汁2 天前
Webpack 快速上手:前端工程化、打包原理、从零搭建基础项目
前端·webpack·前端工程化
晴天162 天前
Webpack3/4/5 核心差异、性能升级与迁移实战指南-Day38
webpack·node.js
晴天163 天前
Vite vs Webpack 全方位对比
前端·webpack·node.js
晴天164 天前
打造自己的 npm 包实战指南
前端·npm·node.js
西西小飞龙4 天前
npm vs pnpm
前端·npm·node.js
PBitW6 天前
为什么vite中TS报错,可以继续运行?Webpack不行?
前端·webpack·typescript·vite
你别说话了6 天前
Webpack 如何迁移重构到 Vite
前端·webpack·重构
李游Leo7 天前
Node.js 开发环境安装与 npm/pnpm 国内镜像配置(Windows / macOS / Linux)
npm·node.js·pnpm·前端开发·开发环境