#web前端#说说前端框架历史和MVVM框架原理
Posted 琴岛知识院
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#web前端#说说前端框架历史和MVVM框架原理相关的知识,希望对你有一定的参考价值。
近几年前端领域发展迅速,可以说是百家争鸣。最早是jquery称霸互联网,最近MVVM模式的框架成为主流,Vue、React和Angular三大框架并驾齐驱。目前这四种是开发者用的最多使用最广的底层框架,也由此衍生了大量免费UI库。
【 前端框架发展的历史 】
在了解MVVM框架之前,我们先回顾一下前端发展的历史阶段,做到心中有数,才会更好理解。
这段回顾历史,由于网上就可查不少资料,但都篇幅很长,晦涩难懂。
所以我引用了 廖雪峰老师网站总结的一段话,言简意骇,方便大家秒懂阅读。
---------------------------------------------------------
在上个世纪的1989年,欧洲核子研究中心的物理学家Tim Berners-Lee发明了超文本标记语言(HyperText Markup Language),简称html,并在1993年成为互联网草案。从此,互联网开始迅速商业化,诞生了一大批商业网站。
最早的HTML页面是完全静态的网页,它们是预先编写好的存放在Web服务器上的html文件。浏览器请求某个URL时,Web服务器把对应的html文件扔给浏览器,就可以显示html文件的内容了。
如果要针对不同的用户显示不同的页面,显然不可能给成千上万的用户准备好成千上万的不同的html文件,所以,服务器就需要针对不同的用户,动态生成不同的html文件。一个最直接的想法就是利用C、C++这些编程语言,直接向浏览器输出拼接后的字符串。这种技术被称为CGI:Common Gateway Interface。
很显然,像新浪首页这样的复杂的HTML是不可能通过拼字符串得到的。于是,人们又发现,其实拼字符串的时候,大多数字符串都是HTML片段,是不变的,变化的只有少数和用户相关的数据,所以,又出现了新的创建动态HTML的方式:ASP、JSP和PHP等——分别由微软、SUN和开源社区开发。
在以前:
在ASP中,一个asp文件就是一个HTML,但是,需要替换的变量用特殊的<%=var%>标记出来了,再配合循环、条件判断,创建动态HTML就比CGI要容易得多。
但是,一旦浏览器显示了一个HTML页面,要更新页面内容,唯一的方法就是重新向服务器获取一份新的HTML内容。如果浏览器想要自己修改HTML页面的内容,怎么办?那就需要等到1995年年底,JavaScript被引入到浏览器。
有了javascript后,浏览器就可以运行JavaScript,然后,对页面进行一些修改。JavaScript还可以通过修改HTML的DOM结构和CSS来实现一些动画效果,而这些功能没法通过服务器完成,必须在浏览器实现。
以上web页面发展历史,摘自廖雪峰总结,有兴趣可以去搜索。
---------------------------------------------------------
下面先来说说前端框架发展的历史。
JavaScript如何操作HTML
至于 js如何在浏览器执行,这又是另外一个高深课题了(前端真的是庞杂),这里我们不做研究,有兴趣的可以自己去搜资料。我们只需要知道浏览器就是也JS执行容器,执行完之后,通过页面显示结果就行了,就像java需要编译器一样原理。
用JavaScript在浏览器中操作HTML,也经历了若干发展阶段,我们利用【院长最帅】这个案例来展示:
【第一阶段】javascript
是JS原生通过浏览器解析机制,它的原理是使用浏览器提供的原生API 结合JS语法,可以直接操作DOM,如:
HTML:
<div id="name" style="color:#fff">前端怎么学</div>
<div id="age">13</div>
JavaScript:
// JavaScript
var dom1 = document.getElementById('name');
var dom2 = document.getElementById('age');
dom1.innerHTML = '院长最帅';
dom2.innerHTML = '666';
dom1.style.color = '#000000'; // css样式也可以操作
结果变成:
<div id="name" style="color:#fff">院长最帅</div>
<div id="age">666</div>
【第二阶段】jQuery
我用一个字总结 就是懒,就是jQuery的前端时代,由于原生API晦涩难懂,语法很长不好用,最重要的是要考虑各种浏览器兼容性,因为他们的解析标准都不一样,造成了,写一段效果代码要写很多的兼容语法,令人沮丧,所以jQuery的出现,迅速占领了世界。
上边的例子用 jQuery 是这样的:
HTML:
<div id="name" style="color:#fff">前端怎么学</div>
<div id="age">13</div>
JavaScript:
// jQuery 一句话就能实现
$('#name').text('院长最帅').css('color', '#000000');
$('#age').text('666').css('color', '#fff');
结果变成:
<div id="name" style="color:#fff">院长最帅</div>
<div id="age">666</div>
【第三阶段】MVC模式
MVC模式,需要服务器端配合,JavaScript可以在前端修改服务器渲染后的数据。
一句话就是所有通信都是单向的: 也就是前期我们最常用的状态,提交一次反馈一次,通信一次相互制约。
比如:提交表单 填写内容 → 点击提交 →业务逻辑处理 →存入数据库 → 刷新页面→服务器取数据库数据→渲染到客户端页面→ 展示上一次你提交的内容
图片来自网络
视图(View):用户界面。
控制器(Controller):业务逻辑
模型(Model):数据保存
各部分之间的通信方式如下:
图片来自网络
View 传送指令到 Controller
Controller 完成业务逻辑后,要求 Model 改变状态
Model 将新的数据发送到 View,用户得到反馈
这个模式缺点是什么呢?
缺点一:它必须等待服务器端的指示,而且如果是异步模式的话,所有html节点、数据、页面结构都是后端请求过来,浏览器只作为一个解析显示容器,Model 作用几乎是废x,Model 层面做的很少几乎前端无法控制,你前端几乎是切图仔和做轮播图的工作。
缺点二:因为你前端渲染的页面结构,几乎是后端服务器包扎一堆数据一起发送过来,前端的你只需要用拼接字符串 或者字符串拼接引擎,比如Mustache、Jade、artTemplate、tmpl、kissyTemplate、ejs等来做事,说白了纯苦力和重复工作居多,这也导致了,如果很多人认为前端并不重要,只负责美工 和 动作体验就好了。
缺点三:一发而动全身。数据、显示不分离!为什么这么说,因为如果业务逻辑要变,比如很简单的需求,你用jsp或者php 拼接出来的ajax数据页面,年龄这个字段我不需要了,把性别字段 区分开,男的单独显示,女的单独显示,以前是一起显示到一个表的
那么,后端先要sql查询把 男、女数据分开,然后渲染字符串时候把 年龄 这个字段去除,然后把男女分开成2个table,然后再推送给前端接收。前端收到了,然后从新在渲染一遍,在加工一次页面甚至是展示动作效果......
---------------------------- s 说一说MVC模式 -----------------------------
MVC,全称是 Model View Controller,是模型 (model)-视图 (view)-控制器 (controller) 的缩写。它表示的是一种常见的客户端软件开发框架。
MVC 的概念最早出现在二十世纪八十年代,但是,几十年过去了,我们对于 MVC 这种设计模式真的用得好吗?其实不是的,MVC 这种分层方式虽然清楚,但是如果使用不当,很可能让大量代码都集中在 Controller 之中,让 MVC 模式变成了 Massive View Controller 模式。
MVC模式的存在问题:
Controller 的臃肿问题?
MVC 这种设计模式很多时候是为了 Don't repeat yourself 原则来做的,该原则要求能够复用的代码要尽量复用,来保证重用。在 MVC 这种设计模式中,我们发现 View 和 Model 都是符合这种原则的。
对于 View 来说,你如果抽象得好,那么一个 App 的动画效果可以很方便地移植到别的 App 上,而 Github 上也有很多 UI 控件,这些控件都是在 View 层做了很好的封装设计,使得它能够方便地开源给大家复用。
对于 Model 来说,它其实是用来存储业务的数据的,如果做得好,它也可以方便地复用。比如我当时在做有道云笔记 iPad 版的时候,我们就直接和 ios 版复用了所有的 Model 层的代码。在创业做猿题库客户端时,iOS 和 iPad 版的 Model 层代码再次被复用上了。当然,因为和业务本身的数据意义相关,Model 层的复用大多数是在一个产品内部,不太可能像 View 层那样开源给社区。
说完 View 和 Model 了,那我们想想 Controller,Controller 有多少可以复用的?我们写完了一个 Controller 之后,可以很方便地复用它吗?结论是:非常难复用。在某些场景下,我们可能可以用addSubViewController 之类的方式复用 Controller,但它的复用场景还是非常非常少的。
如果我们能够意识到 Controller 里面的代码不便于复用,我们就能知道什么代码应该写在 Controller 里面了,那就是那些不能复用的代码。在我看来,Controller 里面就只应该存放这些不能复用的代码,这些代码包括:
-在初始化时,构造相应的 View 和 Model。
-监听 Model 层的事件,将 Model 层的数据传递到 View 层。
-监听 View 层的事件,并且将 View 层的事件转发到 Model 层。
如果 Controller 只有以上的这些代码,那么它的逻辑将非常简单,而且也会非常短。但是,我们却很难做到这一点,因为还是有很多逻辑我们不知道写在哪里,于是就都写到了 Controller 中了,那我们接下来就看看其它逻辑应该写在哪里。
如何对 ViewController 瘦身?
一个非常有名的 iOS 开发博客上就讲了一些技巧,先总结一下它里面的观点:
-将 UITableView 的 Data Source 分离到另外一个类中。
-将数据获取和转换的逻辑分别到另外一个类中。
-将拼装控件的逻辑,分离到另外一个类中。
你想明白了吗?其实 MVC 虽然只有三层,但是它并没有限制你只能有三层。所以,我们可以将 Controller 里面过于臃肿的逻辑抽取出来,形成新的可复用模块或架构层次。
将网络请求抽象到单独的类中
新手写代码,直接就在 Controller 里面用 AFNetworking 发一个请求,请求的完数据直接就传递给 View。入门一些的同学,知道把这些请求代码移到另外一个静态类里面。但是我觉得还不够,所以我建议将每一个网络请求直接封装成类。
把每一个网络请求封装成对象其实是使用了设计模式中的 Command 模式,它有以下好处:
将网络请求与具体的第三方库依赖隔离,方便以后更换底层的网络库。实际上我们公司的 iOS 客户端最初是基于 ASIHttpRequest 的,我们只花了两天,就很轻松地切换到了 AFNetworking。
方便在基类中处理公共逻辑,例如猿题库的数据版本号信息就统一在基类中处理。
方便在基类中处理缓存逻辑,以及其它一些公共逻辑。
方便做对象的持久化。
大家如果感兴趣,可以看我们公司开源的 iOS 网络库:YTKNetwork。它在这种思考的指导下,不但将 Controller 中的代码瘦身,而且进一步演化和加强,现在它还支持诸如复杂网络请求管理,断点续传,插件机制,JSON 合法性检查等功能。
这部分代码从 Controller 中剥离出来后,不但简化了 Controller 中的逻辑,也达到了网络层的代码复用的效果。
将界面的拼装抽象到专门的类中
新手写代码,喜欢在 Controller 中把一个个 UILabel ,UIButton,UITextField 往 self.view 上用 addSubView 方法放。我建议大家可以用两种办法把这些代码从 Controller 中剥离。
方法一:构造专门的 UIView 的子类,来负责这些控件的拼装。这是最彻底和优雅的方式,不过稍微麻烦一些的是,你需要把这些控件的事件回调先接管,再都一一暴露回 Controller。
方法二:用一个静态的 Util 类,帮助你做 UIView 的拼装工作。这种方式稍微做得不太彻底,但是比较简单。
对于一些能复用的 UI 控件,我建议用方法一。如果项目工程比较复杂,我也建议用方法一。如果项目太紧,另外相关项目的代码量也不多,可以尝试方法二。
构造 ViewModel
谁说 MVC 就不能用 ViewModel 的?MVVM 的优点我们一样可以借鉴。具体做法就是将 ViewController 给 View 传递数据这个过程,抽象成构造 ViewModel 的过程。
这样抽象之后,View 只接受 ViewModel,而 Controller 只需要传递 ViewModel 这么一行代码。而另外构造 ViewModel 的过程,我们就可以移动到另外的类中了。
在具体实践中,我建议大家专门创建构造 ViewModel 工厂类,参见 工厂模式。另外,也可以专门将数据存取都抽将到一个 Service 层,由这层来提供 ViewModel 的获取。
专门构造存储类
刚刚说到 ViewModel 的构造可以抽奖到一个 Service 层。与此相应的,数据的存储也应该由专门的对象来做。在小猿搜题项目中,我们由一个叫 UserAgent 的类,专门来处理本地数据的存取。
数据存取放在专门的类中,就可以针对存取做额外的事情了。比如:
-对一些热点数据增加缓存
-处理数据迁移相关的逻辑
如果要做得更细,可以把存储引擎再抽象出一层。这样你就可以方便地切换存储的底层,例如从 sqlite 切换到 key-value 的存储引擎等。
小结
通过代码的抽取,我们可以将原本的 MVC 设计模式中的 ViewController 进一步拆分,构造出 网络请求层、ViewModel 层、Service 层、Storage 层等其它类,来配合 Controller 工作,从而使 Controller 更加简单,我们的 App 更容易维护。
另外,不知道大家注意到没,其实 Controller 层是非常难于测试的,如果我们能够将 Controller 瘦身,就可以更方便地写 Unit Test 来测试各种与界面的无关的逻辑。移动端自动化测试框架都不太成熟,但是将 Controller 的代码抽取出来,是有助于我们做测试工作的。
--------------------------- e 说一说MVC模式 ------------------------------
【第四阶段】MVVM框架模式
为什么在MVC模式我说这么多废话呢,因为你了解了MVC才能更清楚的知道
「 何为mvvm模式? 」
MVVM最早由微软提出来,它借鉴了桌面应用程序的MVC思想,在前端页面中,把Model用纯JavaScript对象表示,View负责显示,两者做到了最大限度的分离。也就是我们常说的,前后分离,真正在这里得以实现。
图片来自网络
它采用双向绑定(data-binding):
View的变动,自动反映在 ViewModel,反之亦然,model数据的变动,也自动展示给页面显示。
把Model和View关联起来的就是ViewModel。
ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。
可能理论知识枯燥无味,那么我们还是实战派,来看代码不就好了吗?
还是刚才的 【院长最帅】案例:
js和jQuery的写法 大家也看到了,那么我们来MVVM 数据绑定怎么实现。
由于数据驱动模式的精髓在于【数据】和【视图】分离,所以我们首先并不关心DOM结构,而是关心数据的展现。最简单的数据存储方式是什么呢?显然不是mysql、数据库而是使用JavaScript对象:
HTML:
// 这次我不关心你事了,哈哈
JavaScript:
// JS基础对象
// 原始数据
var yuanzhang = {name: '前端怎么学',
age: 13,
tag:'干货'
};
结果是:
name: 前端怎么学
age: 13
tag: 干货
假设:
我们把变量yuanzhang 看作Model数据,把HTML某些DOM节点看作View,并假想它们已经通过某种手段被关联起来了。
下面我们把name 从[ 前端怎么学] 改为 [院长],把显示的age从 [13] 改为 [666],tag变成 [最帅!]
按照以前我们肯定操作DOM节点,而现在我们只需要修改JavaScript对象:
JavaScript:
// JS基础对象
// 改变的数据var yuanzhang = {
name: '院长',
age: 666,
tag:'最帅'
};
结果是:
name: 院长
age: 666tag: 最帅
通过理论和实践:
小伙伴惊呆了,我们只要改变JavaScript对象的内容,就会导致DOM结构作出对应的变化!
这让我们的关注点从如何操作DOM变成了如何改变JavaScript对象的状态,而操作JavaScript对象比获取和操作DOM简单了一个地球的距离!
这也是MVVM的核心思想:关注Model的变化,让MVVM框架利用自己的机制去自动更新DOM,从而把开发者从操作DOM的繁琐中解脱出来!
也就是所谓的 数据 - 视图分离,数据驱动视图, 视图不影响数据,再也不用管繁琐的DOM结构操作了,世界顿时清净,完美\\//!
常见的MVVM框架:VueJS、ReactJs、AngularJs 等,我们以后再续~~
以上是关于#web前端#说说前端框架历史和MVVM框架原理的主要内容,如果未能解决你的问题,请参考以下文章