前言
Hi,今天是大年29,在新年前的最后两天写一个年终总结,分享一下我是怎么加入 vue-macros 并走上开源的道路。
由于本人的工作中用到了ag-grid
所以使用JSX
定义cellRenderer
成为了我的刚需。我挺喜欢JSX
的书写方式,除了用三元表达式和map
渲染列表,要是能和vue指令
一样简单就好了,于是我在今年8月份左右的时候写了第一个库:
unplugin-jsx-vue-directive
在 JSX 中实现 v-if
和 v-for
指令,编译的时候把 v-if 转成三元表达式。最初的想法是让所有使用 JSX 的框架都可以使用,所以以把 JSX 放在库名的最前面。也因为这个库让我结识到了vue的大佬三咲智子。
当我把这个库分享到了antfu
的微信群里后,没过几秒钟智子就给我点了第一个 star,并邀请我把这个功能添加到 vue-macros 里。于是我开始研究 vue-macros
,三天后提交了第一个PR。
@vue-macros/jsx-directive
实现了v-if
,v-for
,v-slot
,v-memo
等常用的vue
指令。
html
<script setup lang="tsx">
import Child from './Child.vue'
const { foo, list } = defineProps<{ foo: number list: number[] }>()
defineRender((
<form onSubmit_prevent>
<div v-if={foo === 0}>
<div v-if={foo === 0}>0-0</div>
<div v-else-if={foo === 1}>0-1</div>
<div v-else>0-2</div>
</div>
<div v-for={(i, index) in list} v-memo={[foo === i]} key={index}>
{i}
</div>
<Child v-on={{ submit: () => {} }}>
default slot
<template v-slot:bottom={{ bar }}>
<span>{bar}</span>
</template>
</Child>
</form>
))
</script>
由于在JSX
中的vue指令
没有类型提示,所以又开发了 volar 插件@vue-macros/volar/jsx-directive。其实volar
插件的原理和vite
插件差不多,都是遍历ast
语法树,只不过 volar
使用 TS
遍历,而 vite
使用 babel
。找到 vue指令
然后替换字符串就可以了,基本逻辑都可以复用。
@vue-macros/export-render
灵感来自于智子的 setup-sfc,原理很简单把 export default
转成 defineRender
就可以了。
html
<script setup lang="tsx">
// JSX passed directly
export default <div>ok</div>
// Or using render function
export default () => <div>ok</div>
</script>
@vue-macros/short-bind
如果属性名和属性值一样,可以省略属性值。还可以配合 short-vmodel 一起使用。
html
<template>
<input :msg />
<!-- => <input :msg="msg" /> -->
<demo $msg />
<!-- => <input $msg="msg" /> => <input v-model:msg="msg" /> -->
</template>
没想到在10月份的时候,智子告诉我尤雨溪同意把这个feature
加入到 vue3.4,然后我就给vue/core
提交了第一个 PR。
vue-macros-cli
vue-macors init
为vue-macros
开发的命令行工具,根据你选择的vue-macros
语法糖,自动配置tsconfig.json
和vite.config.ts
或者nuxt.config.ts
。
vue-macors sg
得益于 ast-grep,你可以快速的把已有项目的vue template
一键转换成vue jsx
。之后我所有项目就开始用jsx
写vue
了。
unplugin-vue-components-jsx
在JSX
中直接使用组件,和vue template
一样不需要手动引入了。
灵感来自
antfu
的 eslint-ts-patch,由于使用了module.register 去改写node_modules
,所以需要node18
以上版本。
transformer-attributify-jsx-sg
用 ast-grep 代替正则表达式实现unocss
的valueless attributify。正则表达式真的不适合用来处理JSX
这种复杂的表达式,虽然已经给unocss
提了很多PR
了,但新的问题还是不断出现。
相关提案: github.com/unocss/unoc....
volar-plugin-ignore-attributes
在JSX
中使用unocss
的Attributify Mode
,会有类型提示的报错,官方的解决方案是把所有导致报错的属性注册到AllowedComponentProps
里。
ts
import type { AttributifyAttributes } from '@unocss/preset-attributify'
declare module '@vue/runtime-dom' {
interface HTMLAttributes extends AttributifyAttributes {}
}
declare module '@vue/runtime-core' {
interface AllowedComponentProps extends AttributifyAttributes {}
}
这样就会导致一个问题当我想看到组件有哪些属性的时候,这些属性也会跟着出现,影响调试。 所以我就写了这个volar
插件去忽略这些报错的属性。
unplugin-vue-jsx-vapor
本来我是想做个JSX
版的vapor
,研究了一下vapor
的源码后,既然vapor
最后生成的也是dom
,为什么我不先把JSX
转成template
再交给compiler-vapor
去生成dom
。还支持所有的vue指令
。
ts
// vite.config.ts
import VueJsxVapor from 'unplugin-vue-jsx-vapor/vite'
import { compile } from '@vue/compiler-dom'
// or
// import { compile } from 'vue/vapor'
export default defineConfig({
plugins: [
VueJsxVapor({
compile
}),
],
})
由于vapor
还未正式发布,你可以先用virtual-dom
版的compiler
来提前试用。还能获得和vue template
一样的编译优化。
Playground: stackblitz.com/github/zhiy...
其他的库
还有一些试验性的库 太hack了就不一一介绍了 感兴趣的可以自己看看。