我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战
vue是一个mvvm的框架, 由model,view,viewmodel组成,三要素是数据响应式,模板引擎及其渲染。
1.先来一个小例子
// 数据的响应式的实现
function defineReactive(obj, key ,val){
// 递归
const dep = new Dep()
observe(val)
Object.defineProperty(obj ,key, {
get(){
dep.addDep()
console.log('get',val)
return val
},
set(newVal){
if(newVal != val){
val = newVal
watchers.forEach(w=> w.update())
}
}
})
}
2.遍历传入obj的所有属性,执行响应式处理
function observe(obj){
//首先判断obj是对象
if(typeof obj != 'object' || obj == null){
return obj
}
Object.keys(obj).forEach(key => defineReactive(obj, key , obj[key]))
}
3.新属性的响应式处理
function set(obj ,key ,val){
defineReactive(obj ,key val)
}
4.Vue类
function proxy(vm){
Object.keys(vm).forEach(key => {
Object.defineProperty(vm ,key, {
get(){
Dep.target && dep.addDep(Dep.target)
return vm.$data[key]
},
set(v){
vm.$data[key] = val
dep.notify()
}
})
})
}
class MVue{
constructor(options){
this.$options = options
this.$data = options.data
// 1、响应式:对data进行数据响应式处理
observe(this.$data)
// 1.1 将vue实例做个代理
proxy(this)
// 2、编译
new Compile(options.el,this)
}
}
5.遍历模板树,解析其中动态变量部分,初始化获得渲染函数
class Compile{
constructor(){
//获取宿主元素dom
const el = document.querySelector(el)
// 编译它
this.compile(el)
}
compile(el){
const childNodes = el.childNodes;
childNodes.forEach(node => {
if(this.ieElement(node)){
//元素 解析动态指令 属性 事件
console.log('编译元素' + node.nodeName)
const attrs = node.attributes
Array.from(attrs).forEach(attr => {
// 判断是否是一个动态属性
const attrName = attr.name
const exp = attr.value
// v-text \ v-html \ v-xx
if(this.isDir(attrName)){
const dir = attrName.substring(2)
this[dir] && this[dir](node , exp)
}
})
if(node.childNodes.length){
this.compile(node)
}
}else if(this.isInter(node)){
//插值
this.compileText(node)
}
})
}
isDir(attrName){
return attrName.startsWith('v-')
}
// 处理所有绑定 dir就是指令名称
update(node, exp, dir){
// 初始化
const fn = this[dir + 'updater']
fn && fn(node, this.$vm[exp])
// 创建watcher实例
new Watcher(this.$vm, exp, (val)=> {
fn && fn(node, val)
})
}
// v-text
text(node , exp){
this.update(node, exp , 'text')
// node.textContent = this.$vm[exp]
}
textUpdater(node , val){
node.textContent = val
}
// v-html
html(node , exp){
this.update(node , exp, 'html')
//node.innerHTML = this.$vm[exp]
}
htmlUpdater(node,val){
node.innerHTML = val
}
//解析{{}}
compileText(){
this.update(node, RegExp.$1, 'text')
// node.textContent = this.$vm[RegExp.$1]
}
// 是否是元素
isElement(node){
return node.nodeType == 1
}
// 是否是插值
isInter(node){
return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent)
}
}
this.watchers = []
// 负责具体节点更新
class Watcher{
constructor(vm ,key ,updater){
this.vm = vm;
this.key = key
this.updater = updater
// 读当前值, 触发依赖收集
Dep.target = this
this.vm[this.key]
Dep.target = null
}
update(){
const val = this.vm[this.key]
this.updater.call(this.vm , val)
}
}
// Dep和响应式属性key之间有一一对应关系
// 负责通知watcher更新
class Dep {
constructor(){
this.deps = []
}
addDep (watcher){
this.deps.push(watcher)
}
notify(){
this.deps.forEach(w => w.update())
}
}