您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何創(chuàng)建并發(fā)布npm包”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“如何創(chuàng)建并發(fā)布npm包”文章能幫助大家解決問(wèn)題。
創(chuàng)建一個(gè)新項(xiàng)目,包含package.json
{
"name": "drrq",
"type": "module",
"version": "1.0.0"
}
npm i qs axios
主要思路是用請(qǐng)求的url和參數(shù)作為key記錄請(qǐng)求隊(duì)列,當(dāng)出現(xiàn)重復(fù)請(qǐng)求時(shí),打斷后面的請(qǐng)求,將前面的請(qǐng)求結(jié)果返回時(shí)共享給后面的請(qǐng)求。
import qs from "qs";
import axios from "axios";
let pending = []; //用于存儲(chǔ)每個(gè)ajax請(qǐng)求的取消函數(shù)和ajax標(biāo)識(shí)
let task = {}; //用于存儲(chǔ)每個(gè)ajax請(qǐng)求的處理函數(shù),通過(guò)請(qǐng)求結(jié)果調(diào)用,以ajax標(biāo)識(shí)為key
//請(qǐng)求開(kāi)始前推入pending
const pushPending = (item) => {
pending.push(item);
};
//請(qǐng)求完成后取消該請(qǐng)求,從列表刪除
const removePending = (key) => {
for (let p in pending) {
if (pending[p].key === key) {
//當(dāng)前請(qǐng)求在列表中存在時(shí)
pending[p].cancelToken(); //執(zhí)行取消操作
pending.splice(p, 1); //把這條記錄從列表中移除
}
}
};
//請(qǐng)求前判斷是否已存在該請(qǐng)求
const existInPending = (key) => {
return pending.some((e) => e.key === key);
};
// 創(chuàng)建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) => {
// 判斷是否存在請(qǐng)求中的當(dāng)前請(qǐng)求 如果有取消當(dāng)前請(qǐng)求
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);
}
);
// 請(qǐng)求執(zhí)行前加入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 };
示例入口index.js
import { exampleRequestGet } from './api.js';
const example = async () => {
let res = await exampleRequestGet();
console.log('請(qǐng)求成功 ');
};
example();
api列表api.js
import { request } from './request.js';
// 示例請(qǐng)求Get
export const exampleRequestGet = (data) => request('get', '/xxxx', data);
// 示例請(qǐng)求Post
export const exampleRequestPost = (data) => request('post', '/xxxx', data);
// 示例請(qǐng)求Post 不去重
export const exampleRequestPost2 = (data) => request('post', '/xxxx', data, false);
// 示例請(qǐng)求Post 不去重
export const exampleRequestFile = (data) => request('file', '/xxxx', data, false);
全局請(qǐng)求封裝request.js
import drrq from '../src/index.js';
const baseURL = 'https://xxx';
// 處理請(qǐng)求數(shù)據(jù) (拼接url,data添加token等) 請(qǐng)根據(jù)實(shí)際情況調(diào)整
const paramsHandler = (url, data) => {
url = baseURL + url;
data.token = 'xxxx';
return { url, data };
};
// 處理全局接口返回的全局處理相關(guān)邏輯 請(qǐng)根據(jù)實(shí)際情況調(diào)整
const resHandler = (res) => {
// TODO 未授權(quán)跳轉(zhuǎn)登錄,狀態(tài)碼異常報(bào)錯(cuò)等
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);
};
代碼寫(xiě)完后,我們需要驗(yàn)證功能是否正常,package.json加上
"scripts": {
"test": "node example"
},
執(zhí)行npm run test
功能正常,工具庫(kù)準(zhǔn)備完畢。
(eslint和prettier讀者可視情況選用)
一般項(xiàng)目的打包使用webpack,而工具庫(kù)的打包則使用rollup
通過(guò)下面的命令安裝 Rollup:
npm install --save-dev rollup
創(chuàng)建配置文件
在根目錄創(chuàng)建一個(gè)新文件 rollup.config.js
export default {
input: "src/index.js",
output: {
file: "dist/drrp.js",
format: "esm",
name: 'drrp'
}
};
input —— 要打包的文件
output.file —— 輸出的文件 (如果沒(méi)有這個(gè)參數(shù),則直接輸出到控制臺(tái))
output.format —— Rollup 輸出的文件類(lèi)型
如果要使用 es6 的語(yǔ)法進(jìn)行開(kāi)發(fā),還需要使用 babel 將代碼編譯成 es5。因?yàn)閞ollup的模塊機(jī)制是 ES6 Modules,但并不會(huì)對(duì) es6 其他的語(yǔ)法進(jìn)行編譯。
rollup-plugin-babel 將 rollup 和 babel 進(jìn)行了完美結(jié)合。
npm install --save-dev rollup-plugin-babel@latest
npm install --save-dev @babel/core
npm install --save-dev @babel/preset-env
根目錄創(chuàng)建 .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
]
]
}
rollup 提供了插件 rollup-plugin-commonjs,以便于在 rollup 中引用 commonjs 規(guī)范的包。該插件的作用是將 commonjs 模塊轉(zhuǎn)成 es6 模塊。
rollup-plugin-commonjs 通常與 rollup-plugin-node-resolve 一同使用,后者用來(lái)解析依賴(lài)的模塊路徑。
安裝模塊
npm install --save-dev rollup-plugin-commonjs rollup-plugin-node-resolve
添加 UglifyJS 可以通過(guò)移除注上釋、縮短變量名、重整代碼來(lái)極大程度的減少 bundle 的體積大小 —— 這樣在一定程度降低了代碼的可讀性,但是在網(wǎng)絡(luò)通信上變得更有效率。
安裝插件
用下面的命令來(lái)安裝 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"
},
即可執(zhí)行npm run build將/src/index.js打包為/dist/drrq.js
準(zhǔn)備npm賬號(hào),通過(guò)npm login或npm adduser。這里有一個(gè)坑,終端內(nèi)連接不上npm源,需要在上網(wǎng)工具內(nèi)復(fù)制終端代理命令后到終端執(zhí)行才能正常連接。
完整的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": "能自動(dòng)取消重復(fù)請(qǐng)求的axios封裝",
"type": "module",
"keywords": [
"取消重復(fù)請(qǐng)求",
],
"dependencies": {
"axios": "^1.2.0",
"qs": "^6.11.0"
},
"scripts": {
"test": "node example",
"build": "rollup -c"
},
"devDependencies": {
...
}
}
name 包名稱(chēng) 一定不能與npm已有的包名重復(fù),想一個(gè)簡(jiǎn)單易記的
private 是否為私有
version 版本
main 入口文件位置
repository git倉(cāng)庫(kù)地址
author 作者
license 協(xié)議
description 描述
keywords 關(guān)鍵詞,便于檢索
每個(gè) npm 包都需要一個(gè)版本,以便開(kāi)發(fā)人員在安全地更新包版本的同時(shí)不會(huì)破壞其余的代碼。npm 使用的版本系統(tǒng)被叫做 SemVer,是 Semantic Versioning 的縮寫(xiě)。
不要過(guò)分擔(dān)心理解不了相較復(fù)雜的版本名稱(chēng),下面是他們對(duì)基本版本命名的總結(jié): 給定版本號(hào) MAJOR.MINOR.PATCH,增量規(guī)則如下:
MAJOR 版本號(hào)的變更說(shuō)明新版本產(chǎn)生了不兼容低版本的 API 等,
MINOR 版本號(hào)的變更說(shuō)明你在以向后兼容的方式添加功能,接下來(lái)
PATCH 版本號(hào)的變更說(shuō)明你在新版本中做了向后兼容的 bug 修復(fù)。
表示預(yù)發(fā)布和構(gòu)建元數(shù)據(jù)的附加標(biāo)簽可作為 MAJOR.MINOR.PATCH 格式的擴(kuò)展。
最后,執(zhí)行npm publish就搞定啦
關(guān)于“如何創(chuàng)建并發(fā)布npm包”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。