Vue.js3.0レスポンシブシステムの原則
記事コンテンツの出力ソース:大規模なフロントエンドの高額トレーニングキャンプ
1.はじめに
1.Vue.jsレスポンシブレビュー
- プロキシオブジェクトはプロパティモニタリングを実装します
- マルチレベルの属性のネスト、属性にアクセスするプロセスで次のレベルの属性を処理する
- 動的に追加された属性はデフォルトで監視されます
- デフォルトの監視属性の削除操作
- デフォルトの監視アレイのインデックスと長さのプロパティ
- 個別のモジュールとして使用できます
2.コア機能
- eactive / ref / toRefs / computed
- 効果
- 追跡
- 引き金
2、プロキシオブジェクトのレビュー
1. strictモードでは、Proxy関数はブール値を返す必要があります。そうでない場合、TypeErrorが報告されます。
Uncaught TypeError: 'set' on proxy:trap return falsish for property'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.プロキシとリフレクトでレシーバーを使用する
プロキシのレシーバー:プロキシまたは
Reactのプロキシレシーバーを継承するオブジェクト:ターゲットオブジェクトにゲッターがある場合、ゲッターのこれはレシーバーを指します
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
return Reflect.get(target, key, receiver)
記述されている場合return Reflect.get(target, key)
、レスポンシブ属性fooのthisは元のオブジェクトobjを指し、this.barは未定義であり、レシーバーが渡された後、レスポンシブ属性のthisは新しいレスポンシブオブジェクトプロキシthis.barを指します。戻りvalue - bar
ます。
三、反応性
- パラメータを受け入れ、このパラメータがオブジェクトであるかどうかを判断します
- インターセプターオブジェクトハンドラーを作成し、get / set / deletePropertyを設定します
- Proxyオブジェクトを返します
自分でリアクティブを実現
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)
}
使用する:
<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>
出力は次のとおりです。
セット名
lisiindex.js:39 detele age
index.html:17プロキシ{名前:“ lisi”}
第四に、依存関係を収集します
ファイブ、エフェクト、トラック
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)
}
6、トリガー
export function trigger(target, key) {
// 触发依赖
const depsMap = targetMap.get(target)
if(!depsMap)return
const dept = depsMap.get(key)
if(dept) {
dept.forEach(effect => {
effect()
})
}
}
使用する:
<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>
セブン、ref
有効vs参照
-
refは、基本的なデータタイプをレスポンシブオブジェクトに変換できます
-
refによって返されるオブジェクトは、オブジェクトへの再割り当てにも応答します
-
リアクティブによって返されたオブジェクト、再割り当てはリアクティブを失います
-
リアクティブによって返されたオブジェクトは分解できません
-
反応性
const product = reactive({ name: 'iPhone', price: 5000, count: 3 })
-
ref
const price = ref(5000) const count = ref(3)
参照を実装します。
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
}
使用する:
<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>
8、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
}
使用する
<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>
9、計算
export function computed(getter) {
const result = ref()
effect(() => (result.value = getter()))
return result
}
使用する
<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>
注:Trigger / track / effctは基本的な機能であり、通常は使用されません。効果の代わりに計算を使用する