vueuse的createReusableTemplate函数实现原理

使用vueuse的时候发现里面有一个createReusableTemplate的工具函数很有意思

根据文档的介绍,它的使用方式如下:

开发中也许会经常遇到这种情况

html 复制代码
<template>
  <dialog v-if="showInDialog">
    <!-- something complex -->
  </dialog>
  <div v-else>
    <!-- something complex -->
  </div>
</template>

我们希望代码尽可能的重用,通常情况下我们会将<!-- something complex -->抽取成组件,但是那样做,在抽取出去的组件中,你就没法访问到当前SFC中的各种变量,而只能通过定义propsemits来实现数据传递,这样会有些繁琐

createReuseableTemplate能够在当前组件的template中直接定义一个可重用的片段,就像这样:

html 复制代码
<script setup>
import { createReusableTemplate } from '@vueuse/core'

const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
</script>

<template>
  <DefineTemplate>
    <!-- something complex -->
  </DefineTemplate>

  <dialog v-if="showInDialog">
    <ReuseTemplate />
  </dialog>
  <div v-else>
    <ReuseTemplate />
  </div>
</template>

在这个示例中,<DefineTemplate>中写的代码将不会渲染任何东西,而会渲染在<ReuseTemplate />

不禁惊叹这是什么黑科技,迫切想知道是怎么实现的,于是研究了一下源码

其实实现原理很简单,稍微拆分一下它的代码并手动实现如下:

html 复制代码
<script setup>
import { defineComponent } from 'vue';
import { ref } from 'vue'

const msg = ref('Hello World!')

// 首先是可重用的template的定义
// 其实它就是一个Component
const DefineTmpl = defineComponent({
  setup(_, { slots }) {
    // 拿到默认的插槽,console.log打印一下就能看出来,这玩意实际上就是一个渲染函数
    // 而setup方法,是可以返回一个渲染函数的(也就是一个能返回vnode的函数),具体可看vue官方文档:
    // https://cn.vuejs.org/guide/extras/render-function.html#declaring-render-function
    return () => slots.default()
    // 这里甚至可以直接return默认插槽,因为它本身就是一个函数
    // return slots.default
  }
})
</script>

<template>
  <DefineTmpl>
    <h1>{{ msg }}</h1>
    <input v-model="msg" />
  </DefineTmpl>
</template>

以上代码的结果就是:你在DefineTmpl写的任何东西(其实就是默认插槽的内容)都会被原样渲染出来

其实也就是跟不存在DefineTmpl几乎没有区别:

html 复制代码
<script setup>
import { defineComponent } from 'vue';
import { ref } from 'vue'

const msg = ref('Hello World!')
</script>

<template>
  <h1>{{ msg }}</h1>
  <input v-model="msg" />
</template>

那么DefineTmpl存在的意义其实就是拿到默认插槽的内容(也就是你在DefineTmpl里写的任何html),它是一个渲染函数,那就用一个变量来接收它

然后该怎么重用它呢??也就是再定义一个Component,返回DefineTmpl里获取到的默认插槽内容就行了:

html 复制代码
<script setup>
import { defineComponent } from 'vue';
import { ref } from 'vue'

const msg = ref('Hello World!')

let renderFn;
const DefineTmpl = defineComponent({
  setup(_, { slots }) {
    renderFn = slots.default
  }
})

const Reuse = defineComponent({
  setup() {
    if (!renderFn) return console.warn('未定义模板!')
    return renderFn
    // 或者 return () => renderFn()
  }
})
</script>

<template>
  <p>DefineTmpl部分(不会渲染任何东西)</p>
  <DefineTmpl>
    <h1>{{ msg }}</h1>
    <input v-model="msg" />
  </DefineTmpl>
  <h2>================分割线================</h2>
  <Reuse />
</template>

然后在页面任意地方写上就可以了,渲染结果:

如果想传入一些自定义的东西怎么办呢,稍微改造一下Reuse:

javascript 复制代码
const Reuse = defineComponent({
  setup(_, { attrs }) {
    if (!renderFn) {
      console.warn('未定义模板')
    }
    return () => renderFn({ ...attrs }) // 将attrs作为渲染函数的props传入
  }
})

然后在DefineTmpl部分可以这样写:

html 复制代码
<template>
  <p>DefineTmpl部分(不会渲染任何东西)</p>
  <DefineTmpl #default="{ data }">
    <h1>{{ msg }}</h1>
    <input v-model="msg" />
    <p>自定义内容:{{ data }}</p>
  </DefineTmpl>

  <h2>================分割线================</h2>

  <Reuse :data="'我是自定义内容'" />
</template>

渲染结果:

整个封装一下,就是一个简陋版本的createReusableTemplate了(当然比vueuse中简陋很多,不过差不多核心实现原理也就是这样了)

javascript 复制代码
function createMyReusableTemplate() {
  let renderFn;
  const DefineTmpl = defineComponent({
    setup(_, { slots }) {
      renderFn = slots.default
    }
  })

  const Reuse = defineComponent({
    setup(_, { attrs }) {
      if (!renderFn) {
        console.warn('未定义模板')
      }
      return () => renderFn({ ...attrs })
    }
  })
  
  return [DefineTmpl, Reuse]
}
相关推荐
jiangzhihao051517 小时前
前端自动翻译插件webpack-auto-i18n-plugin的使用
前端·webpack·node.js
软件技术NINI19 小时前
html css网页制作成品——HTML+CSS盐津铺子网页设计(5页)附源码
前端·css·html
mapbar_front20 小时前
面试问题—我的问题问完了,你还有什么想问我的吗?
前端·面试
quweiie20 小时前
thinkphp8+layui多图上传,带删除\排序功能
前端·javascript·layui
李鸿耀21 小时前
React 项目 SVG 图标太难管?用这套自动化方案一键搞定!
前端
闲蛋小超人笑嘻嘻21 小时前
树形结构渲染 + 选择(Vue3 + ElementPlus)
前端·javascript·vue.js
叶梅树21 小时前
从零构建A股量化交易工具:基于Qlib的全栈系统指南
前端·后端·算法
巴博尔21 小时前
uniapp的IOS中首次进入,无网络问题
前端·javascript·ios·uni-app
焚 城21 小时前
UniApp 实现双语功能
javascript·vue.js·uni-app
Asthenia04121 天前
技术复盘:从一次UAT环境CORS故障看配置冗余的危害与最佳实践
前端