Blog Logo

初识Vue.js

写于2016-04-12 13:23 阅读耗时18分钟 阅读量


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 不是一种技术,仅是一种理念

/** 模拟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.添加事件

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。

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代码:

<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代码:

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代码,如:{{xxx}};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代码:

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

js代码:

// 这是我们的 Model
var exampleData = {
  name: 'Vue.js'
}

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

效果:

Hello Vue.js!

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

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

实例2: html代码:

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

js代码:

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

效果:

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代码:

<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带给我们的是前端一种解决问题的新的思维。

一句话概括:

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

Headshot of Maxi Ferreira

怀着敬畏之心,做好每一件事。