松鼠乐园 松鼠乐园
  • 注册
  • 登录
  • 首页
  • 快捷入口
    • Vue
    • Tensorflow
    • Springboot
    • 语言类
      • CSS
      • ES5
      • ES6
      • Go
      • Java
      • Javascript
    • 工具类
      • Git
      • 工具推荐
    • 服务器&运维
      • Centos
      • Docker
      • Linux
      • Mac
      • MySQL
      • Nginx
      • Redis
      • Windows
    • 资源类
      • 论文
      • 书籍推荐
      • 后端资源
      • 前端资源
      • html网页模板
      • 代码
    • 性能优化
    • 测试
  • 重大新闻
  • 人工智能
  • 开源项目
  • Vue2.0从零开始
  • 广场
首页 › Vue › 「干货」实现一个简单的Vue

「干货」实现一个简单的Vue

迦娜王
1年前Vue
207 0 0

这周参考了一些博文,自己写了一个简单的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

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

「干货」实现一个简单的Vue

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




 
 
 
 BabyVue



0
Vue中DOM的异步更新策略以及nextTick机制
上一篇
vue 深度长文之slot 篇
下一篇
评论 (0)

请登录以参与评论。

现在登录
聚合文章
在Gitee收获近 5k Star,更新后的Vue版RuoYi有哪些新变化?
2月前
vue3.x reactive、effect、computed、watch依赖关系及实现原理
2月前
Vue 3 新特性:在 Composition API 中使用 CSS Modules
2月前
新手必看的前端项目去中心化和模块化思想
2月前
标签
AI AI项目 css docker Drone Elaticsearch es5 es6 Geometry Go gru java Javascript jenkins lstm mysql mysql优化 mysql地理位置索引 mysql索引 mysql规范 mysql设计 mysql配置文件 mysql面试题 mysql高可用 nginx Redis redis性能 rnn SpringBoot Tensorflow tensorflow2.0 UI设计 vue vue3.0 vue原理 whistle ZooKeeper 开源项目 抓包工具 日志输出 机器学习 深度学习 神经网络 论文 面试题
相关文章
在Gitee收获近 5k Star,更新后的Vue版RuoYi有哪些新变化?
vue3.x reactive、effect、computed、watch依赖关系及实现原理
Vue 3 新特性:在 Composition API 中使用 CSS Modules
新手必看的前端项目去中心化和模块化思想
松鼠乐园

资源整合,创造价值

小伙伴
墨魇博客 无同创意
目录
重大新闻 Centos CSS Docker ES5 ES6 Go Java Javascript Linux Mac MySQL Nginx Redis Springboot Tensorflow Vue Vue2.x从零开始 Windows 书籍推荐 人工智能 前端资源 后端资源 壁纸 开源项目 测试 论文
Copyright © 2018-2021 松鼠乐园. Designed by nicetheme. 浙ICP备15039601号-4
  • 重大新闻
  • Centos
  • CSS
  • Docker
  • ES5
  • ES6
  • Go
  • Java
  • Javascript
  • Linux
  • Mac
  • MySQL
  • Nginx
  • Redis
  • Springboot
  • Tensorflow
  • Vue
  • Vue2.x从零开始
  • Windows
  • 书籍推荐
  • 人工智能
  • 前端资源
  • 后端资源
  • 壁纸
  • 开源项目
  • 测试
  • 论文
热门搜索
  • jetson nano
  • vue
  • java
  • mysql
  • 人工智能
  • 人脸识别
迦娜王
坚持才有希望
1224 文章
33 评论
235 喜欢
  • 0
  • 0
  • Top