Vue3について とはいえ、Vue3はrc4版まで実施されており、4月にフロントエンドのベータ版リリースが赤っ恥をかいたのをきっかけに、みんな勉強し始めたのでしょうがありません!
vue2 レスポンシブ原則のレビュー
//
function observe(){
if(typeof obj !='object' || obj == null){
return
}
if(Array.isArray(obj)){
Object.setPrototypeOf(obj,arrayProto)
}else{
const keys = Object.keys()
for(let i=0;i<keys.length;i++){
const key = keys[i]
defineReactive(obj,key,obj[key])
}``
}
}
function defineReactive(target, key, val){
observe(val)
Object.defineProperty(obj, key, {
get(){
// 依存関係コレクション
dep.depend()
return val
},
set(newVal){
if(newVal !== val){
observe(newVal)
val = newVal
// お知らせの更新
dep.notify()
}
}
})
}
Array responsive: 配列のプロトタイプメソッドをオーバーライドし、変更を通知するロジックを追加します。
//
const originalProto = Array.prototype
const arrayProto = Object.create(originalProto)
['push','pop','shift','unshift','splice','reverse','sort'].forEach(key=>{
arrayProto[key] = function(){
originalProto[key].apply(this.arguments)
notifyUpdate()
}
})
vue2レスポンシブの問題点
再帰的、消費量が多い 属性の追加/削除、別のAPIの追加実装が必要 配列、マップ・セット・クラスや他のデータ型の追加実装が必要、レスポンシブには対応できない 修正構文には制限があります。
vue3レスポンシブソリューション
ES6のProxyを使用してデータ応答性を高め、上記のVue2の問題点をすべて解決します。
プロキシは、ターゲット・オブジェクトにインターセプション/プロキシのレイヤーを追加することができ、ターゲット・オブジェクトに対する外部からの操作は、このインターセプションのレイヤーを通過します。
// reactive
function reactice(obj){
return new Proxy(obj,{
get(target, key, receiver){
const ret = Reflect.get(target, key, receiver)
return isObject(ret) ? reactice(ret) : ret
},
set(target, key, val, receiver){
const ret = Reflect.set(target, key, val, receiver)
return ret
},
deleteProperty(target, key){
const ret = Reflect.deleteProperty(target, key)
return ret
},
})
}
レスポンシブ原則
レスポンシブデータに依存する関数cbをeffectで宣言し、cb関数を実行し、実行中にレスポンシブデータゲッターをトリガーして、レスポンシブデータゲッターに追跡します 依存性の収集:targetMapに格納されたデータとcbのマッピング関係を確立します レスポンシブデータが変更されると、トリガーがトリガーされ、targetMapに従って、関連する実行するcbを見つけます。レスポンシブデータが変更されると、トリガがトリガされ、targetMapに従って、関連するcbの実行マッピング関係targetMap構造が見つかります:
targetMap: WeakMap{
target:Map{
key: Set[cb1,cb2...]
}
}
手書き vue3 レスポンシブ
構造
// mini-vue3.js
/* レスポンシブデータの作成 */
function reactice(obj){}
/* 応答関数cbを宣言する */
function effect(cb){}
/* 依赖收集:建立 数据&cb 人間関係をマッピングする */
function track(target,key){}
/* トリガー更新:マッピング関係に従って、cbを実行する。 */
function trigger(target,key){}
reactive
/* レスポンシブデータの作成 */
function reactive(obj){
// Proxy:http://..com/#docs/proxy
// Proxyこれは、オブジェクトの外側のレイヤーにインターセプトを追加することと同じである。
// Proxy再帰が不活性なので、再帰ロジックを追加する必要がある
// Reflect:http://..com/#docs/reflect
// Reflect:オブジェクトのデフォルトの操作を実行するために使用され、より標準化され、より親しみやすく、操作オブジェクトのコレクションとして理解することができる。
// ProxyとObjectのメソッドReflectはすべて
if(!isObject(obj)) return obj
const observed = new Proxy(obj,{
get(target, key, receiver){
const ret = Reflect.get(target, key, receiver)
console.log('getter '+ret)
// 跟踪 收集依赖
track(target, key)
return reactive(ret)
},
set(target, key, val, receiver){
const ret = Reflect.set(target, key, val, receiver)
console.log('setter '+key+':'+val + '=>' + ret)
// トリガー更新
trigger(target, key)
return ret
},
deleteProperty(target, key){
const ret = Reflect.deleteProperty(target, key)
console.log('delete '+key+':'+ret)
// トリガー更新
trigger(target, key)
return ret
},
})
return observed
}
effect
/* 応答関数cbを宣言する */
const effectStack = []
function effect(cb){
// 関数の高次カプセル化
const rxEffect = function(){
// 1.例外をキャッチする
// 2.fnスタックからスタックへ
// 3.fnを実行する
try{
effectStack.push(rxEffect)
return cb()
}finally{
effectStack.pop()
}
}
// 最初に依存関係の収集のために一度だけ実行する。
rxEffect()
return rxEffect
}
track
/* 依赖收集:建立 数据&cb 人間関係をマッピングする */
const targetMap = new WeakMap()
function track(target,key){
// マッピングの関係を保存する
const effectFn = effectStack[effectStack.length - 1] // トップ・オブ・スタック機能を取り出す
if(effectFn){
let depsMap = targetMap.get(target)
if(!depsMap){
depsMap = new Map()
targetMap.set(target, depsMap)
}
let deps = depsMap.get(key)
if(!deps){
deps = new Set()
depsMap.set(key, deps)
}
deps.add(effectFn)
}
}
trigger
/* トリガー更新:マッピング関係に従って、cbを実行する。 */
function trigger(target, key){
const depsMap = targetMap.get(target)
if(depsMap){
const deps = depsMap.get(key)
if(deps){
deps.forEach(effect=>effect())
}
}
}
テストデモ
<!-- test.html -->
<div id="app">
{{msg}}
</div>
<script src="./mini-vue3.js"></script>
<script>
// レスポンシブなデータを定義する
const state = reactive({
msg:'message'
})
// 定义一个使用到响应式数据的 dom更新函数
function updateDom(){
document.getElementById('app').innerText = state.msg
}
// 効果を持つ更新関数を宣言する
effect(updateDom)
// レスポンシブデータの時限変更
setInterval(()=>{
state.msg = 'message' + Math.random()
},1000)
</script>
効果
ありがとうございました。
1.この記事を気に入っていただけたなら、賛辞を送ってください。
2.私の友人を追加するには、コードをスキャンし、私は "フロントエンドの高度な交流グループ "にあなたをプル、我々はコミュニケーションと進歩のために一緒に働く。




