一切的开始源于这篇文章:一句话理解Vue核心内容

在文章中,作者给出了这样一个思考:

假设现在有一个这样的需求,有一张图片,在被点击时,可以记录下被点击的次数。
这看起来很简单吧, 按照上面提到到开发方式,应该很快就可以搞定。
那么接下来,需求稍微发生了点变动, 要求有两张图片,分别被点击时,可以记录下各自的点击次数。这次似乎也很简单,只需把原先的代码复制粘贴一份就可以了。
那么当这个需求变成五张图片时,你会怎么做? 还是简单复制粘贴吧,这样完全可以完成这个需求,但是你会觉得很别扭,因为你的代码此时变得很臃肿,存在很多重复的过程,但是似乎还在你的忍受范围内。
这时候需求又发生了微小的变动,还是五张照片分别记录被点击次数,不过这样单独罗列五张图片似乎太占空间,现在只需要存在一个图片的位置,通过选择按钮来切换被点击的图片。 这时候你可能会奔溃掉,因为要完成这个看似微小的改动,你原先写的大部分代码可能都需要被删掉,甚至是完全清空掉,从零开始写起。

也许你应该像我一样,从一张图片到五张图片完成上面的需求。相信我,这个过程很有趣。因为每增加一次需求,你或多或少都会需要重构你的代码。特别是如果你直接从一张跳到五张的话,那么你就需要完全重构你的代码。

二话不说,先看整个项目的效果。这里我直接放了五张图片实现的效果。

说实话,这其实是一个非常简单的demo,只要对JS的知识稍微熟悉一点,并且在写代码时注意一下闭包的问题,就可以轻松的实现效果。在没学vue之前,我们一定是这样写代码的。

<ul>
<li>one</li>
<li>two</li>
<li>three</li>
<li>fore</li>
<li>five</li>
</ul>
<div class="container">
<img class="pic" src='http://www.jqhtml.com/wp-content/themes/sc/images/logo.png'>
<img class="pic" src='http://www.jqhtml.com/wp-content/themes/sc/images/logo.png'>
<img class="pic" src='http://www.jqhtml.com/wp-content/themes/sc/images/logo.png'>
<img class="pic" src='http://www.jqhtml.com/wp-content/themes/sc/images/logo.png'>
<img class="pic" src='http://www.jqhtml.com/wp-content/themes/sc/images/logo.png'>
<p class='num'></p>
<p class='num'></p>
<p class='num'></p>
<p class='num'></p>
<p class='num'></p>
</div>
<script>
var img = document.getElementsByTagName('img');
var num = document.getElementsByTagName('p');
var li = document.getElementsByTagName('li');
for (let i = 0; i < 5; i++) {
li[i].onclick = (function(index) {//形成闭包
return (function(e) {
for (let j = 0; j < 5; j++) {
//console.log(num);
num[j].removeAttribute('class');
img[j].removeAttribute('class');
}
num[index].setAttribute('class','show');
img[index].setAttribute('class','show');
})
})(i)
img[i].onclick = counter(num[i]);
} //计数器函数
function counter(ele) {
var num = 0,//点击的次数
node = ele;
return function(e) {//形成闭包让每个元素都有自己私有num变量
node.innerHTML = ++num; }
}
</script>

这种直接操作DOM来改变视图的开发方式似乎并不能hold住复杂的逻辑和代码量,况且在这个例子中逻辑并非很复杂。这也证明了由JS来直接操作DOM以改变视图的开发方式并不适合如今的前端开发。这也是前端开发为什么需要类似vue这样的框架。

如果你学过vue,你会发现完成这个需求,只需要改一下data对象里的图片数就轻松的实现了需求。(用vue实现上面的需求更加简单,只需要几行代码就可以实现,并且可扩展性也好,感兴趣的同学可以用vue实现一下上面的需求)

我们可以明显的感觉到vue这种数据和视图分离的代码组织方式更加的容易实现扩展,并且代码可读性更强。而我们上面的原生JS 的实现方式将数据和视图都混在一起了,当项目需求越来越复杂的时候会让代码越臃肿,且越不易于扩展。

其实数据和视图分离并不是框架的专利,要知道框架也是由原生的JS实现的。因此原生JS也可以写出数据和视图分离的代码,让项目变得更加易于扩展。

