组件库(测试级)搭建-记录 
目标 
- 跟着已有教程熟悉搭建组件库的整体流程(掘金小册:基于 Vite 的组件库工程化实战)
 - 熟悉并掌握在组件库搭建过程中的前端工程化相关的知识
 
环境准备及架构风格 
- pnpm + monorepo 风格架构模式
 
技术栈选型 
| 技术栈 | 版本 | 安装命令 | 备注 | 
|---|---|---|---|
| pnpm | - | - | - | 
| vite | 3.0.7 | pnpm i vite@"3.0.7" -D | - | 
| typescript | - | - | - | 
| vue3 | 3.2.37 | pnpm i vue@"3.2.37" | vite 支持 vue 语法 | 
| @vitejs/plugin-vue | 3.0.3 | pnpm i @vitejs/plugin-vue@"3.0.3" -D | 提供 vue 单文件语法及 vue 的编译功能 | 
| @vitejs/plugin-vue-jsx | 2.0.0 | pnpm i @vitejs/plugin-vue-jsx@"2.0.0" -D | vite 中支持 jsx 语法 | 
项目工程搭建 
工程目录创建及配置 
// 创建工程文件
mkdir darkdemo-ui
// 进入工程文件
cd darkdemo-ui组件库包管理架构选择 pnpm + monorepo, 后期增量 docs、playRound 等工程。点击了解 pnpm+monorepo
// 初始化软件包配置
pnpm init使用 vite 进行项目构建
// 安装vite | -D Vite 作为开发调试工具,只会在在开发环境中使用
pnpm i vite@"3.0.7" -D测试 vite 是否安装成功,在工程目录下创建
在项目根目录下创建 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>
    <h1>Hello darkdemo UI</h1>
  </body>
</html>使用 npm5.2+版本自带的 npx 启动 vite,其会自动找当前目录文件下的 index.html,开启一个本地服务

页面正常启动,说明成功安装了。
确认测试 vite 是否可以调试 typescript, 在项目根目录下创建 src/index.ts。
// src/index.ts
const s: string = 'Hello Typescript'
console.log(s)在上述创建的 index.html 的 body 中引入 index.ts
<script src="./src/index.ts" type="module"></script>其支持热启动,无须在重新启动了。打开控制台查看是否成功输出 Hello Typescript。
在 package.json 中添加 scripts 命令,方便以后快捷启动
"scripts": {
  "dev": "vite"
},创建测试组件完善组件库工程 
在工程依赖中安装 vue3
pnpm i vue@"3.2.37"创建文件 src/button/button/index.ts
import { defineComponent, h } from 'vue'
export default defineComponent({
  name: 'DButton',
  render() {
    return h('button', null, 'DButton')
  }
})创建导出挂载文件 src/index.ts
import { createApp } from 'vue'
import DButton from './button'
createApp(DButton).mount('#app')在根目录的 index.html 中加入挂载 DOM 入口
<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 插件
pnpm i @vitejs/plugin-vue@"3.0.3" -D在项目根目录下创建 vite.config.ts
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
<template>
  <button>SFC Button</button>
</template>
<script lang="ts">
export default {
  name: 'SFCButton'
}
</script>在 src/index.ts 中引入测试
import { createApp } from 'vue'
// import DButton from "./button";
import SFCButton from './SFCButton.vue'
createApp(SFCButton).mount('#app')小册中 ts 引入.vue 文件的【找不到模块./SFCButton.vue 或其相应的类型声明】的报错并未出现,所以跳过。页面正常渲染则通过。
在项目工程中配置使用 jsx
pnpm i @vitejs/plugin-vue-jsx@"2.0.0" -D修改 vite.config.ts
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
import { defineComponent, h } from 'vue'
export default defineComponent({
  name: 'JSXButton',
  render() {
    return <button>JSX Button</button>
  }
})小册中使用 jsx 语法在 vscode 中的报错【找不到名称“React”】未提示,所以跳过。
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 库为例:
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
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
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 中配置快捷打包命令
"scripts": {
  "build": "vite build"
}执行打包命令
pnpm build打包成功后会在根目录的 dist 目录下生成模块配置对应的包文件。
测试打包的组件库是否正常。
完全引入测试,在项目根目录下创建 demo/es/index.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
<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
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 软件包 
pnpm init