组件库(测试级)搭建-记录
目标
- 跟着已有教程熟悉搭建组件库的整体流程(掘金小册:基于 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