Principe du système réactif Vue.js 3.0
Source de sortie du contenu de l'article: grand camp d'entraînement haut de gamme
1. Introduction
1. Revue réactive de Vue.js
- L'objet proxy implémente la surveillance des propriétés
- Imbrication d'attributs à plusieurs niveaux, traitement du niveau suivant d'attributs dans le processus d'accès aux attributs
- Les propriétés ajoutées dynamiquement sont surveillées par défaut
- L'opération de suppression de l'attribut de surveillance par défaut
- Propriété d'index et de longueur du tableau de surveillance par défaut
- Peut être utilisé comme module séparé
2. Fonctions essentielles
- eactive / ref / toRefs / calculé
- effet
- Piste
- déclencheur
Deux, examen des objets proxy
1. En mode strict, la fonction Proxy doit renvoyer une valeur booléenne, sinon elle signalera une TypeError
Uncaught TypeError: 'set' on proxy: trap retourné falsish pour la propriété 'foo'
'use strict'
// 问题1: set和deleteProperty中需要返回布尔类型的值
// 严格模式下,如果返回false的话,会出现TypeError的异常
const target = {
foo: 'xxx',
bar: 'yyy'
}
// Reflect.getPrototypeOf()
// Object.getPrototypeOf()
const proxy = new Proxy(target, {
get (target, key, receiver) {
// return target[key]
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver) {
// target[key] = value
return Reflect.set(target, key, value, receiver) // 这里得写return
},
deleteProperty(target, key) {
// delete target[key]
return Reflect.deleteProperty(target, key) // 这里得写return
}
})
proxy.foo = 'zzz'
2. Utilisez le récepteur dans Proxy et Reflect
Receiver dans Proxy: Proxy ou un objet
qui hérite du Proxy Receiver dans React: Si l'objet cible a un getter, celui-ci dans le getter pointe vers le receveur
const obj = {
get foo () {
console.log(this)
return this.bar
}
}
const proxy = new Proxy(obj, {
get(target, key, receiver) {
if (key === 'bar') {
return 'value - bar'
}
return Reflect.get(target, key, receiver) // 执行this.bar的时候,this指向代理对象,也就是获取target.bar
}
})
console.log(proxy.foo) // value - bar
S'il est return Reflect.get(target, key, receiver)
écrit return Reflect.get(target, key)
, le this dans l'attribut responsive foo pointe toujours vers l'objet d'origine obj, this.bar n'est pas défini, et une fois que le récepteur est passé, l'attribut this dans l'attribut responsive pointe vers le nouveau proxy d'objet réactif, this.bar Retour value - bar
.
三 、 réactif
- Accepter un paramètre, déterminer si ce paramètre est un objet
- Créer un gestionnaire d'objet d'intercepteur, définir get / set / deleteProperty
- Renvoyer l'objet Proxy
Réalisez réactif par vous-même
function isObject(value) {
return value !== null && typeof value === 'object'
}
function convert(target) {
return isObject(target) ? reactive(target) : target
}
const hasOwnProperty = Object.prototype.hasOwnProperty
function hasOwn(target, key) {
return hasOwnProperty.call(target, key)
}
export function reactive(target) {
if (!isObject(target)) return target
const handler = {
get (target, key, receiver) {
// 收集依赖
// track(target, key) // 稍后解注释
console.log('get', key)
const ret = Reflect.get(target, key, receiver)
return convert(ret)
},
set (target, key, value, receiver) {
const oldValue = Reflect.get(target, key, receiver)
let ret = true
if (oldValue !== value) {
ret = Reflect.set(target, key, value, receiver)
// 触发更新
// trigger(target, key) // 稍后解注释
console.log('set', key, value)
}
return ret
},
deleteProperty (target, key) {
const hasKey = hasOwn(target, key)
const ret = Reflect.deleteProperty(target, key)
if (hasKey && ret) {
// 触发更新
// trigger(target, key) // 稍后解注释
console.log('detele', key)
}
return ret
}
}
return new Proxy(target, handler)
}
utilisation:
<body>
<script type="module">
import {
reactive } from './reactivity/index.js'
const obj = reactive({
name: 'zs',
age: 18
})
obj.name = 'lisi'
delete obj.age
console.log(obj)
</script>
</body>
La sortie est:
définir le nom lisi
index.js: 39 detele age
index.html: 17 Proxy {name: “lisi”}
Quatrièmement, collectez les dépendances
Cinq, effet, piste
let activeEffect = null
export function effect(callback) {
activeEffect = callback
callback() // 访问响应式对象的属性,去收集依赖
activeEffect = null
}
let targetMap = new WeakMap()
export function track(target, key) {
// 收集依赖
if (!activeEffect)return
let depsMap = targetMap.get(target)
if(!depsMap) {
targetMap.set(target, depsMap = new Map())
}
let dep = depsMap.get(key)
if(!dep) {
depsMap.set(key, dep = new Set())
}
dep.add(activeEffect)
}
Six, déclencheur
export function trigger(target, key) {
// 触发依赖
const depsMap = targetMap.get(target)
if(!depsMap)return
const dept = depsMap.get(key)
if(dept) {
dept.forEach(effect => {
effect()
})
}
}
utilisation:
<body>
<script type="module">
import {
reactive, effect } from './reactivity/index.js'
const product = reactive({
name: 'iPhone',
price: 5000,
count: 3
})
let total = 0
effect(() => {
total = product.price * product.count
})
console.log(total) // 15000
product.price = 4000
console.log(total) // 12000
product.count = 1
console.log(total) // 4000
</script>
</body>
Sept, réf
eactive vs ref
-
ref peut convertir des types de données de base en objets réactifs
-
L'objet retourné par ref est également sensible à la réaffectation à un objet
-
Objet renvoyé par réactif, la réaffectation perd de réactif
-
L'objet retourné par réactif ne peut pas être déconstruit
-
réactif
const product = reactive({ name: 'iPhone', price: 5000, count: 3 })
-
réf
const price = ref(5000) const count = ref(3)
Mettre en œuvre réf:
export function ref(raw) {
// 判断raw是否是ref创建的对象,如果是的话直接返回
if (isObject(raw) && raw.__v_isRef)return
let value = convert(raw)
const r = {
__v_isRef: true,
get value () {
track(r, 'value')
return value
},
set value (newValue) {
if(newValue !== value) {
raw = newValue
value = convert(raw)
trigger(r, 'value')
}
}
}
return r
}
utilisation:
<body>
<script type="module">
import {
reactive, effect, ref } from './reactivity/index.js'
const price = ref(5000)
const count = ref(3)
let total = 0
effect(() => {
total = price.value * count.value
})
console.log(total) // 15000
price.value = 4000
console.log(total) // 12000
count.value = 1
console.log(total) // 4000
</script>
</body>
Huit, toRefs
export function toRefs(proxy) {
const ret = proxy instanceof Array ? new Array(proxy.length) : {
}
for (const key in proxy) {
ret[key] = toProxyRef(proxy, key)
}
return ret
}
function toProxyRef(proxy, key) {
const r = {
__v_isRef: true,
get value () {
return proxy[key]
},
set value (newValue) {
proxy[key] = newValue
}
}
return r
}
utilisation
<body>
<script type="module">
import {
reactive, effect, toRefs } from './reactivity/index.js'
function useProduct() {
const product = reactive({
name: 'iPhone',
price: 5000,
count: 3
})
return toRefs(product) // 直接返回解构的product不是响应式对象,所以调用toRefs将reactive对象的每个属性都转化成ref对象
}
const {
price, count } = useProduct()
let total = 0
effect(() => {
total = price.value * count.value
})
console.log(total) // 15000
price.value = 4000
console.log(total) // 12000
count.value = 1
console.log(total) // 4000
</script>
</body>
Neuf, calculé
export function computed(getter) {
const result = ref()
effect(() => (result.value = getter()))
return result
}
utilisation
<body>
<script type="module">
import {
reactive, effect, computed } from './reactivity/index.js'
const product = reactive({
name: 'iPhone',
price: 5000,
count: 3
})
let total = computed(() => {
return product.price * product.count
})
console.log(total.value) // 15000
product.price = 4000
console.log(total.value) // 12000
product.count = 1
console.log(total.value) // 4000
</script>
</body>
Remarque: Trigger / track / effct est la fonction sous-jacente, généralement non utilisée. Utiliser calculé au lieu d'effet