发布一个npm包的具体步骤

1,504 阅读7分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

一.npm 是什么?

npm 是世界上最大的软件注册中心。世界各地的开源开发者都使用 npm 来共享包,许多组织也使用 npm 来管理私有开发[1]

1. npm 的组成

官方文档上介绍到,由三部分组成

2.使用 npm 的好处

  • 方便下载各种工具
  • 使用 npx 无需下载即可运行包。
  • 在任何地方与任何 npm 用户共享代码。
  • 寻找其他正在处理类似问题和项目的开发人员。

二.开始尝试

  • 首先挖掘需求吧,什么情况下需要一个 npm 包呢?
  • 比如,在开发过程中,经常需要的使用的一些代码,我们就可以把它们聚合起来,通过维护一个 npm 包,在不同项目中引入即可。
  • 其实已经准备好了,可以看这几篇:
  1. 前端常用js代码汇总
  2. canvas的简单实用方法汇总
  3. fetch请求下载文件,内含主要步骤和实现代码
  • 这些都可以做成一个 npm 包,独立使用。
  • 当然,主要原因还是 本文第一句

三.编写代码

1.初始化项目

  • yarn init 生成一个package.json文件
{
  "name": "flibrary",
  "version": "1.0.0",
  "description": "聚合各种函数的 JS 工具包,具体看readme",
  "main": "index.js",
  "repository": "",
  "author": "",
  "license": "MIT"
}
  • 需要注意的就是
 "main": "index.js",
  • 这是打包输出文件的入口

2.新建一个index.js

// index.js
export const getAllQueryString = (url) => {
  const r = {};
  const _url = url || window.location.href;
  if (_url.split('?')[1]) {
      let str = _url.split('?')[1];
      str = str.split('&');
      str.forEach((item) => {
          const key = item.split('=')[0];
          const val = item.split('=')[1];
          r[key] = decodeURIComponent(val);
      });
  }
  return r;
}

...
  • 目前的文件目录 image.png

三.打包压缩

1.目前主流的打包工具有两个: rollup 和 webpack

  • rollup 最早支持 Tree-shaking ,主要用来打包类库
  • webpack 主要用来开发应用。但随着发展,该有的功能几乎都有了
  • 所以其实差别不是很大,用任意一个压缩打包代码都可以,看个人喜好

2.为什么要打包压缩

  • 需要babel编译,解决各种兼容问题

  • 压缩代码,减少文件体积

  • 方便对代码进行一些统一处理

  • 所以打包过程实际就是一个输入输出的过程

  • 需要注意的是:

    • 在导入模块的选择上,我们可以使用 require 或者 import
    • require是CommonJs的语法(AMD规范引入方式),CommonJs的模块是对象。
    • require 是运行时加载整个模块(即模块中所有方法),生成一个对象,再从对象上读取它的方法(只有运行时才能得到这个对象,不能在编译时做到静态化)。通过 require 引入基础数据类型时,属于复制该变量。通过 require 引入复杂数据类型时,数据浅拷贝该对象。
    • import 命令会被 JavaScript 引擎静态分析,先于模块内的其他模块执行,具有提升效果。会提升到模块的头部,import导出的类型看定义的对象,定义number导出就是number,定义字符串就是字符串,你定义对象导出的就是对象。
    • 不同的导入引用方式,会有不同的打包效果
    • export和import可以位于模块中的任何位置,但是必须是在模块顶层,如果在其他作用域内,会报错。

2. rollup 压缩打包

(1).安装

npm install rollup

(2).直接使用rollup命令

rollup

由于不加任何参数,打印出rollup的使用说明,和运行 rollup --help 或 rollup -h 的效果一样。

(3).打包进行输入输出

  • 既然打包过程就是一个输入和输出的过程,那么至少要指定输入的文件,和输出的格式
rollup index.js -f cjs

-f 选项(--output.format 的缩写)指定了所创建 bundle 的类型——这里是 CommonJS(在 Node.js 中运行)。

  • 其他参数:
    • amd – 异步模块定义,用于像RequireJS这样的模块加载器
    • cjs – CommonJS,适用于 Node 和 Browserify/Webpack
    • esm – 将软件包保存为 ES 模块文件,在现代浏览器中可以通过 <script type=module> 标签引入
    • iife – 一个自动执行的功能,适合作为<script>标签。(如果要为应用程序创建一个捆绑包,您可能想要使用它,因为它会使文件大小变小。)
    • umd – 通用模块定义,以amd,cjs 和 iife 为一体
    • system - SystemJS 加载器格式
  • 备注:如果直接使用这个命令打包本次的代码,会报错
