sfc编译过程记录

158 阅读2分钟

首先在vite.config.ts文件中里的'@vitejs/plugin-vue'插件引入的vue()方法根据断点内部会执行 vuePlugin方法,

function vuePlugin(){

let options = {
    isProduction: process.env.NODE_ENV === "production",
    compiler: null,
    ...rawOptions,
    include,
    exclude,
    customElement,
    reactivityTransform,
    root: process.cwd(),
    sourceMap: true,
    cssDevSourcemap: false,
    devToolsEnabled: process.env.NODE_ENV !== "production"
  };
return   {
    name: "vite:vue",
    handleHotUpdate(ctx) {.....},
    configResolved(config) {....},
    configureServer(server){...},
    buildStart(){....}
    async resolveId(){...}
    load(id, opt) {.....},
    transform(code, id, opt) {....}
    }

其中在pnpm dev之后执行的为buildStart

 buildStart() {
      options.compiler = options.compiler || resolveCompiler(options.root);
    },
    

buildStart为options.compiler赋值,如果无回调用 resolveCompiler方法

function resolveCompiler(root) {
  const compiler = tryRequire("vue/compiler-sfc", root) || tryRequire("vue/compiler-sfc");
  if (!compiler) {
    throw new Error(
      `Failed to resolve vue/compiler-sfc.
@vitejs/plugin-vue requires vue (>=3.2.25) to be present in the dependency tree.`
    );
  }
  return compiler;
}
const _require = node_module.createRequire((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)));
function tryRequire(id, from) {
  try {
    return from ? _require(_require.resolve(id, { paths: [from] })) : _require(id);
  } catch (e) {
  }
}

根据resolveCompiler 基本上确认compiler赋值为获取vue/compiler-sfc模块的东西此时一阶段完成

后续在打开网页时会调用transform方法,期间会调用其他文件目录下获取数据编译。在遇到app.vue后会调用transformMain方法,之后调用createDescriptor创建了descriptor对象生成了ast语法树,descriptor对象内部已经有styles数组,templat与scriptSetup构成vue的3个部分。

async function transformMain(code, filename, options, pluginContext, ssr, asCustomElement) {
    const { devServer, isProduction, devToolsEnabled } = options;
    const { descriptor, errors } = createDescriptor(filename, code, options);
    //判断errors后后看是否报错,就不写了。
    const { code: scriptCode, map: scriptMap } = await genScriptCode(
    descriptor,
    options,
    pluginContext,
    ssr
  );
  if (hasTemplateImport) {
    ({ code: templateCode, map: templateMap } = await genTemplateCode(
      descriptor,
      options,
      pluginContext,
      ssr
    ));
  }
  const stylesCode = await genStyleCode(
    descriptor,
    pluginContext,
    asCustomElement,
    attachedProps
  );
}

之后就是genScriptCode genTemplateCode genStyleCode 三大块各自编译解析。

genScriptCode部分

async function genScriptCode(descriptor, options, pluginContext, ssr) {
  let scriptCode = `const _sfc_main = {}`;
  let map;
  const script = resolveScript(descriptor, options, ssr);
  
  }

resolveScript函数讲vue的部分编译为乐js文件

 之前
 
 <script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
 
 之后
 
import { defineComponent as _defineComponent } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
export default _defineComponent({ 
__name: 'App',
setup(__props, { expose: __expose }) 
{ __expose();
// This starter template is using Vue 3 <script setup> SFCs 
// Check out [https://vuejs.org/api/sfc-script-setup.html#script-setup
 const __returned__ = { HelloWorld } 
 Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true }) 
 return __returned__ 
     } 
 
 })

在resolveScript内部调用了vue/compiler-sfc的compileScript方法

genTemplateCode部分

调用了transformTemplateInMain 核心方法 compile

function transformTemplateInMain(code, descriptor, options, pluginContext, ssr) {
  const result = compile(code, descriptor, options, pluginContext, ssr);
  return {
    ...result,
    code: result.code.replace(
      /\nexport (function|const) (render|ssrRender)/,
      "\n$1 _sfc_$2"
    )
  };
}
//compile 部分
function compile(code, descriptor, options, pluginContext, ssr) {
  const filename = descriptor.filename;
  const result = options.compiler.compileTemplate({
    ...resolveTemplateCompilerOptions(descriptor, options, ssr),
    source: code
  });

  return result;
}

在调用compileTemplate后走到doCompileTemplate该两个都是@vue/compiler-sfc文件下的方法。

总结

  • vuePlugin
    • buildStart //获取挂载vue/compiler-sfc
      • transform //编译三大部分
      • - transformMain
            - createDescriptor //获取Descriptor
            - genScriptCode  //
            - genStyleCod
            - genTemplateCode