记一次数据、逻辑、视图分离的原生JS项目实践
一切的开始源于这篇文章:一句话理解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项目实践的更多相关文章
- ArcGIS API for JavaScript 入门教程[3] 你看得到:数据与视图分离
这篇开始正式讲API. 数据和视图分离不是什么奇怪的事情了,这是一个著名的设计--数据与视图分开. 转载注明出处,博客园/CSDN/B站:秋意正寒. 目录:https://www.cnblogs.co ...
- Javascript模板及其中的数据逻辑分离思想(MVC)
#Javascript模板及其中的数据逻辑分离思想 ##需求描述 项目数据库的题目表描述了70-120道题目,并且是会变化的,要根据数据库中的数据描述,比如,选择还是填空题,是不是重点题,题目总分是多 ...
- ASP.NET MVC 5 学习教程:控制器传递数据给视图
原文 ASP.NET MVC 5 学习教程:控制器传递数据给视图 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字 ...
- Jmeter(七)Jmeter脚本优化(数据与脚本分离)
午休时间再来记一记,嗯..回顾着使用Jmeter的历程,想着日常都会用到的一些功能.一些组件:敲定了本篇的主题----------是的.脚本优化. 说起脚本优化,为什么要优化?又怎么优化?是个永恒的话 ...
- Atitit 数据存储视图的最佳实际best practice attilax总结
Atitit 数据存储视图的最佳实际best practice attilax总结 1.1. 视图优点:可读性的提升1 1.2. 结论 本着可读性优先于性能的原则,面向人类编程优先于面向机器编程,应 ...
- Oracle数据逻辑迁移综合实战篇
本文适合迁移大量表和数据的复杂需求. 如果你的需求只是简单的迁移少量表,可直接参考这两篇文章即可完成需求: Oracle简单常用的数据泵导出导入(expdp/impdp)命令举例(上) Oracle简 ...
- 10天学会phpWeChat——第三天:从数据库读取数据到视图
在第二天,我们创建了我们的第一个phpWeChat功能模块,但是比较简单.实际生产环境中,我们不可能有如此简单的需求.更多的情况是数据存储在MySql数据库中,我们开发功能模块的作用就是将这些数据从M ...
- 转载-使用 Feed4JUnit 进行数据与代码分离的 Java 单元测试
JUnit 是被广泛应用的 Java 单元测试框架,但是它没有很好的提供参数化测试的支持,很多测试人员不得不把测试数据写在程序里或者通过其它方法实现数据与代码的分离,在后续的修改和维护上有诸多限制和不 ...
- hadoop笔记之Hive的数据存储(视图)
Hive的数据存储(视图) Hive的数据存储(视图) 视图(view) 视图是一种虚表,是一个逻辑概念:可以跨越多张表 既然视图是一种虚表,那么也就是说用操作表的方式也可以操作视图 但是视图是建立在 ...
随机推荐
- WinForm中DataGridView的使用(四) - 区分单双击事件
虽然DataGridView单双击事件都有,但双击事件其实也会触发单击事件的处理,所以如果双击事件和单击事件的行为不同,或者双击时不想触发单击事件,或者单击事件会阻塞双击事件的处理时(比如单击后会有弹 ...
- .net core session cookie操作
配置Session Cookie注意
- C#操作Json数据
JSON是现今各语言实现数据交互应用最广泛的一种格式,在于Xml的比较中,由于 JSON 所使用的字符要比 XML 少得多,可以大大得节约传输数据所占用得带宽. 本文采用的是Newtonsoft.Js ...
- Entity Framework 6 暂停重试执行策略
EF6引入一个弹性连接的功能,也就是允许重新尝试执行失败的数据库操作.某些复杂的场景中,可能需要启用或停用重试执行的策略,但是EF框架暂时尚未提供直接的设置开关,将来可能会加入这种配置.幸运的是,很容 ...
- 「HNOI 2013」数列
题目链接 戳我 \(Solution\) 这道题貌似并不难的样子\(QAQ\) 我们发现这个因为有首项的关系所以有点不太好弄.所以我们要将这个首项对答案的影响给去掉. 我们可以构建一个差分数组,我们令 ...
- 《Beginning Java 7》 - 6 - 深入理解 String
public final class String implements Serializable, Comparable<String>, CharSequence 所以: 1. Str ...
- Delphi XE8中的DBExpress使用ODBC方式连接MySQL 5.6.24 X64!!!!
Delphi:XE2.XE8 MySQL:5.6.24 X64 今天准备做一个使用数据库的REST程序,最开始使用XE2,后来使用XE8,但是我遇到一个让我头疼的问题,就是无法连接我要使用的数据库,这 ...
- OpenCV库文件介绍
以前都是直接用opencv,都没有仔细研究过,这次把库文件都介绍一下. 1.build和source 当我们安装完opencv中,你会发现在opencv文件夹中有两个文件夹,build和source, ...
- docker运行nginx
docker run --name nginx(容器名) -p 80:80 -v 项目文件路径:/usr/share/nginx/html -v 配置文件路径:/etc/nginx/nginx.con ...
- CentOS6.9 minimal版本安装图形化界面
CentOS6.9 minimal版本安装图形化界面 安装步骤如下: 1.安装Desktop组 # yum groupinstall "Desktop" -y 2.安装X Wind ...