手把手带你从0开始创建并发布npm包

都2202年了,不会有人还不会发布npm包吧?下面本篇文章给大家分享一下从0开始创建并发布npm的全过程,希望对大家有所帮助!

手把手带你从0开始创建并发布npm包

背景

4月发布了一篇文章,快来升级你项目内的axios封装,向重复请求say goodbye,介绍了axios的二次封装用于支持常规请求及自定义请求,并对同一时间内的相同请求做拦截处理(如果您没有阅读过这篇文章,建议您花费3分钟大致了解)。恰逢最近准备写一个跨框架组件库(工作量很大,前端三个小伙伴利用空闲时间在卷,待组件库完善后会分享给大家,敬请期待),需要学习发布npm包,昨天就想着利用空闲时间把之前写的去除重复请求的axios封装发布为npm包,便于代码复用,回馈社区的同时也能学以致用。

阅读本文,你将收获:

从0开始创建并发布npm的全过程。【相关教程推荐:nodejs视频教程、编程教学】

一个持续迭代且简单实用的axios请求去重工具库。

工具库准备

创建一个新项目,包含package.json

{    "name": "drrq",    "type": "module",    "version": "1.0.0"}

登录后复制

功能实现 /src/index.js

npm i qs axios

主要思路是用请求的url和参数作为key记录请求队列,当出现重复请求时,打断后面的请求,将前面的请求结果返回时共享给后面的请求。

import qs from "qs";import axios from "axios";let pending = []; //用于存储每个ajax请求的取消函数和ajax标识let task = {}; //用于存储每个ajax请求的处理函数,通过请求结果调用,以ajax标识为key//请求开始前推入pendingconst pushPending = (item) => {    pending.push(item);};//请求完成后取消该请求,从列表删除const removePending = (key) => {    for (let p in pending) {        if (pending[p].key === key) {            //当前请求在列表中存在时            pending[p].cancelToken(); //执行取消操作            pending.splice(p, 1); //把这条记录从列表中移除        }    }};//请求前判断是否已存在该请求const existInPending = (key) => {    return pending.some((e) => e.key === key);};// 创建taskconst createTask = (key, resolve) => {    let callback = (response) => {        resolve(response.data);    };    if (!task[key]) task[key] = [];    task[key].push(callback);};// 处理taskconst handleTask = (key, response) => {    for (let i = 0; task[key] && i  {    let key = url + '?' + qs.stringify(params);    return new Promise((resolve, reject) => {        const instance = axios.create({            baseURL: url,            headers,            timeout: 30 * 1000,        });        instance.interceptors.request.use(            (config) => {                if (preventRepeat) {                    config.cancelToken = new axios.CancelToken((cancelToken) => {                        // 判断是否存在请求中的当前请求 如果有取消当前请求                        if (existInPending(key)) {                            cancelToken();                        } else {                            pushPending({ key, cancelToken });                        }                    });                }                return config;            },            (err) => {                return Promise.reject(err);            }        );        instance.interceptors.response.use(            (response) => {                if (preventRepeat) {                    removePending(key);                }                return response;            },            (error) => {                return Promise.reject(error);            }        );        // 请求执行前加入task        createTask(key, resolve);        instance(Object.assign({}, { method }, method === 'post' || method === 'put' ? { data: !uploadFile ? qs.stringify(params) : params } : { params }))            .then((response) => {                // 处理task                handleTask(key, response);            })            .catch(() => {});    });};export const get = (url, data = {}, preventRepeat = true) => {    return request('get', url, data, getHeaders, preventRepeat, false);}; export const post = (url, data = {}, preventRepeat = true) => {     return request('post', url, data, postHeaders, preventRepeat, false); }; export const file = (url, data = {}, preventRepeat = true) => {     return request('post', url, data, fileHeaders, preventRepeat, true); };export default { request, get, post, file };

登录后复制

新增示例代码文件夹/example

示例入口index.js

import { exampleRequestGet } from './api.js';const example = async () => {    let res = await exampleRequestGet();    console.log('请求成功 ');};example();

登录后复制

api列表api.js

import { request } from './request.js';// 示例请求Getexport const exampleRequestGet = (data) => request('get', '/xxxx', data);// 示例请求Postexport const exampleRequestPost = (data) => request('post', '/xxxx', data);// 示例请求Post 不去重export const exampleRequestPost2 = (data) => request('post', '/xxxx', data, false);// 示例请求Post 不去重export const exampleRequestFile = (data) => request('file', '/xxxx', data, false);

登录后复制

全局请求封装request.js

