持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
在完成了一个项目,去做另一个项目时,会用到重复的组件,我们可以去打开上一个工程,把组件代码拷过来,再重新调整数据,这样会很麻烦,随着项目的越来越多,也不好定位哪个项目中使用了可复用的组件,所以业务组件库就发挥了它的优势,直接安装好后,按需引入,不用再手搬代码。本篇文章会讲述如何从0-1搭建自己的组件库。
技术栈:
- Vue3
- TypeScript
- pnpm
1 环境准备
在cmd终端安装pnpm
npm install pnpm -g
创建工程目录:vue3-components。
用vscode打开工程目录,在vscode终端执行:
pnpm init
根目录新建.npmrc文件,写入下面的语句,作用是与项目不直接相关的不会安装在node modules下:
shamefully-hoist = true
在根目录安装依赖:
pnpm install vue@next typescript -D
配置tsconfig.json:
{
"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文件,写入:
packages:
- "packages/**" # 存放编写的组件
- play # 使用组件的地方
2 项目创建
在根目录新建paly目录,进入play,执行:pnpm init,
修改package.json文件配置:
{
"name": "@vue3-components/play",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "liuhp",
"license": "ISC"
}
在play目录下安装依赖:
pnpm install vite @vitejs/plugin-vue -D
在play目录下新建vite.config.ts, 写入:
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
export default defineConfig({
plugins: [vue()],
})
新建index.html,写入:
<!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, 写入:
<template>
<div>测试</div>
</template>
新建main.js, 写入:
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文件,写入:
declare module "*.vue" {
import type { DefineComponent } from "vue"
const component: DefineComponent<{}, {}, any>
export default component
}
此时,main.ts就不会报错了。 修改package.json里script的命令:
"scripts": {
"dev": "vite"
},
然后就可以在play目录下运行npm run dev, 为了能够在vue3-components根目录下直接运行play项目,需要修改vue3-components根目录下的package.json里script的命令:
"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中:
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:
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全局引入组件:
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中使用组件:
<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>
至此,组件封装完成,接下来需要解决的问题是如何抽离公共样式,随着组件库中组件越来越多,一些重复的样式需要抽离出来,抽离完公共样式后,再解决打包和发布问题,发布后才能在其他项目中安装使用。