如何发布一个npm包?通过开发简易的Vue Router 学习npm包发布

当每个新项目都需要手动复制base-ui中的公共组件和样式以满足公司的UI风格要求时,这一过程变得相当繁琐。因此,我计划将这些组件上传到npm仓库,以便随时通过npm安装和使用,从而简化流程。

为了解释如何学习并成功发布npm包,本文将通过开发一个简单的vue-router学习。

1.新建项目

本项目的vue-router是通过tsx编写,打包工具是vite。

  1. 初始化项目npm init vue
  2. 选择TypescriptJsx
  3. 下载依赖npm install
  4. 启动项目npm run dev

2.开发vue-router

2.1.createRouter函数

当我们使用createRouter时,它接受一个对象作为参数,其中包括routeshistory等属性(在本文中,我们只实现了routes)。这个函数返回一个router对象。由于router对象可以作为插件使用,我们可以通过app.use(router)进行全局注册。因此,router对象还具有一个install属性。在install属性中,我们完成了以下几个关键操作:

  1. 全局共享了router对象,确保了应用程序的不同部分都可以访问和使用它。
  2. router-linkrouter-view组件进行了全局注册,以便它们可以在整个应用程序中使用。
  3. 对hash进行监听,将当前的路径保存在router的属性中,并且是响应式的,以便在router-view中可以动态显示component。
ts 复制代码
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属性中。

tsx 复制代码
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中的组件,没匹配就抛警告⚠️。

tsx 复制代码
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文件中进行配置

  1. 配置打包文件的入口和出口
  2. 其实到这里就已经可以直接打包,但是打包的组件只能给js项目使用,在ts项目下运行会出现一些错误,而且使用的时候还会失去代码提示功能,这样的话我们就失去了用ts开发组件库的意义了。所以我们需要在打包的库里加入声明文件(.d.ts)。只需要引入vite-plugin-dts插件(npm i vite-plugin-dts -D)。并且在vite.config.ts中的plugins中进行注册即可,打包时会自动生成类型声明文件。
ts 复制代码
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属性查找文件的;
json 复制代码
{
  "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.发布及更新包

  1. 在命令行登录:npm login
  2. 发布到npm registry上:npm publish
  3. 更新仓库:修改版本号(最好符合semver规范)、重新发布
  4. 删除发布的包:npm unpublish --force
  5. 让发布的包过期npm deprecate

5.总结

本文实现了一个简易的Vue Router,虽然在这个示例中我们成功实现了基本的路由功能,但是还有很多方法没实现,比如history属性、路由嵌套、路由导航守卫等等,有兴趣的同学可以去实现一下,本文核心还是讲如何发布一个npm包。

总的来说,将Vue组件封装并发布到npm仓库整体难度不大。主要是理解Vue的注册插件(vue.use())以及一些与打包相关的知识。然而,更加关键的是如何设计和封装一个通用组件,使其具有广泛的适用性和高度的可扩展性。

相关推荐
曈欣29 分钟前
vue 中属性值上变量和字符串怎么拼接
前端·javascript·vue.js
计算机学姐38 分钟前
基于SpringBoot+Vue的宠物医院管理系统
java·vue.js·spring boot·后端·mysql·intellij-idea·mybatis
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS教师工作量管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
缘月叙文1 小时前
vue2项目实现国际化(若依框架示例)
vue.js
QGC二次开发1 小时前
Vue3:v-model实现组件通信
前端·javascript·vue.js·前端框架·vue·html
努力的小雨2 小时前
从设计到代码:探索高效的前端开发工具与实践
前端
小鼠米奇2 小时前
详解Ajax与axios的区别
前端·javascript·ajax
Bunury3 小时前
Vue3新组件transition(动画过渡)
前端·javascript·vue.js
zero.cyx3 小时前
JS函数部分
开发语言·前端·javascript
超级小的大杯柠檬水3 小时前
SpringBoot lombok(注解@Getter @Setter)
java·前端·spring