[!] Error: Unexpected keyword 'export'
  • 既然能指定输出的格式,那么肯定可以指定输出的文件名
rollup index.js -o bundle.js -f cjs
  • 如果需要开启各种功能,代码压缩,sourcemap,babel等,直接使用命令行可能就不方便了,所以就需要打包的配置文件,通过指定各种配置文件,来输出想要的结果
rollup -c rollup.config.js
  • 注意:如果同时有命令行选项和配置文件,命令行选项将会覆盖配置文件中的选项

(4).rollup.config.js

import resolve from 'rollup-plugin-node-resolve'; // 支持导入node的包
import commonjs from 'rollup-plugin-commonjs'; // 支持导入commonjs
import babel from 'rollup-plugin-babel'; // 编译代码
import { terser } from 'rollup-plugin-terser'; // 代码压缩,去除注释
import { eslint } from 'rollup-plugin-eslint'; 

export default [
  {
    input: './index.js',
    output: {
      name: '',
      file: 'bundle.js',
      format: 'umd',
      sourcemap: true,
      // 这里需要注意,设置打包压缩后的文件开头,会被 terser 插件去除
      banner: '/*eslint-disable*/',
    },
    plugins: [
      resolve({
        browser: true,
      }), // 这样 Rollup 能找到 `ms`
      commonjs(), // 这样 Rollup 能转换 `ms` 为一个ES模块
      eslint({
        throwOnError: true,
        throwOnWarning: true,
        include: ['main.js'],
        exclude: ['node_modules/**'],
      }),
      babel({
        exclude: 'node_modules/**', // 防止打包node_modules下的文件
        runtimeHelpers: true, // 使plugin-transform-runtime生效
      }),
      terser()
    ],
  },
];

(4).package.json 添加脚本

"scripts": {
    "build": "rollup -c ./rollup.config.js", 
    "test": "echo \"Error: no test specified\" && exit 1",
    "prepublish": "npm run build" 
  },
  • 这样直接执行 npm run build 就能打包
  • prepublish是再执行npm publish(发布前)自动再执行npm run build一次

3.webpack 打包

  • 用完rollup后,同时也用webpack试试
//webpack.config.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  entry: './main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'webpack.bundle.js'
  },
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  },
};
  • 执行命令:npx webpack --config webpack.config.js

四.调试

1.npm link

(1).在需要打包成npm的项目目录内输入npm link生成一个链接,从全局的node/v14.8.0/lib链接到本地正在开发的这个项目上

/Users/.nvm/versions/node/v14.8.0/lib/node_modules/flibrary -> /Users/event/flibrary

(2).链接到全局之后,就可以在开发的时候调试了。在需要引用这个包的环境内使用npm link flibrary(上面生成链接的包的名字)

链接成功,就可以直接引用了

image.png import { getAllQueryString } from "flibrary"

node_modules中会出现一个链接的文件

image.png

2.npm unlink

使用完之后记得unlink就可以了,操作和npm link一样

3.断点调试

  1. vscode的断点调试文档

五.发布

1. 到 npmjs上注册一个账号

2. npm adduser

输入用户名,密码,邮箱即可登入成功

3. 可以用npm whoami进行查看是否登录成功

4. npm publish

  • 注意:npm publish 的时候会把项目目录里面所有的文件都publish到npm仓库中。
  • 发布指定文件方法:
    • 一:使用 .gitignore,在git代码管理和 npm publish 都会被忽略
    • 二:使用 .npmignore: .npmignore 写法跟 .gitignore 完全一样。若同时使用了 .npmignore和 .gitignore,只有 .npmignore 会生效,在npm发布上优先级比.gitignore 更高。
    • 三:使用 package.json 的 files 字段选择发布哪些文件直接在 package.json 中 files 字段设置发布哪些文件或目录。这个优先级高于 .npmignore 和 .gitignore。

PS:选择哪种方法,根据自己的需求而定。

不过注意下发布的版本,在package.json里面,也可以用命令行 npm publish --tag指定

image.png

参考资料

  1. npm官方文档