思路:
1.创建模拟场景
<div id="app">
<input type="text" id="input" v-model="value">
{{ value }}
</div>
var vm = new Vue({
el: 'app',
data() {
return {
value: 'hello, world'
}
}
})
2.初始化vue函数
function Vue(options) {
const { el, data } = options;
this.el = document.getElementById(el)
this.data = data()
}
3.生成虚拟dom
function Vue(options) {
const { el, data } = options;
this.el = document.getElementById(el)
this.data = data()
var dom = nodeToFragment(this.el, this)
this.el.appendChild(dom)
}
function nodeToFragment(node, vm) {
var flag = document.createDocumentFragment()
var child;
while (child = node.firstChild) {
//
}
return flag
}
4.数据劫持
function observer(obj, vm) {
Object.keys(obj).forEach(key => {
defineReactive(vm, key, obj[key])
})
}
function defineReactive(vm, key, value) {
Object.defineProperty(vm, key, {
get() {
return value
},
set(newValue) {
value = newValue
}
})
}
5.页面初始化
function compare(node, vm) {
var reg = /\{\{(.*)\}\}/
if (node.nodeType === 1) {
var attrs = node.attributes;
for (let i = 0; i < attrs.length; i ++) {
if (attrs[i].nodeName === 'v-model') {
var name = attrs[i].nodeValue;
node.addEventListener('input', function(e) {
vm[name] = e.target.value;
}, false)
node.removeAttribute('v-model')
node.value = vm[name]
}
}
}
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
let name = RegExp.$1;
name = name.trim()
node.nodeValue = vm[name]
}
}
}
6.订阅-----观察者模式
function Watcher(vm, node, name, nodeType) {
Dep.target = this
this.node = node
this.name = name
this.nodeType = nodeType
this.vm = vm
this.update()
Dep.target = null
}
Watcher.prototype = {
update: function () {
this.get()
if (this.nodeType === 'input') {
this.node.value = this.value
}
if (this.nodeType === 'text') {
this.node.nodeValue = this.value
}
},
get: function () {
this.value = this.vm[this.name]
}
}
function Dep() {
this.subs = []
}
Dep.prototype = {
addSub: function (sub) {
this.subs.push(sub)
},
notify: function () {
this.subs.forEach(sub => {
sub.update()
})
}
}
7.代码汇总
function Vue(options) {
const { el, data } = options;
this.el = document.getElementById(el)
this.data = data()
observer(this.data, this)
var dom = nodeToFragment(this.el, this)
this.el.appendChild(dom)
}
function observer(obj, vm) {
Object.keys(obj).forEach(key => {
defineReactive(vm, key, obj[key])
})
}
// 数据劫持
function defineReactive(vm, key, value) {
var dep = new Dep()
Object.defineProperty(vm, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target)
}
return value
},
set(newValue) {
value = newValue
dep.notify()
}
})
}
function nodeToFragment(node, vm) {
var flag = document.createDocumentFragment()
var child;
while (child = node.firstChild) {
compare(child, vm)
flag.appendChild(child)
}
return flag
}
function compare(node, vm) {
var reg = /\{\{(.*)\}\}/
if (node.nodeType === 1) {
var attrs = node.attributes;
for (let i = 0; i < attrs.length; i++) {
if (attrs[i].nodeName === 'v-model') {
var name = attrs[i].nodeValue;
node.addEventListener('input', function (e) {
vm[name] = e.target.value;
}, false)
node.value = vm[name]
node.removeAttribute('v-model')
new Watcher(vm, node, name, 'input')
}
}
}
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
let name = RegExp.$1;
name = name.trim()
node.nodeValue = vm[name]
new Watcher(vm, node, name, 'text')
}
}
}
function Watcher(vm, node, name, nodeType) {
Dep.target = this
this.node = node
this.name = name
this.nodeType = nodeType
this.vm = vm
this.update()
Dep.target = null
}
Watcher.prototype = {
update: function () {
this.get()
if (this.nodeType === 'input') {
this.node.value = this.value
}
if (this.nodeType === 'text') {
this.node.nodeValue = this.value
}
},
get: function () {
this.value = this.vm[this.name]
}
}
function Dep() {
this.subs = []
}
Dep.prototype = {
addSub: function (sub) {
this.subs.push(sub)
},
notify: function () {
this.subs.forEach(sub => {
sub.update()
})
}
}
var vm = new Vue({
el: 'app',
data() {
return {
value: 'hello, world'
}
}
})