在完成了一个项目,去做另一个项目时,会用到重复的组件,我们可以去打开上一个工程,把组件代码拷过来,再重新调整数据,这样会很麻烦,随着项目的越来越多,也不好定位哪个项目中使用了可复用的组件,所以业务组件库就发挥了它的优势,直接安装好后,按需引入,不用再手搬代码。本篇文章会讲述如何从 0-1 搭建自己的组件库。
技术栈:
1 环境准备
在 cmd 终端安装 pnpm
创建工程目录:vue3-components。
用 vscode 打开工程目录,在 vscode 终端执行:
根目录新建.npmrc 文件,写入下面的语句,作用是与项目不直接相关的不会安装在 node modules 下:
在根目录安装依赖:
1
| pnpm install vue@next typescript -D
|
配置 tsconfig.json:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| { "compilerOptions": { "module": "esnext", "declaration": false, "noImplicitAny": false, "removeComments":true, "esModuleInterop": true, "moduleResolution": "node", "jsx": "preserve", "noLib": false, "target": "es2018", "sourceMap": true, "lib": ["esnext", "DOM"], "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "strict": true, "skipLibCheck": true, }, "exclude": [ "node_modules", "dist/**", "**/__tests__", ] }
|
根目录新建 pnpm-workspace.yaml 文件,写入:
1 2 3
| packages: - "packages/**" # 存放编写的组件 - play # 使用组件的地方
|
2 项目创建
在根目录新建 paly 目录,进入 play,执行:pnpm init
,
修改 package.json 文件配置:
1 2 3 4 5 6 7 8 9 10
| { "name": "@vue3-components/play", "private": true, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "liuhp", "license": "ISC" }
|
在 play 目录下安装依赖:
1
| pnpm install vite @vitejs/plugin-vue -D
|
在 play 目录下新建 vite.config.ts, 写入:
1 2 3 4 5 6
| import { defineConfig } from "vite" import vue from "@vitejs/plugin-vue"
export default defineConfig({ plugins: [vue()], })
|
新建 index.html,写入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head>
<body> <div id="app"></div> <script src="/main.ts" type="module"></script> </body> </html>
|
新建 app.vue, 写入:
1 2 3
| <template> <div>测试</div> </template>
|
新建 main.js, 写入:
1 2 3 4 5
| import { createApp } from "vue" import App from "./app.vue"
const app = createApp(App) app.mount("#app")
|
main.ts 此时会提示:
所以我们在 vue3-components 根目录下新建typings
文件夹,在 typings 下新建vue-shim.d.ts
文件,写入:
1 2 3 4 5
| declare module "*.vue" { import type { DefineComponent } from "vue" const component: DefineComponent<{}, {}, any> export default component }
|
此时,main.ts 就不会报错了。
修改 package.json 里 script 的命令:
1 2 3
| "scripts": { "dev": "vite" },
|
然后就可以在 play 目录下运行npm run dev
, 为了能够在 vue3-components 根目录下直接运行 play 项目,需要修改 vue3-components 根目录下的 package.json 里 script 的命令:
1 2 3
| "scripts": { "dev": "pnpm -C play dev" },
|
就可以在 vue3-components 根目录下直接运行npm run dev
了。
3 封装组件
3.1 创建三个文件夹
packages 下建立 3 个文件夹:
- components:存放封装的组件;
- utils: 存放公共方法;
- theme-chalk: 存放公共样式;
这三个文件夹下都要执行pnpm init
,为 package.json 中的 name 起好名字(后面安装需要用这个名字),都是独立包,可以独立发布。
然后在 vue3-components 根目录下安装这三个包,例如:pnpm install @vue3-components/components -w
-w 是安装在根目录下,安装好后根目录下的 package.json 如下:
3.2 封装组件:
在 packages\components\下新建组件文件夹,这里以 v-form 为例:
在v-form.vue
和v-form.ts
中封装好组件,也可以先随便写点,方便测试,README.md
中,写好参数说明。index.ts 中:
1 2 3 4 5 6
| import vform from "./src/v-form.vue" import { withInstall } from "@vue3-components/utils/with-install"
const VForm = withInstall(vform)
export default VForm
|
packages\utils\with-install.ts:
1 2 3 4 5 6 7 8 9 10 11
| import type { App, Plugin } from "vue"
export type SFCWithInstall<T> = T & Plugin
export const withInstall = <T>(comp: T) => { (comp as SFCWithInstall<T>).install = function (app: App) { app.component((comp as any).name, comp) } return comp as SFCWithInstall<T> }
|
验证封装组件是否生效
play\main.ts 全局引入组件:
1 2 3 4 5 6 7 8 9 10 11
| import { createApp } from "vue" import App from "./app.vue" import ElementPlus from "element-plus" import "element-plus/dist/index.css"
import VForm from "@vue3-components/components/v-form"
const app = createApp(App) app.use(ElementPlus) app.use(VForm) app.mount("#app")
|
play\app.vue 中使用组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <template> <div>测试</div> <v-form :elements="forms" @search="search"></v-form> </template> <script lang="ts" setup> const forms = [ { label: "角色标识", prop: "roleCode", placeholder: "角色标识", }, { label: "角色名称", prop: "roleName", placeholder: "分组名称", }, { label: "产品", prop: "productCode", placeholder: "产品", type: "select", optionGroup: true, options: [], style: "width:100%;", }, { label: "创建时间", prop: "create_at_start,create_at_end", type: "datetimerange", defaultValue: [], startPlaceholder: "选择开始时间", endPlaceholder: "选择结束时间", valueFormat: "yyyy-MM-dd HH:mm:ss", layout: { lg: { span: 8, }, md: { span: 12, }, sm: { span: 24, }, xs: { span: 24, }, }, }, ]
const search = (params) => { console.log(params) } </script>
|
至此,组件封装完成,接下来需要解决的问题是如何抽离公共样式,随着组件库中组件越来越多,一些重复的样式需要抽离出来,抽离完公共样式后,再解决打包和发布问题,发布后才能在其他项目中安装使用。