整体结构分析
vue类实现
需要实现的功能
- 负责接受初始化参数options
- 负责把data中的属性输入到vue实例,并且转换成getter/setter
- 负责调用observer监听data所有属性的变化
- 调用compiler 解析指定/差值表达式
/* 结构
名称 Vue
属性 $option $data $el
方法 _proxyDada
*/
class Vue {
constructor (options) {
// 1 保存对应的属性数据
this.$options = options || {}
this.$data = options.data
this.$el = typeof options.el === 'string'?document.querySelector(options.el) || options.el
// 2 把data中的属性都转换成getter和setter,并注入vue实例中
this._proxyData(this.$data)
// 3 通过observer对象监听数据变化
new Oberver(this.$data)
// 4 通过compiler对象解析对应的指令和差值表达式
new Compiler(this)
}
_proxyData (data) {
// 遍历data所有属性
Object.keys(data).forEach(key=>{
// 把data的属性注入到vue实例上
Object.defineProperty(this,key,{
configurable:true,
enumerable:true,
get () {
return data.key
},
set (newValue) {
if(newValue === data.key){
return
}
data.key = newValue
}
})
})
}
}
Observer类实现
需要实现的功能
- 负责把data选项的属性转换成响应式数据
- data中的某个属性也是对象,把该属性也要转化成响应式数据
- 数据变化发送通知
/*
结构
observe
walk(data) //把data中的属性转化成getter和setter
defineReactive(data,key,value)
*/
class Oberver {
constructor (data) {
this.walk(data)
}
walk (data) {
// 1. 判断data是否是对象且不为空 如果为空什么都不做
if(!data || typeof data !== 'object') {
return
}
// 2. 遍历data对象中的属性
Object.keys(data).forEach(key=>{
//提问 这个地方为什么要传第三值val
this.defineReactive(data,key,data[key])
})
}
defineReactive (obj,key,val) {
// 如果val里面是一个对象,同时把对象的属性转成响应式数据
this.walk(val)
//为每个属性 创建dep 对象
const dep = new Dep()
const self = this
Object.defineProperty(obj,key,{
configurable:true,
enumerable:true,
get () {
Dep.target && dep.addSub(Dep.target)
return val
},
set (newValue) {
if(newValue === val){
return
}
val = newValue
// 当重新复制的值是对象的时候需要把它转成响应式的数据
self.walk(newValue)
//数据发生变化发送通知
dep.notify()
}
})
}
}
compiler类的实现
功能
- 负责编译模板,解析指令、差值表达式
- 负责页面的首次渲染
- 当数据发生改变后重新渲染视图
/*
结构
名称 compiler
属性 el vm
方法 compile(el)
compileElement(node)
compileText(node)
isDirective(attrName)
isTextNode(node)
isElementNode(node)
*/
class Compiler {
constructor (vm) {
this.vm = vm
this.$el = vm.$el
this.compile(this.$el)
}
// 编译模板 处理文本节点和元素节点
compile (el) {
// 遍历元素下的所有节点
const childNodes = el.childNodes
Array.from(childNodes).forEach(node=>{
if(this.isTextNode(node)){
// 处理文本节点
this.compileText(node)
}else if (this.isElementNode(node)) {
// 处理元素节点
this.compileElement(node)
}
// 当元素还有子节点的时候需要我们递归调用compile 方法处理元素下面的字节点
if(node.childNodes && node.childNodes.length){
this.compile(node)
}
})
}
// 编译元素节点 处理指令
compileElement (node) {
// 遍历所有属性
Array.from(node.attributes).forEach(attr=>{
let attrName = attr.name
//判断属性书否是指令
if(this.isDirective(attrName)){
const key = attr.value
// 处理指令
this.update(node,key,attrName)
}
})
}
// 处理指令
update (node,key,attrName) {
const updateFn = this[attrName + 'Updater']
updateFn && updateFn.call(this,node,this.vm[key],key)
}
// 处理v-text指令
textUpdater (node,value,key) {
node.textContent = value
//创建watcher对象 当数据改变更新视图
new Watcher(this.vm,key,(newValue)=>{
node.textContent = newValue
})
}
// 处理 v-model 指令
modleUpdater (node,value,key) {
node.value = value
//创建watcher对象 当数据改变更新视图
new Watcher(this.vm,key,(newValue)=>{
node.value = newValue
})
//实现双向绑定
node.addEventlistener('input',()=>{
this.vm[key] = node.value
})
}
// 编译文本节点 处理差值表达式
compileText (node) {
const reg = /\{\{(.+?) \}\}/
const value = node.textContent
if(reg.test(value)) {
const key = RegExp.$1.trim()
node.textContent = value.replace(reg,this.vm[key])
//创建watcher对象 当数据改变更新视图
new Watcher (this.vm,key,(newValue)=>{
node.textContent = newValue
})
}
}
// 判断属性是否是指令
isDirective (attrName) {
return attrName.startsWith('v-')
}
// 判断节点是否是文本节点
isTextNode (node) {
return node.nodeType === 3
}
// 判断节点是都是元素节点
isEelmentNode (node) {
return node.nodeType === 1
}
}
Dep (dependency)类的实现
功能
- 收集依赖,添加观察者(watcher)
- 通知所有观察者
/*
结构
名称 Dep
属性 subs //存储观察者
方法 addSub(sub) //添加观察者
notify() // 发布通知
*/
class Dep {
constructor () {
// 存放所有观察者
this.subs = []
}
//添加观察者
addSub (sub) {
if(sub && sub.update) {
this.subs.push(sub)
}
}
// 发送通知
notify () {
this.subs.forEach(sub=>{
sub.update()
})
}
}
watcher类的实现
功能
- 当数据变化触发依赖,dep通知所有的watcher实例更新视图
- 自身实例的时候往dep对象中添加自己
/*
结构
名称 watcher
属性 vm key cb oldValue
方法 update()
*/
class Watcher () {
constructor (vm,key,cb) {
this.vm = vm
this.key = key
// 当数据改变的时候更新视图
this.cb = cb
// 把watcher对象添加到Dep.target实例上
Dep.target = this
// 触发get方法,在get中调用addSub 把watcher实例添加到subs中
this.oldValue = vm[key]
// 防止重复添加watcher实例
Dep.target = null
}
// 数据发生改变更新视图
update () {
const newValue = this.vm[this.key]
if(newValue === this.oldValue) {
return
}
//更改视图
this.cb(newValue)
}
}