import drrq from '../src/index.js';const baseURL = 'https://xxx';// 处理请求数据  (拼接url,data添加token等) 请根据实际情况调整const paramsHandler = (url, data) => {    url = baseURL + url;    data.token = 'xxxx';    return { url, data };};// 处理全局接口返回的全局处理相关逻辑  请根据实际情况调整const resHandler = (res) => {    // TODO 未授权跳转登录,状态码异常报错等    return res;};export const request = async (method, _url, _data = {}, preventRepeat = true) => {    let { url, data } = paramsHandler(_url, _data);    let res = null;    if (method == 'get' || method == 'GET' || method == 'Get') {        res = await drrq.get(url, data, preventRepeat);    }    if (method == 'post' || method == 'POST' || method == 'Post') {        res = await drrq.post(url, data, preventRepeat);    }    if (method == 'file' || method == 'FILE' || method == 'file') {        res = await drrq.file(url, data, preventRepeat);    }    return resHandler(res);};

登录后复制

测试功能

代码写完后,我们需要验证功能是否正常,package.json加上

    "scripts": {        "test": "node example"    },

登录后复制

执行npm run test

image.png

功能正常,工具库准备完毕。

(eslint和prettier读者可视情况选用)

打包

一般项目的打包使用webpack,而工具库的打包则使用rollup

安装 Rollup

通过下面的命令安装 Rollup:

npm install --save-dev rollup

登录后复制

创建配置文件

在根目录创建一个新文件 rollup.config.js

export default {  input: "src/index.js",  output: {    file: "dist/drrp.js",    format: "esm",    name: 'drrp'  }};

登录后复制input —— 要打包的文件output.file —— 输出的文件 (如果没有这个参数,则直接输出到控制台)output.format —— Rollup 输出的文件类型

安装babel

如果要使用 es6 的语法进行开发,还需要使用 babel 将代码编译成 es5。因为rollup的模块机制是 ES6 Modules,但并不会对 es6 其他的语法进行编译。

安装模块

rollup-plugin-babel 将 rollup 和 babel 进行了完美结合。

npm install --save-dev rollup-plugin-babel@latestnpm install --save-dev @babel/core npm install --save-dev @babel/preset-env

登录后复制

根目录创建 .babelrc

{    "presets": [      [        "@babel/preset-env",        {          "modules": false        }      ]    ]}

登录后复制

兼容 commonjs

rollup 提供了插件 rollup-plugin-commonjs,以便于在 rollup 中引用 commonjs 规范的包。该插件的作用是将 commonjs 模块转成 es6 模块。

rollup-plugin-commonjs 通常与 rollup-plugin-node-resolve 一同使用,后者用来解析依赖的模块路径。

安装模块

npm install --save-dev rollup-plugin-commonjs rollup-plugin-node-resolve

登录后复制

压缩 bundle

添加 UglifyJS 可以通过移除注上释、缩短变量名、重整代码来极大程度的减少 bundle 的体积大小 —— 这样在一定程度降低了代码的可读性,但是在网络通信上变得更有效率。

安装插件

用下面的命令来安装 rollup-plugin-uglify:

npm install --save-dev rollup-plugin-uglify

登录后复制

完整配置

rollup.config.js 最终配置如下

import resolve from 'rollup-plugin-node-resolve';import commonjs from 'rollup-plugin-commonjs';import babel from 'rollup-plugin-babel';import { uglify } from 'rollup-plugin-uglify';import json from '@rollup/plugin-json'const paths = {    input: {        root:  'src/index.js',    },    output: {        root:  'dist/',    },};const fileName = `drrq.js`;export default {    input: `${paths.input.root}`,    output: {        file: `${paths.output.root}${fileName}`,        format: 'esm',        name: 'drrq',    },    plugins: [        json(),        resolve(),        commonjs(),        babel({            exclude: 'node_modules/**',            runtimeHelpers: true,        }),        uglify(),    ],};

登录后复制

在package.json中加上

"scripts": {    "build": "rollup -c"},

登录后复制

即可执行npm run build将/src/index.js打包为/dist/drrq.js

发包前的准备

准备npm账号,通过npm login或npm adduser。这里有一个坑,终端内连接不上npm源,需要在上网工具内复制终端代理命令后到终端执行才能正常连接。

image.png

准备一个简单清晰的readme.md

image.png

修改package.json

完整的package.json如下

{    "name": "drrq",    "private": false,    "version": "1.3.5",    "main": "/dist/drrq.js",    "repository": "https://gitee.com/yuanying-11/drrq.git",    "author": "it_yuanying",    "license": "MIT",    "description": "能自动取消重复请求的axios封装",    "type": "module",    "keywords": [        "取消重复请求",    ],    "dependencies": {        "axios": "^1.2.0",        "qs": "^6.11.0"    },    "scripts": {        "test": "node example",        "build": "rollup -c"    },    "devDependencies": {       ...    }}

登录后复制name 包名称 一定不能与npm已有的包名重复,想一个简单易记的private 是否为私有version 版本main 入口文件位置repository git仓库地址author 作者license 协议description 描述keywords 关键词,便于检索

