vueuse源码-启动

346 阅读3分钟

大致理解整个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的配置,主要配置顶部菜单、左侧菜单等