都2202年了,不会有人还不会发布npm包吧

news2024/11/25 8:22:31

背景

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

阅读本文,你将收获:

  1. 从0开始创建并发布npm的全过程
  2. 一个持续迭代且简单实用的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

//请求开始前推入pending
const 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);
};

// 创建task
const createTask = (key, resolve) => {
    let callback = (response) => {
        resolve(response.data);
    };
    if (!task[key]) task[key] = [];
    task[key].push(callback);
};
// 处理task
const handleTask = (key, response) => {
    for (let i = 0; task[key] && i < task[key].length; i++) {
        task[key][i](response);
    }
    task[key] = undefined;
};

const getHeaders = { 'Content-Type': 'application/json' };
const postHeaders = { 'Content-Type': 'application/x-www-form-urlencoded' };
const fileHeaders = { 'Content-Type': 'multipart/form-data' };

const request = (method, url, params, headers, preventRepeat = true, uploadFile = false) => {
    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';
// 示例请求Get
export const exampleRequestGet = (data) => request('get', '/xxxx', data);

// 示例请求Post
export 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

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

(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@latest
npm 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源,需要在上网工具内复制终端代理命令后到终端执行才能正常连接。

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

修改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,增量规则如下:

  1. MAJOR 版本号的变更说明新版本产生了不兼容低版本的 API 等,
  2. MINOR 版本号的变更说明你在以向后兼容的方式添加功能,接下来
  3. PATCH 版本号的变更说明你在新版本中做了向后兼容的 bug 修复。

表示预发布和构建元数据的附加标签可作为 MAJOR.MINOR.PATCH 格式的扩展。

最后,执行npm publish就搞定啦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/58050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浏览器存储(webStorage)常用API以及简单使用

前言 最近正值世纪杯期间&#xff0c;不知道大家心目中的球队成绩如何&#xff0c;在此期间我学了浏览器存储(webStorage)以及API,下面就分享给大家 浏览器存储(webStorage) 存储内容不是Vue团队打造的&#xff0c;原本的js中就有&#xff0c;所以可以不适用脚手架编译&…

UGUI性能优化学习笔记(番外)一些零星的优化点

一、Overdraw 1.1 什么是Overdraw overdraw也就是过度绘制&#xff0c;是指在每个渲染周期内&#xff0c;屏幕上每个像素最理想只渲染一次&#xff0c;但是由于UI元素的重叠会导致像素会被渲染多次&#xff0c;每次渲染从CPU阶段到GPU阶段会消耗大量资源&#xff0c;如果这种…

[附源码]计算机毕业设计JAVA学习资源共享与在线学习系统

[附源码]计算机毕业设计JAVA学习资源共享与在线学习系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; S…

Spring统一异常处理捕获不到CompletableFuture异步编排中的异常的问题

Spring统一异常处理捕获不到CompletableFuture异步编排中的异常的问题Spring统一异常处理捕获不到CompletableFuture异步编排中的异常的问题Spring统一异常处理简单例子多线程无法捕获场合正常场合&#xff08;不使用异步编排&#xff09;代码ServiceImExcpHandlerController结…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java高校车辆租赁管理系统23qhn

要开始我们毕业设计的第一步的关键就是选好我们的课题&#xff0c;有的同学开始选题的时候想着按照传统的课题延续下去&#xff0c;在设计题目时&#xff0c;不要过于笼统广泛&#xff0c;选择题目其实并不难&#xff0c;要多从自身的角度出发&#xff0c;要结合你们当前所处的…

【java】网络编程

文章目录网络编程概述基本概念IP地址概念InetAddress端口与协议概念UDP通信编程UDP发送数据UDP接受数据UDP通信程序练习TCP通信编程TCP发送数据TCP接收数据TCP通信程序练习网络编程概述 基本概念 IP地址概念 终端检查&#xff1a; InetAddress package heima.网络编程;impor…

nginx配置文件 location语法

1&#xff1a;nginx官方文档给出location语法如下&#xff1a; location [|~|~*|^~] uri { ....... }2&#xff1a;路径匹配 开头表示精确匹配。如 A 中只匹配根目录结尾的请求&#xff0c;后面不能带任何字符串&#xff1b;^~ 开头表示uri以某个常规字符串开头&#xff0c;不是…

字符串中第二大的数字(遍历)

力扣链接&#xff1a;力扣 给你一个混合字符串 s &#xff0c;请你返回 s 中 第二大 的数字&#xff0c;如果不存在第二大的数字&#xff0c;请你返回 -1 。 混合字符串 由小写英文字母和数字组成。 示例 1&#xff1a; 输入&#xff1a;s "dfa12321afd" 输出&…

Keras生成式学习(五)

生成式深度学习 生成式学习即创造学习&#xff0c;深度学习开始创造 一、使用LSTM 生成文本 给定前面的标记&#xff08;token&#xff0c;通常是单词或字符&#xff09;&#xff0c;能够对下一个标记的概率进行建模的任何网络都叫作语言模型&#xff08;language model&…

Android Camera性能分析 - 第25讲 CameraServer LatencyHistogram简介

本讲是Android Camera性能分析专题的第25讲 ​&#xff0c;我们介绍CameraServer LatencyHistogram简介&#xff0c;包括如下内容&#xff1a; LatencyHistogram是什么CameraServer默认有哪些Latency的Histogram如何获取CameraLatencyHistogram数据CameraLatencyHistogram类详…

Java项目:SSM CRM人事管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 CRM人事管理系统&#xff0c;主要功能有&#xff1a; 用户管理&#xff1a;用户查询、添加用户、编辑、删除&#xff1b; 职位管理&#xff1a…

Java基于springboot+vue的防护用品销售购物商城系统 前后端分离

开发背景 随着近些年疫情的爆发人们对个人医疗相关防护也越来越重视了&#xff0c;尤其是在疫情开始之初&#xff0c;人们对疫情感受到非常的恐慌&#xff0c;虽然在国家和领导人的领导下疫情一次次的得到了控制&#xff0c;但是我们还是要做好个人防护&#xff0c;为了让更多…

5、Linux文件系统

目录 1、万事万物皆文件 2、Linux二级文件目录 3、Linux的文件操作 4、读取文件信息 在Linux中万物皆文件 1、万事万物皆文件 1)在Linux中&#xff0c;所有的东西都是以文件的方式进行操作 2)在Linux中&#xff0c;文件的访问和Window的不一样。window依靠的是通过盘符进…

Tomcat经验2

背景 资产系统与财务系统对接&#xff0c;开发经常让我在数据库中执行数据库更新语句&#xff0c;并重启Tomcat&#xff0c;将log文件发给他&#xff0c;他能够根据log文件判断&#xff0c;数究竟有没有传对。 在这个过程中会出现一个问题&#xff1a; 部署在同一台Web服务器…

ORT(ONNX runtime)GPU 问题总结

现象 bt的堆栈信息 standard io上的错误输出 从报错信息上看是非法的内存访问&#xff0c;但是报错的位置不一定是真实的位置&#xff0c;因为GPU都是异步发起的&#xff0c;错误可能会被在后面的op捕捉。例如cudaEventDestory&#xff1a; debug方式 思维方式 复现&#…

Python中matplotlib为多个列表数据绘制小提琴图

本文介绍基于Python中matplotlib模块与seaborn模块&#xff0c;利用多个列表中的数据&#xff0c;绘制小提琴图&#xff08;Violin Plot&#xff09;的方法。 小提琴图作为一种将箱型图与核密度图分别所能表达的信息相结合的数据可视化图&#xff0c;在数据分析中得以广泛应用&…

【电动车】电动汽车两阶段优化调度策略(Matlab代码实现)

目录 1 概述 2 数学模型 3 运行结果 4 结论 4 Matlab代码实现 1 概述 在当前阶段,电动汽车通常被视为即插即充的常规负荷,这浪费了其可观的储荷能力"。研究表明,非快充需求的电动汽车停靠时间远大于其充电时间[2,因此可通过充电站管理电动汽车的充放 电功率,使电动…

数据分析必备的5个工具,你用过几个?

数据有了&#xff0c;但是怎么让它们“听话”且输出为好看的图表呢&#xff1f;相信这是每一个做数据的人都思考过的问题吧。我们在工作、学习中经常进行会借助一些表格或者图表来进行信息的展示&#xff0c;一般大家最常用的是Excel表格或者PPT&#xff0c;但是随着近些年物联…

震惊,一个csdn小编用Python语言写了一个足球游戏,成功模拟世界杯决赛现场

前言 halo&#xff0c;包子们下午好 最近世界杯不是很火呀 很多小伙伴应该都知道球赛反正买&#xff0c;别墅靠大海&#xff01; 今天就给大家实现一个类似世界杯的足球小游戏&#xff0c;咱就说真的堪比国足了&#xff01; 哈哈哈~ 好啦 直接开整&#xff01;&#xff01;&am…

39-65-javajvm-运行时数据区-pc-栈

39-javajvm-运行时数据区&#xff1a; 运行时数据区 内存是非常重要的系统资源&#xff0c;是硬盘和CPU的中间仓库及桥梁&#xff0c;承载着操作系统和应用程序的实时运行JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策略&#xff0c;保证了JVM的高效稳定运行。不…