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>