簡易Vue
げんりぶんせき
データを巡回することによって、データのために1つのパブリッシャーDepを作成して、そしてデータのgetとsetを修正して、setの中でパブリッシャーDepを利用して、サブスクライバWatcherにデータ更新を行うことを通知します(実際にはWatcherを呼び出す関数です)
コード#コード#
データを巡回することによって、データのために1つのパブリッシャーDepを作成して、そしてデータのgetとsetを修正して、setの中でパブリッシャーDepを利用して、サブスクライバWatcherにデータ更新を行うことを通知します(実際にはWatcherを呼び出す関数です)
コード#コード#
class Vue {
constructor(options) {
if (typeof options !== 'object') {
console.error('options is not a object')
return
}
if (!'el' in options || !'data' in options) {
console.error('options do not contain el and data')
}
this.el = typeof options.el == 'string' ? options.el : 'body'
let tempData = options.data
this.data = {}
//
for(let key in tempData){
let temp = tempData[key]
const dep = new Dep(key) //
Object.defineProperty(this.data, key, {
enumerable: true,
configurable: true,
get: function(){
return temp
},
set: function(val){
if(val != temp){
temp = val
dep.notify() //
}
}
})
}
this.root = null
// this.html = null
this.compile()
}
compile() {
//
this.root = document.getElementById(this.el)
// this.html = this.root.innerHTML //
this.innerText_handle(this.root)
this.attr_handle(this.root)
this.compileChildren(this.root)
}
innerText_handle(e){
//
const inner_text_Reg = /\{\{[a-zA-Z0-9]+\}\}/g
let nodeList = e.childNodes
for(let i = 0; i < nodeList.length; i++){
if(nodeList[i].nodeType == 3){
let node = nodeList[i]
let value = node.nodeValue
if(value != ''){
new Watcher(this, value, e) //
}
let arr = value.match(inner_text_Reg)
if(arr){
arr.forEach(item => {
let name = item.split('{{')[1].split('}}')[0]
if(name in this.data){
node.nodeValue = value.replace(item, this.data[name])
}
})
}
}
}
}
attr_handle(e){
//
let attr = e.attributes
let _this = this
for(let i = 0; i < attr.length; i++){
switch(attr[i].name){
case 'v-text':
new Watcher(this, attr[i].value, e)
if(attr[i].value in this.data){
e.innerText = this.data[attr[i].value]
}
break
case 'v-model':
if(e.nodeName != 'INPUT' && e.nodeName != 'TEXTAREA')
console.error('v-bind is valid')
new Watcher(this, attr[i].value, e)
if(attr[i].value in this.data){
e.value = this.data[attr[i].value]
}
e.addEventListener('input', function(){
_this.data[attr[i].value] = e.value
})
}
}
}
compileChildren(parent){
//
let children = parent.children
for(let i = 0; i < children.length; i++){
this.innerText_handle(children[i])
this.attr_handle(children[i])
if(children[i].children !== 0){
this.compileChildren.call(this, children[i])
}
}
}
}
class Dep{
static depMap = {}
constructor(dataName){
this.dataName = dataName //data
this.subs = []
Dep.depMap[dataName] = this
}
addSub(sub){
//
this.subs.push(sub)
}
notify(){
//
let subs = this.subs.slice()
subs.forEach(item => {
item.update()
})
}
}
class Watcher{
constructor(vm, express, node){
this.vm = vm //vue
this.express = express //
this.node = node // node
//
this.expressArr = []
let str = this.express
let data = this.vm.data
for(let key in data){
if(str.indexOf(key) > -1){
this.expressArr.push(key)
}
}
this.addDep()
}
update(){
//
this.node.innerText = this.get()
}
addDep(){
//
// Dep depMap
let arr = this.expressArr.slice()
arr.forEach(item => {
if(Object.keys(Dep.depMap).indexOf(item) > -1){
Dep.depMap[item].addSub(this)
}
})
}
get(){
//
let data = this.vm.data
let _express = this.express
let _expressArr = this.expressArr
_expressArr.forEach(item => {
_express = _express.replace(item, data[item])
})
_express = _express.replace('{{', '')
_express = _express.replace('}}', '')
return _express
}
}