记一次数据、逻辑、视图分离的原生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) 视图是一种虚表,是一个逻辑概念:可以跨越多张表 既然视图是一种虚表,那么也就是说用操作表的方式也可以操作视图 但是视图是建立在 ...
随机推荐
- First App on Phonegap | Cordova
Phonegap简介 PhoneGap是一能够让你用普通的web技术编写出能够轻松调用api接口和进入应用商店的 html5应用开发平台,是唯一支持7个平台的开源移动框架. 优势: 1.兼容性:多平台 ...
- wp8.1启动协议
var uri = new Uri(string.Format(@"ms-windows-store:navigate?appid={0}", appid));商店根据appid跳 ...
- 提示缺少python.h解决办法
在安装uwsgi时,提示缺少python.h In file included :: plugins/python/uwsgi_python.h::: fatal error: Python.h: N ...
- 解除SVN的控制
win10操作就是把文件夹隐藏的对勾勾上 然后就可以看见一个.SVN的文件夹 把他直接删除即可 然后刷新 自己亲测的 win10 家庭版
- 引用HM.Util.Ioc 的时候报错
引用HM.Util.Ioc 的时候报错 错误:The type name or alias SqlServer could not be resolved. Please check your con ...
- [转]什么是VUE?为什么用VUE?
Vue.js新手入门指南 最近在逛各大网站,论坛,以及像SegmentFault等编程问答社区,发现Vue.js异常火爆,重复性的提问和内容也很多,楼主自己也趁着这个大前端的热潮,着手学习了一段时间的 ...
- Centos系统优化
系统优化脚本 #!/bin/bash #author junxi by #this script is only for CentOS 7.x #check the OS platform=`unam ...
- css中设置background属性
属性解释 background属性是css中应用比较多,且比较重要的一个属性,它是负责给盒子设置背景图片和背景颜色的,background是一个复合属性,它可以分解成如下几个设置项: backgrou ...
- POJ2279 Mr Young's Picture Permutations
POJ2279 Mr Young's Picture Permutations 描述: 有N个学生合影,站成左对齐的k排,每行分别有N1,N2…NK个人,第一排站最后,第k排站之前.学生身高依次是1… ...
- P3879 [TJOI2010]阅读理解 题解
P3879 [TJOI2010]阅读理解 题解 题目描述 英语老师留了N篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都在哪几篇短文中出现过. 输 ...