搭建项目
项目架构:
- Vue3
- TypeScript
- ESLint
- Prettier
- Sass
- Vitest
- Rollup
- monorepo
搭建项目框架
整个组件库我们会采用 monorepo 的代码管理方式,搭建 monorepo 项目的方式很多,这里我们选择 pnpm + workspace 的方式来进行搭建。
首先创建一个 fuxui-plus 的项目根目录,使用 pnpm init 进行初始化。
接下来创建如下的目录:
- documents:文档目录
- packages:组件库代码目录
- components:具体的组件代码
- theme-chalk:组件样式
- utils:工具库
然后针对 components、theme-chalk、utils 分别进行 pnpm init 包的初始化,分别将packge.json中的name改为@fuxui-plus/components @fuxui-plus/theme-chalk @fuxui-plus/utils。之后在项目根目录下创建 pnpm-workspace.yaml 文件,写入如下的内容:
packages:
- packages/*
- documents
- examples
# exclude packages that are inside test directories
- "!**/test/**"
接下来我们来进行依赖的安装:
pnpm add typescript -D -w
这里需要加--workspace 强制拉取
pnpm add @fuxui-plus/components @fuxui-plus/theme-chalk @fuxui-plus/utils -w --workspace
至此,整个项目的架子的第一阶段告一段落。
创建第一个组件
接下来我们要创建第一个组件,所有的组件都放在 components 这个包里面,每个组件都是一个单独的目录,每个目录的结构大致如下:
- button
- src:源码目录
- button.vue:具体的组件代码
- button.ts:组件 props 类型
- index.ts:组件入口文件
- src:源码目录
button.vue 组件的代码,当前如下:
<template>
<div>这是一个按钮组件</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "FuxButton",
});
</script>
<style scoped></style>
接下来假设我们的 button 组件已经开发完毕,下一步就是将这个组件应用到具体的项目里面。但是由于现在组件和具体使用组件的项目是分开的,所以组件需要通过插件的形式进行安装,所以我们需要编写一个统一安装组件的工具方法。
utils 工具方法
首先我们在 utils 目录下面创建一个 with-install.ts 的文件,代码如下:
// 该文件会导出一个工具方法,该方法用于组件的安装
import type { App, Plugin } from "vue";
// 这里定义了一个新的 SFCWithInstall 的类型
// 这是一个泛型类型,代表的是一个即是类型 T 又是 Plugin 类型的类型
// 也就是说,这个类型的对象既有 T 类型的所有属性和方法,也有 Plugin 的所有属性和方法
export type SFCWithInstall<T> = T & Plugin;
export const withInstall = <T>(com: T) => {
(com as SFCWithInstall<T>).install = function(app: App){
app.component((com as any).name, com as SFCWithInstall<T>);
}
return com;
};
// 之后外部在使用这个 withInstall 的时候,大致是这么使用
// withInstall(MyComponent) ---> 这里其实就是对这个组件做一个增强效果,添加了一个 install 方法
// 之后在其他项目中:
// app.use(MyComponent)
接下来创建一个 index.ts,这是整个包的入口文件,代码如下:
// 这是整个 utils 包的入口文件
export * from "./with-install";
最后就是为这个包提供一个类型声明文件,在utils下面创建 types.d.ts文件,内容如下:
declare module "@fuxui-plus/utils"
之后还需要在 utils/pakcage.json 中添加 types 字段:
"types": "types.d.ts",
工具方法完成之后,就可以在 components 下面的各个组件中导出增强后的组件,例如在 button/index.ts 文件中写入如下的内容:
// 每个组件的入口文件
import Button from "./src/button.vue";
import { withInstall } from "@fuxui-plus/utils";
export default withInstall(Button);
创建组件展示项目 examples
目前我们的第一个组件已经写好了,我们需要有一个项目来引入这个组件查看效果,这个项目就是 examples。
可以通过如下的命令来创建一个 vue 的项目:
pnpm create vue@latest
搭建 examples 项目的时候,我们选择安装 typescript、router、eslint、prettier,考虑到这些依赖有很大通用性,我们选择安装到工作空间里面:(我这里要用node18版本才行,22版本就挂了,不知道为啥)
pnpm i -w
拉取好的 examples 项目下面会有三个关于 ts 的配置文件,这三个文件实际上是可以合并为一个的,它们都是 ts 的配置文件
- tsconfig.app.json:用于构建应用程序的一个配置
- tsconfig.json:最终合并的配置文件
- tsconfig.node.json:构建运行在 node.js 环境代码的一个配置
我们将这三个 ts 的配置文件移动到 fuxui-plus 根目录下面,作为整个 monorepo 全局配置。
期间可能会报 @vue/tsconfig/tsconfig.dom.json 无法找到的错误,安装一下 @vue/tsconfig
pnpm add @vue/tsconfig -D -w
pnpm add @tsconfig/node22 -D -w 可能需要
pnpm add sass-embedded -D -w 可能需要
pnpm add sass -D -w 可能需要
然后对 tsconfig.app.json 配置的 include 进行一个修改,如下:
"include": [
"packages/components/**/*.ts",
"packages/components/**/*.vue",
],
表示要解析的文件包含 components 下面的 ts 以及 vue 文件。
之后我们要启动项目,在根目录下面的 package.json 中添加如下的命令脚本:
"scripts": {
"dev" : "pnpm -C examples dev"
},
`pnpm -C examples dev` 中: `-C examples`:`-C` 是 `--dir` 的缩写,指定在 `examples` 目录下执行后续命令(无需手动 `cd examples`);
回头就可以在根目录下面通过 pnpm dev 启动 examples 项目。
搭建 examples 展示组件的架子
首先创建相应的 XXXView 组件,例如 ButtonView.vue,代码如下:
<template>
<div>this is button view</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
接下来我们需要修改路由,对应的路由如下:
import { createRouter, createWebHistory } from 'vue-router'
import ButtonView from '../views/ButtonView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'button',
component: ButtonView
},
{
path: '/card',
name: 'card',
component: () => import('../views/CardView.vue')
}
],
linkActiveClass: "active",
})
export default router
之后是改造 App.vue,形成基本的组件展示切换的架子:
<template>
<h1 class="title">Fuxui 组件展示</h1>
<nav class="nav">
<router-link to="/">Button</router-link>
<router-link to="/card">Card</router-link>
</nav>
<div class="displayArea">
<router-view></router-view>
</div>
</template>
<script setup lang="ts"></script>
<style scoped lang="scss">
h1.title {
text-align: center;
// margin-top: 5em;
font-weight: 200;
}
p {
text-align: center;
font-weight: 200;
margin-top: 1em;
font-size: 18px;
}
.nav {
// border: 1px solid #ccc;
display: flex;
height: 50px;
justify-content: space-evenly;
align-items: center;
}
.displayArea {
margin-top: 2em;
// border: 1px solid #ccc;
display: flex;
justify-content: center;
}
.active {
border-bottom: 3px solid hsla(160, 100%, 37%, 1);
}
</style>
这里需要注意,我们上面的代码使用到了 sass,所以需要安装 sass 模块:
pnpm add sass -D -w
最后就是对 main.css 稍作修改:
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
}
解决 examples 的路由文件 index.ts 报和 import.meta 相关的错误。
首先在 examples 下面新建一个 tsconfig.json 文件,该文件会继承根目录下面的 ts 配置,同时拥有自己独特的配置,具体的内容如下: 报错的话可能要用这个 "moduleResolution": "Bundler"
{
"extends": "../tsconfig.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "NodeNext"
}
}
最后一步
最后一步就是要引入 components 下面的组件。
首先是在 examples/main.ts 下面引入该组件:
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 单独引入各个组件
import FuxButton from '@fuxui-plus/components/button'
const app = createApp(App)
// 挂载组件
app.use(FuxButton)
app.use(router)
app.mount('#app')
之后在 ButtonView 里面使用引入的 button 组件:
<template>
<div>
<!-- 测试按钮组件 -->
<div class="row">
<fux-button></fux-button>
</div>
</div>
</template>
<script setup lang="ts"></script>
<style scoped lang="scss">
.row {
margin-bottom: 20px;
width: 800px;
display: flex;
justify-content: space-evenly;
margin: 20px auto;
}
</style>
至此,我们整个基于 vue3 + ts 的 monorepo 组件库项目的搭建工作结束。