export function Vue(options = {}){
this.__init(options)
}
Vue.prototype.__init = function(options) {
this.$el = options.el
this.$options = options
this.$data = options.data
this.$methods = options.methods
// this.$data.message -> this.message, 将this.$data 代理到this上;
proxy(this, this.$data)
// observer 设置data属性响应式;配合Dep收集依赖和触发依赖;
observer(this.$data)
// complile
new Compiler(this)
}
function proxy(target, data) {
Object.keys(data).forEach(key => {
Object.defineProperty(target, key, {
configurable:true,
enumerable:true,
get(){
return data[key]
},
set(newVal){
if(newVal !== null && newVal !== data[key]){
data[key] = newVal
}
}
})
})
}
function observer(data) {
new Observer(data)
}
// 递归遍历属性设置响应式;配合Dep收集依赖和触发依赖;
class Observer {
constructor(data){
this.walk(data)
}
walk(data){
if(typeof data !== "object"){
return data
}
if(data && typeof data === "object") {
Object.keys(data).forEach(key => {
this.defineReactives(data, key, data[key])
})
}
}
defineReactives(target, key, value){
let that = this
// 递归遍历子属性
this.walk(value)
let dep = new Dep()
Object.defineProperty(target, key, {
enumerable:true, // ke bianli
configurable:true, // ke set
get(){
// collect dependence
if(Dep.target){
dep.add(Dep.target)
}
return value
},
set(newVal){
//
if(newVal !== null && newVal !== value){
value = newVal
}
// 递归遍历子属性
that.walk(value)
// 触发依赖;
dep.notify()
}
})
}
}
// 依赖 类;
class Dep {
constructor(){
this.watchers = new Set()
}
add(watcher){
watcher && watcher.update && this.watchers.add(watcher)
}
notify(){
this.watchers.forEach(watch => watch.update())
}
}
// 观察者 类;
class Watcher {
constructor(vm, key, cb){
this.vm = vm
this.key = key
this.cb = cb
Dep.target = this
this.__old = vm[key]
Dep.target = null
}
update(){
let newVal = this.vm[this.key]
if(newVal !== this.__old){
this.cb(newVal)
}
}
}
// 模板编译;<template> -> render() -> vdom
class Compiler {
constructor(vm){
this.vm = vm
this.el = vm.$el
this.methods = vm.$methods
this.compile(vm.$el)
}
compile(el){
let childNodes = el.childNodes
Array.from(childNodes).forEach(node => {
if(node.nodeType == 3){
this.compileText(node)
} else if(node.nodeType == 1){
this.compileElement(node)
}
if(node.childNodes && node.childNodes.length){
this.compile(node)
}
})
}
compileText(node){
// 匹配插值表达式 {{ }}
let reg = /\{\{(.+?)\}\}/
let textContent = node.textContent
if(reg.test(textContent)){
let key = RegExp.$1.trim()
node.textContent = textContent.replace(reg, this.vm[key])
new Watcher(this.vm, key, val => {
// reflesh page
node.textContent = val
})
}
}
compileElement(node){
if(node.attributes.length){
Array.from(node.attributes).forEach(attr => {
// v-on:click v-model
let attrName = attr.name
if(attrName.startsWith("v-")){
attrName = attrName.indexOf(":") > -1 ? attrName.substr(5) : attrName.substr(2)
}
let key = attr.value
this.update(node, attrName, key, this.vm[key])
})
}
}
update(node, attrName, key, value){
if(attrName == "model"){
node.value = value
node.addEventListener("input", () => {
this.vm[key] = node.value
})
new Watcher(this.vm, key, val => {
node.value = val
})
} else if(attrName == "click"){
node.addEventListener("click", this.methods[key].bind(this.vm))
}
}
}