Vue中的renderless 非渲染组件

本文围绕Vue的Renderless组件展开。先介绍了render函数,它由vuejs将template元素编译而来,最终会被优化成VNode。接着引入Renderless component概念,即只提供数据操作、不渲染内容的组件。最后通过示例展示其使用,利用Slots负责渲染,实现功能逻辑与页面渲染分离。

面试中遇到的一个问题,

       什么是 renderless ?

       renderless 即非渲染组件开发的思想,

请看下文

 

01  什么是 render 函数

 

众所周知,在工程中,我们会在.vue文件中定义 <template>、<script>和<style>三种tag,分别盛放组件html、javascript和css。

<template>
<button class="mood">
    {{ state ? 'On' : 'Off' }}
</button>
</template>
​
<script>
export default {
data: () => ({ state: false })
}
</script>
​
<style>
  .mood:after {
      color: white;
      background: blue;
  }
</style>

 

但事实上,最后在生产环境中,我们只使用了一个巨大的JS文件,究其缘由还是得益于webpack的vue loader

 

vuejs会把template元素提取出来,并进一步编译成一个叫render的函数。(有关render函数可以参考官方文档)

 

render(h) {
  return h(
    'button',
    {class: 'mood'},
    state ? 'On' : 'Off'
  )
}

 

 

render函数最后会被vue优化成VNode(虚节点),具体过程我不再赘述了。不过,这里提供了一个很有趣的思路:编写组件时,我们其实可以不写vue文件,不写template,只需要写render函数。

 

const button = {
  render(h) {
    return h(
    'button',
          {class: 'mood'},
    state ? 'On' : 'Off'
        )
  },
  data() {
    return {state: false}
  }
}


 

 

02 Renderless component 概念

 

 

这里引入一个Renderless component的概念,直译的话应该叫非渲染组件

 

Renderless意思就是组件只提供数据操作,不渲染任何内容

 

const toggle = {
    render() {
    },
data() {
return { state: true }
    },
    methods: {
        toggle() {
this.state = !this.state
        }
    }
}
​
new Vue({
    el: '#parent',
    components: { toggle },
    ...
})

 

toggle就是所谓的Renderless组件了,只有数据和方法,不提供html template。父组件直接将其放入components即可当作一般子组件使用。

 

 

 

03 Renderless 示例

 

Renderless 组件不渲染,那谁负责渲染工作呢?嗯,就是Slots!父组件通过传递自定义的slots来定制子组件的html template。

 

<toggle v-slot:default="{on, toggle}">
    <div class="container">
        <button @click="click(toggle)">
            {{on ? 'On' : 'Off'}}
        </button>
    </div>
</toggle>

 

这里提一下v-slot,它是vue 2.6以后的新语法,用来代替之前的 slot 和 slot-scope;v-slot:default 还可以简写成 #default。Vue3应该不会再保留 slot 和 slot-scope 这种不伦不类的标签了。

 

 

希望能让插槽访问到子组件toggle里的数据和方法,以便之后点击button更改状态。子组件暴露作用域插槽也很简单,只要在render函数里返回$scopedSlots对象即可,这里因方便起见使用了默认的default插槽,自己实现的时候也可以重命名为任意插槽。

 

//toggle.js
const toggle = {
    render() {
return this.$scopedSlots.default({
            on: this.state,
            toggle: this.toggle,
        })
    },
data() {
return { state: true }
    },
    methods: {
        toggle() {
this.state = !this.state
        }
    }
}

最后我们在父组件调用renderless组件:

 

<template>
  <toggle v-slot="{on, toggle}">
        <div class="container">
        <button @click="click(toggle)">
                        {{on ? 'On' : 'Off'}}
        </button>
        </div>
  </toggle>
</template>
​
<script>
import toggle from 'toggle';
​
export default {
components: { toggle },
methods: {
        click(fn) {
            fn()
        },
    },
}
</script>

这样一个简单的renderless开关就实现了。

 

假如你想自定义组件样式,或是说控制toggle渲染方式,更改也很容易,只需要在插槽里写下自定义代码即可:

 

<toggle #default="{ on, toggle  }">
    <div class="container">
    <button @click="click(toggle)"
    :style="{background: on ? 'green' : 'red'}">
                {{on ? 'On' : 'Off'}}
    </button>
    </div>
</toggle>

 

因为toggle的逻辑不变,所以我们不需要更改这个renderless组件。只需稍微改动一下slot,button的背景色就会随着开关一起改变了。

 

 

嗯,这就是Renderless组件的效果,功能逻辑和页面渲染分开。


 

苟有恒 , 何必三更眠五更起

关注我,一起学习吧

鼓励一下,赐个赞 和 在看

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值