Vue.js源码解析一
依赖追踪
前三章主要讲解的是响应式系统,vue侦测数据的变化,当数据变化时,会通知视图进行响应的更新。
侦测一个对象的变化 Object.defineProperty(后面会使用ES6的Proxy)
Object.defineProperty(obj,key,{
// 这里都有固定的含义 javascript中自己查找含义
// enumerable: false,
// writable: true,
// configurable: true,
// 每当从obj的key中读取数据时,get函数被触发,修改key数据时,set被触发
get() {
console.log(`getting key "${key}": ${oldvalue}`)
return oldvalue
},
set(newvalue) {
console.log(`setting key "${key}" to: ${newvalue}`)
oldvalue = newvalue
}
})
先收集依赖,把用到数据的地方收集起来,然后等属性发生变化时,把之前收集好的依赖循环触发一遍,即getter中收集依赖,setter中触发依赖
个人感觉书籍上讲解的很麻烦,还是尤雨溪作者本人讲解的简易版本比较清楚
getter和setter函数重写
<!-- practice 1:try to complete getter and setter -->
<script>
// 这里判断是否是对象数据
function isObject (obj) {
return typeof obj === 'Object'
&& !Array.isArray(obj)
&& obj !== null
&& obj !== undefined
}
function convert (obj) {
// Implement this!
if(!isObject(obj)) {
throw new TypeError()
}
Object.keys(obj).forEach( key => {
let oldvalue = obj.key
Object.defineProperty(obj,key,{
// 这里都有固定的含义 javascript中自己查找含义
// enumerable: false,
// writable: true,
// configurable: true,
get() {
console.log(`getting key "${key}": ${oldvalue}`)
return oldvalue
},
set(newvalue) {
console.log(`setting key "${key}" to: ${newvalue}`)
oldvalue = newvalue
}
})
})
}
</script>
dependency-tracking
<!-- practice 2:try to complete class Dep and function autorun -->
<!--
they realize the function of dependency tracking 跟踪依赖
how to realize it:
1、class Dep has two function: -depend -notify
2、if you call function dep.notity,then autorun will execute.
3、autorun will execute the function dep.depend,then it will console.log "updatesd"
-->
<script>
window.Dep = class Dep {
constructor() {
this.subscriber = new Set()
}
// Implement this
depend() {
// whole point:通过这个activeUpdate确保在update执行的时候,
// 我们能够通过depend(依赖类)访问到update
if(activeUpdate) {
// now we get the current update function that's being executed
// then we need to register the current active update as a subscriber
this.subscriber.add(activeUpdate)
}
}
notify() {
// run all subscribers
this.subscriber.forEach(sub => sub())
}
}
let activeUpdate;
function autorun (update) {
// 这里使用一个包装函数 在执行update的时候 activeUpdate肯定是不为空的
// 因此可以通过activeUpdate的状态看是否在执行更新函数
// 从而实现自动化更新
function wrappedUpdate() {
activeUpdate = wrappedUpdate;
update();
activeUpdate = null
}
wrappedUpdate()
}
autorun( () => {
dep.depend()
})
</script>
mini-observer
observe()
converts修改 the properties in the received object and make them reactive. For each converted property, it gets assigned aDep
instance which keeps track of a list of subscribing update functions, and triggers them to re-run when its setter is invoked.autorun()
takes an update function and re-runs it when properties that the update function subscribes to have been mutated修改. An update function is said to be “subscribing” to a property if it relies on that property during its evaluation.
<!-- practice 3: miniwacther -->
<!--
in this practice, you need to connect practice1 and practice2, when
a object execute setter or getter, autorun should register the update
through dep.depend,then execute dep.notify to achive the update.
-->
<script>
function isObject (obj) {
return typeof obj === 'object'
&& !Array.isArray(obj)
&& obj !== null
&& obj !== undefined
}
function observe (obj) {
if (!isObject(obj)) {
throw new TypeError()
}
Object.keys(obj).forEach(key => {
let internalValue = obj[key]
// 每个对象的每个属性都新建了一个dep类来保存它的依赖
let dep = new Dep()
Object.defineProperty(obj, key, {
get () {
// 收集依赖
dep.depend()
return internalValue
},
set (newValue) {
const isChanged = internalValue !== newValue
if (isChanged) {
internalValue = newValue
// 修改依赖
dep.notify()
}
}
})
})
}
window.Dep = class Dep {
constructor () {
this.subscribers = new Set()
}
depend () {
if (activeUpdate) {
// register the current active update as a subscriber
this.subscribers.add(activeUpdate)
}
}
notify () {
// run all subscriber functions
this.subscribers.forEach(subscriber => subscriber())
}
}
let activeUpdate
function autorun (update) {
function wrappedUpdate () {
activeUpdate = wrappedUpdate
update()
activeUpdate = null
}
wrappedUpdate()
}
</script>
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 邹阳 の 博客!