资讯专栏INFORMATION COLUMN

vue在项目中的应用

spacewander / 1305人阅读

摘要:而使用的中方法来实现和层的关联,他可以精确的将数据的变化映射到对应视图上,和的复杂度没有正向关系。在版本中会看到下面的代码类为每个的每个属性添加方法,并且实现当对象属性值发生变化时,会发出一个通知。

1,为什么要用vue

大前端目前已经到一个空前的繁荣阶段,各种框架类库层出不穷,我想每选择一个框架,肯定都能找到高度同质化的两到三个框架。那么在目前mvvm盛行的前端架构下,我们为什么选择了vue,而不去用react,不用angular呢?

首先他们都是非常典型的前端mvvm框架,很好的解决了业务逻辑中view和model之间的关系,用到这些框架之后,我们不会再像之前使用jQuery一样,页面的展示和数据有高度的耦合性,甚至不在需要选择器了。

而vue相比于react、angular,首先他是一位我们国内的开发者开发的,有很好的API文档、样例等。国外的技术性文档在翻译的过程中对译者还是有很高要求,否则对于大部分开发者通过简单阅读之后还是很难有较深的理解;

其次他有一个很好的入门起点,对于不是那么熟练node,npm,webpack和ES6的开发者来讲,也可以看着文档demo很快的入手,进入到项目开发中,而且vue组件的模板写法对于使用过传统模板的开发者来说更易于理解上手。

虽然react的jsx能够允许把设计师提供的HTML原型直接黏贴过去,但依然有大量的改造工作,而这部分的改造对于开发者来说就像是需要掌握一门新的模板语言一样,比如开发者要手动把class和for属性替换成className和htmlFor,还要把内联的style样式从css语法改成JSON语法等。

在数据的双向绑定方面,angular采用了脏检查的方式,当有数据发生变化时,对所有的数据和视图的绑定关系做一次检查,当随着ng-app根节点下的DOM变得越发复杂的时候,脏检查的效率会变得越来越低,这就要求我们在写业务逻辑的过程中,需要不断的去考虑怎么解决框架所带来的的性能瓶颈。而vue使用的ES5中Object.defineProperty()方法来实现model和view层的关联,他可以精确的将数据的变化映射到对应视图上,和DOM的复杂度没有正向关系。(当然vue在这里的劣势就是不能够在不支持ES5的浏览器上使用)

2,vue的数据观察者模式,先从源码开始

var data = { a: 1 };
var vm = new Vue({
    data: data
})
vm.$watch("a", function() {
    console.log("the data a value changed");
});

vm.a = 2;

这个实例中,当每次改变了data中a属性的值,都会输出 the data a value changeed,我们看看这个过程中到底发生了什么。

在Vue 2.2.4版本中会看到下面的代码:

/**
 * Observer class that are attached to each observed
 * object. Once attached, the observer converts target
 * object"s property keys into getter/setters that
 * collect dependencies and dispatches updates.
 */
var Observer = function Observer (value) {
  this.value = value;
  this.dep = new Dep();
  this.vmCount = 0;
  def(value, "__ob__", this);
  if (Array.isArray(value)) {
    var augment = hasProto
      ? protoAugment
      : copyAugment;
    augment(value, arrayMethods, arrayKeys);
    this.observeArray(value);
  } else {
    this.walk(value);
  }
};

Observer类为每个object的每个属性添加getter、setter方法,并且实现当对象属性值发生变化时,会发出一个通知。

def( value, ‘__ob__’, this );

def方法是通过ES5的Object.defineProperty()来注册对象,

/**
 * Define a property.
 */
function def (obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  });
}

this.observeArray 和 this.walk 方法中通过递归实现了对object的深度遍历并且对每个属性绑定setter、getter方法

/**
 * Walk through each property and convert them into
 * getter/setters. This method should only be called when
 * value type is Object.
 */
Observer.prototype.walk = function walk (obj) {
  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
    defineReactive$$1(obj, keys[i], obj[keys[i]]);
  }
};

/**
 * Observe a list of Array items.
 */
