Mvvm.js
function Mvvm(options = {}) {
this.options = options
let _data = this._data = this.options.data
observe(_data)
//this本身代理this._data
for (let key in _data) {
Object.defineProperty(this, key, {
enumerable: true,
get() {
return this._data[key]
},
set(newVal) {
this._data[key] = newVal
}
})
}
initComputed.call(this)//实现computed
new Compile(options.el, this)
}
function initComputed() {
let vm = this
let computed = this.options.computed
Object.keys(computed).forEach(key => {//给对象挂载塑像,即映射
Object.defineProperty(vm, key, {
get: typeof computed[key] === 'function' ? computed[key] : computed[key].get,
// get:function(){
// return 11+2
// },
set() {
}
})
})
}
//把原先data的属性重新写,使其可以数据劫持
function Observe(_data) {
let dep = new Dep()
for (let key in _data) {
let val = _data[key]
observe(val);
Object.defineProperty(_data, key, {
enumerable: true,
configurable: true,
get() {
//存在就添加
if (Dep.target) {
dep.addSub(Dep.target)
}
return val
},
set(newVal) {
if (newVal === val) {
return
}
val = newVal
observe(val)//如果设置的值是对象,则需要继续劫持
// console.log("aaa",dep.subs[0])
dep.notify()//执行里面的方法
}
})
}
}
//调用数据而已
function observe(_data) {
if (typeof _data != "object") return
return new Observe(_data)
}
//编译模板
function Compile(el, vm) {
var child = null;
vm.$el = document.querySelector(el)
var fragment = document.createDocumentFragment()
while (child = vm.$el.firstChild) {//把节点加载到内存中
fragment.appendChild(child)
}
replace(fragment)
//替换大括号的数据
function replace(fragment) {
//先把为数组变成数组
Array.from(fragment.childNodes).forEach((node) => {
let text = node.textContent
let reg = /\{\{(.*)\}\}/
if (node.nodeType == 3 && reg.test(text)) {
let arr = RegExp.$1.split('.')
let val = vm;
arr.forEach((key) => { //把this.a的值获取出来
val = val[key]
})
new Watch(vm, RegExp.$1, (newVal) => { //RegExp.$1这个值不能传错
// console.log(node,newVal)
node.textContent = text.replace(reg, newVal)
})
//替换更新
node.textContent = text.replace(reg, val)
}
//input双向数据绑定实现
if (node.nodeType == 1) {
let nodeAttrs = node.attributes
Array.from(nodeAttrs).forEach(item => {
let exp = item.value
let name = item.name
if (name.indexOf('v-model') == 0) { //v-model
node.value = vm[exp]
}
//订阅一下
new Watch(vm, exp, (newVal) => {
node.value = newVal
})
//触发
node.addEventListener('input', function (e) {
let newVal = e.target.value
vm[exp] = newVal
})
})
}
if (node.childNodes) {
replace(node)
}
})
}
vm.$el.appendChild(fragment)
}
//发布订阅
function Dep() {
this.subs = []
}
//订阅
Dep.prototype.addSub = function (sub) {
this.subs.push(sub)
}
//执行
Dep.prototype.notify = function () {
this.subs.forEach((item) => {
item.update()
})
}
//watch
function Watch(vm, reg, fn) {
this.vm = vm
this.reg = reg
this.fn = fn
Dep.target = this
let val = vm
let arr = reg.split(".")
arr.forEach((k) => {
val = val[k] //这个会执行get方法,先执行
})
Dep.target = null
}
Watch.prototype.update = function () {
let val = this.vm
let arr = this.reg.split(".")
arr.forEach((k) => {
val = val[k] //这个会执行get方法,先执行
})
this.fn(val)
}
index.html
<!DOCTYPE html>
<html>
<head>
<script src="mvvm.js"></script>
</head>
<body>
<div id="app">
<p>a的值:{{a}}</p>
<p>b的值:{{b.b}}</p>
<input type="text" v-model="a">
<p>{{sum}}</p>
</div>
</body>
<script>
var vm = new Mvvm({
el: "#app",
data: {
a: 9527,
b:{b:"hahah"}
},
computed:{
sum(){
return this.a+this.b.b
}
}
})
</script>
</html>