Vite是怎么识别.vue文件的?

411 阅读1分钟

Vite是怎么识别.vue文件的? 或者说浏览器怎么识别到了.vue文件, 总体上来说, 点开浏览器中的.vue文件,可以看到内容已经不是真正的Vue SFC文件内容了, 而是经过编译的代码, 符合浏览器对JS的解析规范!

vite处理.vue文件大体如下步骤:

1. 读取到.vue文件

对于node来讲, 这个是可以的, 没有问题

2. 使用@vue/compiler-sfc解析读取到的文件内容

使用@vue/compiler-sfc的compileScript, compileTemplate, compileStyle三个方法分别解析vue文件的script, template, style模块, 解析到相应的代码字符串

3. 将解析到的代码字符串组装成Vue Component的范式

例如

const SomeComponent = {
    name: 'SomeComponent,
    data() {},
    render() {}
}

4. 利用打包工具esbuild打包成浏览器能够识别的文件格式

完整代码如下:

main.vue

<template>
    <div class="message">{{ message }}</div>
</template>
  
<script>
import { ref } from "vue";

export default {
    name: "Main",
    setup() {
        const message = ref("Main");
        return {
            message,
        };
    },
};
</script>
  
<style scoped>
.message {
    font-size: 60px;
    font-weight: 900;
}
</style>
  

main.mjs

import pkg from 'fs-extra';
const { readFile, writeFile } = pkg;
import { parse, compileScript, compileTemplate, rewriteDefault } from "@vue/compiler-sfc";

import { build } from "esbuild";
import externalGlobalPluginPkg from 'esbuild-plugin-external-global';
const { externalGlobalPlugin } = externalGlobalPluginPkg;

async function main() {
    const file = await readFile('./main.vue', 'utf8');
    const { descriptor, error } = parse(file);

    const id = Date.now().toString();
    const scopeId = `data-v-${id}`;

    const script = compileScript(descriptor, { id: scopeId });
    const codeList = [];
    codeList.push(rewriteDefault(script.content, "__sfc_main__"));
    codeList.push(`__sfc_main__.__scopeId='${scopeId}'`);

    const template = compileTemplate({
        source: descriptor.template?.content,
        filename: "main.vue", // 用于错误提示
        id: scopeId,
    });
    codeList.push(template.code);
    codeList.push(`__sfc_main__.render=render`);
    codeList.push(`export default __sfc_main__`);

    const code = codeList.join("\n");
    await writeFile("build.temp.js", code);

    await build({
        entryPoints: ["build.temp.js"],	// 入口文件
        format: "esm",			// 打包成 esm
        outfile: "bundle.js",		// 设置打包文件的名字
        bundle: true,				// bundle 为 true 才是打包模式
        external: ["vue"],
        plugins: [
          externalGlobalPlugin({
            vue: "window.Vue",	// 将 import vue 模块,替换成 window.Vue
          }),
        ],
      });
}
main();