<p><button onclick="testReactivity1()">Test</button> the basic mechanism of reactivity system in Vue 0.x - 2.x</p>
<p><button onclick="testProxy1()">Test</button> ES <code>Proxy</code></p>
<p><button onclick="testProxy2()">Test</button> ES <code>Proxy</code> with ES <code>Reflect</code></p>
<p><button onclick="testReactivity2()">Test</button> the basic mechanism of reactivity system in Vue 3.0 (without flag properties)</p>
<p>Please open the console panel to find out all the result.</p>
// basic mechanism of reactivity system in Vue 0.x - 2.x
const testReactivity1 = () => {
// data
const data = { x: 1, y: 2 }
// real data and deps behind
let realX = data.x
let realY = data.y
const realDepsX = []
const realDepsY = []
// make it reactive
Object.defineProperty(data, 'x', {
get() {
trackX()
return realX
},
set(v) {
realX = v
triggerX()
}
})
Object.defineProperty(data, 'y', {
get() {
trackY()
return realY
},
set(v) {
realY = v
triggerY()
}
})
// track and trigger a property
const trackX = () => {
if (isDryRun && currentDep) {
realDepsX.push(currentDep)
}
}
const trackY = () => {
if (isDryRun && currentDep) {
realDepsY.push(currentDep)
}
}
const triggerX = () => {
realDepsX.forEach(dep => dep())
}
const triggerY = () => {
realDepsY.forEach(dep => dep())
}
// observe a function
let isDryRun = false
let currentDep = null
const observe = fn => {
isDryRun = true
currentDep = fn
fn()
currentDep = null
isDryRun = false
}
// define 3 functions
const depA = () => console.log(`x = ${data.x}`)
const depB = () => console.log(`y = ${data.y}`)
const depC = () => console.log(`x + y = ${data.x + data.y}`)
// dry-run all dependents
observe(depA)
observe(depB)
observe(depC)
// output: x = 1, y = 2, x + y = 3
// mutate data
data.x = 3
// output: x = 3, x + y = 5
data.y = 4
// output: y = 4, x + y = 7
}
// demo of ES Proxy
const testProxy1 = () => {
const data = { x: 1, y: 2 }
// all behaviors of a proxy by operation types
const handlers = {
get(data, propName, proxy) {
console.log(`Get ${propName}: ${data[propName]}!`)
return data[propName]
},
// has(data, propName) { ... },
// set(data, propName, value, proxy) { ... },
// deleteProperty(data, propName) { ... },
// ...
}
// create a proxy object for the data
const proxy = new Proxy(data, handlers)
// print: 'Get x: 1' and return `1`
console.log(proxy.x)
}
// demo of ES Proxy + ES Reflect
const testProxy2 = () => {
const data = { x: 1, y: 2 }
// all behaviors of a proxy by operation types
const handlers = {
get(data, propName, proxy) {
console.log(`Get ${propName}: ${data[propName]}!`)
// same behavior as before
return Reflect.get(data, propName, proxy)
},
has(...arguments) { return Reflect.set(...arguments) },
set(...arguments) { return Reflect.set(...arguments) },
deleteProperty(...arguments) { return Reflect.set(...arguments) },
// ...
}
// create a proxy object for the data
const proxy = new Proxy(data, handlers)
// print: 'Get x: 1' and return `1`
console.log(proxy.x)
}
// basic mechanism of reactivity system in Vue 3.0 (without flag properties)
const testReactivity2 = () => {
// a WeakMap to record dependets
const dependentMap = new WeakMap()
// track and trigger a property
const track = (type, data, propName) => {
if (isDryRun && currentFn) {
if (!dependentMap.has(data)) {
dependentMap.set(data, new Map())
}
if (!dependentMap.get(data).has(propName)) {
dependentMap.get(data).set(propName, new Set())
}
dependentMap.get(data).get(propName).add(currentFn)
}
}
const trigger = (type, data, propName) => {
dependentMap.get(data).get(propName).forEach(fn => fn())
}
// observe
let isDryRun = false
let currentFn = null
const observe = fn => {
isDryRun = true
currentFn = fn
fn()
currentFn = null
isDryRun = false
}
// all behaviors of a proxy by operation types
const handlers = {
get(...arguments) { track('get', ...arguments); return Reflect.get(...arguments) },
has(...arguments) { track('has', ...arguments); return Reflect.set(...arguments) },
set(...arguments) { Reflect.set(...arguments); trigger('set', ...arguments) },
deleteProperty(...arguments) {
Reflect.set(...arguments);
trigger('delete', ...arguments)
},
// ...
}
// make data and arr reactive
const data = { x: 1, y: 2 }
const proxy = new Proxy(data, handlers)
const arr = [1, 2, 3]
const arrProxy = new Proxy(arr, handlers)
// observe functions
const depA = () => console.log(`x = ${proxy.x}`)
const depB = () => console.log(`y = ${proxy.y}`)
const depC = () => console.log(`x + y = ${proxy.x + proxy.y}`)
const depD = () => {
let sum = 0
for (let i = 0; i < arrProxy.length; i++) {
sum += arrProxy[i]
}
console.log(`sum = ${sum}`)
}
// dry-run all dependents
observe(depA)
observe(depB)
observe(depC)
observe(depD)
// output: x = 1, y = 2, x + y = 3, sum = 6
// mutate data
proxy.x = 3
// output: x = 3, x + y = 5
arrProxy[1] = 4
// output: sum = 8
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.