STAY HUNGRY , STAY FOOLISH.

求知若饥,虚心若愚。

       浏览:

初识Vue.js

1.传统前端架构

如果按照以前传统前端的方式去架构的话,随着前端业务逻辑的增多,开发和维护会变得越来越困难,请看:
这是我去年做的华为项目Telkomsel Moovigo的目录,主要业务逻辑的代码有65个之多(每个包含page、fss、js),虽然页面、样式、逻辑代码确实是分开的,但是都是合在同一个文件里面。
src

去年做的另一个华为项目Digicel也是同样:
src

做这些项目的团队,就我们前端人员每个团队都有10人左右,维护相对简单,一人搞定6~7个就OK,那么问题来了,假如1人来搞定所有呢?因为是外包,所以这样做没太大问题,只要控制了成本,在时间少的情况下做出来且赚钱就行。有些创业公司是自主研发做产品的,如果还是这样做,会出现什么问题呢?
请看,这是我现在负责重构的微信端产品代码目录:
src
卧槽,这么简单,这么清爽,完全没问题呀?
这样分的好处确实有,html文件夹里面就是界面,css文件夹里面就是样式,js文件夹里面就是业务逻辑,“结构”看起来特别清晰。
但是当你打开后,近100个html、js、css文件都在各自的同一个文件夹里面,请看:
src
这样写最主要的两个问题是:

  1. 难复用
  2. 难维护

可能有些人不理解难复用的意思,复用怎么困难了?不是有现成的代码吗,复制粘贴重新创建一个改一改不就行了吗?这就是复用了呀!非也。
复用的意思是在不改变现有代码的基础上,依赖以前写好的代码,进行二次开发。在程序的世界,有句话一直很经典:“不要重复造轮子”。举个例,如果将每个页面看成是一辆汽车,一辆汽车由轮胎,中控,发动机等构成,且可以自由组装。同理,前端页面可以抽成很多组件,每个页面都是由这些组件组装而成。

components

维护的意思是需要在已上线的产品上新增业务;或者去改动有相同业务的页面。不管是修改页面上的图片、样式、位置、业务、Bug、性能都算维护。如果重复造轮子的页面越来越多,维护改动的地方就越多,到最后会越来越麻烦,改动一处,动则n处。为了解决这些问题,最好的方式就是实现组件化.


2.什么是MVVM框架?

从三层架构说起,一个完整应用可以分成持久层、业务层、表现层三部分。
持久层即根据业务实现表及其映射关系,完善数据的CRUD功能;
业务层即根据业务实现对应的接口,调用持久层的东西为每个页面需要掉什么接口,返回什么数据进行封装;
表现层即根据业务实现相应的交互页面,调用业务层的东西实现数据的展现及CRUD。

三层架构各自其责,其实最痛苦的莫过于表现层。因为持久层和业务层的最终实现在表现层
表现层是最重要的环节,做前端就是做交互,因为产品好坏的决定因素是用户的交互体验,当然其他两层也同样重要,因为表现层依赖于它们
表现层说简单点就是做页面,三个字说起来容易,实际却不易,因为做一个页面,要做它的交互、数据展现、性能优化,还要理解页面与页面之间的关系等。页面多了,重复做这些事情就很麻烦。

为了更好的说明表现层,于是乎表现层出现了MVC架构,MVC是三个单词的首字母缩写,它们是Model(模型)、View(视图)和Controller(控制)
刚入行web前端的人员可能是这样理解的,HTML就是Model,CSS就是View,JS就是Controller。
因为他们想的:
模型层就是指结构,HTML语义化就是在说结构呢;
视图层就是指展现效果,CSS样式就是给HTML标签实现效果呢,
控制层就是指操作,JS就是操作HTML、CSS、调用接口。
如果这样理解就大错特错啦,这里有通过 JavaScript实现的一个基础MVC模型,请注意的是:MVC 不是一种技术,仅是一种理念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** 模拟Model,View,Controller */
var M = {}, V = {}, C = {};

/** Model 负责存放数据 */
M.data = "hello world";

/** View 负责将数据呈现到屏幕上 */
V.render = function (M) { alert(M.data); }

/** Controller 作为一个 M 和 V 的桥梁 */
C.handleOnload = function () { V.render(M); }

/** 在网页读取的时候呼叫Controller */
window.onload = C.handleOnload;

又说多了,现在来讲什么是MVVM?
但是要讲清楚它之前,就必须讲MVC,因为根据它衍生出了MVP(这里不做讲解)和MVVM架构。
所以MVVM是MVC的衍生物,也是一种架构

那么,MVC、MVVM两者的区别是什么呢?
依据一个简单的实例来讲解:

noData

data

