Vue - template
Vue
官方推荐使用template
语法来创建应用,虽然写法像html
,但Vue
最终会把template
解析为render
函数返回虚拟DOM
,这点可以在Vue Dev Tools
中看到:
template
渲染流程:
因此在某些特定情况下,我们可以直接使用render
函数来实现我们的组件。
示例
根据接口返回的数值level
,动态渲染标题组件h1~h6
。
采用Vue
的模板语法,实现如下:
<template>
<h1 v-if="level==1" class="template_class"><slot></slot></h1>
<h2 v-if="level==2" class="template_class"><slot></slot></h2>
<h3 v-if="level==3" class="template_class"><slot></slot></h3>
<h4 v-if="level==4" class="template_class"><slot></slot></h4>
<h5 v-if="level==5" class="template_class"><slot></slot></h5>
<h5 v-if="level==6" class="template_class"><slot></slot></h5>
</template>
<script>
export default {
props: {
level: Number,
},
}
</script>
不过实现过程有些冗余了。因此我们可以使用render
函数来动态返回我们的组件。在此之前我们需要先了解下Vue
中的h
函数;
Vue - Render & H
h 函数
h
函数是Vue
创建一个Vnode
的函数。
<!--模板语法-->
<div class="className" style="color:red" @click="clicked">
<h1>hello,world</h1>
</div>
等价于
let vnode = h(
'div', //标签名
{
class: 'className',
style: { color: 'red' },
onClick() {
console.log("点击事件")
}
}, ///标签属性
h( ///子节点
'h1',
'hello,world'
),
)
render函数
render
函数在Vue
中可以代替template
标签,直接返回虚拟DOM
。
接下来,我们使用render
函数的方式实现示例需求,有三种写法:
Vue 2
<script>
import { h } from 'vue'
export default {
props: {
level: Number,
},
methods: {
clicked() {
alert('点击事件')
}
},
render() {
let tag = 'h' + this.level
return h(
tag,//tag type
{
class: 'head_h',
onClick: this.clicked
}, ///props
this.$slots.default() //children
)
}
}
</script>
Vue 3
<script>
import { h } from 'vue'
export default {
props: {
level: Number,
},
setup(props, { slots }) {
return () => {
let tag = 'h' + props.level
return h(
tag,//tag type
{
class: 'head_h',
onClick: ()=> {
alert('点击事件')
}
}, ///props
slots.default() //children
)
}
}
}
</script>
Vue 3 变体
<script>
import { h } from 'vue'
export default (props, { slots }) => {
//或 export default function head_h(props, { slots }) {
let tag = 'h' + props.level
return h(
tag,//tag type
{
class: 'head_h',
onClick: () => {
alert('点击事件')
}
}, ///props
slots.default() //children
)
}
</script>
注意:setup
语法糖是不能直接使用render
函数的。
<script setup>
//这里不能使用render函数
</script>
render
函数除了返回单个vNode
外,也可以返回字符串和数组,不过组件树中的vNodes
必须是唯一的(同一个vNode
实例,不能在组件中多次使用)。
极简且合法的Vue
组件:
function Hello() {
return 'hello world!'
}
小结
render
函数+h
函数虽可以处理动态性较高的场景,但是遇到复杂的组件时h
函数层层嵌套,各种属性对象,写起来很复杂 ~ ~;有没有简单,方便的写法呢? 说到这里,就不得不说说JSX
。
Vue - Render & JSX
JSX
JSX
是在JavaScript
语法上的拓展,允许 HTML
代码和 JS
一起写。
与React
框架的结合是比较紧密的。
///JSX 表达式 :单行代码
const heading = <h1>Mozilla Developer Network</h1>;
///JSX 表达式:多行代码
const header = (
<header>
<h1>Mozilla Developer Network</h1>
</header>
);
与React
一样,Vue
会将JSX
表达式,经过parcel
或babel
编译后,转换为创建虚拟DOM
节点的函数。不同的是:React
是React.createElement
函数,而Vue
是createVNode
:
///React JSX 编译后
const header = React.createElement("header", null,
React.createElement("h1", null, "Mozilla Developer Network")
);
///Vue JSX 编译后
const header = createVNode("header", null,
createVNode("h1", null, "Mozilla Developer Network")
);
Vue JSX
编译后的产物,在Vue Dev Tools
也能看见:
因此在Vue
中要想使用JSX
,则必须安装可以将JSX
表达式转换为createVNode
的编译器插件。
环境
场景一:
Vue
新项目,若要使用JSX
,则在Vue
项目创建时,配置支持JSX
,如此Vue
便会自动帮我们配置好JSX
的编译插件。
场景二:
Vue
非新建项目,若要使用JSX
:
# -D @vitejs/plugin-vue-jsx 插件 开发环境下有效
npm install @vitejs/plugin-vue-jsx -D
其次找到vite.config.js
文件,配置plugin-vue-jsx
插件如下:
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
plugins: [vue(),vueJsx()]
})
使用
新建文件后缀更名.jsx
,文件内容不再需要写<script>
标签;同样,我们使用render
函数+JSX
的方式实现示例需求,有三种写法:
Vue 2
import HelloWorld from '../components/HelloWorld.vue'
import './head_jsx.css'
export const head_jsx = {
props: {
level: Number,
},
components: {
HelloWorld
},
data() {
return {};
},
render() {
let tag = 'h' + this.level
return <tag class='head_jsx' >{this.$slots.default()}</tag>
}
}
Vue 3
export const head_jsx = {
props: {
level: Number,
},
components:{
HelloWorld
},
setup(props, { slots }) {
return () => {
let tag = 'h' + props.level
return <tag class='head_jsx' >{slots.default()}</tag>
}
}
}
Vue 3 变体
export const head_jsx = (props, { slots }) => {
let tag = 'h' + props.level
return <tag class='head_jsx'>{slots.default()}</tag>
}
小结
基于上述写法的Vue JSX
组件,虽然展示&交互都没有问题,但却有一个共同的问题:不支持Vue
的HMR
机制,每次都需要重新载入,于开发不便利。如何让Vue JSX
组件支持HMR
呢?
defineComponent
Vue JSX
组件支持HMR
检测的必要条件有两个;
- 必须导出组件。
- 组件必须使用
defineComponent
根级语句调用来声明。
defineComponent
是一个函数,除了HMR
检测外,也可在定义 Vue
组件时提供类型推导的辅助。
并且代码写入时也会有提示。
使用
基于 Render & JSX
使用部分所讲的Vue 2
、Vue 3
的代码,外层包装defineComponent
即可;Vue 3
变体,defineComponent
不可用。以下为Vue 3 JSX
组件使用defineComponent
示例。
export const head_jsx = defineComponent({
props: {
level: Number,
},
components: {
HelloWorld
},
setup(props, { slots }) {
return () => {
let tag = 'h' + props.level
return <tag class='head_jsx' >{slots.default()}</tag>
}
}
})
v-model
v-model
需要注意ref
响应式变量,在JSX
表达式中的取值
export const inputer = {
setup(props, { slots }) {
let val = ref(0)
return () => (
<div>
<input type='number' v-model={val.value} value={val.value}/>
<div> 结果:{val.value} </div>
</div>
)
}
}
v-slot
在JSX
,v-slot
应替换为 v-slots
插槽组件定义
export const sloter = {
setup(props, { slots }) {
return () => {
return (
<div>
<div style={{ color: 'green' }}>
我是默认插槽:
{slots.default ? slots.default() : ''}
</div>
<div style={{ color: 'red' }}>
我是插槽A:
{slots.A ? slots.A() : ''}
</div>
<div style={{ color: 'blue' }}>
我是插槽B:
{slots.B ? slots.B() : ''}
</div>
</div>
)
}
}
}
插槽组件使用
-
template
使用
<sloter>
<template #default>
默认
</template>
<template #A>
A
</template>
</sloter>
-
JSX
使用
export const slotsUse = {
components: {
sloter
},
setup(props,) {
const slots = {
default: ()=>(<span>JSX下默认插槽的值</span>),
A : ()=>(<span>JSX下A插槽的值</span>),
}
return () => <sloter v-slots={slots}> </sloter>
}
}
小结
上文简单列举了渲染函数和JSX
配合基本使用,详细使用可见Vue JSX 插件文档
Vue - Template VS Render&JSX
template
优势:
- 官方推荐,模板化固定的语法,便于静态标记和分析,使得编译器能应用许多编译时的优化,提升性能。
- 贴近实际的
HTML
,利于重用已有的HTML
代码片段,便于理解和修改。
Render & JSX
优势:
- 处理高度动态的逻辑时,渲染函数相比于模板更加灵活。
- 一个文件中可以返回多个组件。
- 利于
React
开发者快速上手Vue
参考
https://cn.vuejs.org/guide/extras/rendering-mechanism.html
网友评论