资讯专栏INFORMATION COLUMN

实现一个简单的Vue

roadtogeek / 2552人阅读

摘要:再获取其事件属性,我们这里只简单地获取属性,我们将它的属性值和元素标识保存到中最后等待模版挂载在元素中后,我们遍历数组,挂载事件至此,我的已基本实现了实现的是一个简单的计数器有兴趣的小伙伴可以复制以下代码运行查看效果

这周参考了一些博文,自己写了一个简单的vue,网上这类实现很多,我的实现也没什么新奇,权当一个自我练习吧

本文同时发在我的github博客上,欢迎star

具体实现

首先,得先有一个Vue类,当然,我写的一个很粗糙的Vue类,所以我把它叫做BabyVue:

function BabyVue(options) {
    const { data, root, template, methods } = options;
    this.data = data;
    this.root = root;
    this.template = template;
    this.methods = methods;
    this.observe();
    this.resolveTemplate();
}

BabtVue构造函数接受一个options,options中包含data,root(即html中指定的根结点),template模版,methods四个option,我们把这些option挂载到this方法上,以便后续的函数能轻松地拿到他们。然后执行observe和resolveTemplate方法

observe方法:

BabyVue.prototype.observe = function() {
    Object.keys(this.data).forEach(key => {
        let val = this.data[key];
        const observer = new Observer();
        Object.defineProperty(this.data, key, {
            get: () => {
                if (Observer.target) {
                    observer.subscribe(Observer.target);
                }
                return val;
            },
            set: newValue => {
                if (val === newValue) {
                    return;
                }
                val = newValue;
                observer.publish(newValue);
            }
        });
    });
};

observe方法中先对this.data中的数据进行遍历,这里没有考虑更深层的结构,只对第一层数据进行遍历,利用闭包缓存它的当前值val和一个观察者observer,并用Object.defineProperty方法设置它的get和set属性,在获取值的时候判断Observer.target是否存在,若存在,则将Observer.target加入订阅者(后面再详述其作用),最后返回val;设置值的时候,将新值与val对比,若不同,则更新val值,并通知订阅者更新

下面是Observer的代码,实现了一个简单的观察者模式:

function Observer() {
    this.subscribers = [];
}
Observer.prototype.subscribe = function(subscriber) {
    !~this.subscribers.indexOf(subscriber) && this.subscribers.push(subscriber);
};
Observer.prototype.publish = function(newVal) {
    this.subscribers.forEach(subscriber => {
        const ele = document.querySelector(`[${subscriber}]`);
        ele && (ele.innerHTML = newVal);
    });
};

订阅者用其特殊属性进行标识,在更新时,先通过属性选择器拿到目标dom再更新其值

下面是resolveTemplate的代码,其主要是渲染模版、增加元素标识和挂载事件,Vue中对模版解析使用的应当是更高级的方法,我这里只是对template字符串一些简单的解析

BabyVue.prototype.resolveTemplate = function() {
    const root = document.createElement("div");
    root.innerHTML = this.template;
    const children = root.children;
    const nodes = [].slice.call(children);
    let index = 0;
    const events = [];
    while (nodes.length !== 0) {
        const node = nodes.shift();
        const _index = index++;
        node.setAttribute(`v-${_index}`, "");
        if (node.children.length > 0) {
            nodes.push(...node.children);
        } else {
            if (/{{(.*)}}/.test(node.innerHTML)) {
                const key = node.innerHTML.replace(/{{(.*)}}/, "$1");
                Observer.target = `v-${_index}`;
                node.innerHTML = this.data[key];
                Observer.target = null;
            }

            const method = node.getAttribute("v-on:click");
            if (method) {
                events.push({
                    key: `v-${_index}`,
                    type: "click",
                    method
                });
            }
        }
    }
    this.root.innerHTML = root.innerHTML;
    events.forEach(event => {
        const { key, type, method } = event;
        const ele = document.querySelector(`[${key}]`);
        ele.addEventListener(type, this.methods[method].bind(this));
    });
};

