Vue3组件库搭建1

216 阅读6分钟

搭建项目

项目地址 gitee.com/fx666/fuxui…

项目架构:

  • 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:组件入口文件

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 组件库项目的搭建工作结束。