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

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

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

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

// Hello.vue
<template>
  <div class="hello">
    <h1 v-if="level===1"><slot></slot></h1>
    <h2 v-else-if="level===2"><slot></slot></h2>
    <h3 v-else="level===3"><slot></slot></h3>
  </div>
</template>
<script>
export default {
  name: "Hello",
  props: {
    level: Number,
    require: true
  }
}
</script>
// Home.vue
<template>
  <div>
    <hello :level="1">
      hello world
      <span>span标签</span>
    </hello>
  </div>
</template>
<script>
  import Hello from '../components/Hello'
  export default {
    name: "Home",
    components: {
      Hello
    }
</script>

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

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

// Hello.vue
<script>
export default {
  name: "Hello",
  functional: true,
  props: {
    level: Number,require: true
  },
  render: function(createElement) {
    return createElement(`h${this.level}`, {}, ['', ...this.$slots.default])
  }
}
</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()` 构建而成。

 

理解刚开始的例子:

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

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

 

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

它是一个数组,

 

createElement返回什么:

下边是官方文档的解释,

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

 

插槽:

(1)this.$slot

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

export default {
    functional: true,
    render: function(createElement) {
        console.log('$slot:', this.$slots.default)
        return createElement('h1', {}, ['测试'])
    },
    data() {return{}}
}

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

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

// hello.vue
<script>
export default {
    props: {
        text: String,
        def: String
    },
    render: function(createElement) {
        return createElement('h1', {}, [
            this.$scopedSlots.default({
                msg: {text: this.text, def: this.def}
            })
        ])
    }
}
</script>
// home.vue
<template>
  <div class="home">
	  <hello text="123" def="456" v-slot:default="{msg}">
		   hello world
		   {{msg}} // 会输出:{ "text": "123", "def": "456" }
	  </hello>
  </div>
</template>

 

函数式组件

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

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

// hello.vue
<script>
export default {
    functional: true,
    props: {}, // 该属性可省略,在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。
    render:function(createElement, ctx) {
        console.log('props:', ctx.props) // {text: "123", def: "456"}
        console.log('children:', ctx.children) // [VNode, VNode]
        console.log('slots:', ctx.slots()) // {default:[VNode, VNode]}
        // 注意这里是defualt,而不是default!!!
        console.log('scopedSlots:', ctx.scopedSlots.defualt()) // [VNode, VNode]
        console.log('data:', ctx.data) // {attrs: {text:"123", def: "456"}}
        console.log('parent:', ctx.parent) // VueComponents 父组件home
        console.log('listeners:', ctx.listeners) // {click:f()} 就是父组件log函数
        console.log('injections:', ctx.injections) // undefined
        return createElement('h2', {}, ctx.children)
    }      
}
</script>
// 父组件 home.vue
<template>
  <div class="home">
	  <hello text="123" def="456" @click="log">
		   hello world
		  <span>span标签</span>
	  </hello>
  </div>
</template>
<script>
    export default {
        methods: {
            log() {}
        }
    }
</script>

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

 

补充:

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

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

<script>
    export default {
        functional: true,
        render: function(h, ctx) {
            return [<div>123321321</div>]
        }
    }
</script>

 页面渲染如下:

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

// com.js
export default {
    functional: true,
    render: function(createElement, ctx) {
        console.log(ctx.scopedSlots.defualt())
        return ctx.scopedSlots.defualt && ctx.scopedSlots.defualt({msg:ctx.props})
    }
}

// Home.vue
<template>
  <div>
    <com text="123" def="456">
      <template v-slot:defualt="{msg}">
        {{msg}} // 可以在这里边使用 { "text": "123", "def": "456" }
      </template>
    </com>
  </div>
</template>
<script>
import com from './com.js'
export default {
  name: "Home",
  components: {com}
}
<script>

 

 

 

 

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue是一款非常流行的JavaScript框架之一,其渲染函数renderVue中非常重要的一部分,用于处理如何将组件渲染为虚拟DOM。在render函数中,我们可以使用条件判断语句来根据不同的情况渲染不同的内容。 比如,我们可以使用if/else语句或者三元表达式来实现条件判断。例如,我们可以通过判断props中的值是否为true,来决定是否渲染特定的内容: ```javascript render(h) { return h('div', [ this.props.isTrue ? h('p', '这是真的') : h('p', '这是假的') ]) } ``` 另外,我们还可以使用switch/case语句来处理多个不同的情况,例如: ```javascript function getComponentType(type) { switch (type) { case 'text': return TextComponent case 'photo': return PhotoComponent case 'video': return VideoComponent default: return TextComponent } } render(h) { const Component = getComponentType(this.props.type) return h(Component, { props: { data: this.props.data } }) } ``` 在这个例子中,我们使用switch/case语句来选择不同类型的组件,然后将渲染后的组件返回。这样可以使得组件逻辑更加灵活,根据不同的props渲染不同的内容。 总之,使用条件判断语句来渲染不同的内容是Vue的一个非常重要的功能,在render函数中使用条件判断可以使得组件的逻辑更加灵活,实现更多的功能。但是我们需要注意代码的可读性和维护性,避免嵌套过深和代码过于复杂。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值