实现这个页面,一般思路是:
1.掉接口获取数据 2.在回调里使用获取的值渲染页面 3.添加事件

1
2
3
4
5
6
7
8
9
10
11
var PageView={
initialize:function(){
var $this=this;
$this.el=$('#main');
$this.fetch(function(record){ //1.
$this.render(record); //2.
$this.addEvent(); //3.
});
}
};
PageView.initialize();

code

在该代码中,模型(Model)就是从接口返回的record;视图(View)就是将获取的record设值到页面的render方法;控制(Controller)就是根据业务调取接口去获取数据的fetch方法。
执行的顺序是Model -> View -> Controller。


再举个稍复杂点例子,如图:
1.进入发起页面
demo


2.添加数据
demo


3.跳转成功发布的页面
demo

当用户打开发起页面添加数据,点提交跳转到成功发布的页面,这一过程的顺序就是View -> Controller -> Model -> View。用户看到发起的页面是View,点提交这一步骤是Controller、提交完成数据保存到数据库是Model,从发起页面跳转到成功发布的页面是View。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
submit: function(el) {
var $this = this;
if (basis.pub.isDisable(el)) return;
basis.pub.disable(el); //禁用
basis.pub.clear();
basis.wait.show();

var postData = {
createKey: config.data.createKey,
openID: config.params.openID,
title: $('.q-title', $this.el).val(), //e
desc: $('.q-desc', $this.el).val(), //e
endType: $('.endType', $this.el).val(), //e
picTemplateID: $('.picTemplateID', $this.el).val(), //e
isOpenAnswer: $('.isOpenAnswer', $this.el).val(), //e
itemimg: [],
itemUrlDesc: [],
questionID:config.params.questionID,
isCopy:config.params.copy
};

$('.q-img-options .has-img', $this.el).each(function() {
var tag = $(this),
next = tag.next('.img-item-desc');
postData.itemimg.push(tag.children('.itemimg').val()); //e
if (next.length) {
postData.itemUrlDesc.push(next.children('.itemUrlDesc').val().replace(/,/ig, ','));//e
}
});
postData.itemimg = postData.itemimg.join(','); //e
postData.itemUrlDesc = postData.itemUrlDesc.join(','); //e
...
}

上面的问题在于:
1.用户点击发布时,提交表单前需要挨个获取DOM的val值(标注e的),这样写的坏处就是重复去获取不同DOM,重复去设值
2.当用户提交后跳转到成功发布的页面,这样写的坏处就是跳转后需要重新加载该页面的所有资源(html、css、js、图片等),还需要调很多接口获取数据,等数据获取后又要重复去获取DOM,然后根据业务改变其样式,展示出最终的页面效果


MVC流程图:

mvc

MVC特点:所有通信都是单向的


同样的页面用MVVM来实现,该是怎样的呢?从简单的实例入手,这里我使用了Vue.js,代码如下:
HTML代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="main">
<div class="top box">
<div class="left box-flex-1">
<h1 class="vote_create_count">{{ user.vote_create_count }}</h1><span>累计发起</span>
</div>
<div class="right box-flex-1">
<h1 class="can_vote_count"> {{ user.can_vote_count }} </h1><span>今日可发</span>
</div>
</div>
<div class="mid-name">
<h2 class="nikename">{{ user.nikename }}</h2>
</div>
<div class="user-head" >
<div class="head-image" style='background:url( {{ user.headimage }} ) no-repeat;background-size:100%'></div>
</div>
</div>

JavaScript代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var vm = new Vue({
el:'#main',
data:{
user: {}
},
created:function(){
var self=this;
this.fetch(function(record){
self.user=record;
});
},
methods:{
fetch:function(callback){
...
basis.req.ajax(ops);
},
addEvent:function(){
...
}
}
});

在该代码中,模型(Model)就是vm实例对象的data里的值,如:user;视图(View)就是上面整个Html代码,如:;ViewModel就是vm实例对象,它建立了模型和视图的桥梁。

demo

通过ViewModel能实现View和Model的实时更新,这样写的好处在于:
1.解决不需要重复去获取DOM、重复去操作DOM等事情,关注点能重点放在业务及页面组成上
2.方便组件化即复用
3.接口返回的数据是什么,前端马上展示什么,你变我也变


MVVM流程图:

mvp

MVVM特点:实现数据和视图的双向绑定


3.Vue.js的简单介绍

Vue.js的作者为**Evan You,任职于Google Creative Lab,虽然Vue是一个个人项目,但在发展前景上个人认为绝不输于Google的AngularJs,不要小看一个个人项目,比如一个人创建了backbone.js、underscore.js、coffeescript的前端大神Jeremy Ashkenas**,我们在使用其框架的时候还不是风生水起。

