<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h3>测试</h3>
<h3>{{msg}}</h3>
<input type="text" v-model="msg">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<h3 v-text="msg"></h3>
<h3 v-text="persion.name"></h3>
<h3>123</h3>
<h3>{{persion.name}}</h3>
<h3 v-html="msg"></h3>
<button @click="changeName">改变姓名</button>
</div>
<script src="./index.js"></script>
<script>
let vm = new MVue({
el: '#app',
data: {
persion: {
name: '张三',
age: 18
},
msg: '测试'
},
methods: {
changeName() {
this.$data.persion.name = '李四'
this.$data.msg = "说谎"
}
}
})
</script>
</body>
</html>
class MVue {
constructor(options){
this.$el = options.el;
this.$data = options.data;
this.$options = options;
if(this.$el){
//1.实现一个数据观察者
new Observer(this.$data)
//2.实现一个指令解析器
new Compile(this.$el,this)
//代理
this.proxyData(this.$data)
}
}
proxyData(data){
for(const key in data){
Object.defineProperties(this,key,{
get(){
return data[key]
},
set(newVal){
data[key] = newVal
}
})
}
}
}
class Compile {
constructor(el,vm){
this.el = this.isElementNode(el)? el:document.querySelector(el);
this.vm = vm
//1.获取文档碎片对象 放入内存中会减少页面的回流和重绘
const fragment = this.node2Fragment(this.el)
//2. 编译模板
this.compile(fragment)
//3.追加子元素到根元素
this.el.appendChild(fragment)
}
compile(fragment){
//1.获取子节点
const childNode = fragment.childNodes;
childNode.forEach(child=>{
if(this.isElementNode(child)){
//元素节点
this.compileElement(child)
}else{
this.compileText(child)
//文本节点
}
if(child.childNodes&&child.childNodes.length){
this.compile(child)
}
})
}
compileElement(node){
const attributes = node.attributes;
if(attributes&&attributes.length){
[...attributes].forEach(attr=>{
const { name, value} = attr;
if(this.isDirective(name)){
const [,dirctive] = name.split('-');
const [dirName,eventName] = dirctive.split(':');
//数据更新视图
compileUtil[dirName](node,value,this.vm)
// 删除标签上的属性
node.removeAttribute('v-'+ dirctive)
}else if(this.isEventName(name)){
let [,eventName] = name.split('@');
compileUtil['on'](node,value,this.vm,eventName)
}
})
}
}
isEventName(attrName){
return attrName.startsWith('@')
}
isDirective(attrName){
return attrName.startsWith('v-')
}
compileText(node){
const content = node.textContent
if(/\{\{(.+?)\}\}/.test(content)){
compileUtil['text'](node,content,this.vm)
}
}
node2Fragment(el){
//创建文档碎片
const f = document.createDocumentFragment()
let firstChild;
while(firstChild = el.firstChild){
f.appendChild(firstChild)
}
return f
}
isElementNode(node){
return node.nodeType===1;
}
}
const compileUtil = {
getVal(expr,vm){
return expr.split('.').reduce((data,currentVal)=>{
return data[currentVal]
},vm.$data)
},
setVal(expr,vm,inputVal){
return expr.split('.').reduce((data,currentVal)=>{
data[currentVal] = inputVal
},vm.$data)
},
getContentVal(expr,vm){
return expr.replace(/\{\{(.+?)\}\}/,(...args)=>{
return this.getVal(args[1],vm)
})
},
text(node,expr,vm){
let value;
if(expr.indexOf('{{')!==-1){
expr.replace(/\{\{(.+?)\}\}/,(...args)=>{
new Watcher(vm,...args[1],(newVal)=>{
this.updater.htmlUpdater(node,this.getContentVal(expr,vm))
})
return this.getVal(args[1],vm)
})
}else{
value = this.getVal(expr,vm)
}
this.updater.textUpdater(node,value)
},
html(node,expr,vm){
const value = this.getVal(expr,vm)
new Watcher(vm,expr,(newVal)=>{
this.updater.htmlUpdater(node,newVal)
})
this.updater.htmlUpdater(node,value)
},
model(node,expr,vm){
const value = this.getVal(expr,vm)
//数据=》视图
new Watcher(vm,expr,(newVal)=>{
this.updater.modelUpdater(node,newVal)
})
// 视图=》数据=》视图
node.addEventListener('input',(e)=>{
this.setVal(expr,vm,e.target.value)
})
this.updater.modelUpdater(node,value)
},
on(node,expr,vm,eventName){
let fn = vm.$options.methods&&vm.$options.methods[expr]
node.addEventListener(eventName,fn.bind(vm),false)
},
updater:{
textUpdater(node,value){
node.textContent = value
},
htmlUpdater(node,value){
node.innerHTML = value
},
modelUpdater(node,value){
node.value = value
}
}
}
class Observer{
constructor(data){
this.observer(data)
};
observer(data){
if(data&&typeof data === 'object'){
Object.keys(data).forEach(key=>{
this.defineReactive(data,key,data[key])
})
}
};
defineReactive(obj,key,value){
const dep = new Dep()
this.observer(value)
Object.defineProperty(obj,key,{
enumerable:true,
configurable:false,
get(){
//订阅数据 往dep中添加观察者
Dep.target && dep.addSub(Dep.target)
return value
},
set:(newVal)=>{
this.observer(value)
if(newVal!=value){
value = newVal
}
dep.notify()
}
})
}
}
class Dep{
constructor(){
this.subs = []
}
//收集观察者
addSub(watcher){
this.subs.push(watcher)
}
//通知观察者去更新
notify(){
console.log(98)
this.subs.forEach(w=>w.update())
}
}
class Watcher{
constructor(vm,expr,cb){
this.vm = vm;
this.expr = expr;
this.cb= cb;
//先把旧值保存下来
this.oldVal = this.getOldValue()
}
//判断是否有变化 更新视图
update(){
const newVal = compileUtil.getVal(this.expr,this.vm)
if(newVal != this.oldVal){
this.cb(newVal)
}
}
getOldValue(){
Dep.target = this
const oldVal = compileUtil.getVal(this.expr,this.vm)
Dep.target = null
return oldVal
}
}