Vue3: JSX 更灵活的开发场景
Vue2
应对灵活场景的方法:Render函数
如果某个组件比较灵活,用一般的template
模版结构无法满足需求,或者实现起来很不优雅,在Vue2
时代,我们只能手写render
函数。
在Vue2
介绍render
函数时,有一个例子:通过一个变量,范围在数字 1 到 6 之间,去渲染标题组件 h1~h6
。
如果使用template
实现,非常的丑陋且冗余。
<h1 v-if="num==1">{{title}}</h1>
<h2 v-if="num==2">{{title}}</h2>
<h3 v-if="num==3">{{title}}</h3>
<h4 v-if="num==4">{{title}}</h4>
<h5 v-if="num==5">{{title}}</h5>
<h6 v-if="num==6">{{title}}</h6>
这个时候Vue2
推荐了render
函数:
Vue.component('anchored-heading', {
render: function (h) {
return h(
'h' + this.level, // 标签名称
this.$slots.default // 子节点数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
可以看到,使用render
函数后代码量变得非常少,且十分优雅。
在Vue3
中,我们也可以使用render
函数:
import { defineComponent, h } from 'vue'
export default defineComponent({
props: {
level: {
type: Number,
required: true
}
},
setup(props, { slots }) {
return () => {
return h('h' + props.level, {}, slots.default())
}
}
})
但是如果是复杂的场景,h
函数写起来就显得非常繁琐,需要自己把所有的属性都转变成对象。并且组件嵌套的时候,对象也会变得非常复杂。
不过,因为 h
函数也是返回虚拟 DOM 的,所以有没有更方便的方式去写 h
函数呢?
答案是肯定的,这个方式就是 JSX
。
JSX
JSX
来源自 React
框架,下面这段代码就是 JSX
的语法,我们给变量 title
赋值了一个 h1
标签。
const element = <h1 id="app">Hello, Vue3!</h1>
这种在 JavaScript
里面写 HTML
的语法,就叫做 JSX
,算是对 JavaScript
语法的一个扩展。
上面的代码直接在 JavaScript
环境中运行时,会报错。
JSX 的本质就是下面代码的语法糖,h
函数内部也是调用 createVnode
来返回虚拟 DOM。
const element = createVnode('h1',{id:"app"}, 'hello vue3')
从 JSX
到 createVNode
函数的转化过程中,我们需要安装一个 JSX
插件。在项目的根目录下,打开命令行,执行下面的代码来安装插件:
npm install @vitejs/plugin-vue-jsx -D
// vite.config.js
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx';
export default defineConfig({
plugins: [vue(),vueJsx()]
})
对于上面的例子,我们就可以这么来写了,首先创建于一个后缀是.jsx
的文件:
setup(props, { slots }) {
const tag = 'h'+props.level
return () => <tag>{slots.default()}</tag>
}
下面来使用JSX
对一个稍微复杂组件的改写:
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup(props) {
let title = ref('')
let todos = ref([{ title: "学习 Vue 3", done: true },{ title: "睡觉", done: false }]);
function addTodo(){
todos.value.push({
title:title.value
})
title.value = ''
}
return () => <div>
<input type="text" vModel={title.value} />
<button onClick={addTodo}>click</button>
<ul>
{
todos.value.length ? todos.value.map(todo=>{
return <li>{todo.title}</li>
}): <li>no data</li>
}
</ul>
</div>
}
})
这与template
模板有如下不同:
- 使用
defineComponent
的方式来定义组件; return
了一个函数,该函数用来生成虚拟DOM;- 使用
vModel
取代v-model
,并且使用单个大括号包裹的形式传入变量title.value
; - 使用
onClick
取代@click
; - 循环渲染清单的时候,使用
.map
映射取代v-for
,使用三元表达式取代v-if
;
通过这个例子,发现使用 JSX 的本质,还是在写 JavaScript。
在组件库的设计中,有很多场景需要使用JSX
,比如下面这个例子:
export const Timeline = (props)=>{
const timeline = [
<div class="start">8.21 开始自由职业</div>,
<div class="online">10.18 专栏上线</div>
]
if(props.reverse){
timeline.reverse()
}
return <div>{timeline}</div>
}
如果你使用template
模板实现上面这个简单的功能,会感觉是非常不优雅。
JSX
和 Template
的选择
template
的语法是固定的,如 v-if
、v-for
等,这种固定格式的语法书写可以让 Vue
在编译层面就可以很方便地去做静态标记的优化。
也就是,在页面更新重新执行 render
时,代码里的纯静态的标签,如<div>123<div>
,它没有引用响应式数据,那么这类静态标签在虚拟 DOM 计算的时候,会直接越过 Diff
过程。
当然,除了静态标记外,Vue3
还利用了缓存机制来加快Diff
过程。这也是 Vue 3 的虚拟 DOM 能够比 Vue 2 快的一个重要原因。
JSX
只是 h
函数的一个语法糖,本质就是 JavaScript
,想实现条件渲染可以用 if else
,也可以用三元表达式,还可以用任意合法的 JavaScript 语法。
也就是说,JSX
可以支持更动态的需求。而 template
则因为语法限制原因,不能够像 JSX 那样可以支持更动态的需求。这是 JSX 相比于 template 的一个优势。
JSX
相比于 template
还有一个优势,是可以在一个文件内返回多个组件,我们可以像下面的代码一样,在一个文件内返回 Button、Input
等多个组件。
export const Button = (props,{slots})=><button {...props}>slots.default()</button>
export const Input = (props)=><input {...props} />
在 template
和 JSX
这两者的选择问题上,只是选择框架时角度不同而已。我们实现业务需求的时候,也是优先使用 template
,动态性要求较高的组件使用 JSX
实现,尽可能地利用 Vue
本身的性能优化。
总结
首先回顾了Vue2
时代如何应对一些灵活性的场景,主要是手写render
函数,但是对于一些复杂的组件,这种方式就显得力不从心了。
于是在Vue3
中诞生了JSX
,JSX
的概念来自于react
,它可以使用JavaScript
来书写HTML
,这样就可以灵活的书写复杂组件了。
在最后编译时,JSX
仍然是被编译为render
函数,它只是方便了我们书写而已。
到底选择哪种模式开发,主要看使用的场景,一般业务组件都是使用template
模板语法,这是尽可能利用Vue
本身的性能优化,对于动态性较高的场景则可以使用JSX
。
转载自:https://juejin.cn/post/7241790368948617253