文章目录
一. 知识背景
在进入前端框架的学习之前,我们先来了解一波框架有关的背景知识(先来个送分题):
框架的定义 : 一套完整的解决方案。
1.1 学习必要性 —— 提高开发效率
这是一个效率至上的时代,高效意味着高回报。 对于企业来说,时间就是效率,效率就是金钱,所以在企业级开发中常常使用各种框架技术。
相应的前端技术更新换代的速度也非常快,我们学习也要与时俱进。
- 前端技术发展路线
1.2 前端框架历史与现状
从2009年的第一款前端框架Angular.js
发布开始,到如今市面上的前端框架技术是换了一波又一波(看下图),经过市场的筛选,目前使用量前三的是图中红色框内的,依次为:
- React.js—— FaceBook出品,目前主流框架之一,呈下滑趋势;
- Vue.js —— 最早由国人尤雨溪开发,目前发展潜力巨大;
- Angular.js —— 现由Google经营,最早的前端框架,目前使用者已经很少了;
Vue,React,Angular三大框架大有三国鼎立之势。Vue
在国内很受欢迎,React
和Angular
是国际上都很受欢迎。下面是Libscore网站上的三大框架的使用情况图示:
(现在要学习哪个框架不用我多说了吧)
1.3 框架与库的区别
之前我们学过的jQuery
技术,其实最开始我们把jQuery
就称为JavaScript的框架,随着Angular等更完整的解决方案的提出,jQuery
就被准确定义为了库。具体区别如下:
优点 | 缺点 | 适用场景 | |
---|---|---|---|
框架 | 功能完善 能提供一套完整的解决方案 |
对项目入侵程度较大 如需更换框架 需要重新架构项目 |
中大型项目 |
库 | 对项目入侵程度小 可与其他库或框架搭配只用使用 技术切换很方便 |
只提供某个方面的功能 | 小中型项目 |
二. Vue框架概述
2.1 Vue是一套渐进式JavaScript框架
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。
2.1.1 自底向上增量开发的设计模式
渐进式(Progressive)是一种类似于自底向上增量开发的设计模式,通俗讲就是Vue框架可以做减法,是可拆分的,从你自身项目场景需求出发,用到哪一块就学习哪一块,不需要全部学习。
这种渐进式的思想其实在很多领域都有体现,可以理解为一种解决问题的通用方案。
理解到Vue的渐进式思想,我立马就联想到了之前做小游戏开发用过的cocos creator引擎,它也支持引擎功能模块的选装,不需要的模块就可以不打包进引擎文件,很有效的减小了最后项目包的大小。
2.1.2 三大框架的主张强势对比
问题再深入一步就涉及到框架本身的主张强势问题,就是你用我的框架所必须接受的东西。强主张就意味着用户的选择很少,必须接受框架的某些特性。对比目前的三大框架:
① Angular是强主张的框架,用户必须接受的是:
- 必须使用它的模块机制;
- 必须使用它的依赖注入;
- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免);
② React也有一定程度的主张:
- 函数式编程的理念;
③ Vue是轻量渐进式的视图框架,没有强主张的因素;
这就相当于给了用户很大的自由度,整个框架就显得很灵活。 用户可根据自身需要,自行选择模块。就像汽车选配,亦或是租房:
Vue
是给你一个空房间,只提供基本的水电网床,需要其他的自己添;
Angular
和React
就像拎包入住,但可能你住了一阵子这不满意那不满意。
2.2 发展历史及作者
2013年底,当时还在谷歌(Google)工作的尤雨溪(Evan You),工作之余开始研究一个个人实验性的项目——Vue框架。最初尤雨溪把它上传分享到GitHub社区,得到了很多人的喜爱。
直到2014年2月,Vue才作为一个完整的前端框架正式发布,同年11月发布0.11版本;
2016年10月正式发布2.0版本,Vue技术生态逐渐完善,至此Vue才开始流行起来,逐渐火爆。
2.3 特点
- 轻量级:
Vue.js压缩后只有20+kb,对比于Angular的60+kb和React的35+kb - 数据驱动
- 易于上手,学习成本低,文档齐全
我们学习Vue的成本低,因为它的一个模板语法是基于HTML的,如果说你有HTML的一个基础,可以很快的去上手Vue的框架。 - 综合其他框架的优点并有所创新
综合了Angular的模块化和React的虚拟DOM,并且创新性的提出了计算属性的概念。 - Vue生态繁荣
- 代码开源,社区活跃度高
- 深受国内大厂的喜爱
三. Vue核心理念一 —— 声明式渲染
在Vue中,数据变量是声明式的渲染在DOM模板(HTML语法片段)中,开发者不需要再关心底层的数据绑定,vue会帮我们自动将数据绑定在DOM模板上。
<div id="app">
{
{
msg}}
</div>
<script>
let vm = new Vue({
el:"#app",
data:{
msg:"zevin"
}
})
</script>
与声明式渲染对应的是之前MVC模式下的命令式渲染。 两者的区别是:
- 声明式渲染:
只需要关心数据变量要声明在哪里,做什么,而无需关心如何实现 - 命令式渲染:
对于数据变量的声明位置,具体作用,实现方式都要关心
<div id="app"></div>
<script>
var msg = "zevin";
document.getElementById("app").innerText = msg;
</script>
对比以上命令式渲染的代码,声明式渲染帮我们省去了通过选中DOM对象进行手动数据绑定的DOM操作。
四. Vue核心理念二 —— MVVM模式
4.1 回顾传统MVC模式
先来回顾一下我们熟悉的MVC
模式:模型(M),视图(V)和控制器(C)的概念信手拈来,分别对应应用程序的输入、输出和处理。这种模式在前后端的开发中都有具体体现:
① 后端开发中最典型的MVC
就是JSP + servlet + javabean
的模式:
字母 | 中文 | 作用 |
---|---|---|
M(Model) | 模型 | 数据库中存取数据 |
V(View) | 视图 | 页面显示 |
C(Control) | 控制器 | 从视图读取数据,控制用户输入,并向模型发送数据 |
② 以下我们具体讨论前端部分的MVC
模式:
- M(Model模型)—— JS数据等;
- V(View视图) —— 页面显示,比如HTML,CSS文件等;
- C(Control控制器) —— 通过选中,操作DOM对象来绑定事件,完成数据交互
4.2 MVVM模式的实现者
MVVM是Model-View-ViewModel
的简写,它本质上就是我们熟悉的MVC
模式的改进版。
字母 | 中文 | 作用 |
---|---|---|
M(Model) | 模型 | 数据库中存取数据 |
V(View) | 视图 | 页面显示 |
VM(View-Model) | 视图模型 | 封装着代表各种DOM操作的指令,负责业务逻辑处理 |
我们现在学习的Vue
框架就属于MVVM
模式中的ViewModel
层的一个具体实现者,它作为连接视图和模型(数据)的中间件,负责传递两者之间的通信。它的工作流程主要有以下两种:
① DOM监听:
- View ——> ViewModel(DOM Listeners)——> Model
- 监听视图的变化,并通知数据发生改变
② 数据绑定:
- Mode ——> ViewModel(Data Bindings)——> View
- 监听数据的变化,并通过内部封装的代表各种DOM操作的指令(Directives)对视图的对应内容进行更新
4.3 双向数据绑定
可以看到上述MVVM模式中ViewModel
层的工作流程其实构成了一个完整闭环,这样就实现了数据的双向绑定。具体体现就是:
数据源发生变化,前端视图也发生变化;同时视图的改变,也会引起相应数据的立即更新。
Vue中可以通过v-model的指令来快速实现表单输入和应用状态之间的双向绑定。 接下来我们就在刚才声明式渲染的代码基础上增加一个输入框,绑定msg
变量。快速验证一下Vue中的数据双向绑定:
<div id="app">
双向数据绑定演示<br>
{
{
msg}}
<input type="text" v-model="msg">
</div>
<script>
let vm = new Vue({
el:"#app",
data:{
msg:""
}
})
</script>
看上述动图,我们每次输入一个字符都相当于改变了视图,相应模型数据中的msg
属性值也同步更新了,并被渲染到了页面中。这是验证了View ——> ViewModel ——> Model。接下来我们在浏览器控制台手动更新msg
的值,来验证Mode ——> ViewModel ——> View :
可以看到我们为msg变量重新赋值后回车,页面内容就立即刷新了。这样就完整验证了Vue中的双向数据绑定。
❀拓展一下❀
用原生JS中的DOM操作模拟Vue双向数据绑定的原理:
其实也很简单,我们刚说Vue
框架是MVVM
模式中的ViewModel
层的一个具体实现者,我们也可以通过原生JS来模拟一下ViewModel
层的工作闭环:
<div id="app">
<span id="msg"></span>
<input type="text" id="inputMsg">
</div>
<script>
var obj = {
};
// 数据绑定:M ——> VM ——> V
Object.defineProperty(obj, 'inputMsg', {
set: function (val) {
document.getElementById('msg').innerText = val
document.getElementById('inputMsg').value = val
},
get: function () {
}
});
// DOM监听:V ——> VM ——> M
document.getElementById('inputMsg').addEventListener('keyup' ,function () {
obj.inputMsg = event.target.value
});
</script>
上述代码你运行会发现达到的效果和Vue一模一样,这就是Vue在背后帮我们解决的繁杂DOM操作。
再说一点:
上边数据绑定那一步使用了Object.defineProperty()
的对象静态方法中的存取描述符形式,对此有疑惑的童鞋可以看我之前发的专项博客:
【JavaScript笔记 · 拓展篇(二)】Object.defineProperty()方法详细解读 —— 为对象添加自定义属性或修改已有属性
4.4 响应式的更新机制
你可能会发现Vue中的所有操作都是响应式的,有木有 ?数据跟视图互相关联,互相影响,同步更新。 这都是得益于刚才讲的Vue的声明式渲染和基于MVVM模式的双向数据绑定呀。
Vue中把原本既复杂又严重影响性能的DOM操作都封装了起来,用各种简短的指令代替,让开发者可以不再关心DOM节点的选择和数据绑定,可以更加专注于业务逻辑的处理。
在之前我们使用js代码控制页面显示时,虽然有jQuery
帮我们封装了部分DOM操作,一定程度上简化了原生JS,但是不可避免的我们还是要通过不断的选中DOM节点,添加事件来实现业务逻辑:
五. Vue核心理念三 —— 虚拟DOM
虚拟 DOM(Virtual DOM) 这个概念最早出现在React框架中,后来Vue框架在设计的时候就综合了React的这一大优点。它最大的作用就是:
- 减少了JavaScript对真实DOM对象的操作带来的性能消耗,优化了整体性能。
5.1 设计痛点
以往在数据更新引起视图中某些DOM节点发生变化的时候,会重新渲染整个视图。 其中那些没有发生变化的DOM节点也重新渲染了,这样就造成了很大程度上的资源浪费。
就比如你想把你家的一块地板换了,只有重新买9块才能完成。
而且是有一个节点发生改变就需要重新绘制DOM
树,通常一次数据更新会同时改变多个节点,所以就会很频繁的重绘DOM
树。这样的机制不仅会极大的浪费系统资源,而且响应速度也会很慢,特别影响整体性能和用户体验。
再比如地板的例子:
你想把你房间的全部三块地板都换成你喜欢的,就需要执行三次上图的步骤:一共买了3 x 9 = 27块地板,重新铺了三次地板。
5.2 虚拟DOM的实质
在分析具体原理之前先来明确一下虚拟DOM到底是什么:
简单来说,虚拟DOM就是一个包含了 tag
、props
、children
属性的普通JavaScript
对象。
<div id="app">
<div class="name">zevin</div>
</div>
比如上面的 HTML 片段转换为虚拟 DOM 如下:
{
tag: 'div',
props: {
id: 'app'
},
chidren: [
{
tag: 'div',
props: {
className: 'name'
},
chidren: [
'zevin'
]
}
]
}
5.3 实现原理解析
虚拟DOM的实现原理简单来说就是:
- 当数据发生变化时,能够智能地计算出需要重新渲染的节点,用最小的代价(最少的DOM操作)完成视图的更新。
详细来说,完整步骤请看上图:
① diff 算法比对 JavaScript 原生对象,计算出需要变更的 DOM 即模板HTML(Template);
② 将模版编译(compile)成 h 函数渲染的形式;
React 是通过 babel 将 jsx 转换为 h 函数渲染的形式,而 Vue 是使用 vue-loader 将模版转为 h 函数渲染的形式;
③ 通过 h 函数渲染成虚拟 DOM (树形结构);
④ 将虚拟 DOM 渲染成真实 DOM 后,再插入到HTML文档对应的根节点;
❀拓展一下❀
有关虚拟DOM更深层次的讲解请看另外两篇大佬之作(学到了):
六. Vue核心理念四 —— 组件化开发
一个页面可以看作由多个组件组合而成,即一个页面可映射为组件树。
组件化涉及的知识比较多,这里就只是提出这个概念,大家先记住组件系统也是 Vue 的另一大核心理念。之后有时间再更新具体的组件博文。