下面我们就按照数据、视图、逻辑分离的思路来重构一下我们这个项目的代码。项目源码链接

首先,我们把数据给抽离,可以看到视图的样子大概是这样的一个形式。

<body>
<ul id="cat-list">
  //列表
</ul> <section id="cat">//猫图片的显示区域
<h2 id="cat-name"></h2>
<div id="cat-count"></div>
<img src="" alt="" id="cat-img">
</section>
</body>

我们将数据存储在一个名为model的对象中。

var model = {
currentCat: null,
cats: [ //猫的图片数据
{
clickCount : 0,
name : 'Tabby',
imgSrc : 'img/434164568_fea0ad4013_z.jpg',
},
//省略余下的图片数据
]
}

在初始化页面的时候,我们要加载数据,渲染页面。

var catView = { //图片区域的视图
init: function() {
//储存DOM元素,方便后续操作
this.cat = document.getElementById('cat');
this.catName = document.getElementById('cat-name');
this.catCount = document.getElementById('cat-count');
this.catImg = document.getElementById('cat-img');
this.cat.addEventListener('click',function() {//给每张图片添加点击事件
controler.addCount();
},false);
this.render();
}, render: function() {
let currentCat = controler.getCurrentCat();
this.catName.textContent = currentCat.name;
this.catCount.textContent = currentCat.clickCount;
this.catImg.src = '../' + currentCat.imgSrc;
}
} var listView = { //列表区域的视图
init: function() {
this.catList = document.getElementById('cat-list');
this.render();
}, render: function() {
let cats = controler.getCats();
let fragment = document.createDocumentFragment('ul');
cats.forEach((item,index) => {
let li = document.createElement('li');
li.textContent = item.name;
li.setAttribute('class','item');
li.addEventListener('click',function() {//给li添加点击事件
controler.setCurrentCat(item);
catView.render();
})
fragment.appendChild(li);
})
this.catList.appendChild(fragment);
fragment = null;
}
}

从上面的视图对象可以知道,视图并不直接从model中获取数据,而是通过一个中间对象controler来间接访问model,也就是说controler对象实现了所有的视图和数据间的逻辑操作。

var controler = {
init: function() {
model.currentCat = model.cats[0];
catView.init();
listView.init();
},
//获取全部的猫
getCats: function() {
return model.cats;
},
//获取当前显示的猫
getCurrentCat: function() {
return model.currentCat;
}, //设置当前被点击的猫
setCurrentCat: function(cat) {
return model.currentCat = cat;
}, addCount: function() {
model.currentCat.clickCount++;
catView.render();
}
}

到这里,我们用数据、视图、逻辑分离的代码组织方式重构了一个小型的项目,从该项目中可以清楚的看到:数据model只负责存储数据,而视图view只负责页面的渲染,而controler负责view和model之间的交互逻辑的实现。

等一下,既然说交互逻辑是放在controler中实现的,而视图只负责渲染页面,那为什么click点击事件会放在视图层呢?

这里要明确一下的就是(仅个人理解):视图并不是侠义上的静态页面,视图指的是静态页面和动态入口(用户交互,如点击事件),所以事件的绑定放在view层是完全可以理解的,view层实现了一个动态的入口,而用户点击后的所有逻辑操作都是在controler层实现的。

