如何发布一个npm包?通过开发简易的Vue Router 学习npm包发布
当每个新项目都需要手动复制base-ui中的公共组件和样式以满足公司的UI风格要求时,这一过程变得相当繁琐。因此,我计划将这些组件上传到npm仓库,以便随时通过npm安装和使用,从而简化流程。
为了解释如何学习并成功发布npm包,本文将通过开发一个简单的vue-router学习。
1.新建项目
本项目的vue-router是通过tsx编写,打包工具是vite。
- 初始化项目
npm init vue
- 选择
Typescript
和Jsx
- 下载依赖
npm install
- 启动项目
npm run dev
2.开发vue-router
2.1.createRouter函数
当我们使用createRouter
时,它接受一个对象作为参数,其中包括routes
、history
等属性(在本文中,我们只实现了routes
)。这个函数返回一个router
对象。由于router
对象可以作为插件使用,我们可以通过app.use(router)
进行全局注册。因此,router
对象还具有一个install
属性。在install
属性中,我们完成了以下几个关键操作:
- 全局共享了
router
对象,确保了应用程序的不同部分都可以访问和使用它。 - 对
router-link
和router-view
组件进行了全局注册,以便它们可以在整个应用程序中使用。 - 对hash进行监听,将当前的路径保存在router的属性中,并且是响应式的,以便在router-view中可以动态显示component。
import type { IConfig } from "./type";
import RouterLink from "@/components/RouterLink";
import RouterView from "@/components/RouterView";
import { ref, type App } from "vue";
function createRouter(config: IConfig) {
const router = {
// 保存当前路径(如:/home),必须加ref,才能让router-view动态显示组件
currentPath: ref(location.hash.slice(1) || "/"),
config,
install(app: App) {
// 全局共享router
app.config.globalProperties.$router = router;
// 全局组成组件
app.component("routerLink", RouterLink);
app.component("routerView", RouterView);
// 监听hash变化
window.addEventListener("hashchange", () => {
router.currentPath.value = location.hash.slice(1);
});
},
};
return router;
}
export default createRouter;
2.2.routerLink组件
router-link
组件无非就是一个a标签,接受一个to
属性,将to
属性的值拼接#,传递给a标签的herf
属性中。
import { defineComponent } from 'vue';
export default defineComponent({
name: 'routerLink',
props: {
to: {
type: String,
default: '',
},
},
setup(props, { slots }) {
const { to } = props;
return () => {
return <a href={'#' + to}>{slots.default?.()}</a>;
};
},
});
2.3.routerView组件
在router-view
组件中,我们利用之前在createRouter
中共享的当前路径(currentPath
)和路由配置(routes
)。让path去routes中匹配对应的路由,匹配到就返回route中的组件,没匹配就抛警告⚠️。
import { defineComponent, getCurrentInstance, unref } from "vue";
import type { Component, ComponentInternalInstance } from "vue";
export default defineComponent({
name: "routeView",
setup() {
// 获取实例
const instance = getCurrentInstance() as ComponentInternalInstance;
// 从实例中获取之前在createRouter中保存的当前path和routes
const {
currentPath,
config: { routes },
} = instance.proxy!.$router;
return () => {
let component: Component;
// 从routes中查找和当前path匹配的路由
const route = routes.find(
(route: any) => route.path === unref(currentPath)
);
// 如果找到就将routes中的组件返回
if (route) {
component = route.component;
} else { // 没找到就抛一个警告
component = <div></div>;
if (currentPath.value !== "/") {
console.warn("current route not match!");
}
}
return <component></component>;
};
},
});
3.组件打包
在vite.config.ts文件中进行配置
- 配置打包文件的入口和出口
- 其实到这里就已经可以直接打包,但是打包的组件只能给js项目使用,在ts项目下运行会出现一些错误,而且使用的时候还会失去代码提示功能,这样的话我们就失去了用ts开发组件库的意义了。所以我们需要在打包的库里加入声明文件(.d.ts)。只需要引入
vite-plugin-dts
插件(npm i vite-plugin-dts -D
)。并且在vite.config.ts中的plugins中进行注册即可,打包时会自动生成类型声明文件。
import { fileURLToPath, URL } from "node:url";
import { resolve } from "node:path";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx"; // 项目中使用了Jsx得注册该插件
import dts from "vite-plugin-dts";
export default defineConfig({
plugins: [vue(), vueJsx(), dts()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)), // 路径别名
},
},
build: {
lib: {
entry: resolve(__dirname, "src/main.ts"), // 打包入口
fileName: "bundle", // 输出的包文件名,默认 `fileName` 是 `package.json` 的 `name` 选项
formats: ["cjs", "es"], // 输出格式,可选cjs(node环境)、es(浏览器环境)、umd(通用)、iife(打包成立即执行函数,避免变量和函数与其他代码发生冲突)
name: "vue-router", // 暴露的全局变量
},
outDir: "dist", // 指定输出路径(相对于 [项目根目录]).
sourcemap: true, // 为了方便其他开发人员可以更轻松地调试代码
rollupOptions: {
external: ["vue"]
},
},
});
4.发布到npm
4.1.配置package.json文件
-
name是项目的名称(必填);
-
version是当前项目的版本号(必填);
-
description是描述信息,很多时候是作为项目的基本描述;
-
author是作者相关信息(发布时用到);
-
license是开源协议(发布时用到);
-
files是限制将哪些文件发布到 npm 注册表;
-
keywords是关键字,提供关于包内容的关键描述信息;
-
private属性:
- private属性记录当前的项目是否是私有的;
- 当值为true时,npm是不能发布它的,这是防止私有项目或模块发布出去的方式;
-
main属性:设置程序的入口。
- 比如我们使用axios模块 const axios = require('axios');
- 如果有main属性,实际上是找到对应的main属性查找文件的;
{
"name": "vue-router-demo",
"version": "0.0.3",
"private": false,
"main": "dist/bundle.js", // commonJs入口
"module": "dist/bundle.mjs", // esModule入口
"types": "dist/main.d.ts",
"files": [
"dist"
],
"scripts": {
"dev": "vite",
"build": "run-p type-check build-only",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false"
},
"dependencies": {
"vue": "^3.3.4"
},
"devDependencies": {
...
}
}
4.2.注册账号
想要发布到npm仓库,就必须要有一个账号,先去npm官网注册一个账号,注意记住用户名、密码和邮箱,发布的时候可能会用到。npm注册
4.3.设置npm源
有些小伙伴可能本地的npm镜像源采用的是淘宝镜像源或者其它的,如果想要发布npm包,我们得把我们得npm源切换为官方得源,命令如下:
npm config set registry=https://registry.npmjs.org
4.4.发布及更新包
- 在命令行登录:
npm login
- 发布到npm registry上:
npm publish
- 更新仓库:修改版本号(最好符合semver规范)、重新发布
- 删除发布的包:
npm unpublish --force
- 让发布的包过期
npm deprecate
5.总结
本文实现了一个简易的Vue Router,虽然在这个示例中我们成功实现了基本的路由功能,但是还有很多方法没实现,比如history属性、路由嵌套、路由导航守卫等等,有兴趣的同学可以去实现一下,本文核心还是讲如何发布一个npm包。
总的来说,将Vue组件封装并发布到npm仓库整体难度不大。主要是理解Vue的注册插件(vue.use()
)以及一些与打包相关的知识。然而,更加关键的是如何设计和封装一个通用组件,使其具有广泛的适用性和高度的可扩展性。
转载自:https://juejin.cn/post/7276659061570551867