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

网友投稿 286 2023-02-08


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

数据变化之后更新视图




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('vueIhttp://D')

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

})

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:vue多页面开发和打包正确处理方法
下一篇:详解Spring Cloud 熔断机制
相关文章

 发表评论

暂时没有评论,来抢沙发吧~