エッグは、vue3.0関連の知識を学ぶために、最近の意図の前に書き込むようになり、物事を学ぶの精神に沿って、最良の方法は、それの書き込みを模倣することですので、vue3.0の簡易版を書くために自分で行うには、彼らはミニvue3.0と呼ばれる 大きな助けの理解のvue3.0またはvue2.xのコア原則の感触は、それを共有するように。主に、テンプレートのコンパイル、レスポンシブ、コンポーネントのレンダリング処理などが含まれ、リポジトリのアドレスは、、welcome starです!
レスポンシブ・プリンシプル
Proxy
ご存知の通り、vue2.xのレスポンシブはObject.definePropertyのデータハイジャックによって実現されていますが、vue3.0では新しいES6 API Proxyを使ってデータハイジャックを実現しています。
長所:
- オブジェクトに新しく追加されたプロパティを乗っ取ることができます。
const obj = { a: 1 } const proObj = new Proxy(obj, ...) proObj[b] = 2 // Object.defineProperty プロキシを乗っ取ることはできないが、プロキシを乗っ取ることはできる。 - 配列のプッシュ、シフト、その他の関連操作をハイジャック可能
const ary = [1] const proObj = new Proxy(ary, ...) proObj[1] = 2 // Object.defineProperty プロキシを乗っ取ることはできないが、プロキシを乗っ取ることはできる。
欠点:
- オブジェクトのプロパティをディープハイジャックすることはできません。
- 複数のデータハイジャックコールが発生する可能性があります。
const ary = [1, 2, 3]
const proObj = new Proxy(ary, ...)
proObj.slice(1, 0, 4) // 数字4を挿入すると、複数のproObjデータハイジャック更新がトリガーされる。
```
## vue3.0 レスポンシブ原則分析
vue responsiveの原則を分析する際には、オブザーバー・パターンを念頭に置く必要がある。オブジェクトがコールバックを保存し、適切なタイミングでそれをトリガーすることは理解しやすい。
アイデアの本質はまだ比較的単純だ。次に、単純にデータ応答関数を実装する。
### データハイジャック
ここでの主なことは、テンプレートをレンダリングするとき、レスポンシブ・データにアクセスするとき、依存関係の収集を行うデータの遮断を行うことである。簡単な実装は以下のとおりで、コードには詳細なコメントがある。
```js
// いくつかのヘルパー関数
const isObject = (val) => val !== null && typeof val === 'object'
const hasOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (obj, key) => hasOwnProperty.call(obj, key)
const toRaw = new WeakMap() // raw -> proxy オブジェクトのマッピング
const toProxy = new WeakMap() // proxy -> raw オブジェクトのマッピング
const targetMap = new Map() // コールバック・コレクション・マップ
// レスポンシブを設定する
const reactive = (obj) => {
// すでにプロキシオブジェクトであれば、プロキシオブジェクトを直接返す。
if (toProxy.has(obj)) {
return toProxy.get(obj)
}
// すでにプロキシオブジェクトであれば、プロキシオブジェクトを直接返す。
if (toRaw.has(obj)) {
return obj
}
// Proxyは1つのレイヤーにしかプロキシできないことに注意
const proxy = new Proxy(obj, {
get(target, key, receiver) {
track(target, key) // ロジックは以下を参照のこと。
const value = Reflect.get(target, key, receiver)
return isObject(value) ? reactive(value) : value
},
set(target, key, value, receiver) {
const oldValue = Reflect.get(target, key, receiver)
value = toRaw.get(value) || value
const observed = Reflect.set(target, key, value, receiver)
// 配列の複数トリガーの問題を解決する
if (!hasOwn(target, key)) {
trigger(target, key)
} else if (value !== oldValue) {
trigger(target, key)
}
if (!targetMap.has(target)) {
// オブジェクトコールバック関数Mapを設定する
targetMap.set(target, new Map())
}
return observed
}
})
toRaw.set(proxy, obj)
toProxy.set(obj, proxy)
return proxy
}
依存コレクション
// 依存関係コレクション
const track = (target, key) => {
// オブジェクトのコールバック関数を取得する Map
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
// コールバック関数の属性に対応するオブジェクトを取得する Set
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
// ここでのactiveEffectは、実際にはレンダー関数であると考えることができる。
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
}
}
トリガーの実行
// ここでトリガーを実行する。
const trigger = (target, key) => {
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
const effects = depsMap.get(key)
effects.forEach(effect => effect())
}
簡単な例を挙げましょう:
// 例えば、以下のデータをレスポンシブに設定する。
const data = {
a: 1
}
// レスポンシブデータを設定する
const proxyData = reactive(data);
//
toProxy = {
data: proxyData
}
toRaw = {
proxyData: data
}
// データプロパティにアクセスするとき
activeEffect = () => {
console.log(proxyData.a)
}
activeEffect()
// はコールバック関数activeEffectを収集する。
targetMap = {
data: {
a: [activeEffect]
}
}
// レスポンスデータを変更する
proxyData.a = 2
// そして、実行をトリガーし、依存関係の再収集を開始する。
targetMap[data][a].forEach(cb => cb())
上記はvue 3.0レスポンシブの核心原理を簡単に説明しただけで、vue 3.0データソースコードはより複雑で、興味のある学生は自分で理解することができます。上記の基礎があれば、次のことがもっと簡単になります。
refcomputedの原則
データ・レスポンスの原則を紹介します。
ref実装の原則
なぜref関数が必要なのですか?
reactiveとrefは2つのコードスタイルを満たすからです。
- reactive
const reac = reactive({
a: 1,
b: 2
})
- ref
const a = ref(1)
const b = ref(2)
典型的な応用例として、よくページ内の様々なロードを設定し、ロードを制御します。
//
const loading = {
a: false,
b: false
}
//
let loadingA = false
let loadingB = false
参照 ソースコード解析
function ref(raw) {
// refが処理されたかどうかを判断する。
if (isRef(raw)) {
return raw
}
// 値がオブジェクトの場合、data responsiveを設定する。
raw = reactive(raw)
const r = {
_isRef: true,
get value() {
// 依存関係コレクション
track(r, TrackOpTypes.GET, 'value')
return raw
},
set value(newVal) {
raw = reactive(newVal)
// 反応するコールバックをトリガーする
trigger(
r,
TriggerOpTypes.SET,
'value',
)
}
}
return r
}
実際、refの実装原理は比較的単純で、元のデータの外側に、プロキシのレイヤーでラッピングすることで、応答性の高い実装を実現しています。
computed実装の原則
以下は
function computed(getterOrOptions)
let getter
let setter
// パラメータが関数の場合、デフォルトではゲッター関数になる。
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = () => {
console.warn('Write operation failed: computed value is readonly')
}
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
let dirty = true
let value
// effect vue2に相当する.x ウォッチャーで
const runner = effect(getter, {
lazy: true, // すぐに実行されるわけではないので、computedはキャッシュとして使うことができる。
computed: true,
scheduler: () => {
dirty = true
}
})
return {
_isRef: true,
get value() {
// 値がダーティなときだけ再計算する。
if (dirty) {
value = runner()
dirty = false
}
// 以下の分析を参照のこと
trackChildRun(runner)
return value
},
set value(newValue: T) {
setter(newValue)
}
}
}
- 分析例です:
const b = reactive({ a: 1})
const c = computed(() => b.a)
bの値を変更する場合、例えばb.a = 2の場合、計算されたスケジューラのみがトリガーされ、dirty = trueとなります。
c.value値にアクセスしたときのみ、計算されたgetエージェントがトリガーされ、ランナー関数が実行され、求められた値が再計算されます。
- trackChildRunエフェクト:連鎖した計算プロパティを実装します。親エフェクトは、計算ランナーによって記録されたdepコールバック関数を記録し、連鎖した計算プロパティを実装します。
少しわかりやすい例を挙げましょう。
const obj = {a: 1}
const objProxy = reactive(obj)
const comp = computed(() => { console.log(objProxy.a)} )
// コンピュート
comp.value
// obj1 レスポンシブディペンデンシーコレクションの場合、計算された属性のランナー関数をコールバックとして取得する。
targetMap[obj].a = [runner]
// レンダーテンプレート
//<div>{{comp.value}}</div>
// レンダー関数を呼び出した後
targetMap[obj].a = [runner, render]
objProxy.a = 2
同時に、連鎖した呼び出しを実現するために、計算された属性式が再評価され、テンプレートが更新される。