引用Vue的官网(**http://cn.vuejs.org/**)所介绍的那样,其主要特点有两个:

Vue.js的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件。

1.响应的数据绑定

Vue.js的核心是一个响应的数据绑定系统,它让数据与DOM保持同步非常简单。
在使用jQuery/Zepto.js手工操作DOM时,我们的代码常常是命令式的、重复的与易错的。
Vue.js拥抱数据驱动的视图概念。
通俗地讲,它意味着我们在普通的HTML模板中使用特殊的语法将DOM“绑定”到底层数据。一旦创建了绑定,DOM将与数据保持同步。每当修改了数据,DOM便相应地更新。这样我们应用中的逻辑就都是直接修改数据,不必与DOM更新搅在一起。这让我们的代码更容易撰写、理解与维护。

mvvm

实例1:
html代码:

1
2
3
4
<!-- 这是我们的 View -->
<div id="example-1">
Hello {{ name }}!
</div>

js代码:

1
2
3
4
5
6
7
8
9
10
11
// 这是我们的 Model
var exampleData = {
name: 'Vue.js'
}

// 创建一个 Vue 实例或 "ViewModel"
// 它连接 View 与 Model
var exampleVM = new Vue({
el: '#example-1',
data: exampleData
})

效果:

1
Hello Vue.js!

看起来这跟单单渲染一个模板非常类似,但是Vue.js在背后做了大量工作。并且DOM会自动响应数据的变化。我们如何知道?打开你的浏览器的控制台,修改exampleData.name,你将看到上例相应地更新。

总结一下就是使用Vue.js我们不需要撰写任何DOM操作代码:被绑定增强的HTML模板是底层数据状态的声明式的映射,数据不过是存在了普通的JavaScript对象里。我们的视图完全由数据驱动。

实例2:
html代码:

1
2
3
<div id="example-2">
<p v-if="greeting">Hello!</p>
</div>

js代码:

1
2
3
4
5
6
var exampleVM2 = new Vue({
el: '#example-2',
data: {
greeting: true
}
})

效果:

1
Hello!

这里我们遇到新东西。你看到的 v-if 特性被称为指令。指令带有前缀 v-,以指示它们是 Vue.js 提供的特殊特性。并且如你所想象的,它们会对绑定的目标元素添加响应式的特殊行为。继续在控制台设置 exampleVM2.greeting 为 false,你会发现 “Hello!” 消失了。

第二个例子演示了我们不仅可以绑定DOM文本到数据,也可以绑定DOM结构到数据。而且,Vue.js也提供一个强大的过渡效果系统,可以在Vue插入/删除元素时自动实现过渡效果。

也有一些其它指令,每个都有特殊的功能。例如v-for指令用于显示数组元素,v-bind指令用于绑定HTML特性。更多内容可以去官网查看API。


2.组合的视图组件

组件系统是Vue.js另一个重要概念,因为它提供了一种抽象,让我们可以用独立可复用的小组件来构建大型应用。如果我们考虑到这点,几乎任意类型的应用的界面都可以抽象为一个组件树:

components

实际上,一个典型的用Vue.js构建的大型应用将形成一个组件树。后续将详述组件,不过这里有一个假想的例子,看看使用了组件的应用模板是什么样的:
html代码:

1
2
3
4
5
6
7
<div id="app">
<app-nav></app-nav>
<app-view>
<app-sidebar></app-sidebar>
<app-content></app-content>
</app-view>
</div>

你可能已经注意到Vue.js组件非常类似于自定义元素——它是Web组件规范的一部分。实际上Vue.js的组件语法参考了该规范。例如Vue组件实现了Slot API与is特性。但是,有几个关键的不同:

  1. Web组件规范仍然未完成,并且没有浏览器实现。相比之下,Vue.js组件不需要任何补丁,并且在所有支持的浏览器(IE9及更高版本)之下表现一致。必要时,Vue.js组件也可以放在原生自定义元素之内。
  2. Vue.js组件提供了原生自定义元素所不具备的一些重要功能,比如组件间的数据流,自定义事件系统,以及动态的、带特效的组件替换。

组件系统是用Vue.js构建大型应用的基础。另外,Vue.js生态系统也提供了高级工具与多种支持库,它们和Vue.js一起构成了一个更加“框架”性的系统。

最近还在继续深入学习Vue.js,最后想说:
个人认为前端的一些技术都是融会贯通的,学习一门语言或者框架本身并不是为了学习它的技术,最重要的是学习它的思维,只有思维层面得到了延伸,学习其他技术的时候才会得心应手。Vue带给我们的是前端一种解决问题的新的思维。

一句话概括:

现阶段前端人员最大的问题不是技术实现,而是思维转变。