美文网首页
vue3单文件组件编译过程

vue3单文件组件编译过程

作者: 硅谷干货 | 来源:发表于2022-03-18 09:54 被阅读0次

最近产品给我提了一个非常好玩(e xin)的需求:用户输入单文件组件(sfc)的代码就能显示对应的界面。具体可以参考vue playground

提出问题

作为一个成熟的前端,要善于挖掘产品的隐含意思:

我:“用户输入的代码中会包含UI库的组件吗,例如element plus”。

产品:“当然要啊,不然用户怎么用”。

我: “你知道的,vue3有多种不同的写法,主要是optional和composition,其中composition还能使用< script setup>的写法”

产品: “不能把用户局限住了,要支持的”。

ok,需求弄清楚了咱们开始做技术调研。

技术调研

用户输入代码可以使用vscode的web版编辑器monaco-editor,这个很简单。主要问题是输入的代码怎么显示出对应的界面呢?

createApp

vue3中,我们通常会在main.js中通过下面的代码,将根组件挂载到dom节点中:

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app');
复制代码

createApp可以接收不同的参数,在optional的写法中,只要将sfc的template,data,methods这些对象获取到传入其中,不就可以显示出界面了嘛。

// 伪代码
let code = monacoInstance.getValue()  // 用户输入的代码字符串
// 假设compile函数是对code做一系列正则匹配,用于获取template,data等内容
let { template,data,methods } = compile(code)
createApp({
    template,
    data,
    methods
}).mount('#container');
复制代码

这么干确实可以把界面显示出来,但是有一个问题,compile函数超级难写,因为我们要支持多种不同的写法,自己写这个compile不太现实,有没有现成的库可以用呢?

@vue/compiler-sfc

当然有了,不然vue怎么去编译sfc呢,在vue3中编译sfc主要会使用@vue/compiler-sfc这个包,大概的流程是这样的:

                                  +--------------------+
                                  |                    |
                                  |  script transform  |
                           +----->+                    |
                           |      +--------------------+
                           |
+--------------------+     |      +--------------------+
|                    |     |      |                    |
|  facade transform  +----------->+ template transform |
|                    |     |      |                    |
+--------------------+     |      +--------------------+
                           |
                           |      +--------------------+
                           +----->+                    |
                                  |  style transform   |
                                  |                    |
                                  +--------------------+
复制代码

用伪代码再描述一下:

import { parse,compileTemplate,compileScript,compileStyleAsync } from '@vue/compiler-sfc'

let code = monacoInstance.getValue()  // 用户输入的代码字符串

const descriptor = parse(code) //  facade transform,生成的descriptor中已经可以找到我们所需要的tempplate,但是methods,data这些数据还无法获取

const compiledScript = compileScript(descriptor)

const result = rewriteDefault(compiledScript.content) // result是一个字符串,通过动态生成script来执行字符串的内容,最后会返回一个__sfc__,这也就是我们需要传入到createApp里面的
复制代码

上述这段伪代码的思路,来自于sfc-playground这个项目,有兴趣可以去读一下。

原本我以为,要提取data,methods等传入createApp中才能显示出一个完成的sfc,实际上如果你输入的代码中包含setup,并不会有data,methods这些属性,而是会产生一个setup函数,这个函数会返回data和methods,将这个setup函数传入createApp即可。

@vue/compiler-sfc有broswer版本,也有nodejs的版本,我选择在服务端编译,因为在使用broswer版本中爆了一些我无法解决的错误,能力有限,希望有人能出一个broswer版本的使用教程。

在做完这些后,已经可以将用户输入的sfc显示出来了,但是还有一个问题,如果输入的代码中有UI库组件,则编译不出来,例如使用了el-button。

UI库组件的不显示

我发现当使用optional的写法,传入createApp中是template,data,methods这些的时候,只需要这样写就可以正常显示出UI库的组件:

import { createApp,nextTick } from 'vue'
let app = createApp({
    template,
    methods,
    ...
})
nextTick(async () => {
    // 根据条件判断,导入哪些依赖
    const ElementPlus = await import('element-plus');
    _app.use(ElementPlus).mount(#container)
})
复制代码

而使用setup则会导致UI库的组件无法渲染,这个问题其实很简单。使用setup之后会sfc会包含一个render函数,我们知道sfc的编译过程,其实就是将template编译成render函数,我们传入template可以显示出UI组件,而传入render函数无法显示UI组件,那么原因就是当传入createApp中存在render函数时,不会再次进行compile。

如果希望use(ElementPlus)能成功执行,则需要在前端再compile一次,这时只需将传入createApp中的render设置为null,以及传入从descriptor中获取的template即可。另外:

// 如果设置inlineTemplate为true,那么setup函数返回的并不是data,methods这些对象而是一个render函数
const compiledScript = SFCCompiler.compileScript(descriptor, {
    inlineTemplate: true
})
复制代码

在sfc-playground中,解析< script setup>的代码,inlineTemplate是为true的,所以setup中会返回render函数。设置inlineTemplate为false,则会生成单独的render函数,设置这个render函数为null,就可以触发前端的compile。

最后

如果你也遇到类似的需求,我建议可以先看看@vue/compiler-sfc和sfc-playground的源码。

相关文章

  • vue3单文件组件编译过程

    最近产品给我提了一个非常好玩(e xin)的需求:用户输入单文件组件(sfc)的代码就能显示对应的界面。具体可以参...

  • vue3 简陋的实现vite

    vue3 最大的优点: 编译时的优化 vite 是一个基于 Vue3 单文件组件的非打包开发服务器,它做到了本地快...

  • Vue(2.6.11)源码阅读——编译

    定义 把模板编译成render函数的过程我们称之为编译 [编译入口函数的位置] 又有我们平常的单文件组件是通过vu...

  • vue性能优化

    Vue 应用运行时性能优化措施 引入生产环境的 Vue 文件 使用单文件组件预编译模板 提取组件的 CSS 到单独...

  • Linux C 编程基础

    1、gcc编译过程 基本命令: 2、编译文件 3、静态链接库 (1) 首先用-c编译各个单源文件.c生成.o文件 ...

  • 为什么 Vue3 选择了 CSS 变量

    为什么 Vue3 选择了 CSS 变量 Vue 3 新增了一条实验性的功能——「单文件组件状态驱动的 CSS 变量...

  • Vue3 script-setup 超清新单文件写法

    Vue3刚出来的时候,我觉得 vue-hooks 时代来了,终于可以抛弃单文件组件的写法,彻底融入到函数式编程中,...

  • vue-cli

    === 单文件组件结合webpack处理单文件组件配置webpack相关loader使用vue文件创建vue组件引...

  • vue3 + tsx 的几种写法(探索版)

    类组件写法 vue-class-component也是vue3提供的一个编译库。 个人更喜欢类组件写法,你喜欢什么...

  • provide inject在vue2和vue3中的使用

    vue2父组件 vue2子组件 vue3父组件 vue3子组件 vue3官方详细使用provide inject地...

网友评论

      本文标题:vue3单文件组件编译过程

      本文链接:https://www.haomeiwen.com/subject/cpdidrtx.html