<div id="app">{{ message }} - {{ arr }}</div><script>var vm = new Vue({data: {message: 'Hello Vue!',arr: [[0]] } }) vm.$mount('#app')setTimeout(() => { vm.message = 'vue updater'vm.arr.push(100) }, 2000)</script>复制代码
/** * @description 建立标签vnode */export function createElement(vm, tag, data = {}, ...children) {return vnode(vm, tag, data, data.key, children, undefined); }/** * @description 建立文本的vnode */export function createTextElement(vm, text) {return vnode(vm, undefined, undefined, undefined, undefined, text); }/** 核心方法 *//** * @description 套装vnode */function vnode(vm, tag, data, key, children, text) {return { vm, tag, data, key, children, text,// .....} }复制代码
export function renderMixin(Vue){ Vue.prototype._c = function() {return createElement(this,...arguments) } Vue.prototype._v = function(text) {return createTextElement(this,text) } Vue.prototype._s = function(val) {if(typeof val == 'object') return JSON.stringify(val)return val; } Vue.prototype._render = function(){ const vm = this let render = vm.$options.render let vnode = render.call(vm) return vnode } }复制代码
function Vue (options) {/** 初始化 */this._init(options) }/** * @description 扩展原型 */initMixin(Vue) renderMixin(Vue)复制代码
export function initMixin (Vue) { Vue.prototype._init = function (options) { vm.$options = options/** 数据初始化 */initState(vm)/** compile */if(vm.$options.el){ vm.$mount(vm.$options.el); } } Vue.prototype.$mount = function (el) {const vm = this;const options = vm.$options el = document.querySelector(el); vm.$el = el;if(!options.render) {let template = options.template;if(!template && el) { template = el.outerHTML; }// 生成的render函数let render = compileToFunction(template); options.render = render; }/** 组件挂载 */mountComponent(vm,el) } }复制代码
import { patch } from './vdom/patch'import Watcher from './observer/watcher'/** 生命周期 */export function lifecycleMixin(Vue) { Vue.prototype._update = function(vnode) {const vm = this// patch是将老的vnode和新的vnode作比对 而后生成真实的domvm.$el = patch(vm.$el, vnode) } }export function mountComponent(vm, el) {// 更新函数 数据变化后 会再次调用此函数let updateComponent = () => { vm._update(vm._render()) }new Watcher(vm, updateComponent, () => {console.log('视图更新了') }, true) }复制代码
/** patch 文件 这里只是简单处理 直接生成真实的dom */export function patch(oldVnode, vnode) {if (oldVnode.nodeType == 1) {const parentElm = oldVnode.parentNodelet elm = createElm(vnode)// 在第一次渲染后 删除掉节点parentElm.insertBefore(elm, oldVnode.nextSibling) parentElm.removeChild(oldVnode)return elm } }function createElm(vnode) {let { tag, data, children, text, vm } = vnodeif (typeof tag === 'string') { vnode.el = document.createElement(tag) children.forEach(child => { vnode.el.appendChild(createElm(child)) }) } else { vnode.el = document.createTextNode(text) }return vnode.el }复制代码
Watcher 与 Dep 关联起来 是靠当组件渲染时 会走数据 数据已劫持 在getter中 每一个属性 都有Dep 在Dep上加类属性 Dep.target 在渲染时将Dep.target指向这个渲染Watcher(神来之笔)javascript
import { popTarget, pushTarget } from './dep'import { queueWatcher } from './scheduler'let id = 0class Watcher {constructor(vm,exprOrFn,cb,options){this.vm = vmthis.exprOrFn = exprOrFnthis.cb = cbthis.options = optionsthis.id = id++// 视图更新 就是上面的updateComponent方法this.getter = exprOrFn this.deps = [] this.depsId = new Set()this.get() }get(){// 将渲染wather指向给dep 在数据getter时 依赖起来 将dep与watcher关联起来(神来之笔)pushTarget(this)this.getter()// 页面渲染后将 Dep.target = nullpopTarget() }update(){ // 异步更新操做 就是将更新缓存起来(作一些去重, 防抖)而后一块儿调用,最后仍是调用下方run方法 queueWatcher(this) }run(){this.get() } /** * @description 将watcher 存储dep dep也存储watcher实现双向双向存储, 并作去重处理 * @description 给每一个属性都加了个dep属性,用于存储这个渲染watcher (同一个watcher会对应多个dep) * @description 每一个属性可能对应多个视图(多个视图确定是多个watcher) 一个属性也要对应多个watcher */addDep(dep){let id = dep.id;if(!this.depsId.has(id)){this.depsId.add(id);this.deps.push(dep); dep.addSub(this) } } }export default Watcher复制代码
/** 每一个劫持的属性 加上惟一的标识 */let id = 0/** * @description 每一个劫持的属性 new Dep */class Dep {constructor() {this.id = id++this.subs = [] }/** dep传给watcher */depend() {if (Dep.target) { Dep.target.addDep(this) } }addSub(watcher) {this.subs.push(watcher) }notify() {this.subs.forEach(watcher => watcher.update()) } } Dep.target = nulllet stack = []export function pushTarget(watcher) { Dep.target = watcher stack.push(watcher) }export function popTarget() { stack.pop() Dep.target = stack[stack.length - 1] }export default Dep复制代码
- 是普通的对象, 直接dep.append()
- 是数组或者对象,再起上单独增长一个Dep
- 多层数组要进行递归操做
import { isObject } from '../utils'import { arrayMethods } from './array'import Dep from './dep'class Observer {constructor(data) {// 看这里 数据 加了个Depthis.dep = new Dep()Object.defineProperty(data, '__ob__', {value: this,enumerable: false}) /** 数据是数组 */if (Array.isArray(data)) {// 针对数组中使用的方法 如push splice... 修改原数组增长的元素(是对象)进行劫持data.__proto__ = arrayMethods// 初始化 劫持数组中的每一个元素 若是是对象进行劫持this.observeArray(data)return}/** 数据是对象 */this.walk(data) }walk(data) {Object.keys(data).forEach(key => { defineReactive(data, key, data[key]) }) }observeArray(data) { data.forEach(item => observe(item)) } }/** * @description 看这里 劫持数据只劫持对象 不劫持数组 经过current.__ob__.dep依赖watcehr * @description 多层数组 依赖收集 watcher */function dependArray(value) {for (let i = 0; i < value.length; i++) {let current = value[i] current.__ob__ && current.__ob__.dep.depend()if (Array.isArray(current)) { dependArray(current) } } }/** 核心方法 *//** * @description 劫持对象数据 */function defineReactive(data, key, value) {let childOb = observe(value)let dep = new Dep()Object.defineProperty(data, key, {get() { if (Dep.target) { dep.depend()// 看这里 数组进行依赖收集watcherif (childOb) { childOb.dep.depend()// 看这里 多层数组[[[]]] if (Array.isArray(value)) { dependArray(value) } } }return value },set(newValue) {if (newValue !== value) { observe(newValue) value = newValue dep.notify() } } }) }export function observe(data) {if (!isObject(data)) returnif (data.__ob__) return data.__ob__return new Observer(data) }复制代码
const oldArrayPrototype = Array.prototypeexport let arrayMethods = Object.create(oldArrayPrototype)/** * @description 改变原数组的方法 */const methods = ['push','pop','unshift','shift','reverse','sort','splice'] methods.forEach(method => { arrayMethods[method] = function (...args) { oldArrayPrototype[method].call(this, ...args) let ob = this.__ob__let insertedswitch (method) {case 'push':case 'unshift': inserted = argsbreak;case 'splice': inserted = args.slice(2)break;default:break; }if (inserted) ob.observeArray(inserted)// 看这里ob.dep.notify() } })复制代码