Observer.prototype.observeArray = function observeArray (items) {
  for (var i = 0, l = items.length; i < l; i++) {
    observe(items[i]);
  }
};

/**
 * Attempt to create an observer instance for a value,
 * returns the new observer if successfully observed,
 * or the existing observer if the value already has one.
 */
function observe (value, asRootData) {
  if (!isObject(value)) {
    return
  }
  var ob;
  if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) {
    ob = value.__ob__;
  } else if (
    observerState.shouldConvert &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value);
  }
  if (asRootData && ob) {
    ob.vmCount++;
  }
  return ob
}

/**
 * Define a reactive property on an Object.
 */
function defineReactive$$1 (
  obj,
  key,
  val,
  customSetter
) {
  var dep = new Dep();

  var property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  var getter = property && property.get;
  var setter = property && property.set;

  var childOb = observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      var value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
        }
        if (Array.isArray(value)) {
          dependArray(value);
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      var value = getter ? getter.call(obj) : val;
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if ("development" !== "production" && customSetter) {
        customSetter();
      }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = observe(newVal);
      dep.notify();
    }
  });
}

注意看set里面的dep.notify方法,这个就是每次对象属性值发生改变的时候,发出一个通知,看看notify里面都干了哪些事情:

/**
 * A dep is an observable that can have multiple
 * directives subscribing to it.
 */
var Dep = function Dep () {
  this.id = uid$1++;
  this.subs = [];
};

Dep.prototype.addSub = function addSub (sub) {
  this.subs.push(sub);
};

Dep.prototype.removeSub = function removeSub (sub) {
  remove(this.subs, sub);
};

Dep.prototype.depend = function depend () {
  if (Dep.target) {
    Dep.target.addDep(this);
  }
};

Dep.prototype.notify = function notify () {
  // stabilize the subscriber list first
  var subs = this.subs.slice();
  for (var i = 0, l = subs.length; i < l; i++) {
    subs[i].update();
  }
};

notify是Dep中的一个方法,Dep主要实现了一个消息列表的管理,每一条消息会通过addSub方法push进来,当触发notify时,会把所有消息update一次(在update中会做diff判断,没有发生改变的状态,不会被做逻辑处理)

接下来看看这个通知是怎么被接收到的,类库里面有一个Watcher类专门处理被接受到的消息,Watcher的构造函数如下:

/**
 * A watcher parses an expression, collects dependencies,
 * and fires callback when the expression value changes.
 * This is used for both the $watch() api and directives.
 */
var Watcher = function Watcher (
  vm,
  expOrFn,
  cb,
  options
) {
  this.vm = vm;
  vm._watchers.push(this);
  // options
  if (options) {
    this.deep = !!options.deep;
    this.user = !!options.user;
    this.lazy = !!options.lazy;
    this.sync = !!options.sync;
  } else {
    this.deep = this.user = this.lazy = this.sync = false;
  }
  this.cb = cb;
  this.id = ++uid$2; // uid for batching
  this.active = true;
  this.dirty = this.lazy; // for lazy watchers
  this.deps = [];
  this.newDeps = [];
  this.depIds = new _Set();
  this.newDepIds = new _Set();
  this.expression = expOrFn.toString();
  // parse expression for getter
  if (typeof expOrFn === "function") {
    this.getter = expOrFn;
  } else {
    this.getter = parsePath(expOrFn);
    if (!this.getter) {
      this.getter = function () {};
      "development" !== "production" && warn(
        "Failed watching path: "" + expOrFn + "" " +
        "Watcher only accepts simple dot-delimited paths. " +
        "For full control, use a function instead.",
        vm
      );
    }
  }
  this.value = this.lazy
    ? undefined
    : this.get();
};

/**
 * Scheduler job interface.
 * Will be called by the scheduler.
 */
Watcher.prototype.run = function run () {
  if (this.active) {
    var value = this.get();
    if (
      value !== this.value ||
      // Deep watchers and watchers on Object/Arrays should fire even
      // when the value is the same, because the value may
      // have mutated.
      isObject(value) ||
      this.deep
    ) {
      // set new value
      var oldValue = this.value;
      this.value = value;
      if (this.user) {
        try {
          this.cb.call(this.vm, value, oldValue);
        } catch (e) {
          handleError(e, this.vm, ("callback for watcher "" + (this.expression) + """));
        }
      } else {
        this.cb.call(this.vm, value, oldValue);
      }
    }
  }
};

