🔥由浅入深,揭秘vue3中ref、setup、template三者的藕断丝连!

前言

大家好,今天打算写一个vue3中的ref函数,当然不单单是封装一个ref函数这么简单,还会在此基础上引申出vue在template模板中读取及设置ref值时怎么做到省略.value的写法,要深入理解其中原理又得引申出setup函数给template暴露属性时都做了什么,从而理解三者(setup、template、ref) 之间的藕断丝连。我们这里不讲源码,旨在用最通俗的方式让大家理解其中的实现思路。

ref函数揭秘

vue官方文档 对ref是这样介绍的:如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。

对其有了基本了解后,我们创建一个myvue.js文件,尝试自己封装一个ref函数:

js 复制代码
import { reactive } from 'vue'
export const ref = (value) => {
  const wrapper = {
    value
  }
  return reactive(wrapper)
}

封装好后,我们尝试在vue页面中引入使用:

js 复制代码
<template>
  <div>
    <span>当前值:{{ count.value }}</span>
    <button @click="increment">加一</button>
  </div>
</template>

<script setup>
// 自己封装的ref
import { ref } from './hooks/myvue'

const count = ref(0)
const increment = () => {
  count.value++
}
</script>

当我们点击加一按钮 时,页面显示的count.value值也同样能够正常刷新。But ,还没完!细心的同学可能已经发现,在template模板中使用count变量时,需要.value才行,我们想要的效果是像vue提供的ref函数 一样,不用.value也能正常读取值。

template中省略.value揭秘

其实不难 ,我们只需在前文自己封装的ref函数里增加一行代码即可!

js 复制代码
import { reactive } from 'vue'
export const ref = (value) => {
  const wrapper = {
    value
  }
  /**
   * 在wrapper上定义一个不可修改且不可迭代的私有属性,
   * vue内部会通过这个__v_isRef私有属性判断是否是ref响应式数据
   */
  // 新增代码
  Object.defineProperty(wrapper, '__v_isRef', {value: true}) 
  return reactive(wrapper)
}

这样我们就能在template中省略.value写法了!!!

到这,虽然功能实现了,但我们不能知其然而不知其所以然,下面说说其实现原理。

setup返回值暴露原理揭秘

我们这次不使用<script setup>语法糖,采用原始的setup函数暴露属性 给template模板使用:

js 复制代码
import { ref } from './hooks/myvue'
export default {
  setup() {
    const count = ref(0)
    const increment = () => {
      count.value++
    }
    // 将 ref 暴露给模板
    return {
      count,
      increment
    }
  }
}
</script>

从以上可看出,setup的返回值是一个对象 ,而这个对象中的属性之所以能够在template中直接使用,是归功于webpack/vite之类的编译打包工具,在.vue文件编译成js文件中的render函数执行时,依然是通过对象.属性(比如:obj.count)的形式读取setup返回的对象属性,只不过这个setup函数返回的对象经过了Proxy包装,变成了一个响应式对象,在读取/设置 属性时,在get/set方法中拦截判断是否为ref响应式对象,如果是则会自动补全.value并返回/设置相应值。

下面我们通过编写一个proxyRefs包装函数,更直观的了解其中原理。

js 复制代码
const proxyRefs = (obj) => {
  return new Proxy(obj, {
    get(target, key, receiver) {
      // 读取target[key]的值
      const value = Reflect.get(target, key, receiver)
      // __v_isRef为我们标记为ref的属性
      return value.__v_isRef ? value.value : value
    },
    set(target, key, newValue, receiver) {
      const value = target[key]
      if(value.__v_isRef) {
        // 是ref则设置.value值
        value.value = newValue
        // 返回设置成功
        return true
      }
      // 设置target[key]的值,并返回是否设置成功
      return Reflect.set(target, key, newValue, receiver)
    }
  })
}

以上就是我们实现的setup返回对象包装方法,之后我们使用ref时就不再需要.value了!主要是通过Proxy的get方法拦截读取操作,如果读取的值是ref则返回其.value值,如果不是则直接返回原始值;set拦截也是同理,目的是在设置ref值时也能省略.value写法。

下面我们通过一个例子测试一下:

js 复制代码
<script>
import { ref, proxyRefs } from './hooks/myvue'
export default {
  setup() {
    const count = ref(0)
    const increment = () => {
      count.value++
    }
    const state = {
      count,
      increment
    }
    
    const state2 = proxyRefs(state)
    console.log(state2.count) // 0
    state2.count++
    console.log(state.count.value)  // 1
    // 将 ref 暴露给模板
    return state
  }
}
</script>

以上我们把setup返回对象通过刚刚我们自己写的proxyRefs方法做一层包装,并赋值给state2,并且我们通过state2.count成功读取和设置了state中的count值。

总结

首先,封装ref函数,并创建一个具有value属性wrapper对象,并把传入的参数赋值给value属性,最后再把wrapper对象通过reactive响应式API,包装成一个深层的响应式对象。

其次,为了使ref能够省略.value的写法,我们给wrapper包装对象设置了一个私有属性__v_isRef,由于vue内部对setup函数返回的对象做了Proxy拦截包装,所以在template模板中读取ref的值时,可以省略.value读取及设置ref的值。

最后,我们手写了一个在读取或设置 ref的值时,可以省略.value的包装函数proxyRefs,深入了解setup函数给模板暴露属性时,是如何包装返回的对象给模板使用。

相关推荐
Coder_张三9 分钟前
iOS 26 适配
前端·ios·swift
m0_5873830010 分钟前
无人自助拼豆系统落地开发实战指南 核心逻辑实现与部署经验分享
前端·javascript·vue.js·人工智能·经验分享·需求分析
zhaiyy15 分钟前
HTML Popover API 实践
前端·javascript·html
OpsEye1 小时前
企业 AI 管理新需求:不仅要接入模型,还要看懂每一次调用
javascript·ai编程
牧艺1 小时前
SPA 也能像原生 App:View Transitions API 从原理到可上线实战
前端·javascript·css
Dovis(誓平步青云)1 小时前
现场问题先关闭还是先验收,建筑任务怎样处理依赖
前端·javascript·数据库·mysql·音视频
用户84298142418101 小时前
压缩 ≠ 加密:前端代码保护的三层实践与踩坑记录
前端·javascript
前端一课1 小时前
别把人生过成一场等待退休的倒计时
前端
.Hypocritical.1 小时前
SpringBoot+Vue 前后端分离项目部署到云服务器
服务器·vue.js·spring boot
CENTER SAYS1 小时前
企业400电话办理全流程详解与材料准备(实操向)
前端·crm