原文地址:Bougie的博客
最终效果如下:
image
构造器(constructor)
构造一个TinyVue对象,包含基本的el,data,methods
class TinyVue{ constructor({el, data, methods}){ this.$data = data this.$el = document.querySelector(el) this.$methods = methods // 初始化
this._compile() this._updater() this._watcher()
}
}编译器(compile)
用于解析绑定到输入框和下拉框的v-model和元素的点击事件@click。
先创建一个函数用来载入事件:
// el为元素tagName,attr为元素属性(v-model,@click)_initEvents(el, attr, callBack) { this.$el.querySelectorAll(el).forEach(i => { if(i.hasAttribute(attr)) { let key = i.getAttribute(attr)
callBack(i, key)
}
})
}载入输入框事件
this._initEvents('input, textarea', 'v-model', (i, key) => {
i.addEventListener('input', () => { Object.assign(this.$data, {[key]: i.value})
})
})载入选择框事件
this._initEvents('select', 'v-model', (i, key) => {
i.addEventListener('change', () => Object.assign(this.$data, {[key]: i.options[i.options.selectedIndex].value}))
})载入点击事件
点击事件对应的是methods中的事件
this._initEvents('*', '@click', (i, key) => {
i.addEventListener('click', () => this.$methods[key].bind(this.$data)())
})视图更新器(updater)
同理先创建公共函数来处理不同元素中的视图,包括input、textarea的value,select的选择值,div的innerHTML
_initView(el, attr, callBack) { this.$el.querySelectorAll(el, attr, callBack).forEach(i => { if(i.hasAttribute(attr)) { let key = i.getAttribute(attr),
data = this.$data[key]
callBack(i, key, data)
}
})
}更新输入框视图
this._initView('input, textarea', 'v-model', (i, key, data) => {
i.value = data
})更新选择框视图
this._initView('select', 'v-model', (i, key, data) => {
i.querySelectorAll('option').forEach(v => { if(v.value == data) v.setAttribute('selected', true) else v.removeAttribute('selected')
})
})更新innerHTML
这里实现方法有点low,仅想到正则替换{{text}}
let regExpInner = /\{{ *([\w_\-]+) *\}}/gthis.$el.querySelectorAll("*").forEach(i => { let replaceList = i.innerHTML.match(regExpInner) || (i.hasAttribute('vueID') && i.getAttribute('vueID').match(regExpInner)) if(replaceList) { if(!i.hasAttribute('vueID')) {
i.setAttribute('vueID', i.innerHTML)
}
i.innerHTML = i.getAttribute('vueID')
replaceList.forEach(v => { let key = v.slice(2, v.length - 2)
i.innerHTML = i.innerHTML.replace(v, this.$data[key])
})
}
})监听器(watcher)
数据变化之后更新视图
_watcher(data = this.$data) { let that = this
Object.keys(data).forEach(i => { let value = data[i] Object.defineProperty(data, i, { enumerable: true, configurable: true, get: function () { return value;
}, set: function (newVal) { if (value !== newVal) {
value = newVal;
that._updater()
}
}
})
})
}使用
<div id="app">
<input type="text" v-model="text1"><br>
<input type="text" v-model="text2"><br>
<textarea type="text" v-model="text3"></textarea><br>
<button @click="add">加一</button>
<h1>您输入的是:{{text1}}+{{text2}}+{{text3}}</h1>
<select v-model="select">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
</select>
<select v-model="select">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
</select>
<h1>您选择了:{{select}}</h1></div><script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="./TinyVue.js"></script><script>
let app = new TinyVue({ el: '#app', data: { text1: 123, text2: 456, text3: '文本框', select: 'saab'
}, methods: {
add() { this.text1 ++ this.text2 ++
}
}
})</script>TinyVue全部代码
class TinyVue{ constructor({el, data, methods}){ this.$data = data this.$el = document.querySelector(el) this.$methods = methods this._compile() this._updater() this._watcher()
}
_watcher(data = this.$data) { let that = this
Object.keys(data).forEach(i => { let value = data[i] Object.defineProperty(data, i, { enumerable: true, configurable: true, get: function () { return value;
}, set: function (newVal) { if (value !== newVal) {
value = newVal;
that._updater()
}
}
})
})
}
_initEvents(el, attr, callBack) { this.$el.querySelectorAll(el).forEach(i => { if(i.hasAttribute(attr)) { let key = i.getAttribute(attr)
callBack(i, key)
}
})
}
_initView(el, attr, callBack) { this.$el.querySelectorAll(el, attr, callBack).forEach(i => { if(i.hasAttribute(attr)) { let key = i.getAttribute(attr),
data = this.$data[key]
callBack(i, key, data)
}
})
}
_updater() { this._initView('input, textarea', 'v-model', (i, key, data) => {
i.value = data
}) this._initView('select', 'v-model', (i, key, data) => {
i.querySelectorAll('option').forEach(v => { if(v.value == data) v.setAttribute('selected', true) else v.removeAttribute('selected')
})
}) let regExpInner = /\{{ *([\w_\-]+) *\}}/g
this.$el.querySelectorAll("*").forEach(i => { let replaceList = i.innerHTML.match(regExpInner) || (i.hasAttribute('vueID') && i.getAttribute('vueID').match(regExpInner)) if(replaceList) { if(!i.hasAttribute('vueID')) {
i.setAttribute('vueID', i.innerHTML)
}
i.innerHTML = i.getAttribute('vueID')
replaceList.forEach(v => { let key = v.slice(2, v.length - 2)
i.innerHTML = i.innerHTML.replace(v, this.$data[key])
})
}
})
}
_compile() { this._initEvents('*', '@click', (i, key) => {
i.addEventListener('click', () => this.$methods[key].bind(this.$data)())
}) this._initEvents('input, textarea', 'v-model', (i, key) => {
i.addEventListener('input', () => { Object.assign(this.$data, {[key]: i.value})
})
}) this._initEvents('select', 'v-model', (i, key) => {
i.addEventListener('change', () => Object.assign(this.$data, {[key]: i.options[i.options.selectedIndex].value}))
})
}
}
作者:儿时的烛光
链接:https://www.jianshu.com/p/78058c7922bf
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
