1.善用h(createVNode)和render 函数
h 函数本质上是用了 Vue 内置函数 —— createVNode()、render() 实现的
createVNode —— 创建虚拟 DOM:
- @param 参数1 创建元素类型,必选
- @param 参数2 创建元素属性
- @param 参数3 创建元素内容
render —— 渲染虚拟 DOM:
- @param 参数1 要被渲染的虚拟 DOM,必选
- @param 参数2 要渲染的位置,必选
h() 函数的使用方式:
// 除了类型必填以外,其他的参数都是可选的
h('div')
h('div', { id: 'foo' })
// attribute 和 property 都能在 prop 中书写
// Vue 会自动将它们分配到正确的位置
h('div', { class: 'bar', innerHTML: 'hello' })
// 可以添加.prop 和 .attr等修饰符
// 分别带有 '.' 和 `^' 前缀
h('div', { '.name': 'some-name', '^width': '100' })
// 类与样式可以像在模板中一样
// 用数组或对象的形式书写
h('div', { class: [foo, { bar }], style: { color: 'red' } })
// 事件监听器应以 onXxx 的形式书写
h('div', { onClick: () => {} })
// children 可以是一个字符串
h('div', { id: 'foo' }, 'hello')
// 没有 props 时可以省略不写
h('div', 'hello')
h('div', [h('span', 'hello')])
// children 数组可以同时包含 vnodes 与字符串
h('div', ['hello', h('span', 'hello')])
h() 函数 接收的参数
- type —— 元素的类型,必选
- propsOrChildren —— 数据对象(props、attrs、dom、class、style、...)
- children —— 子节点,可以包含混合的 VNode 和 字符串 除类型 type 之外,所有参数都是可选的 得到的 vnode 为如下形式:
const vnode = h('div', { id: 'foo' }, [])
vnode.type // 'div'
vnode.props // { id: 'foo' }
vnode.children // []
<template>
<div></div>
</template>
<script lang="ts" setup>
import { createVNode, render } from 'vue'
/**
* createVNode —— 创建虚拟 DOM
* @param 参数1 创建元素类型,必选
* @param 参数2 创建元素属性
* @paran 参数3 创建元素内容
* @description 虚拟 DOM 创建完成后,需要使用 render 函数,才能在页面中渲染
*/
const testDiv = createVNode('div', { id: "myDivId" }, 'Lyrelion');
/**
* render —— 渲染虚拟 DOM
* @param 参数1 要被渲染的虚拟 DOM,必选
* @param 参数2 要渲染的位置,必选
* @description 虚拟 DOM 创建完成后,需要使用 render 函数,才能在页面中渲染
*/
render(testDiv, document.body);
</script>
2.善用JSX/TSX
JSX 是 JavaScript 的一个类似 XML 的扩展,有了它,我们可以用以下的方式来书写代码:
const vnode = <div>hello</div>
在 JSX 表达式中,使用大括号来嵌入动态值:
const vnode = <div id={dynamicId}>hello, {userName}</div>
下面我们提供了几个常见的用等价的渲染函数 / JSX 语法,实现模板功能的案例:
v-if
模板:
<div>
<div v-if="ok">yes</div>
<span v-else>no</span>
</div>
等价于使用如下渲染函数 / JSX 语法:
h('div', [ok.value ? h('div', 'yes') : h('span', 'no')])
<div>{ok.value ? <div>yes</div> : <span>no</span>}</div>
v-for
模板:
<ul>
<li v-for="{ id, text } in items" :key="id">
{{ text }}
</li>
</ul>
等价于使用如下渲染函数 / JSX 语法:
h(
'ul',
// assuming `items` is a ref with array value
items.value.map(({ id, text }) => {
return h('li', { key: id }, text)
})
)
<ul>
{items.value.map(({ id, text }) => {
return <li key={id}>{text}</li>
})}
</ul>
v-on
以 on 开头,并跟着大写字母的 props 会被当作事件监听器。比如,onClick 与模板中的 @click 等价。
h(
'button',
{
onClick(event) {
/* ... */
}
},
'click me'
)
<button
onClick={(event) => {
/* ... */
}}
>
click me
</button>
事件修饰符
对于 .passive、.capture 和 .once 事件修饰符,可以使用驼峰写法将他们拼接在事件名后面:
h('input', {
onClickCapture() {
/* 捕捉模式中的监听器 */
},
onKeyupOnce() {
/* 只触发一次 */
},
onMouseoverOnceCapture() {
/* 单次 + 捕捉 */
}
})
<input
onClickCapture={() => {}}
onKeyupOnce={() => {}}
onMouseoverOnceCapture={() => {}}
/>
组件
在给组件创建 vnode 时,传递给 h() 函数的第一个参数应当是组件的定义。这意味着使用渲染函数时不再需要注册组件了 —— 可以直接使用导入的组件:
import Foo from './Foo.vue'
import Bar from './Bar.jsx'
function render() {
return h('div', [h(Foo), h(Bar)])
}
function render() {
return (
<div>
<Foo />
<Bar />
</div>
)
}
动态组件在渲染函数中也可直接使用:
import Foo from './Foo.vue'
import Bar from './Bar.jsx'
function render() {
return ok.value ? h(Foo) : h(Bar)
}
function render() {
return ok.value ? <Foo /> : <Bar />
}
3.善用依赖注入(Provide / Inject)
在vue中,我们套用依赖注入的概念,其实就是在父组件中声明依赖,将他们注入到子孙组件实例中去,可以说是能够很大程度上代替全局状态管理的存在