我对模版中的每一个元素增加一个特殊标示,形似v-xxx,方便根据表示标示获取真实dom(为什么不直接保存node?可以试试使用了createElement创建的元素再设置innerHTML,会出现一些问题)。

先根据正则匹配{},若符合条件,获取了大括号的标识符后,先将Object.target设为元素的标识,在将元素的innerHTML置为data中的数据,要注意,在此时,我们获取了一次this.data[key],会触发之前设置的get属性,在其中判断Observer.target是否存在,因为我们刚刚设置过,Observer.target当前为元素的标识,所以,它被加到订阅者中。

再获取其事件属性,我们这里只简单地获取v-on:click属性,我们将它的属性值和元素标识保存到events中

最后等待模版挂载在root元素中后,我们遍历events数组,挂载事件

至此,我的BabyVue已基本实现了

Demo

实现的是一个简单的计数器:

有兴趣的小伙伴可以复制以下代码运行查看效果:





  
  
  
  BabyVue



  

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106383.html

相关文章

  • vue框架基本原理,简单实现一个todo-list

    摘要:前言最近在学习框架的基本原理,看了一些技术博客以及一些对源码的简单实现,对数据代理数据劫持模板解析变异数组方法双向绑定有了更深的理解。 前言 最近在学习vue框架的基本原理,看了一些技术博客以及一些对vue源码的简单实现,对数据代理、数据劫持、模板解析、变异数组方法、双向绑定有了更深的理解。于是乎,尝试着去实践自己学到的知识,用vue的一些基本原理实现一个简单的todo-list,完成...

    Karrdy 评论0 收藏0
  • 面试被问到Vue?想进一步提升?那就停下来看一下吧

    摘要:两个对象键名冲突时,取组件对象的键值对。允许声明扩展另一个组件可以是一个简单的选项对象或构造函数,而无需使用。这主要是为了便于扩展单文件组件。 Vue作为最近最炙手可热的前端框架,其简单的入门方式和功能强大的API是其优点。而同时因为其API的多样性和丰富性,所以他的很多开发方式就和一切基于组件的React不同,如果没有对Vue的API(有一些甚至文档都没提到)有一个全面的了解,那么在...

    andot 评论0 收藏0
  • JavaScript之实现一个简单Vue

    摘要:的使用相信大家都很熟练了,使用起来简单。但是大部分人不知道其内部的原理是怎么样的,今天我们就来一起实现一个简单的实现之前我们得先看一下的实现,因为主要是通过数据劫持来实现的,通过来完成数据的读取和更新。 vue的使用相信大家都很熟练了,使用起来简单。但是大部分人不知道其内部的原理是怎么样的,今天我们就来一起实现一个简单的vue Object.defineProperty() 实现之前我...

    Gilbertat 评论0 收藏0
  • vue简单入门(一)vue是什么,为什么我们要学vue

    摘要:是什么为什么我们要使用说到了,我们就不得不先聊一下是什么以及为什么我们要使用,他能给我们的开发带来什么样的便利呢首先,我们来看一下的自我介绍读音,类似于是一套用于构建用户界面的渐进式框架。 作为一个刚入行不久的菜鸟不知从什么时候开始就有了写一个自己的专栏的想法,刚好今天没事就给自己挖一个坑,分享一下我对vue的见解和一些领悟,整个专栏应该会包括vue,vue-cli,vue-route...

    Lucky_Boy 评论0 收藏0
  • VUE - MVVM - part13 - inject & 总结

    摘要:通过装作这些变化,我们实现了从而到达了数据变化触发函数的过程。于此同时,我们还实现了来扩展这个可响应的结构,让这个对象拥有了触发和响应事件的能力。最后,根据我们的实现,这是最终的产出,一个框架,了解一下系列文章地址优化优化总结 看这篇之前,如果没有看过之前的文章,移步拉到文章末尾查看之前的文章。 provide / inject 在上一步我们实现了,父子组件,和 props 一样 pr...

    niuxiaowei111 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<