最近在考虑搭个小的组件库,主要是用来学习TS、Vue3,由于组件库需要文档,测试项目,测试用例等等,如果安装多个项目,安装依赖这活就有点上头。看了些社区的文章,monorepo这种方式可以帮助把依赖放到统一的仓库中进行管理。过去使用lerna + yarn workspace, 现在比较流行的是pnpm workspace。确定使用pnpm后向各方借鉴学习,写了个小的MVP,作为组件库的第一步。
一、搭建workspace
-
新建项目并初始化
mkdir jedi-space
cd jedi-space
pnpm init -
新建
pnpm-workspace.yaml文件
packages:
# packages 目录下的所有次级目录
- "packages/**"
# 测试文件夹
- "example"
# 文档目录
- "docs"
- 完善目录文件夹
jedi-space
├─ docs
├─ example
├─ package.json
├─ packages
└─ pnpm-workspace.yaml
4. 在根目录安装依赖
pnpm i vite -w
pnpm i vue@latest -w
packages文件夹 新增components文件夹,进入components路径pnpm init初始化一下
{
"name": "@jedi-space/components",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
name是workspace中其他项目判断的重要依据,根据自己需要重命名
二、搭建测试项目
example文件夹重复操作并且新增html.index文件
<!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>Example</title>
</head>
<body>
<div id="app"></div>
<script src="./src/main.ts" type="module"></script>
</body>
</html>
- 新增src文件夹,src目录新增
main.ts
import {createApp} from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
- 新建app.vue
<template>
<div class=''>
hello example
</div>
</template>
<script lang='ts' setup name='App'>
</script>
-
安装依赖, -w 表示安装workspace依赖,
pnpm-workspace.yaml中涵盖的目录会用到
pnpm i vite -wD
pnpm i vue -w
pnpm i @vitejs/plugin-vue -wD
pnpm i @vitejs/plugin-vue-jsx -wD
喜欢SFC就安装@vitejs/plugin-vue, 喜欢jsx就安装@vitejs/plugin-vue-jsx。 -
在example文件夹中新增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()
]
})
- example目录下package.json 的script中添加
"scripts": {
...
"dev": "vite"
},
启动命令pnpm vite
example 暂且搞定。
另外,根目录添加shims-vue.d.ts文件,处理下图的警告提示
declare module "*.vue" {
import { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
三、组件库编写
packages/components目录下新建sfcButton文件夹,编写简单的sfc版本button组件 sfcButton/src/Button.vue
<template>
<button>
<slot>SFC按钮</slot>
</button>
</template>
<script lang='ts' setup>
defineOptions({ name: 'SfcButton'})
</script>
sfcButton/index.ts
import SfcButton from './src/Button.vue'
export default SfcButton
packages/components目录下新建jsxButton文件夹,编写简单的jsx版本button组件 jsxButton/src/Button.vue
import {defineComponent} from 'vue'
export default defineComponent({
name:'JsxButton',
setup(props,context){
return <button>
{context.slots.default?.()}
</button>
}
})
jsxButton/index.ts
import JsxButton from './src/Button'
export default JsxButton
- 处理报错
根目录新建tsconfig.json
{
"compilerOptions": {
"jsx": "preserve"
}
}
4. 好了,此时在packages/components目录新建index.ts文件用于导出文件
import {App} from 'vue'
import JsxButton from './jsxButton'
import SfcButton from './sfcButton'
export{JsxButton,SfcButton}
export default{
install(app:App){
app.component(JsxButton.name, JsxButton)
app.component(SfcButton.name, SfcButton)
}
}
- 然后在example项目中安装组件,名称如图
pnpm i @jedi-space/components
安装成功后如图
- 更新
example/src目录的main.ts
import {createApp} from 'vue'
import App from './App.vue'
import JediUI from '@jedi-space/components'
const app = createApp(App)
app.use(JediUI)
app.mount('#app')
在app.vue文件使用组件
<template>
<div class=''>
hello example
<br>
<SfcButton>JediButton🍊</SfcButton>
<br>
<JsxButton>jsxButton🍓</JsxButton>
</div>
</template>
<script lang='ts' setup>
defineOptions({name:'App'})
</script>
It works!
四、下面处理文档项目
- 老样子先
pnpm init初始化项目 docs路径安装依赖
pnpm i vitepress@0.22.4 -D
pnpm i @jedi-space/components
docs目录新建index.md文件
docs项目终端输入echo '# Hello deji-space'>index.md
在package.json中加入启动脚本
"scripts": {
...
"docs:dev": "vitepress dev",
"docs:build": "vitepress build",
"docs:serve": "vitepress serve"
},
在docs目录运行pnpm docs:dev可以看到项目可以跑起来了
- 下面就是展示
button组件了
新建.vitepress文件夹
添加config.js文件
const sidebar = {
'/': [
{ text: '快速开始', link: '/' },
{
text: '按钮',
children: [
{ text: 'Button 按钮', link: '/components/button/' },
]
},
]
}
const config = {
themeConfig: {
sidebar,
}
}
export default config
docs/components/button目录新增index.md文件
# JediButton
<SfcButton>Jedi-SFC-Button🍊</SfcButton>
<JsxButton>Jedi-JSX-Button🍓</JsxButton>
.vitepress/theme目录新建index.ts,用以获取vue实例并注册组件
import Theme from 'vitepress/theme'
import JediUI from '@jedi-space/components'
export default {
...Theme,
enhanceApp: async ({ app}) => {
app.use(JediUI)
},
}
- 然后还需要解决jsx文件的引入问题docs目录新增
vite.config.ts文件
import { defineConfig } from "vite"
import vueJsx from "@vitejs/plugin-vue-jsx"
export default defineConfig({
plugins: [
vueJsx(),
]
})
完成后pnpm docs:dev 启动项目
- 可以通过安装vitepress-theme-demoblock优化演示效果
pnpm i vitepress-theme-demoblock@1.4.2 -D
.vitepress/theme/index.ts路径文件添加配置
...
import Demo from 'vitepress-theme-demoblock/components/Demo.vue'
import DemoBlock from 'vitepress-theme-demoblock/components/DemoBlock.vue'
export default {
...Theme,
enhanceApp: async ({ app}) => {
...
app.component('Demo', Demo)
app.component('DemoBlock', DemoBlock)
},
}
.vitepress/config.ts目录文件添加配置
import {demoBlockPlugin} from 'vitepress-theme-demoblock'
...
const config = {
...
markdown: {
config: (md) => {
// 添加DemoBlock插槽
md.use(demoBlockPlugin)
}
}
}
export default config
components/button/index.md文件修改
# JediButton
:::demo
```vue
<template>
<SfcButton>Jedi-SFC-Button🍊</SfcButton>
<JsxButton>Jedi-JSX-Button🍓</JsxButton>
</template>
```
:::
pnpm docs:dev重启项目, 搞定!
[仓库地址](youknowHRT/jedi-space-demo: pnpm workspace MVP (github.com))