参考教程
相较于模板,应用于更加灵活的场景 – 渲染函数
预备知识实例 property 的 api

{
  rander() {
	return h('h1',xxx) // return h('h1',{},xxx)
  }
}
// 相当于
template: `
  <h1>
    xxx
  </h1>
`

h() 与 VNode

h() ==> createNodeDescription 创建节点描述 返回VNode

应该命名为 createVNode() 但由于频繁使用故 简化为 h()

参考博客

  • 第一个参数用于 确定tag 必需

    对于全局组件 需要解析(resolveComponent),局部注册可以跳过

    参数类型: 字符串、对象、函数
    一个 HTML 标签名 字符串、一个组件、一个异步组件、或 一个函数式组件。

  • 第二个参数 与 attribute、prop 和事件相对应的对象对象 可选

    参数类型: 对象
    prop 可以在组件 props选项 中定义,其他同理;
    当第二参数为空时,可以省去

例如 class attribute 的对象:

// css
.btn{
    width: 80px;
    line-height: 40px;
    text-align: center;
    color:#fff;
    border-radius: 5px;
    background-color: #ccc;
}
.success{background-color: green;}
.error{background-color: red;}
.info{background-color: pink;}
h (
	// 第一个参数
	'my-btn', 
	// 第二个参数
	{
		class:{
			btn:true,
			success:this.type=="success", // 通过 type 配置 class
			error:this.type=="error",
			info:this.type=="info"
		}
	},
	// 第三个参数
	this.$slots.default
)
props:{
	type:{
        type:String,
        defalut:''
    }
}

等价于:

<template>
	<div :class=ClassObj>
	</div>
</tempalte>
<script>
export default {
	name: 'MyBtn',
	props:{
		type: {
		    type:String,
		    defalut:''
		}
	},
	data() {
		return {
			ClassObj: {
				btn: true,
				success: this.type=="success", // 通过 type 配置 class
				error: this.type=="error",
				info: this.type=="info"
			}
		}
	}
}
</script>
<style>
.btn{
    width: 80px;
    line-height: 40px;
    text-align: center;
    color:#fff;
    border-radius: 5px;
    background-color: #ccc;
}
.success{background-color: green;}
.error{background-color: red;}
.info{background-color: pink;}
</style>
  • 第三个参数 可以 控制插值 {数组 内容按元素顺序渲染} 可选

    参数类型: 字符串、对象、函数
    可以是文本,也可以是 VNode 理解成 children
    使用渲染函数编写一个组件时,访问 this.$slots 会很有帮助 可以规定命名插槽

// @returns {VNode}
h(
  'div',
  {}, // 可省略
  [
    'Some text comes first.', //字符串
    h('h1', 'A headline'), //通过h()
    h(MyComponent, { // 第二个参数对象中 定义了 someProp
      someProp: 'foobar' //prop 对象
    })
  ]
)

如果没有 prop,那么通常可以将 children 作为第二个参数传入。如果会产生歧义,可以将 null 作为第二个参数传入,将 children 作为第三个参数传入。


使用原生 JS 代替模板功能

v-for 和 v-if 的嵌套

props: ['items'],
render() {
  if (this.items.length) { //v-if="this.items.length"
    return h('ul', this.items.map((item) => { //v-for="item in items"
      return h('li', item.name)
    }))
  } else { //v-else
    return h('p', 'No items found.')
  }
}

v-model

必须自己提供prop: modelValueonUpdate:modelValue

props: ['modelValue'],
emits: ['update:modelValue'],
render() {
  return h(SomeComponent, {
    modelValue: this.modelValue,
    'onUpdate:modelValue': value => this.$emit('update:modelValue', value)
  })
}

v-on

注意 on前缀 和 驼峰命名

render() {
  return h('div', {
    onClick: $event => console.log('clicked', $event.target)
  })
}
事件修饰符

对于 .capture、.passive 和 .once 事件修饰符,使用驼峰写法将他们拼接在事件名后面

render() {
  return h('input', {
    onClickCapture: xxx,
    onKeyUp: event => {
      // 如果触发事件的元素不是事件绑定的元素
      // 则返回
      if (event.target !== event.currentTarget) return
      // 如果向上键不是回车键,则终止
      // 没有同时按下按键 (13) 和 shift 键
      if (!event.shiftKey || event.keyCode !== 13) return
      // 停止事件传播
      event.stopPropagation()
      // 阻止该元素默认的 keyup 事件
      event.preventDefault()
      // ...
    }
  })
}
修饰符处理函数中的等价操作
.stopevent.stopPropagation()
.preventevent.preventDefault()
.selfif (event.target !== event.currentTarget) return
按键:.enter, .13if (event.keyCode !== 13) return (对于别的按键修饰符来说,可将 13 改为另一个按键码
修饰键:.ctrl, .alt, .shift, .metaif (!event.ctrlKey) return (将 ctrlKey 分别修改为 altKey, shiftKey, 或 metaKey)

插槽

this.$slots 可以向VNode中添加插槽

render() {
  // `<div><slot :text="message"></slot></div>`
  return h('div', {}, this.$slots.default({
    text: this.message
  }))
  // 函数式 插槽
  // 模板 `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
  // 将插槽以 { name: props => VNode | Array<VNode> } 的形式传递给子对象。
  //{
  //  default: (props) => Vue.h('span', props.text)
  //}
}

<component> 和 is

在底层实现里,模板使用 resolveDynamicComponent 来实现 is attribute

// `<component :is="name"></component>`
render() {
  const Component = resolveDynamicComponent(this.name)
  return h(Component)
}

与 标签一样, 标签仅在模板中作为语法占位符需要,当迁移到 render 函数时,应被丢弃。

自定义指令

使用 withDirectives 将自定义指令应用于 VNode

内置组件

无法通过 resolveComponent 或 resolveDynamicComponent 访问它们
需要显式引入

render 渲染函数的返回值

  • 单个根 VNode return h()
  • 一个字符串时会创建一个文本 VNode return str
  • 一个子元素数组 return ['Hello', h('br'), 'world!'] 这会创建一个片段
  • 返回 null,因为数据依然在加载中的关系,组件不需要渲染 会渲染一个注释节点

函数式组件

自身没有任何状态的组件的另一种形式。
它们在渲染过程中不会创建组件实例,并跳过常规的组件生命周期。
使用的是一个简单函数,而不是一个选项对象,来创建函数式组件
该函数实际上就是该组件的 render 函数

const FunctionalComponent = (props, context) => {
  // ...
}

函数式组件里没有 this 引用,Vue 会把 props 当作第一个参数传入
第二个参数 context 包含三个 property:attrs、emit 和 slots。

还是可以把 props 和 emits 作为对象属性加入FunctionalComponent.props = ['value']

将一个函数作为第一个参数传入 h(),它将会被当作一个函数式组件来对待。

Logo

前往低代码交流专区

更多推荐