class Vue { constructor(options) { this.$options = options const vm = this if (this.$options.data) { this.initData(vm) } if (this.$options.el) { compile(this.$options.el, vm) } } initData(vm) { let data = vm.$options.data data = typeof data === 'function' ? data.call(vm) : data vm._data = data observe(data) for (let key in data) { proxy(vm, key, data[key]) } } }
function proxy(target, key, value) { Object.defineProperty(target, key, { get() { return target['_data'][key] }, set(newValue) { target['_data'][key] = newValue } }) }
function observe(data) { if (data === null || typeof data !== 'object') { return } return new Observer(data) }
class Observer { constructor(data) { this.walk(data) } walk(data) { Object.keys(data).forEach(key => defineReactive(data, key, data[key])) } }
function defineReactive(target, key, value) { observe(value) let dep = new Dep() Object.defineProperty(target, key, { get() { Dep.target && dep.addSub(Dep.target) return value }, set(newValue) { value = newValue observe(newValue) dep.notify() } }) }
class Dep { constructor() { this.subs = [] } addSub(watcher) { this.subs.push(watcher) } notify() { this.subs.forEach(watcher => watcher.update()) } }
class Watcher { constructor(vm, key, callback) { this.vm = vm this.key = key this.callback = callback Dep.target = this key.split('.').reduce((total, current) => total[current], vm._data) Dep.target = null } update() { const value = this.key.split('.').reduce((total, current) => total[current], this.vm._data) this.callback(value) } }
function compile(el, vm) { vm.$el = el = document.querySelector(el)
const fragment = document.createDocumentFragment() let child while (child = el.firstChild) { fragment.append(child) }
fragment_compile(fragment)
function fragment_compile(node) { const parttern = /\{\{\s*(\S+)\s*\}\}/ if (node.nodeType === 3) { const match = parttern.exec(node.nodeValue) if (match) { const needChangeValue = node.nodeValue let arr = match[1].split('.') let value = arr.reduce( (total, current) => total[current], vm._data ) node.nodeValue = needChangeValue.replace(parttern, value) const updateFn = value => { node.nodeValue = needChangeValue.replace(parttern, value) } new Watcher(vm, match[1], updateFn) } return } if (node.nodeType === 1 && node.nodeName === 'INPUT') { const attrs = node.attributes let attr = Array.prototype.slice.call(attrs) attr.forEach(item => { if (item.nodeName === 'v-model') { let value = getVmValue(item.nodeValue, vm) node.value = value new Watcher(vm, item.nodeValue, newValue => node.value = newValue) node.addEventListener('input', e => { const name = item.nodeValue const arr1 = name.split('.') const arr2 = arr1.slice(0, arr1.length - 1) const head = arr2.reduce((total, current) => total[current], vm._data) head[arr1[arr1.length - 1]] = e.target.value }) } }) } node.childNodes.forEach(child => fragment_compile(child)) }
vm.$el.appendChild(fragment) }
function getVmValue(key, vm) { return key.split('.').reduce((total, current) => total[current], vm._data) }
function setVmValue(key, vm) { let tem = key.split('.') let fin = tem.reduce((total, current) => total[current], vm._data) return fin }
window.Vue = Vue;
|