Skip to content

组件库(测试级)搭建-记录

目标

  • 跟着已有教程熟悉搭建组件库的整体流程(掘金小册:基于 Vite 的组件库工程化实战
  • 熟悉并掌握在组件库搭建过程中的前端工程化相关的知识

环境准备及架构风格

  • pnpm + monorepo 风格架构模式

技术栈选型

技术栈版本安装命令备注
pnpm---
vite3.0.7pnpm i vite@"3.0.7" -D-
typescript---
vue33.2.37pnpm i vue@"3.2.37"vite 支持 vue 语法
@vitejs/plugin-vue3.0.3pnpm i @vitejs/plugin-vue@"3.0.3" -D提供 vue 单文件语法及 vue 的编译功能
@vitejs/plugin-vue-jsx2.0.0pnpm i @vitejs/plugin-vue-jsx@"2.0.0" -Dvite 中支持 jsx 语法

项目工程搭建

工程目录创建及配置

bash
// 创建工程文件
mkdir darkdemo-ui

// 进入工程文件
cd darkdemo-ui

组件库包管理架构选择 pnpm + monorepo, 后期增量 docs、playRound 等工程。点击了解 pnpm+monorepo

bash
// 初始化软件包配置
pnpm init

使用 vite 进行项目构建

bash
// 安装vite | -D Vite 作为开发调试工具,只会在在开发环境中使用
pnpm i vite@"3.0.7" -D

测试 vite 是否安装成功,在工程目录下创建

在项目根目录下创建 index.html

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>
    <h1>Hello darkdemo UI</h1>
  </body>
</html>

使用 npm5.2+版本自带的 npx 启动 vite,其会自动找当前目录文件下的 index.html,开启一个本地服务

vite启动成功

页面正常启动,说明成功安装了。

确认测试 vite 是否可以调试 typescript, 在项目根目录下创建 src/index.ts

typescript
// src/index.ts

const s: string = 'Hello Typescript'
console.log(s)

在上述创建的 index.html 的 body 中引入 index.ts

html
<script src="./src/index.ts" type="module"></script>

其支持热启动,无须在重新启动了。打开控制台查看是否成功输出 Hello Typescript

在 package.json 中添加 scripts 命令,方便以后快捷启动

json
"scripts": {
  "dev": "vite"
},

创建测试组件完善组件库工程

在工程依赖中安装 vue3

bash
pnpm i vue@"3.2.37"

创建文件 src/button/button/index.ts

typescript
import { defineComponent, h } from 'vue'

export default defineComponent({
  name: 'DButton',
  render() {
    return h('button', null, 'DButton')
  }
})

创建导出挂载文件 src/index.ts

typescript
import { createApp } from 'vue'

import DButton from './button'

createApp(DButton).mount('#app')

在根目录的 index.html 中加入挂载 DOM 入口

html
<div id="app"></div>

启动 pnpm dev, 看页面组件是否渲染成功。

上述组件中使用 render 函数渲染,是因为 vue3.0 的包不支持模板编译功能,也就是说, template 语法现在还不能用。在 Vue3.0 中编译功能推荐在构建阶段完成,而不是放到浏览器中运行。如果希望在浏览器中的话,可以选择 ./node_modules/vue/dist/vue.global.js 这个包。

即可以这样理解,vite 作为一个前端工程的构建及打包工具,默认只能支持 TS 代码,而 Vue 的模板需要在编译阶段转换为 typescript 代码(渲染函数)才可以运行。vue 插件提供模板的编译,同时也支持 Vue 单文件(SFC)组件的编译。

安装 vite 的 vue 插件

bash
pnpm i @vitejs/plugin-vue@"3.0.3" -D

在项目根目录下创建 vite.config.ts

typescript
import { defineConfig } from 'vite'

// https://vitejs.dev/config/
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()]
})

注意

配置完 vite.config.ts 后,需要重启 vite 服务,否则不会生效。

下面创建一个单文件组件进行测试,创建 src/SFCButton.vue

vue
<template>
  <button>SFC Button</button>
</template>

<script lang="ts">
export default {
  name: 'SFCButton'
}
</script>

在 src/index.ts 中引入测试

typescript
import { createApp } from 'vue'
// import DButton from "./button";
import SFCButton from './SFCButton.vue'

createApp(SFCButton).mount('#app')

小册中 ts 引入.vue 文件的【找不到模块./SFCButton.vue 或其相应的类型声明】的报错并未出现,所以跳过。页面正常渲染则通过。

在项目工程中配置使用 jsx

bash
pnpm i @vitejs/plugin-vue-jsx@"2.0.0" -D

修改 vite.config.ts

typescript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://github.com/vuejs/jsx-next
import vueJsx from '@vitejs/plugin-vue-jsx'

export default defineConfig({
  plugins: [
    vue(),
    vueJsx({
      // options are passed on to @vue/babel-plugin-jsx
    })
  ]
})

使用 jsx 创建组件 /src/JSXButton.tsx

