Vue2開發(fā)插件并發(fā)布到npm
使用VitePress靜態(tài)網(wǎng)站生成器創(chuàng)建組件庫文檔網(wǎng)站并部署到GitHub
目標:創(chuàng)建 vue-amazing-ui 組件庫 ,并發(fā)布到npm
該組件庫已發(fā)布到 npm,直接安裝即可使用:
pnpm i vue-amazing-ui
#or
yarn add vue-amazing-ui
#or
npm install vue-amazing-ui
Vue Amazing UI 在線預覽
目前擁有的 Components 組件:
Component name | Descriptions | Component name | Descriptions |
---|---|---|---|
Alert | 警告提示 | Breadcrumb | 面包屑 |
Button | 按鈕 | Card | 卡片 |
Carousel | 走馬燈 | Cascader | 級聯(lián)選擇 |
Checkbox | 多選框 | Collapse | 折疊面板 |
Countdown | 倒計時 | DatePicker | 日期選擇 |
Dialog | 對話框 | Divider | 分割線 |
Empty | 空狀態(tài) | Grid | 柵格 |
Image | 圖片 | InputNumber | 數(shù)字輸入框 |
Message | 全局提示 | Modal | 信息提示 |
Notification | 通知提醒框 | Pagination | 分頁 |
Progress | 進度條 | QRCode | 二維碼 |
Radio | 單選框 | Rate | 評分 |
Result | 結果 | Select | 選擇器 |
Slider | 滑動輸入條 | Space | 間距 |
Spin | 加載中 | Statistic | 統(tǒng)計數(shù)值 |
Steps | 步驟條 | Swiper | 觸摸滑動插件 |
Switch | 開關 | Table | 表格 |
Tabs | 標簽頁 | TextScroll | 文字滾動 |
Timeline | 時間軸 | Tooltip | 文字提示 |
Upload | 上傳 | Video | 播放器 |
Waterfall | 瀑布流 |
目前擁有的 Functions 工具函數(shù):
Function name | Descriptions | Arguments |
---|---|---|
dateFormat | 簡單易用的日期格式化函數(shù)! | (timestamp: number|string|Date, format = ‘YYYY-MM-DD HH:mm:ss’) => string |
requestAnimationFrame | 針對不同瀏覽器進行兼容處理! | 使用方式不變 |
cancelAnimationFrame | 針對不同瀏覽器進行兼容處理! | 使用方式不變 |
rafTimeout | 使用 requestAnimationFrame 實現(xiàn)的定時器函數(shù),等效替代 (setTimeout 和 setInterval)! | (func: Function, delay = 0, interval = false) => object |
cancelRaf | 用于取消 rafTimeout 函數(shù)! | (raf: { id: number }) => void |
throttle | 使用 rafTimeout 實現(xiàn)的節(jié)流函數(shù)! | (fn: Function, delay = 300) => any |
debounce | 使用 rafTimeout 實現(xiàn)的防抖函數(shù)! | (fn: Function, delay = 300) => any |
add | 消除js加減精度問題的加法函數(shù)! | (num1: number, num2: number) => number |
downloadFile | 下載文件并自定義文件名! | (url: string, name: string) => void |
moneyFormat | 金額格式化函數(shù)! | (value: number|string, decimal = 2, split = ‘,’) => string |
創(chuàng)建 vue3+ts+vite 項目:
輸入項目名稱,并依次選擇需要安裝的依賴項
pnpm create vue@latest
項目目錄結構截圖如下:
③在項目根目錄新建
packages/
文件夾用于存放組件 (以Breadcrumb為例,其他類似)
在項目根目錄中的 vite.config.ts 中寫入相關配置項:
import { fileURLToPath, URL } from 'node:url'
import { resolve } from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// ant-desing按需引入
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
// 打包體積可視化插件
// import { visualizer } from 'rollup-plugin-visualizer'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
// visualizer({ // 生成的分析圖文件名,默認stats.html
// file: 'stats.html',
// open: true // 打包后自動打開分析圖
// }),
Components({
resolvers: [AntDesignVueResolver()]
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'images': fileURLToPath(new URL('./src/assets/images', import.meta.url))
}
},
css: {
preprocessorOptions: {
less: {
modifyVars: { // 或者globalVars
// `themeColor` is global variables fields name
themeColor: '#1677FF' // #1890FF
},
javascriptEnabled: true
},
},
},
// 構建為庫
build: {
lib: { // 構建為庫。如果指定了 build.lib,build.cssCodeSplit 會默認為 false。
// __dirname的值是vite.config.ts文件所在目錄
entry: resolve(__dirname, 'packages/index.ts'), // entry是必需的,因為庫不能使用HTML作為入口。
name: 'VueAmazingUI', // 暴露的全局變量
fileName: 'vue-amazing-ui' // 輸出的包文件名,默認是package.json的name選項
},
rollupOptions: { // 自定義底層的Rollup打包配置
// https://rollupjs.org/configuration-options/
// 確保外部化處理那些你不想打包進庫的依賴
external: ['vue', 'swiper', '@vuepic/vue-datepicker', 'qrcode'],
output: {
// format: 'es', // 默認es,可選 'amd' 'cjs' 'es' 'iife' 'umd' 'system'
exports: 'named', // https://rollupjs.org/configuration-options/#output-exports
// // 在 UMD 構建模式下為這些外部化的依賴提供一個全局變量
globals: {
vue: 'Vue',
// 'vue-router': 'VueRouter', // 引入vue-router全局變量,否則router.push將無法使用
swiper: 'Swiper',
'@vuepic/vue-datepicker': 'VueDatePicker',
qrcode: 'qrcode'
}
}
},
/** 設置為 false 可以禁用最小化混淆,或是用來指定使用哪種混淆器。
默認為 Esbuild,它比 terser 快 20-40 倍,壓縮率只差 1%-2%。
注意,在 lib 模式下使用 'es' 時,build.minify 選項不會縮減空格,因為會移除掉 pure 標注,導致破壞 tree-shaking。
當設置為 'terser' 時必須先安裝 Terser。(yarn add terser -D)
*/
minify: 'terser', // Vite 2.6.x 以上需要配置 minify: "terser", terserOptions 才能生效
terserOptions: { // 在打包代碼時移除 console、debugger 和 注釋
compress: {
/* (default: false) -- Pass true to discard calls to console.* functions.
If you wish to drop a specific function call such as console.info and/or
retain side effects from function arguments after dropping the function
call then use pure_funcs instead
*/
drop_console: true, // 生產(chǎn)環(huán)境時移除console
drop_debugger: true
},
format: {
comments: false // 刪除注釋comments
}
}
}
})
在 packages/ 目錄下創(chuàng)建 UI 組件
例如:新建
breadcrumb/
和pagination/
文件夾,截圖如下:
在
breadcrumb/
文件夾下新建Breadcrumb.vue
組件文件和index.ts
文件,截圖如下:
在
Breadcrumb.vue
中編寫組件代碼:
<script setup lang="ts">
import { computed } from 'vue'
interface Query {
[propName: string]: any // 添加一個字符串索引簽名,用于包含帶有任意數(shù)量的其他屬性
}
interface Route {
path: string // 路由地址
query?: Query // 路由查詢參數(shù)
name: string // 路由名稱
}
interface Props {
routes: Array<Route> // 或者Route[] router的路由數(shù)組,沒有 ? 時,即表示 required: true
fontSize: number // 字體大小
height?: number // 面包屑高度
maxWidth?: number // 文本最大顯示寬度,超出后顯示省略號
separator?: string // 自定義分隔符
target?: '_self'|'_blank' // 如何打開目標URL,當前窗口或新窗口
}
const props = withDefaults(defineProps<Props>(), {
routes: () => [],
fontSize: 14,
height: 21,
maxWidth: 180,
separator: '',
target: '_self'
})
const len = computed(() => {
return props.routes.length
})
function getUrl (route: Route) {
var targetUrl = route.path
if (route.query && JSON.stringify(route.query) !== '{}') {
const query = route.query
Object.keys(query).forEach((param, index) => {
if (index === 0) {
targetUrl = targetUrl + '?' + param + '=' + query[param]
} else {
targetUrl = targetUrl + '&' + param + '=' + query[param]
}
})
}
return targetUrl
}
</script>
<template>
<div class="m-breadcrumb" :style="`height: ${height}px;`">
<div class="m-bread" v-for="(route, index) in routes" :key="index">
<a
:class="['u-route',{ active: index===len-1 }]"
:style="`font-size: ${fontSize}px; max-width: ${maxWidth}px;`"
:href="index === len - 1 ? 'javascript:;' : getUrl(route)"
:title="route.name"
:target="index === len - 1 ? '_self' : target">
{{ route.name || '--' }}
</a>
<template v-if="index !== len - 1">
<span v-if="separator" class="u-separator">{{ separator }}</span>
<svg v-else class="u-arrow" viewBox="64 64 896 896" data-icon="right" aria-hidden="true" focusable="false"><path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 0 0 302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 0 0 0-50.4z"></path></svg>
</template>
</div>
<div class="assist"></div>
</div>
</template>
<style lang="less" scoped>
.m-breadcrumb {
display: flex;
align-items: center;
.m-bread {
display: inline-flex;
align-items: center;
line-height: 1.5;
.u-route {
color: rgba(0, 0, 0, 0.45);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 4px;
border-radius: 4px;
transition: color 0.2s, background-color 0.2s;
&:hover {
background-color: rgba(0, 0, 0, 0.05);
color: rgba(0, 0, 0, 0.88);
}
}
.active {
color: rgba(0, 0, 0, 0.88);
cursor: default;
&:hover {
background-color: transparent;
}
}
.u-separator {
margin: 0 4px;
color: rgba(0, 0, 0, 0.45);
}
.u-arrow {
width: 12px;
height: 12px;
fill: rgba(0, 0, 0, 0.45);
}
}
.assist {
height: 100%;
width: 0;
display: inline-block;
vertical-align: middle;
}
}
</style>
在
breadcrumb/index.ts
中導出組件
import type { App } from 'vue'
import Breadcrumb from './Breadcrumb.vue'
// 使用install方法,在app.use掛載
Breadcrumb.install = (app: App) => {
app.component(Breadcrumb.__name as string, Breadcrumb)
}
export default Breadcrumb
在 packages/index.ts 文件中對整個組件庫進行導出:
import type { App } from 'vue'
import Breadcrumb from './breadcrumb'
import Pagination from './pagination'
// 所有組件列表
const components = [
Breadcrumb,
Pagination
]
// 定義 install 方法
const install = (app: App): void => {
// 遍歷注冊所有組件
/*
component.__name ts報錯
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
Type 'undefined' is not assignable to type 'string'.ts(2345)
解決方式一:使用// @ts-ignore
解決方式二:使用類型斷言 尖括號語法(component.__name) 或 as語法(component.__name as string)
*/
components.forEach(component => app.component(component.__name as string, component))
}
export {
Breadcrumb,
Pagination
}
const VueAmazingUI = {
install
}
export default VueAmazingUI
打包組件庫
pnpm build
在
src/main.ts
中導入剛創(chuàng)建的組件,檢測是否正常可用
// import VueAmazingUI from '../packages'
import VueAmazingUI from '../dist/vue-amazing-ui.js'
import '../dist/style.css'
// import { Breadcrumb } from '../dist/vue-amazing-ui.js'
const app = createApp(App)
app.use(VueAmazingUI)
// app.use(Breadcrumb)
app.mount('#app')
?在終端執(zhí)行
pnpm init
初始化包,選填并配置package.json
:
{
"name": "vue-amazing-ui",
"version": "0.0.30",
"private": false,
"type": "module", // 如果 package.json 不包含 "type": "module",Vite 會生成不同的文件后綴名以兼容 Node.js。.js 會變?yōu)?.mjs 而 .cjs 會變?yōu)?.js
"files": ["dist"], // 檢測dist打包目錄的所有文件
"main": "./dist/vue-amazing-ui.umd.cjs",
"module": "./dist/vue-amazing-ui.js",
"exports": {
"./dist/style.css": "./dist/style.css", // 子目錄別名,方便樣式引入
"./css": "./dist/style.css",
".": { // 模塊的主入口,優(yōu)先級高于main字段,利用.這個別名,為 ES6 模塊(import)和 CommonJS (require)指定不同的入口
"import": "./dist/vue-amazing-ui.js",
"require": "./dist/vue-amazing-ui.umd.cjs"
}
},
"scripts": {
"dev": "vite --port 9000 --open --force",
"build": "run-p type-check build-only",
"docs:dev": "vitepress dev docs --port 8000 --open",
"docs:build": "vitepress build docs",
"docs:deploy": "sh script/deploy.sh",
"pub": "sh script/publish.sh",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"@vuepic/vue-datepicker": "^4.5.1",
"@vueuse/core": "^10.1.2",
"@vueuse/integrations": "^10.1.2",
"ant-design-vue": "^3.2.20",
"core-js": "^3.30.2",
"date-fns": "^2.30.0",
"qrcode": "^1.5.3",
"swiper": "^9.3.2",
"vue": "^3.3.4",
"vue-amazing-ui": "^0.0.30",
"vue-router": "^4.2.1"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.0",
"@types/node": "^18.16.14",
"@vitejs/plugin-vue": "^4.2.3",
"@vue/eslint-config-typescript": "^11.0.3",
"@vue/tsconfig": "^0.1.3",
"eslint": "^8.41.0",
"eslint-plugin-vue": "^9.14.0",
"less": "^4.1.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.8",
"rollup-plugin-visualizer": "^5.9.0",
"terser": "^5.17.6",
"typescript": "~4.7.4",
"unplugin-vue-components": "^0.25.0",
"vite": "^4.3.8",
"vitepress": "1.0.0-beta.1",
"vue-tsc": "^1.6.5"
},
"description": "This template should help get you started developing with Vue Amazing UI in Vue 3.",
"repository": {
"type": "git",
"url": "git+https://github.com/themusecatcher/vue-amazing-ui.git"
},
"keywords": [
"Vue3",
"TS",
"Vite",
"Amazing",
"UI",
"Components"
],
"author": "theMuseCatcher",
"license": "ISC",
"bugs": {
"url": "https://github.com/themusecatcher/vue-amazing-ui/issues"
},
"homepage": "https://github.com/themusecatcher/vue-amazing-ui#readme"
}
name : 包名,該名字是唯一的。可在 npm 官網(wǎng)搜索名字,不可重復。
version: 版本號,每次發(fā)布至 npm 需要修改版本號,不能和歷史版本號相同。
private:是否私有,需要修改為 false 才能發(fā)布到 npm
description: 關于包的描述。
main: 入口文件,需指向最終編譯后的包文件。
keywords:關鍵字,以空格分離希望用戶最終搜索的詞。
author:作者
license: 開源協(xié)議
vite build --watch:當啟用 --watch 標志時(啟用 rollup 的監(jiān)聽器),對 vite.config.ts 的改動,以及任何要打包的文件,都將觸發(fā)重新構建
vite --port 9000 --open --force:指定端口9000,啟動時打開瀏覽器,強制優(yōu)化器忽略緩存并重新構建。
編譯打包
執(zhí)行打包命令
pnpm build
執(zhí)行結果如下圖:
在項目根目錄創(chuàng)建
.npmignore
文件,設置忽略發(fā)布的文件,類似.gitignore
文件
# 只有編譯后的 dist 目錄、package.json、README.md是需要被發(fā)布的
# 忽略目錄
.DS_Store
.vscode/
node_modules
packages/
public/
src/
# 忽略指定文件
.eslintrc.cjs
.gitignore
.npmignore
.npmrc
.prettierrc.json
components.d.ts
env.d.ts
index.html
pnpm-lock.yaml
stats.html
tsconfig.config.json
tsconfig.json
vite.config.ts
編寫 README.md 文件
# vue-amazing-ui
## Document & Online preview
[Vue Amazing UI](https://themusecatcher.github.io/vue-amazing-ui/)
## Install & Use
```bash
pnpm i vue-amazing-ui
# or
npm install vue-amazing-ui
# or
yarn add vue-amazing-ui
```
Import and register component
**Global**
```ts
import { createApp } from 'vue'
import App from './App.vue'
import VueAmazingUI from 'vue-amazing-ui'
import 'vue-amazing-ui/css'
const app = createApp(App)
app.use(VueAmazingUI)
```
**Local**
```vue
<script setup lang="ts">
import { Button } from 'vue-amazing-ui'
import 'vue-amazing-ui/css'
</script>
```
## Project
- Get the project code
```sh
git clone https://github.com/themusecatcher/vue-amazing-ui.git
```
- Install dependencies
```sh
cd vue-amazing-ui
pnpm i
```
- Run project
```sh
pnpm dev
```
## Components
Component name | Descriptions | Component name | Descriptions
-- | -- | -- | --
Breadcrumb | 面包屑 | Button | 按鈕
Carousel | 走馬燈 | Cascader | 級聯(lián)選擇
Checkbox | 多選框 | Collapse | 折疊面板
Countdown | 倒計時 | DatePicker | 日期選擇
Dialog | 對話框 | Divider | 分割線
Empty | 空狀態(tài) | Image | 圖片
InputNumber | 數(shù)字輸入框 | Message | 全局提示
Modal | 信息提示 | Notification | 通知提醒框
Pagination | 分頁器 | Progress | 進度條
QRCode | 二維碼 | Radio | 單選框
Rate | 評分 | Select | 選擇器
Slider | 滑動輸入條 | Spin | 加載中
Steps | 步驟條 | Swiper | 觸摸滑動插件
Switch | 開關 | Table | 表格
Tabs | 標簽頁 | TextScroll | 文字滾動
Timeline | 時間軸 | Tooltip | 文字提示
Upload | 上傳 | Video | 播放器
Waterfall | 瀑布流
## Details
[My CSDN Blogs](https://blog.csdn.net/Dandrose)
## Functions
Function name | Descriptions | Arguments
-- | -- | --
dateFormat | 簡單易用的日期格式化函數(shù)! | (timestamp: number|string|Date, format = 'YYYY-MM-DD HH:mm:ss') => string
requestAnimationFrame | 針對不同瀏覽器進行兼容處理! | 使用方式不變
cancelAnimationFrame | 針對不同瀏覽器進行兼容處理! | 使用方式不變
rafTimeout | 使用 requestAnimationFrame 實現(xiàn)的定時器函數(shù),等效替代 (setTimeout 和 setInterval)! | (func: Function, delay = 0, interval = false) => object
cancelRaf | 用于取消 rafTimeout 函數(shù)! | (raf: { id: number }) => void
throttle | 使用 rafTimeout 實現(xiàn)的節(jié)流函數(shù)! | (fn: Function, delay = 300) => any
debounce | 使用 rafTimeout 實現(xiàn)的防抖函數(shù)! | (fn: Function, delay = 300) => any
add | 消除js加減精度問題的加法函數(shù)! | (num1: number, num2: number) => number
downloadFile | 下載文件并自定義文件名! | (url: string, name: string) => void
登錄 npm
如果沒有 npm 賬號,可以去 npm官網(wǎng) 注冊一個賬號
注冊成功后在本地查看
pnpm / npm
鏡像:
pnpm/npm config get registry
輸出:http://registry.npmjs.org 即可
如果不是則需要設置為npm鏡像:
pnpm/npm config set registry https://registry.npmjs.org
然后在終端執(zhí)行:
pnpm/npm login
依次輸入用戶名,密碼,郵箱,輸出Logged in as…即可
pnpm/npm whoami
// 查看當前用戶是否已登錄
發(fā)布組件到 npm
在終端執(zhí)行:
pnpm/npm publish
發(fā)布成功后即可在npm官網(wǎng)搜索到該組件,如下圖
并可以通過 pnpm/npm install vue-amazing-ui
(或 yarn add vue-amazing-ui
)進行安裝
為方便打包構建、發(fā)布、提交代碼到github等操作,可以通過腳步一次性執(zhí)行以上操作:
在項目中新建 script/
文件夾,并創(chuàng)建 publish.sh
腳本文件,如下圖:
在
publish.sh
中創(chuàng)建以下腳本:
# /bin/bash
# 確保腳本拋出遇到的錯誤
set -e
# 讀取package.json中的version
version=`jq -r .version package.json`
# 打包構建
pnpm build
# 提交代碼到github
git add .
git commit -m "update $version"
git push
# 發(fā)布到npm,pnpm(高性能的npm)
pnpm publish
# 升級 vue-amazing-ui 依賴版本
pnpm up vue-amazing-ui@$version
# 提交版本更新代碼到github
git add .
git cm -m "update $version"
git push
之后打包構建、發(fā)布、提交代碼到github 只需新增 version 版本號之后執(zhí)行:sh publish.sh
即可!
在要使用的項目中安裝并注冊插件:
pnpm i vue-amazing-ui
#or
yarn add vue-amazing-ui
# or
npm install vue-amazing-ui
然后在
main.ts
文件中引入并注冊:文章來源:http://www.zghlxwxcb.cn/news/detail-476843.html
import VueAmazingUI from 'vue-amazing-ui'
// import { Pagination, Breadcrumb } from 'vue-amazing-ui'
import 'vue-amazing-ui/css'
app.use(VueAmazingUI)
// app.use(Pagination).use(Breadcrumb)
在要使用組件的頁面直接使用即可:文章來源地址http://www.zghlxwxcb.cn/news/detail-476843.html
<script setup lang="ts">
const routes = [
{
path: '/first', // 路由地址
query: { id: 1, tab: 2 }, // 路由參數(shù)
name: '一級路由' // 路由名稱
},
{
path: '/second',
name: '二級路由'
},
{
path: '/third',
name: '三級路由三級路由三級路由三級路由'
}
]
</script>
<template>
<Breadcrumb :routes="routes" />
</template>
到了這里,關于Vue3+TS+Vite開發(fā)組件庫并發(fā)布到npm的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!