每个 npm 包都需要一个版本,以便开发人员在安全地更新包版本的同时不会破坏其余的代码。npm 使用的版本系统被叫做 SemVer,是 Semantic Versioning 的缩写。不要过分担心理解不了相较复杂的版本名称,下面是他们对基本版本命名的总结:给定版本号 MAJOR.MINOR.PATCH,增量规则如下:MAJOR 版本号的变更说明新版本产生了不兼容低版本的 API 等,MINOR 版本号的变更说明你在以向后兼容的方式添加功能,接下来PATCH 版本号的变更说明你在新版本中做了向后兼容的 bug 修复。表示预发布和构建元数据的附加标签可作为 MAJOR.MINOR.PATCH 格式的扩展。

最后,执行npm publish就搞定啦

image.png

image.png

本文的完整代码已开源至gitee.com/yuanying-11… ,感兴趣的读者欢迎fork和star!

转载地址:https://juejin.cn/post/7172240485778456606

更多node相关知识,请访问:nodejs 教程!

以上就是手把手带你从0开始创建并发布npm包的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2697155.html

(0)
上一篇 2025年3月7日 18:40:08
下一篇 2025年3月6日 22:53:49

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 一文带你深入了解Node中的Buffer类

    本篇文章带大家深入了解下node中 buffer(缓冲区)类,希望对大家有所帮助! 在TypedArray出来之前,JavaScript这门语言是不能很好地处理原始二进制数据(raw binary data)的,这是因为一开始的时候Java…

    2025年3月7日
    200
  • 聊聊如何选择一个最好的Node.js Docker镜像?

    选择一个node的docker镜像看起来像是一件小事,但是镜像的大小和潜在漏洞可能会对你的ci/cd流程和安全造成重大的影响。那我们如何选择一个最好node.js docker镜像呢? 我们在使用FROM node:latest或只是FRO…

    2025年3月7日
    200
  • 一文聊聊node中的path模块

    path 模块是 nodejs 中用于处理文件/目录路径的一个内置模块,可以看作是一个工具箱,提供诸多方法供我们使用,当然都是和路径处理有关的。同时在前端开发中 path 模块出现的频率也是比较高的,比如配置 webpack 的时候等。本文…

    2025年3月7日
    200
  • 实战学习:聊聊Node.js怎么操作数据库

    本篇文章分享node.js服务端实战,介绍一下node操作数据库的方法,希望对大家有所帮助! 本系列是使用node作为服务器开发的操作过程记录,记录一下主要的内容并且整理过程的脉络,以初学者的方式将学习内容记录下来,从0到1逐步的学习nod…

    2025年3月7日 编程技术
    200
  • 一文详解多版本node的安装和管理

    本篇文章带大家聊聊多版本node的管理,介绍一下多版本node的安装与切换详细操作,希望对大家有所帮助! 安装多版本node的原因: 在项目开发过程中,不同项目使用的nodejs版本不同,有时会因为node版本过高或太低,导致报错;如何在同…

    2025年3月7日 编程技术
    200
  • 一文聊聊Node包管理发展的五个阶段

    从2009年 node 诞生至今,node 生态发展繁荣,围绕 node 生态衍生出的 node 包管理器百花齐放,先后出现了 npm、kpm、pnpm、yarn、cnpm 等等。实际上 node 包管理器发展主要分5个阶段,下面我们一起看…

    2025年3月7日 编程技术
    200
  • 一文详解Nodejs怎么卸载(步骤分享)

    node怎么卸载?下面本篇文章给大家分享一下node卸载超详细步骤,有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。 Node卸载超详细步骤 1、打开系统的控制面板,点击卸载程序,卸载nodejs 【相关教程推荐:node、…

    2025年3月7日 编程技术
    200
  • 浅析nodejs中怎么使用JWT?

    本篇文章带大家了解一下jwt,介绍一下jwt在node中的应用,以及jwt的优缺点,希望对大家有所帮助! 什么是JWT JWT也就是JSON Web Token的缩写,也就是为了在网络应用环境中一种认证解决方案,在传统的认证机制中,无非是一…

    2025年3月7日 编程技术
    200
  • 一文带你了解Node.js中的http模块

    本篇文章给大家了解一下node.js http模块,介绍一下使用http模块创建服务器的方法,希望对大家有所帮助! 1、什么是http 在百度百科的解释: 超文本传输协议(Hyper Text Transfer Protocol,HTTP)…

    2025年3月7日 编程技术
    200
  • Node实战学习:浏览器预览项目所有图片

    在前端实际项目开发中,会有这样一种场景。每次引入新的图片,并不知道这个资源是否被引用过,所以会点开存放图片的资源一个个去看。实际问题是: 1.图片并不是放到一个目录下的,可能存在任何的地方,不好查找 2.费时间,费力 3.可能会重复引入图片…

    2025年3月7日
    200

发表回复

登录后才能评论