# 响应式的本质
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}) |
| |
| |
| 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) { |
| |
| const effectMap = []; |
| return new Proxy(obj, { |
| |
| get(target, key) { |
| if (activeEffect) { |
| |
| if (!effectMap[key]) { |
| effectMap[key] = []; |
| } |
| effectMap[key].push(activeEffect); |
| } |
| |
| console.info("收集数据", activeEffect); |
| |
| return target[key]; |
| }, |
| |
| set(target, key, value) { |
| target[key] = value; |
| |
| if (effectMap[key]) { |
| effectMap[key].forEach((fn) => fn()); |
| } |
| return true |
| }, |
| }); |
| } |