用ES6的class模仿Vue写一个双向绑定的示例代码

本文介绍了用ES6的class模仿Vue写一个双向绑定的示例代码,分享给大家,具体如下:

最终效果如下:

构造器(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_\-]+) *\}}/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])

})

}

})

监听器(watcher)

数据变化之后更新视图

<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}))

})

}

}

以上是 用ES6的class模仿Vue写一个双向绑定的示例代码 的全部内容, 来源链接: utcz.com/z/319175.html

回到顶部