当object属性的值发生变化时,会触发watcher通过构造函数传入的callback方法,最终实现对数据变化的监听。

3,项目中的vue

在传统的团队协作开发中,通常会按照页面的粒度来分工合作,团队每个成员负责一个页面或者多各页面,基本上细化到一个页面的逻辑至少由一位成员来负责完成;

Vue按照组件化的方式开发,能够把粒度化拆解的更细:

    

页面中可以按照功能拆接出若干个模块,其中每个模块的逻辑都相对独立。当前页面的所有数据逻辑在一个model里面管理, 以map-phono-cp为例:

var mapPhotoCp = Vue.extend({
    extends: baseCp,
    template:template,
    created:function(){
    },
    methods:{
        onOrderDetailReady:function(data){
        },
        initMapLink:function(){
        },
        statusInArray:function(status,array){
        },
        tap:function(){
        }
    },

    data:function(){
    }
});

Vue.component("map-photo-cp", mapPhotoCp);

实例化出来一个组件,通过Vue.component来注册成Vue的全局组件,这样在初始化Vue的时候,会将页面DOM中对应的所有组件解析。

每一个组件都可以直接操作页面的model,当model发生改变,vue会将数据的变化直接映射到view上。这样每个组件的开发者都会有一个更清晰的开发思路,不会再有复杂的DOM操作,不会再有各种获取DOM节点绑定事件的行为,让开发变得更顺畅起来。

最后也来展望一下vue的未来,vue作为mvvm的后起之秀,有着非常高的关注度,这不光是前面提到的一些特点,主要也继承了之前框架的大部分优点,比如在vue2.0中也支持了virtual DOM,使DOM的操作有更高的效率,并且支持SSR(server side render),对有首屏渲染加速有更好的支持。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82054.html

相关文章

  • Vue作为组件前端项目中的应用&Vue.set 的用法

    摘要:业务背景是,在的前端项目中加入作为组件。但随着需要登录的页面的增多,多个页面都需要添加相同的,,以及前端登录逻辑,所以在原先的项目中添加了,将重复的添加的代码加入到了文件中,然后通过方法将对象挂载到某个的节点上。 业务背景是,在jq的前端项目中加入Vue作为组件。 原本的登录功能是每个页面加一个登录弹窗(手机号+验证码验证登录),然后发ajax请求到后端,登录成功后再进行一些操作。 但...

    hersion 评论0 收藏0
  • 关于Vue2一些值得推荐的文章 -- 五、六月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    sutaking 评论0 收藏0
  • 关于Vue2一些值得推荐的文章 -- 五、六月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    khs1994 评论0 收藏0
  • vue从零开发和部署一款移动端pwa单页应用

    摘要:另外,单页应用因为数据前置到了前端,不利于搜索引擎的抓取。所以我们需要对自己的单页应用进行一些优化。 前言 最近秋招之余空出时间来按自己的兴趣动手做了一个项目,一个基于vue-cli3.0, vue,typescript的移动端pwa,现在趁热打铁,将这个项目从开发到部署整个过程记录下来,并将从这个项目中学习到的东西分享出来,如果大家有什么意见或补充也可以在评论区提出。先介绍一下这个项...

    Channe 评论0 收藏0
  • Vue 2019开发者图谱

    摘要:为了便于您更清晰的理解的体系架构,在这里我将为您展示年开发者知识图谱,它包含了所有开发过程中的关键部分。在数据展示前端导入导出图表面板数据绑定等场景无需大量代码开发和测试,可极大节省企业研发成本并降低交付风险。 作为 Vue 的初学者,您或许已经听过很多关于它的专业术语了,例如:单页面应用程序、异步组件、服务器端呈现等,您可能还听过和Vue经常一起被提到的工具和库,如Vuex、Webp...

    cgspine 评论0 收藏0

发表评论

0条评论

spacewander

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<