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

数据变化之后更新视图

_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()

}

}

})

})

}

使用

加一

您输入的是:{{text1}}+{{text2}}+{{text3}}

Volvo

Saab

Volvo

Saab

您选择了:{{select}}

let app = new TinyVue({

el: '#app',

data: {

text1: 123,

text2: 456,

text3: '文本框',

select: 'saab'

},

methods: {

add() {

this.text1 ++

this.text2 ++

}

}

})

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

})

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值