记一次数据、逻辑、视图分离的原生JS项目实践的更多相关文章

  1. ArcGIS API for JavaScript 入门教程[3] 你看得到:数据与视图分离

    这篇开始正式讲API. 数据和视图分离不是什么奇怪的事情了,这是一个著名的设计--数据与视图分开. 转载注明出处,博客园/CSDN/B站:秋意正寒. 目录:https://www.cnblogs.co ...

  2. Javascript模板及其中的数据逻辑分离思想(MVC)

    #Javascript模板及其中的数据逻辑分离思想 ##需求描述 项目数据库的题目表描述了70-120道题目,并且是会变化的,要根据数据库中的数据描述,比如,选择还是填空题,是不是重点题,题目总分是多 ...

  3. ASP.NET MVC 5 学习教程:控制器传递数据给视图

    原文 ASP.NET MVC 5 学习教程:控制器传递数据给视图 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字 ...

  4. Jmeter(七)Jmeter脚本优化(数据与脚本分离)

    午休时间再来记一记,嗯..回顾着使用Jmeter的历程,想着日常都会用到的一些功能.一些组件:敲定了本篇的主题----------是的.脚本优化. 说起脚本优化,为什么要优化?又怎么优化?是个永恒的话 ...

  5. Atitit 数据存储视图的最佳实际best practice attilax总结

    Atitit 数据存储视图的最佳实际best practice attilax总结 1.1. 视图优点:可读性的提升1 1.2. 结论  本着可读性优先于性能的原则,面向人类编程优先于面向机器编程,应 ...

  6. Oracle数据逻辑迁移综合实战篇

    本文适合迁移大量表和数据的复杂需求. 如果你的需求只是简单的迁移少量表,可直接参考这两篇文章即可完成需求: Oracle简单常用的数据泵导出导入(expdp/impdp)命令举例(上) Oracle简 ...

  7. 10天学会phpWeChat——第三天:从数据库读取数据到视图

    在第二天,我们创建了我们的第一个phpWeChat功能模块,但是比较简单.实际生产环境中,我们不可能有如此简单的需求.更多的情况是数据存储在MySql数据库中,我们开发功能模块的作用就是将这些数据从M ...

  8. 转载-使用 Feed4JUnit 进行数据与代码分离的 Java 单元测试

    JUnit 是被广泛应用的 Java 单元测试框架,但是它没有很好的提供参数化测试的支持,很多测试人员不得不把测试数据写在程序里或者通过其它方法实现数据与代码的分离,在后续的修改和维护上有诸多限制和不 ...

  9. hadoop笔记之Hive的数据存储(视图)

    Hive的数据存储(视图) Hive的数据存储(视图) 视图(view) 视图是一种虚表,是一个逻辑概念:可以跨越多张表 既然视图是一种虚表,那么也就是说用操作表的方式也可以操作视图 但是视图是建立在 ...

随机推荐

  1. python学习之路 七 :生成器、迭代器

    本节重点: 掌握列表生成式.生成器.迭代器 一.生成式 ​现在有个需求,把[1,2,3,4,5,6,7,8,9,10]中的每个值加1. # 二逼青年版 a = [0,1,2,3,4,5,6,7,8,9 ...

  2. vector妙用轻松水过平衡树???

    极短代码预警 今天听身边的神仙说,可以用vector来写平衡树,代码极短. 然后去网上搜了一下,看到了attack dalao的这篇文章. 蒟蒻表示ssfd 赶紧膜拜了一波,并发表了一篇博客表示纪念. ...

  3. python字符串的切片

    # 字符串的切片 """ (5)字符串的切片 :切片就是截取字符串的意思 (1)语法 =>字符串[::] 完整格式:[开始索引:结束索引:间隔值 (2)[:结束索引 ...

  4. javascript显示年月日时间代码显示电脑时间

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. [Flex] 组件Tree系列 —— 利用firstVisibleItem属性,设置或取得第一个显示节点

    mxml: <?xml version="1.0" encoding="utf-8"?> <!--功能描述: 利用firstVisibleIt ...

  6. ubuntu和centos 编译安装nginx及常用命令

    转自http://www.cnblogs.com/piscesLoveCc/p/5794926.html 一. ubuntu安装 1. 安装依赖库 安装gcc g++的依赖库 ubuntu平台可以使用 ...

  7. docker搭建tomcat环境

    1.拉取镜像 docker pull tomcat 2.运行容器 docker run --name tomcat -p : -v /data/tomcat/test:/usr/local/tomca ...

  8. Nginx 常用命令总结

    查看某个程序的进程:ps -aux | grep nginx (进程的名字:httpd-apahe进程 mysqld-mysql的进程 svn-svn的进程 php-fpm - PHP进程 ) 查看某 ...

  9. JVM理解

    在阅读本文之前,先向大家强烈推荐一下周志明的<深入理解Java虚拟机>这本书. 前些天面试了阿里的实习生,问到关于Dalvik虚拟机能不能执行class文件,我当时的回答是不能,但是它执行 ...

  10. mybatis的入门(一)

    一.mybatis的介绍 mybatis是Apache的一个开源项目ibatis,2010年这个项目由apache software foundation 迁移到了google code,并且改名为M ...