设计理念 数据驱动 依赖收集
1、data Bindings 数据绑定响应式
2、Dom listener
3、编译:模板引擎和渲染函数
数据响应式:监听
vue2:Object.defineProperty()
vue3:Proxy
模板引擎:描述视图、模板语法、转换为函数
插值{{}}
指令 v-bind, v-on,v-model, v-for, v-if
渲染 将模板转换为html
模板 vdom=>dom
编译模板中vue模板特殊语法,初始化视图、更新视图
html版本的
<div id='app'>
<p>{{counter}}</p>
</div>
<script scr="node_modules/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
//model
data:{
counter:1
}
})
setInterval(()=>{
app.counter++
},1000)
</script>
[myVue.js]
class myVue{
constructor(options){
this.$options = options
this.$data = options.data
//1 对data遍历 做响应式处理
observe(this.$data)
//对data做代理 可以直接使用data中的key
proxy(this)
//编译模板
new Compile(options.el,this)
}
}
//Vue.util.defineReactive(this,'current','/')
function defineReactive(obj,key,val){
//递归
observe(val)
//data中的key 与dep一一对应
const dep = new Dep()
//属性拦截
Object.defineProperty(obj,key,{
get(){
//依赖收集建立
Dep.target && dep.addDep(Dep.target)
return val
},
set(newVal){
if(newVal !== val){
observe(newVal)
val = newVal
// 通知更新
dep.notify()
}
}
})
}
//遍历传入的obj属性,执行响应式处理
function observe(obj){
Object.keys(obj).forEach(key=>{
if(typeof obj !=='object' || obj ==null){
return obj
}
defineReactive(obj,key,obj[key])
})
}
//将data代理一下
function proxy(vm){
Object.keys(vm.$data).forEach(key=>{
// data的key直接放到vm上
Object.defindePropery(vm,key,{
get(){
return vm.$data[key]
}
set(v){
vm.$data[key] =v
}
})
})
}
// 1.全量更新 2.利用vdom,对比得到dom操作
function update(){
}
//动态新增属性
function set(obj,key,val){
defineReactive(obj,key,val)
}
数组会复杂 需要拦截数组的7个方法 push pop shift unshift。。。
遍历模板,解析动态部分,初始化
[compile]
class compile{
constructor(el,vm){
this.$vm = vm
//获取dom节点
const dom = document.querySelector(el)
this.complie(dom)
}
compile(el){
//遍历
const childNodes = el.childNodes
childNodes.forEach(node=>{
if(this.isElement(node)){
//元素 解析指令、属性绑定、事件等
const attrs = node.attributes
Arry.form(attrs).forEach(attr =>{
//判断是否是动态的属性 指令 v-xx='counter'
const attrName = attr.name
const exp = attr.value
if(this.isDir(attrName)){
const dir = attrName.substring(2)
//指令是否存在,并执行
this.[dir] && this[dir](node,exp)
}
})
//递归
if(node.childNodes.length>0){
this.complie(node)
}
}else if(this.isInter(node)){
//插值表达式
this.compileText(node)
}
})
}
// 重构 抽象 (节点 ,表达式, 指令名称)
update(node,exp,dir){
//初始化
const fn = this[dir + 'Updater']
fn && fn(node,this.$vm[exp])
//创建watcher实例,负责后续更新
new Water(this.$vm,exp,function(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内容
html(node,exp){
//node.innerHTML = this.$vm[exp]
this.update(node,exp,'html')
}
htmlUpdater(node,val){
node.innerHTML = val
}
compileText(node){
//获取正则的动态部分并赋值
// node.textContent = this.$vm[RegExp.$1]
this.update(node,RegExp.$1,'text')
}
isElement(node){
return node.nodeType ===1
}
// {{name}}
isInter(node){
return node.nodeType ===3 && /\{\{(.*)\}\}/.test(node.textContent)
}
isDir(attrName){
return attrName.startsWith('v-')
}
}
负责节点的更新
class Watcher{
constructor(vm,key,updater){
this.vm = vm
this.key = key
this.updater = updater
//读取当前值,触发依赖收集 静态存watcher
Dep.target = this
//触发get
this.vm[this.key]
Dep.target = null
}
//dep会调用
update(){
//对最新值更新
this.updater.call(this.vm,this.vm[this.key])
}
}
Dep和属性key有一一个对应关系,负责通知watcher更新
class Dep{
constructor(){
this.deps = []
}
addDep(){
this.deps.push(dep)
}
notify(){
this.deps.forEach(dep=>dep.update())
}
}
相当做了一个简易的vue1版本,不含虚拟dom