第一次尝试使用 @vue/compiler-sfc

27,757 阅读4分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

铛铛,这次是我第一次发文,由衷感谢读者,下面我讲的是我对vue3的近期理解

在你执行 npm i vue@next 之后你一定发现了在vue的直属目录下有一个 compiler-sfc,这个文件夹里面的内容主要是能够编译 templatestyle, script(setup)的字符串,生成对应的内容。

分别对应的是 compileTemplatecompileStylecompileScript 当然我相信你可能看不懂,是了,我也看不懂,经过我的多方百度,我才知道到这三个方法的使用方式。

你需要通过使用编译 parse 编译一下你所需要解析的字符串生成对应的 AST 树(不知道对不对),后面才是根据对应的树来生成你所需要的render渲染(或者说是可以直接理解的代码)。

parse

相信还是看代码比较直观

import { parse } from 'vue/compiler-sfc'
const parseValue = parse(
    `<template><h1>12312312</h1></template>`
    ,
    {
        id: 'uid',
        filename: 'XX.vue'
    }
)
console.log(parseValue)

最后在控制台生成

{
  descriptor: {
    filename: 'XX.vue',
    source: '<template><h1>12312312</h1></template>\n    ',
    template: {
      type: 'template',
      content: '<h1>12312312</h1>',
      loc: [Object],
      attrs: {},
      ast: [Object],
      map: [Object]
    },
    script: null,
    scriptSetup: null,
    styles: [],
    customBlocks: [],
    cssVars: [],
    slotted: false
  },
  errors: []
}

看,啥都有,但就是啥也不知道干啥的,所以接下来就需要我们的 compileTemplate

compileTemplate

使用上面的数据进行处理

const { code } = compileTemplate({
    id: 'XX',
    filename: 'XX.vue',
    source: parseValue.descriptor.template.content
})

最终你的code 就是你所能认识函数

import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache) {
  return (_openBlock(), _createElementBlock("h1", null, "12312312"))
}

有没有感觉很高大上,我所热爱的就是看着自己一步一步从无知变成探索者,我没有在百度上明确的找到上述代码,全都是我看 vue 的 ts 来推测和判断,所以在此,我要感谢 typescript!!! 下面是我完整的代码,执行只要 node 运行文件就行,(别忘记 package.json中type的更改,你需要了解commonjs 和 module)

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

const parseValue = parse(
    `
        <template><h1>12312312</h1></template>
        <script setup>
            import { ref } from 'vue'
            const name = ref('myName')
        </script>
    `,
    {
        id: 'uid',
        filename: 'XX.vue'
    }
)
const { code } = compileTemplate({
    id: 'XX',
    filename: 'XX.vue',
    source: parseValue.descriptor.template.content
})
const { content } = compileScript(parseValue.descriptor, {
    filename: 'XX.vue'
})
console.log(code, content)

控制台中打印

import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache) {
  return (_openBlock(), _createElementBlock("h1", null, "12312312"))
} import { ref } from 'vue'


export default {
  setup(__props, { expose }) {
  expose()

            const name = ref('myName')

    const __returned__ = { name, ref }
    Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })
    return __returned__
    }

}

这下子总算完成了。是的结束了,有人会问,这有什么用吗?平时干业务会用到吗,我能准确的回答你,用不到,但是可能方便你以后造轮子吧,哈哈。我觉得很强,虽然没看过源码,但是能用出来对我来说就是一大进步。

追更

在你在自己的代码中引入 './XX/xx.vue' 的时候你有没有想过 他为什么能知道 .vue 文件,.vue 文件可不可以变成我们熟知的 .js/ts 能 =) 下面就是我过了一晚上想到的事情,脑子要是好的话,怎么可能隔了一天才知道...

以下就是流程了

  1. 使用 fs-extra 来获取 .txt/vue 文件的内容,

  2. 获取文件内容后使用 @vue/compiler-sfc 进行编译 得到 code, content

  3. 将俩个代码分别存到俩个文件中,分别命名为 indexCode.js, indexContent.js

  4. 最后参考 vue/compiler-sfc npm 官网上的解说, 最后变成我们所需要的样子

  5. 上码

indexCode.js
import {
	openBlock as _openBlock,
	createElementBlock as _createElementBlock,
} from "vue";

export function render(_ctx, _cache) {
	return _openBlock(), _createElementBlock("h1", null, "12312312");
}
indexContent.js
import { ref } from "vue";

export default {
	setup(__props, { expose }) {
		expose();

		const name = ref("myName");

		const __returned__ = { name, ref };
		Object.defineProperty(__returned__, "__isScriptSetup", {
			enumerable: false,
			value: true,
		});
		return __returned__;
	},
};
index.js
import { render } from "./indexCode";
import script from "./indexContent";

script.render = render;
script.__filename = "XX.vue";

比较

image.png

使用方式一个是 './index.vue',另一个是 './App/index.js' 是不是有种非常雄伟的成就感,是的,我有!

其实我最近也有在尝试很多东西,但是都没有怎么实现(更或者说我都不知道怎么实现) 比如我想要一个 使用 esbuild 就能编写的后端代码,因为我想要使用 ts 写,而不需要任何的 egg.js 或者 nest.js。总之我就是觉得人就是要多思考,仅此而已,不管是对是错,亦要尝试。

我虽然写不出来这么优质的代码,但是不能阻止我追赶你们。 尤大 加油