一切的开始源于这篇文章:一句话理解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. Cordova deploy on Android

    网上找了几篇Phonegap在安卓上的部署,版本都比较老了,不过还是部署成功了, 写篇博客以做纪录. 1.先下载IDE:戳 2.下载Phonegap:戳 3.启动ADT,新建普通Android App ...

  2. .net core i上 K8S(五).netcore程序的hostip模式

    上一章讲了pod的管理,今天再分享一个pod的访问方式 1.Pod的HostIP模式 Pod的HostIP模式,可以通过宿主机访问pod内的服务,创建yaml文件如下 apiVersion: v1 k ...

  3. c# 读取网卡Mac

    ///<summary> /// 通过NetworkInterface读取网卡Mac ///</summary> ///<returns></returns& ...

  4. django重写form表单中的局部钩子函数

    from django import forms from django.core.exceptions import ValidationError from jax import models c ...

  5. POJ 1330 Nearest Common Ancestors(lca)

    POJ 1330 Nearest Common Ancestors A rooted tree is a well-known data structure in computer science a ...

  6. “全栈2019”Java第七十八章:内部类可以继承其他类吗?

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. dict字典;dict的操作

    一.字典: 1. 字典 dict 用{}来表示 键值对数据 {key:value} 唯一性 键   都必须是可哈希的 不可变的数据类型就可以当做字典中的键 值   没有任何限制 1.1  字典的创建: ...

  8. P4173 残缺的字符串

    题目链接 题意分析 啥 ? ? ? \(FFT\)做字符串匹配 可是就是这样 我们定义匹配函数 我们定义\(A\)是匹配串 \(B\)是被匹配串 我们当前到达\(B\)串的\(x\)位置 \[P(x) ...

  9. [转载]Flip an image in UIImageView using UIView transitionWithView

    View animations on the iPhone are wonderful. Used properly they will delight your users and help you ...

  10. 大象——Thinking in UML

    目录 大象--Thinking in UML 面向对象 普通民众的面向对象 大师眼中的面向对象 大象--Thinking in UML 大音希声,大象希形, 近来闲暇,随手翻起一些曾经看过的书籍,才发 ...