用ES6的class模仿Vue寫一個(gè)雙向綁定的示例代碼
本文介紹了用ES6的class模仿Vue寫一個(gè)雙向綁定的示例代碼,分享給大家,具體如下:
最終效果如下:

構(gòu)造器(constructor)
構(gòu)造一個(gè)TinyVue對(duì)象,包含基本的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和元素的點(diǎn)擊事件@click。
先創(chuàng)建一個(gè)函數(shù)用來(lái)載入事件:
// 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}))
})
載入點(diǎn)擊事件
點(diǎn)擊事件對(duì)應(yīng)的是methods中的事件
this._initEvents('*', '@click', (i, key) => {
i.addEventListener('click', () => this.$methods[key].bind(this.$data)())
})
視圖更新器(updater)
同理先創(chuàng)建公共函數(shù)來(lái)處理不同元素中的視圖,包括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
這里實(shí)現(xiàn)方法有點(diǎn)low,僅想到正則替換{{text}}
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])
})
}
})
監(jiān)聽器(watcher)
數(shù)據(jù)變化之后更新視圖
<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 src="./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}))
})
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 原生JavaScript之es6中Class的用法分析
- ES6 class類鏈?zhǔn)嚼^承,實(shí)例化及react super(props)原理詳解
- ES6 Class中實(shí)現(xiàn)私有屬性的一些方法總結(jié)
- ES6 class的應(yīng)用實(shí)例分析
- ES6中的class是如何實(shí)現(xiàn)的(附Babel編譯的ES5代碼詳解)
- es6新特性之 class 基本用法解析
- 解讀ES6中class關(guān)鍵字
- ES6 javascript中Class類繼承用法實(shí)例詳解
- ES6 javascript中class靜態(tài)方法、屬性與實(shí)例屬性用法示例
- 詳解ES6中class的實(shí)現(xiàn)原理
相關(guān)文章
javascript 文章截取部分無(wú)損html顯示實(shí)現(xiàn)代碼
近在做一些內(nèi)容搜索的工作,搜索出來(lái)的內(nèi)容為html格式,列表部分需要顯示每項(xiàng)內(nèi)容的一部分。2010-05-05
js獲取URL的參數(shù)的方法(getQueryString)示例
getQueryString方法默認(rèn)返回的是 string如果是int類型,則JS使用的時(shí)候,要進(jìn)行轉(zhuǎn)換一下,下面有個(gè)不錯(cuò)的示例,大家可以參考下2013-09-09
JavaScript的DOM與BOM的區(qū)別與用法詳解
這篇文章主要為大家詳細(xì)介紹了JavaScript的DOM與BOM的區(qū)別與用法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03
JavaScript實(shí)現(xiàn)找出數(shù)組中最長(zhǎng)的連續(xù)數(shù)字序列
這篇文章主要介紹了JavaScript實(shí)現(xiàn)找出數(shù)組中最長(zhǎng)的連續(xù)數(shù)字序列的方法,需要的朋友可以參考下2014-09-09
js的window.showModalDialog及window.open用法實(shí)例分析
這篇文章主要介紹了js的window.showModalDialog及window.open用法,實(shí)例分析了window.showModalDialog與window.open方法的定義、功能與使用技巧,需要的朋友可以參考下2015-01-01
JS實(shí)現(xiàn)點(diǎn)擊網(wǎng)頁(yè)判斷是否安裝app并打開否則跳轉(zhuǎn)app store
這篇文章主要介紹了JS實(shí)現(xiàn)點(diǎn)擊網(wǎng)頁(yè)判斷是否安裝app并打開否則跳轉(zhuǎn)app store的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
javascript下查找父節(jié)點(diǎn)的簡(jiǎn)單方法
javascript下查找父節(jié)點(diǎn)的簡(jiǎn)單方法...2007-08-08

