vue 由slot插槽引出渲染函数render

ゝ一世哀愁。 2023-01-18 09:24 74阅读 0赞

今天又看了一遍vue文档中的渲染函数,理解的差不多了,这里记录下,方便查阅,平时写的template组件vue内部通过正则匹配还是会编译成一棵虚拟节点树,最后放到渲染函数中去执行。

我们先用一个slot插槽的例子引出渲染函数:

很简单,Hello组件根据父组件传递的level值来判断渲染h1还是h2或h3 。

  1. // Hello.vue
  2. <template>
  3. <div class="hello">
  4. <h1 v-if="level===1"><slot></slot></h1>
  5. <h2 v-else-if="level===2"><slot></slot></h2>
  6. <h3 v-else="level===3"><slot></slot></h3>
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. name: "Hello",
  12. props: {
  13. level: Number,
  14. require: true
  15. }
  16. }
  17. </script>
  18. // Home.vue
  19. <template>
  20. <div>
  21. <hello :level="1">
  22. hello world
  23. <span>span标签</span>
  24. </hello>
  25. </div>
  26. </template>
  27. <script>
  28. import Hello from '../components/Hello'
  29. export default {
  30. name: "Home",
  31. components: {
  32. Hello
  33. }
  34. </script>

上边写了好几个slot,代码有一些冗余!下边使用渲染函数改造下:

简单几行代码搞定,代码很简洁

  1. // Hello.vue
  2. <script>
  3. export default {
  4. name: "Hello",
  5. functional: true,
  6. props: {
  7. level: Number,require: true
  8. },
  9. render: function(createElement) {
  10. return createElement(`h${this.level}`, {}, ['', ...this.$slots.default])
  11. }
  12. }
  13. </script>

createElement参数:

参数一:String | Object | Function

最常用的是:标签或组件,如:’div’

参数二:一个模板中对应的数据对象,可选项,具体常用属性如下:

{

class: { foo: true, bar: false }, // 与 `v-bind:class` 的 API 相同

style: { color: ‘red’, fontSize: ‘14px’ }, // 与 `v-bind:style` 的 API 相同

attrs: { id: ‘foo’ }, // 普通的 HTML attribute

props: { myProp: ‘bar’ }, // 组件 prop

on: { click: this.clickHandler }, // 点击事件

}

参数三:String | Array

如果是字符串,一定是标签里的文本节点,如果是数组,里边是子级虚拟节点 (VNodes),由 `createElement()` 构建而成。

理解刚开始的例子:

20210505212116644.png

createElment函数中,参数一是标签;参数二,没有属性,直接给它一个空对象;参数三,一个数组,其中第一项是一个空字符串,表示创建的标签里边子元素最前边没有内容,第二项就是VNode了。

红框中的文本节点和元素节点,会传递到子组件中,

20210505210507368.png

子组件render函数中,两个节点会被编译成虚拟子节点(VNodes),并且通过:this.$slots.default 可以获取到:

它是一个数组,

20210505211813626.png

createElement返回什么:

下边是官方文档的解释,

createElement 到底会返回什么呢?其实不是一个实际的 DOM 元素。它更准确的名字可能是 createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为“VNode”。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。

插槽:

(1)this.$slot

上边已经记录过,可以通过 this.$slot 访问静态插槽的内容(也就是默认插槽),每个插槽都是一个VNode数组:

  1. export default {
  2. functional: true,
  3. render: function(createElement) {
  4. console.log('$slot:', this.$slots.default)
  5. return createElement('h1', {}, ['测试'])
  6. },
  7. data() {return{}}
  8. }

20210506122717599.png

(2)通过 this.$scopedSlots 访问作用于插槽,每个作用域插槽都是一个返回若干 VNode 的函数:

有什么用呢,我们可以通过 this.$scopedSlots.default() 传递参数,这样父级可以拿到该参数,进行处理,很方便!

  1. // hello.vue
  2. <script>
  3. export default {
  4. props: {
  5. text: String,
  6. def: String
  7. },
  8. render: function(createElement) {
  9. return createElement('h1', {}, [
  10. this.$scopedSlots.default({
  11. msg: {text: this.text, def: this.def}
  12. })
  13. ])
  14. }
  15. }
  16. </script>
  17. // home.vue
  18. <template>
  19. <div class="home">
  20. <hello text="123" def="456" v-slot:default="{msg}">
  21. hello world
  22. {
  23. {msg}} // 会输出:{ "text": "123", "def": "456" }
  24. </hello>
  25. </div>
  26. </template>

函数式组件

函数式组件在开发中经常用的一种方式。

上边渲染函数,很简单,只是通过props接收了一些参数而已, 没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。在这样的场景下,我们可以将组件标记为 functional:true,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。一个函数式组件就像这样 hello.vue

  1. // hello.vue
  2. <script>
  3. export default {
  4. functional: true,
  5. props: {}, // 该属性可省略,在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。
  6. render:function(createElement, ctx) {
  7. console.log('props:', ctx.props) // {text: "123", def: "456"}
  8. console.log('children:', ctx.children) // [VNode, VNode]
  9. console.log('slots:', ctx.slots()) // {default:[VNode, VNode]}
  10. // 注意这里是defualt,而不是default!!!
  11. console.log('scopedSlots:', ctx.scopedSlots.defualt()) // [VNode, VNode]
  12. console.log('data:', ctx.data) // {attrs: {text:"123", def: "456"}}
  13. console.log('parent:', ctx.parent) // VueComponents 父组件home
  14. console.log('listeners:', ctx.listeners) // {click:f()} 就是父组件log函数
  15. console.log('injections:', ctx.injections) // undefined
  16. return createElement('h2', {}, ctx.children)
  17. }
  18. }
  19. </script>
  20. // 父组件 home.vue
  21. <template>
  22. <div class="home">
  23. <hello text="123" def="456" @click="log">
  24. hello world
  25. <span>span标签</span>
  26. </hello>
  27. </div>
  28. </template>
  29. <script>
  30. export default {
  31. methods: {
  32. log() {}
  33. }
  34. }
  35. </script>

组件需要的一切,可以通过ctx参数来获取、传递,ctx对象包含如下字段(下边的属性在上边例子中都有输出):

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyNzc4MDAx_size_16_color_FFFFFF_t_70

补充:

1、你也可以这样写,返回一个数组,数组中是标签:

参数一必须为h参数,否则无效或报错!

  1. <script>
  2. export default {
  3. functional: true,
  4. render: function(h, ctx) {
  5. return [<div>123321321</div>]
  6. }
  7. }
  8. </script>

页面渲染如下:

20210506200821875.png20210506200751119.png

2、通过 ctx.scopedSlots,可以编写一个临时变量

  1. // com.js
  2. export default {
  3. functional: true,
  4. render: function(createElement, ctx) {
  5. console.log(ctx.scopedSlots.defualt())
  6. return ctx.scopedSlots.defualt && ctx.scopedSlots.defualt({msg:ctx.props})
  7. }
  8. }
  9. // Home.vue
  10. <template>
  11. <div>
  12. <com text="123" def="456">
  13. <template v-slot:defualt="{msg}">
  14. {
  15. {msg}} // 可以在这里边使用 { "text": "123", "def": "456" }
  16. </template>
  17. </com>
  18. </div>
  19. </template>
  20. <script>
  21. import com from './com.js'
  22. export default {
  23. name: "Home",
  24. components: {com}
  25. }
  26. <script>

20210506204555543.png

发表评论

表情:
评论列表 (有 0 条评论,74人围观)

还没有评论,来说两句吧...

相关阅读

    相关 vue slot

    一、介绍:slot可以理解为占位符,在子组件中给父组件站位。 二、举例说明: 1、没有插槽: (1)父组件: <template> <div>

    相关 Vue slot

    我们对于slot这个概念或许不是很清楚,但是我们在实际的使用中可能已经遇到很多次了。比如我们在使用ivew组件库的时候,就会经常遇到slot这个单词。而实际上,slot的使用更

    相关 Vue(slot)

    slot:向组件内部指定位置传递内容 单个插槽(匿名插槽) 在App.vue引入子组件About.vue,在about标签加上一个div标签传递到子组件中: Ap