博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用ES6的class模仿Vue写一个双向绑定
阅读量:6682 次
发布时间:2019-06-25

本文共 6053 字,大约阅读时间需要 20 分钟。

原文地址:

最终效果如下:

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

使用

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

您选择了:{
{select}}

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.cnblogs.com/bougie/p/8999881.html

你可能感兴趣的文章
javascript在我的工作的用法
查看>>
市场活动课件:SQL Server 索引优化
查看>>
转 .NET4.5之初识async与await
查看>>
[React ] React Fundamentals: Component Lifecycle - Mounting Usage
查看>>
How to install Bekeley Extension Software Switch(BESS)?
查看>>
MySql数据库笔试题总结
查看>>
CocoaPods使用详细说明
查看>>
SQL那些事儿(十一)--ODBC,OLE-DB,ADO.NET区别[转]
查看>>
Logback中文文档(三):配置
查看>>
【java】【多线程】等待开启的多个线程都执行完成,再做事情,怎么实现
查看>>
java 判断String字符串是不是json数据
查看>>
psql: FATAL: role “postgres” does not exist
查看>>
新版剑指offer14 剪绳子
查看>>
Feign 请求拦截器和日志
查看>>
WPF内实现与串口发送数据和接收数据
查看>>
Ideal test 不执行main方法了
查看>>
kbengine_js_plugins
查看>>
ElasticSearch的各种服务的URL
查看>>
工厂模式之数据工厂
查看>>
IBM Java多线程 - 1. 线程基础
查看>>