我不知道大家有没有记得Vue3之前有一个很激进的RFC$:
,目的是消除使用ref
时的.value
。但是后来很多反对,说Vue篡改js语法含义,后来才变成了$ref
,当时这个这个功能就是通过Macros来配置的。
Vue3其实有很多很先进或者是灰度测试中的功能,都会放到Vue Macros这个插件里去实现,因此我们通过这个插件提前体验未来的Vue Features。
先把官网放这里 vue-macros.dev/zh-CN/
如何配置Macros
Macros是一个插件,我们首先需要安装一下 npm i -D unplugin-vue-macros
,然后我们在具体的打包器里配置一下,我目前都是用vite的,因此我们直接看vite的配置
typescript
// vite.config.ts
import VueMacros from 'unplugin-vue-macros/vite'
import Vue from '@vitejs/plugin-vue'
// import VueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
plugins: [
VueMacros({
plugins: {
vue: Vue(),
// vueJsx: VueJsx(), // 如果需要
},
}),
]
})
从这个配置方法看,它其实是在Vue插件外层包了一层,具体的源码实现不在此篇文章的讨论范畴之内,我们看一下它提供了哪些有趣的功能
Macros提供了什么
【稳定】betterDefine 支持引入类型
vue3的props一开始是不支持引入外部类型的,所以才有了这个提案。但我记得好像3.3就解决这个问题了,评论区里知道的可以给我说下
vue
<script setup lang="ts">
import type { BaseProps } from './types'
interface Props extends BaseProps { foo: string }
defineProps<Props>()
</script>
【稳定】$defineProps 让props可以直接解构
我们都知道,正常的vue3里的props是不能解构的,解构会失去响应,因此才有了这个提案。
vue
const {a} = $defineProps({
a:{type:String},
b:{type:Boolean}
})
console.log(a.value)
其实我们也可以使用toRefs
来实现这个功能
vue
const props = defineProps({
a:{type:String},
b:{type:Boolean}
})
const {a} = toRefs(props)
console.log(a.value)
【实验】defineProp、defineEmit 单个声明prop或emit
这个其实只是一个很小的功能点,我甚至觉得它跟上一个功能是一样的,只是实现思路不同。它主要用来声明单个prop或emit。 先看一下vue3正常的写法
vue
const props = defineProps({
a:{type:String},
b:{type:Boolean}
})
console.log(props.a,props.b)
当你开启这个功能的时候,你就可以这样写.defineEmit同理,就不再赘述了
vue
const a = defineProp('a',{type:String})
const b = defineProp('b',{type:String})
console.log(a.value,b.value)
【稳定】# Reactivity Transform 减少Ref使用的心智负担
【实验】defineRender 让Vue更像React
如果说上面两个只是小功能,小tips,那么这个defineRender
就是一个颠覆性的更新了。 先看一下vue3中如何使用的渲染函数
vue
<script>
import {h} from 'vue'
export default defineComponent({
name:'TestRenderer',
setup(){
return ()=>h('h1','renderer')
}
})
</script>
当我们使用defineRender
之后,就可以直接渲染函数了,通过配置jsx,我们可以不需要再用数组嵌套的写法,可以直接写jsx
vue
<script setup lang="tsx">
// 可以直接传递 JSX
defineRender(
<div>
<span>Hello</span>
</div>,
)
// 或使用渲染函数
defineRender(() => {
return (
<div>
<h1>Hello World</h1>
</div>
)
})
</script>
看看这写法,不就是妥妥的react-like
【实验】# setupComponent、setupSFC 完全拥抱JSX,简直和React一模一样
当我觉得defineRender
已经是我想象力的极限的时候,vue这群大佬更加激进,反正已经react-like
了,不如就变成react
。 首先按照文档,我们需要配置一下vite,扩展一下Vue支持的文件后缀,新增.setup和jsx,没错就是react常用的jsx,tsx。
javascript
export default defineConfig({
plugins: [
VueMacros({
plugins: {
vue: Vue({ include: [/\.vue$/, /\.setup\.[cm]?[jt]sx?$/],
// ⬆️ 需要添加 setup 模式
}),
},
}),
],
})
这两种方法使用起来基本一样,我就没分开。
tsx
export const App = defineSetupComponent(() => {
defineProps<{
foo: string
}>()
defineEmits<{
(evt: 'change'): void
}>()
defineOptions({
name: 'App',
})
return (
<div>
<h1>Hello World</h1>
</div>
)
})
这不就是react的函数式写法吗,稍有不同的vue自定义了需要宏函数,方便咱们使用。
setupSFC
则是更加激进,我们无需在包裹defineSetupComponent这个函数了,可以用script setup的写法,甚至连script setup都省了,我把上面那个写法改一下,你们就知道了。
tsx
// Foo.setup.tsx
defineProps<{
foo: string
}>()
defineEmits<{
(evt: 'change'): void
}>()
export default () => (
<div>
<h1>Hello World</h1>
</div>
)
好吧,这很react
,如果哪一天Vue的知名度远远超过React,会不会有人会说这是Vue-like的写法呢?