jsx
import { defineComponent, h } from 'vue'

export default defineComponent({
  name: 'JSXButton',
  render() {
    return <button>JSX Button</button>
  }
})

小册中使用 jsx 语法在 vscode 中的报错【找不到名称“React”】未提示,所以跳过。

ts
import { createApp } from 'vue'

// 导入 SButton 组件
// import DButton from "./button";
// import SFCButton from "./SFCButton.vue";
import JSXButton from './JSXButton'

// 创建 app 实例
createApp(JSXButton).mount('#app')

刷新页面,页面正常渲染则通过。

库文件封装

组件库常见的引入方式:

  • 完整引入: 一次性引入全部组件,使用 Vue.use 以 Vue 插件的形式引入;
  • 按需引入: 按需引入,导出单个组件,使用 Vue.component 注册。

以 elementUI 库为例:

js
import Vue from 'vue'
import Element from 'element-ui'

// 完整引入
Vue.use(Element)

// or
import {
  Select,
  Button
  // ...
} from 'element-ui'

// 按需引入
Vue.component(Select.name, Select)
Vue.component(Button.name, Button)

设计一个分发入口,需要同时满足按需引入和完整引入两种方式。

/src/entry.ts

ts
import { App } from 'vue'

// 导入 SButton 组件
import DButton from './button'
import SFCButton from './SFCButton.vue'
import JSXButton from './JSXButton'

// 支持按需导出
export { DButton, SFCButton, JSXButton }

// 支持全量导出
export default {
  install(app: App): void {
    app.component(DButton.name, DButton)
    app.component(SFCButton.name, SFCButton)
    app.component(JSXButton.name, JSXButton)
  }
}

默认 vite 就是可以支持构建的,使用 vite 的 build 命令,如果导出库文件的话,还需要配置【导出模块类型】并确定导出的文件名。配置如下:

vite.config.ts

ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'

export default defineConfig({
  plugins: [
    vue(),
    vueJsx({
      // options are passed on to @vue/babel-plugin-jsx
    })
  ],
  // 添加库模式配置
  build: {
    rollupOptions: {
      external: ['vue', 'vue-router'],
      output: {
        globals: {
          vue: 'Vue'
        }
      }
    },
    minify: false,
    lib: {
      entry: './src/entry.ts',
      name: 'DarkdemoUI',
      fileName: 'darkdemo-ui',
      // 导出模块格式
      formats: ['es', 'umd', 'iife']
    }
  }
})

输出格式不懂 es、umd、iife? 点这里去瞅瞅!

注意:

原小册中 formats 中的 esm 是错误的,报错【不能将类型“"esm"”分配给类型“LibraryFormats”。ts(2322)】。想要输出 esModule 格式的,需要配置 formats: ["es"]。同时也要注意打包后生成的文件格式是 .mjs 格式的。下文引入的时候不在提及。

在 package.json 中配置快捷打包命令

bash
"scripts": {
  "build": "vite build"
}

执行打包命令

bash
pnpm build

打包成功后会在根目录的 dist 目录下生成模块配置对应的包文件。

测试打包的组件库是否正常。

完全引入测试,在项目根目录下创建 demo/es/index.html

html
<h1>Demo</h1>
<div id="app"></div>
<script type="module">
  import { createApp } from 'vue/dist/vue.esm-bundler.js'
  import DarkdemoUI, {
    SFCButton,
    JSXButton,
    DButton
  } from '../../dist/darkdemo-ui.esm.js'

  createApp({
    template: `
      <DButton/>
      <JSXButton/>
      <SFCButton/>
    `
  })
    .use(DarkdemoUI)
    .mount('#app')
</script>

按需引入测试,在项目根目录下创建 demo/es/button.html

html
<h1>Demo</h1>
<div id="app"></div>
<script type="module">
  import { createApp } from 'vue/dist/vue.esm-bundler.js'
  import SmartyUI, {
    SFCButton,
    JSXButton,
    DButton
  } from '../../dist/smarty-ui.esm.js'

  createApp({
    template: `
      <DButton/>
      <JSXButton/>
      <SFCButton/>
    `
  })
    .component(SFCButton.name, SFCButton)
    .component(JSXButton.name, JSXButton)
    .component(DButton.name, DButton)
    .mount('#app')
</script>

启动 vite

bash
1. 访问 http://localhost:5173/demo/es/index.html
2. 访问 http://localhost:5173/demo/es/button.html

成功渲染则表单简单的组件库已经创建好了。

pnpm + monorepo 改造项目工程架构

pnpm + monorepo 不知道?点击这里去了解

修改软件包目录结构

├── packages
|   ├── darkdemo-ui      // UI组件库
|   |   ├── package.json
|   ├── darkdemo-ui-docs // docs文档
|   |   ├── package.json
├── package.json
  • 将组件库相关代码迁移至 packages/darkdemo-ui
  • 创建组件库文档子工程 packages/darkdemo-ui-docs

初始化 Monorepo 软件包

bash
pnpm init

实现组件库的按需引入功能