Reactivity in the Wild
2020-09-03 本文已影响0人
7dbe8fdb929f
If you begin to understand what you are without trying to change it, then what you are undergoes a transformation.
-- Jiddu Krishnamurti
In this article, we want to achieve something like Vue, to perform dependency-tracking and change-notification when properties are accessed or modified, by adding getter/setters which are invisible to the user.
<template>
<div>Clicked {{count}} times!</div>
<button type="button" id="counter-button">Click me!</button>
</template>
<div id="app"></div>
const data = {
message: "a",
count: 0,
}
function render() {
const template = document.querySelector("template")
let content = template.innerHTML
content = content.replace(/{{(\w+)}}/g, (all, key) => {
return data[key]
})
document.getElementById("app").innerHTML = content
document.getElementById("counter-button").addEventListener("click", () => {
data.count++
})
}
We will walk through all of properties and convert them to getter/setters using Object.defineProperty
, this is an ES5-only and un-shimmable feature.
function defineReactive(obj, key, val) {
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
Object.defineProperty(obj, key, {
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value) {
return
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// update dom
render()
},
})
}
Object.keys(data).forEach(key => {
defineReactive(data, key, data[key])
})
render()