使用vite构建Vue3组件库,上传到npm官网

440 阅读4分钟

创建项目

npm create vite

根目录下建立一个ts声明文件

// env.d.ts
// env.d.ts
declare module "*.vue" {
  import { DefineComponent } from "vue"
  const component: DefineComponent<{}, {}, any>
  export default component
}
declare interface Window {
  Vue: any,
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,
    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["packages/**/*.ts", "packages/**/*.d.ts", "packages/**/*.tsx", "packages/**/*.vue", "./*.d.ts"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

改造目录

  • 首先需要创建一个 packages 目录,用来存放组件
  • 将 src 目录改为 examples 用作示例
  • 启动项目的时候,默认入口文件是 src/main.ts,将 src 目录改为 examples 之后,就需要重新配置入口文件,在index.html中把/src/main.ts改为/examples/main.ts

组件开发

  • packages目录下存放每个组件单独的开发目录,和一个 index.js 整合所有组件,并对外导出
  • 每个组件都应该归类于单独的目录下,包含其组件源码目录 src,和 index.js 便于外部引用
  • 这里以组件 el-button 为例,完整的 packages 目录结构
packages
  el-button
    src
    index.ts
  • el-button/src/index.vue 就是组件的入口文件
<template>
  <button class="tButton">测试 发布 按钮组件1</button>
</template>
<script lang="ts" setup>
</script>
<style lang="scss" scoped>
.tButton {
    background-color: #1188ff;
    color: white;
    border: none;
    padding: 6px 12px;
  }
</style>
  • el-button/index.ts 实现组件的导出
import elButton from './src/index.vue' // 如果这里报红, 加一个env.d.ts,再去 tsconfig.json 里面这样配置:"include": ["packages/**/*.ts", "packages/**/*.d.ts", "packages/**/*.tsx", "packages/**/*.vue", "./*.d.ts"],

// 一定要先给name赋值,这样后面的局部install和全局install方法才能读到同一个name
elButton.name = 'el-button'

// 为组件添加 install 方法,用于按需引入
// 如果想通过CDN方式引入,需要编写install函数
elButton.install = function(Vue: any) {
  // 注册组件
  Vue.component('el-button1', elButton)
}
export default elButton
  • 编辑 packages/index.ts 文件,实现组件的全局注册
// 导入单个组件
import elButton from './el-button/index'
import elButtonPlus from './el-button-plus/index'
// 以数组的结构保存组件,便于遍历
const components = [
  elButton,
  elButtonPlus
]
// 用于按需导入
export {
  elButton,
  elButtonPlus
}
// 定义 install 方法
const install = function (Vue: any) {
  if ((install as any).installed) return;
  (install as any).installed = true
  // 遍历并注册全局组件
  components.map(component => {
      Vue.component(component.name, component)
  })
}
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}
export default {
  // 导出的对象必须具备一个 install 方法
  install,
}

本地导入组件测试

examples\App.vue

<template>
  <tButton />
</template>
<script setup lang="ts">
import tButton from "../packages/el-button/index";
</script>
<style scoped>
</style>

编写 package.json 文件

package.json 文件里面有很多字段要填写,否则不能正确发布。最重要的是以下几个

  • name: 包名,该名字是唯一的。可在 npm 官网搜索名字,如果存在则需换个名字。
  • version: 版本号,不能和历史版本号相同。
  • files: 配置需要发布的文件。
  • main: 入口文件,默认为 index.js,这里改为 dist/xxx-xxxx.umd.js。
  • module: 模块入口,这里改为 dist/xxx-xxxx.es.js。
{
  "name": "xxx-xxxx",
  "private": false,
  "version": "0.0.1",
  "type": "module",
  "main": "dist/xxx-xxxx.umd.js",
  "module": "dist/xxx-xxxx.es.js",
  "files": [
    "dist/*",
    "xxx-xxxx.d.ts"
  ],
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.2.47"
  },
  "devDependencies": {
    "@types/node": "^20.2.5",
    "@vitejs/plugin-vue": "^4.1.0",
    "@vitejs/plugin-vue-jsx": "^3.0.1",
    "sass": "^1.62.1",
    "typescript": "^5.0.2",
    "vite": "^4.3.9",
    "vue-tsc": "^1.4.2"
  }
}

vite打包配置

因为组件库一般都是 jsx 语法编写,所以要加上 @vitejs/plugin-vue-jsx,打包成 lib,编辑 vite.config.ts:

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx' // pnpm add -D @vitejs/plugin-vue-jsx
import path from "path";
const resolve = (dir) => path.join(__dirname, dir);
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), vueJsx({})],
  resolve: {
    alias: {
      "@": resolve("examples"),
      packages: resolve("packages"),
    },
  },
  build: {
    rollupOptions: {
      // 请确保外部化那些你的库中不需要的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue',
        },
      },
    },
    lib: {
      entry: 'packages/index.ts',
      name: 'xxx-xxxx',
      fileName: (format) => `xxx-xxxx.${format}.js`,
    },
  },
})

运行打包命令

如果后缀是 .mjs 而不是 .es.js 那就在 lib里面加上 fileName: (format) => xxx-xxxx.${format}.js,

npm run build

本地模拟

main.ts 引入样式文件'../dist/style.css'

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import '../dist/style.css'
createApp(App).mount('#app')

在app.vue导入dist中的组件

<template>
  <elButton />
</template>
<script setup lang="ts">
import { elButton } from "../dist/xxx-xxxx.es.js";
</script>
<style scoped>
</style>

main.ts全局声明组件

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import '../dist/style.css'
import xxxXxxx from "../dist/xxx-xxxx.es.js";
const app = createApp(App);
app.use(xxxXxxx)
app.mount('#app');

上传到npm官网

注册npm账号

设置npm源

如果本地的npm镜像源采用的是淘宝镜像源或者其它的,若发布npm包,需要将npm源切换为官方得源

## 查看
npm config get registry
## 设置
npm config set registry=https://registry.npmjs.org

登录到npm

在打包后的文件根目录打开终端,输入npm login登录

npm login

推送到npm仓库

在hb-vue3-ui目录下执行终端命令npm publish就可以正式发布到npm仓库了

npm publish

demo项目来安装

npm install xxx-xxxx

//main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import xxxXxxx from 'xxx-xxxx'
import 'xxx-xxxx/dist/style.css'
const app = createApp(App)
app.use(xxxXxxx)
app.mount('#app')

类型自动生成

使用 vite-plugin-dts会自动把包导出的方法和对象对应的ts类型生成 .d.ts类型声明文件

npm i vite-plugin-dts -D

在 vite.config.ts 使用

import { resolve } from 'path'
import { defineConfig } from 'vite'
import dts from 'vite-plugin-dts'

export default defineConfig({
  plugins: [dts()] // 重点是这一行写了就行
}) 

在package.json中,把types字段指向自动生成的dist目录下得index.d.ts

{
"types": "dist/index.d.ts"
}

跑 npm run build 的时候dist中就会自动生成 index.d.ts文件 发布之后别人下载使用时就会有类型提示了