大致理解整个vueuse项目是如何运行、打包、发布的
文件结构
vueuse
├── meta
│ ├── ecosystem-functions.ts
│ ├── packages.ts // 各个packages包的信息
│ └── versions.ts // 历史版本号,docs用到
├── packages // pnpm-workspace,也是vitepress的文档目录
│ ├── add-ons.md
│ ├── components // @vueuse/components
│ ├── contributors.json // 通过git读取开发者
│ ├── contributors.ts // 核心开发者信息
│ ├── core // @vueuse/core
│ ├── electron // @vueuse/electron
│ ├── export-size.json // 每个hook包体积信息
│ ├── firebase // @vueuse/firebase
│ ├── integrations // @vueuse/integrations
│ ├── math // @vueuse/math
│ ├── metadata // @vueuse/metadata
│ ├── nuxt // @vueuse/nuxt
│ ├── router // @vueuse/router
│ ├── rxjs // @vueuse/rxjs
│ ├── shared // @vueuse/shared
│ └── vite.config.ts // vitepress的vite配置
├── playgrounds // 各个场景的demo
│ ├── build.sh
│ ├── nuxt
│ ├── vite
│ └── vite-vue2.7
├── rollup.config.ts // rollup配置
├── scripts // 各种node脚本
├── tsconfig.json
├── unocss.config.ts
└── vitest.config.ts
启动命令
本地启动,执行命令nr dev
{
"dev": "nr update && nr docs",
"docs": "vue-demi-switch 3 && vitepress dev packages --open",
"update": "nr -C packages/metadata update && esno scripts/update.ts"
}
- nr 命令是什么? nr是 @antfu/ni提供出来的,自动根据项目执行
pnpm/npm/yarn命令
因此nr update就是执行本scripts中的update命令。
再看nr -C packages/metadata update,执行packages/metadata这个包的update命令
因此需要到packages/metadata/package.json中看下scripts命令配置如下:
{
"update": "esno scripts/update.ts"
}
- esno 是什么,esno是esno带出来的命令,用esbuild解析ts并执行ts,现在核心也是用的tsx,已经推荐使用tsx了。
因此找到/packages/metadata/scripts/update.ts里面,摘抄代码如下:
export async function readMetadata() {
for (const info of packages) {
// 通过fast-glob找到 `packages/[子包]`下的文件夹,理解获取所有useXXX的文件夹
const functions = await listFunctions(dir);
await Promise.all([
// `git log -1 <指定文件>`: 最近一次某个文件git提交的log日志,
// `--format=%at`: 输出格式为时间戳
// 即得到每个useXXX函数的最近修改时间
const gitlog = await git.raw(['log', '-1', '--format=%at', tsPath]);
// 遍历子包中的文件夹
functions.map(async (fnName) => {
const mdRaw = await fs.readFile(mdPath, 'utf-8')
const { content: md, data: frontmatter } = matter(mdRaw); // 解析出md文件头部的元信息
// 匹配以 # 开头的一级标题,并捕获标题下的第一段文本。
// 这个正则表达式使用了多行模式(m 标志),以便可以匹配多行文本。
// 匹配到的文本将作为数组返回,其中第一个元素是整个匹配的字符串,而第二个元素是捕获的文本。
let description = (md.replace(/\r\n/g, '\n').match(/# \w+[\s\n]+(.+?)(?:, |\. |\n|\.\n)/m) || [])[1] || '';
});
]);
}
// 将按照 `name` 属性的字典顺序对 `indexes.functions` 数组进行排序。
indexes.functions.sort((a, b) => a.name.localeCompare(b.name));
}
async function run() {
const indexes = await readMetadata()
// 写入到 `/packages/metadata/index.joson`
await fs.writeJSON(join(DIR_PACKAGE, 'index.json'), indexes, { spaces: 2 });
}
run()
上面脚本最后是为了在 /packages/metedata/index.json生成这样的信息
{
/** 子包信息 **/
"packages": {
"shared": {
"name": "shared",
"display": "Shared utilities",
"dir": "packages/shared"
},
"core": {
"name": "core",
"display": "VueUse",
"description": "Collection of essential Vue Composition Utilities",
"dir": "packages/core"
},
"components": {
"name": "components",
"display": "Components",
"description": "Renderless components for VueUse",
"author": "Jacob Clevenger<https://github.com/wheatjs>",
"external": [
"@vueuse/core",
"@vueuse/shared"
],
"dir": "packages/components"
},
"router": {
"name": "router",
"display": "Router",
"description": "Utilities for vue-router",
"addon": true,
"external": [
"vue-router"
],
"globals": {
"vue-router": "VueRouter"
},
"dir": "packages/router",
"docs": "https://mockhost.vueuse.org/router/README.html"
}
},
/** 分类信息,后面用文档建设需要 **/
"categories": [
"Animation",
"Component",
"DOM",
"Reactivity",
"Unknown",
"@Router"
],
/** 每个useXXX的信息 **/
"functions": [
{
"name": "toValue",
"package": "shared",
"lastUpdated": 1697120544000,
"docs": "https://mockhost.vueuse.org/shared/toValue/",
"category": "Reactivity",
"description": "get the value of value/ref/getter",
"alias": [
"resolveUnref"
]
},
{
"name": "tryOnScopeDispose",
"package": "shared",
"lastUpdated": 1698708493000,
"docs": "https://mockhost.vueuse.org/shared/tryOnScopeDispose/",
"category": "Component",
"description": "safe `onScopeDispose`"
}
]
}
执行结束后执行nr docs,同理找到
{
"docs": "vue-demi-switch 3 && vitepress dev packages",
}
这条命令,一方面调用vue-demi切换为vue3的兼容模式,并用vitepress启动,以packages作为文档库跟目录。
所以整个packages里面的md文件,都会映射为文档库的页面
vitepress配置
在 /packages/vite.config.ts 是文档库vite的配置:
export default defineConfig(async () => {
const [changeLog, contributions] = await Promise.all([
// 通过git log查日志,最后返回格式
// [{hash:<gitlog的hash>, date:<日期>, message:<commit信息>,author_name:'提交人',author_email:'提交人邮箱',functions:['useXXX']}}]
getChangeLog(process.env.CI ? 1000 : 100),
// 获取开发者信息
// ['useXXX', [{name:人,count:数量,hash:邮箱的md5}]]
getFunctionContributors(),
]);
return {
resolve: {
// 这里设置别名,在vitepress中的demo.vue才可以知道对应的包
alias: {
'@vueuse/core': resolve(__dirname, 'core/index.ts'),
}
},
plugins: [
// 位于`/packages/.vitepress/plugins/markdownTransform.ts`
MarkdownTransform(),
// 组件和icon的自动引入
Components(),
Icons(),
PWA(),
UnoCSS(),
]
};
})
在/packages/.vitepress/config.ts也有vitepress的配置,主要配置顶部菜单、左侧菜单等