# 响应式的本质

vue 的响应式就是函数和数据的关联,当数据发生变化那么依赖这个数据的函数重新运行起来。 那这句话怎么理解呢?我们来看具体的一些例子

# 函数和数据的关联

下面的一些示例代码都是 vue3

watchEffect(()=>{
    props.name
 })
 上面这个代码就是一个函数和数据的关联,当props.name发生变化的时候,这个函数就会重新运行起来。
  • 那是不是所有的函数和数据会产生关联呢?答案是不是的。那什么样的函数和什么样的数据会产生关联呢

# 函数

被监控的数据 (不止这些,只是举例)
vue2 watcher
vue3 effect
     render
     watchEffect
     computed
     watch

# 数据

必须是响应式数据 (ref,recative,props....)
 必须在函数中使用

# 主体代码

<template>
  <div>
    <test-a :count="count" />
    <button @click="hanldClick">点我</button>
  </div>
</template>
<script setup>
import TestA from './components/TestA.vue'
import { ref } from 'vue'
function hanldClick() {
  count.value++
  console.info(count.value)
}
</script>

# 示例 (下面的代码都是组件内的)

  • 示例一
 <template>
    <div>
        <div>得到传入的属性:{{ count }}</div>
        <div>doubled:{{ doubleCount }}</div>
    </div>
</template>

<script setup>
import { computed, ref, watch, watchEffect,defineProps } from 'vue'

const props = defineProps({
    count: Number
})
//示例一
const doubleCount = ref(props.count * 2)
</script>

有人可能会发出疑问,template 中的代码是怎么发生响应式的呢。其实 template 的本质是render函数。我们来具体看一张图


回到这个示例一的问题上,上面当 count 发生变化的时候,doubleCount 会发生变化吗?答案是:不会发生变化的。
为什么会出现这中情况呢,我们就要回到响应式的本质上来看了。响应式的本质是函数和数据的关联,当数据发生变化那么依赖这个数据的函数重新运行起来。doubleCount 有任何函数和他关联吗。
答案是没有的。所以当 count 发生变化的时候,doubleCount 不会发生变化

那我们怎么解决这个问题呢?看下示例二

  • 示例二
 <template>
    <div>
        <div>得到传入的属性:{{ count }}</div>
        <div>doubled:{{ doubleCount }}</div>
    </div>
</template>

<script setup>
import { computed, ref, watch, watchEffect,defineProps } from 'vue'

const props = defineProps({
    count: Number
})
//示例二
  const doubleCount = computed(() => props.count * 2)
</script>

这样当 count 发生变化的时候,doubleCount 也会发生变化。因为 computed 是一个函数,他和数据是关联的。当数据发生变化的时候,这个函数就会重新运行起来。

# 实现一个简单的响应式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
   <button id="btn">按钮</button>
   <div id="app"></div>
</body>
<script type="module">
    import { WatchEffect,Recative  } from './index.js'
 
    var app=document.getElementById('app')
    var btn=document.getElementById('btn')
 
    var state=Recative({num:0})
 
    // 这里相当于写 template
    WatchEffect(()=>{
     app.textContent=state.num
    })
 
    // 原生的事件监听
    btn.addEventListener('click',()=>{
     state.num++
    })
 
    </script>
</html>
let activeEffect = null;
// 监控数据变化
export function WatchEffect(fn) {
  console.info("WatchEffect");
  activeEffect = fn;
  fn();
  activeEffect = null;
}
// 模拟响应式
export function Recative(obj) {
  // 创建一个 map
  const effectMap = [];
  return new Proxy(obj, {
    //get 方法
    get(target, key) {
      if (activeEffect) {
        // 将 activeEffect 存入 effectMap
        if (!effectMap[key]) {
          effectMap[key] = [];
        }
        effectMap[key].push(activeEffect);
      }
      // 收集依赖
      console.info("收集数据", activeEffect);
      return target[key];
    },
    //set 方法
    set(target, key, value) {
      target[key] = value;
      // 重新运行 effect
      if (effectMap[key]) {
        effectMap[key].forEach((fn) => fn());
      }
      return true
    },
  });
}
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

teapus 微信支付

微信支付

teapus 支付宝

支付宝