Vue的组件和数据流管理

1、灵活的Vue

1. 高效的追踪机制

      Vue经过Object.defineProperty定义数据的存取描述符(set和get),来追踪数据变化。css

      在Vue组件初始化时,data下的属性会被循环遍历添加上set和get,直到全部的子孙属性都被添加完。当data的某一属性值发生了变化(执行set)时,它就会向订阅了该数据的组件发布通知,去更新组件。前端

      在set里,组件能够订阅该数据变化的通知。vue

      set和get叫对象的存取描述符,与存取描述符对应的叫数据描述符。两者不能共存,默认状况下,是数据描述符。以下: react

      Vue在初始化的时候,会删掉数据描述符,加上存取描述符。这种方式追踪数据变化,比React更高效。

      React用比较引用来追踪数据变化的方式。在setState的时候,React会用新的state直接替换掉旧state。webpack

      即使不改变数据的数值属性,只要数据的引用地址发生变化了,React组件也会触发更新。若是不加处理,React组件会多不少次更新。

      以下面,每次执行handleClick,即使没有改变数据的数值,(但引用地址发生变化了),组件也会从新渲染。这个渲染是不必的。ios

      为了不组件的这种没意义的渲染,React推出了PureComponent(纯组件),帮助开发者作一层数据的浅比较,仅引用地址发生变化的,就不必从新渲染了。开发的时候,还能够拦截shouldComponentUpdate生命周期钩子,显式地干预是否须要从新渲染组件。

      在获得通知数据变化时,React和Vue都采用了vDOM diff算法,各自去更新组件。在追踪数据变化上,Vue比React更加高效。es6

2. 使人尴尬的v-model

      Vue没法检测对象属性的增删变化和数组索引长度的变化,在定义object类型的数据时,通常把它下面的属性也定义了。可是,JS是一门动态语言,能够不预先定义对象,而且任意地操做对象的属性。结合v-model使用时,object类型数据的属性也能够不用预先定义。web

      例如,在表单型的组件里,一个常见的行为,不给formData定义title、type等这些属性,v-model会自动响应用户输入,并添加属性到formData上。ajax

<template>
    <div>
        <input v-model="formData.title" placeholder="请输入标题" />
        <select v-model="formData.type">
            <option value="1">类别</option>
        </select>
    </div>
</template>
data() {
    return {
        formData: {}
    }
}
复制代码

      props是组件间自上向下通讯数据的方式,子组件逆向修改props,Vue会警告应当避免直接操做props,用data或者computed属性代替,修改值也不会被传递到父组件。算法

      然而,借助v-model,object类型props值,也能够由子组件传递给父组件。简单类型的props值仍旧没法修改。

      原来,v-model内部检测到绑定的是对象的key时(indexOf('.') > 0),会调用set方法,更新属性到对象上,并给组件添加了订阅事件。

Vue1.0采用的是双向数据绑定,Vue2.0采用的是单向数据流,即只能父对子通讯,子对父通讯要用回调函数的方式。 Vue3.0之前没法检测到数据属性的增删变化,经常使用Vue.prototype.$set和JSON.parse(JSON.stringify)去纠正这一点。将来Vue3.0将用proxy代替Object.defineProperty监听数据变化,proxy将直接监听对象,而不是监听属性,它能够检测到数组和对象的增删变化。

3. 做用域问题

      JS开发有三大难点,原型、闭包和做用域。在es6 module的帮助下,原型和闭包的坑已经很少了,剩下的做用域成了最常遇到的坑。

      在React当中,给组件添加事件必需要修正函数的执行做用域。像下面这样,onClick是定义在全局做用域上的,它的this就是undefined。由于js是静态做用域语言,它做用域是定义时肯定的。在这里,要拿到React组件实例内部的state,全部bind函数的做用域到组件实例上。

      在每个事件函数上,不厌其烦地绑定函数的做用域,这在Vue里是根本不care的。Vue会在组件实例化的时候,把methods里的函数做用域都绑定到组件实例上

      CSS的做用域问题,也是困扰前端开发者的大问题。命名冲突的className会带来意想不到的惊喜,采用提高权重的方式,能够提升选择器的竞争力,但这会让项目更加臃肿,CSS管理混乱不堪,维护开发很是麻烦。

      Vue采用相似shadow DOM的方式对CSS进行封装,添加了scoped属性后,CSS只做用在组件内部,组件之间的CSS不会互相影响。

      React采用css in js、css module、style-component的方式封装CSS,都没有Vue好用。

      React的css in js写法

4. Vue的灵活性

      相比React,Vue和JS这门语言契合度是最高的,它没有科班化的数据流管理,没有刀耕火种的JS编写方式。

      Vue的追踪数据机制、v-model双向绑定和js/css做用域,都很巧妙的利用JS做为一门动态语言的优点。这使Vue成为一门很是容易上手的技术框架,在快节奏、频繁的迭代的开发需求中占有一席之地。我在商业产品部一年多一共开发和维护了9个项目,包括1个react、1个angular和7个vue项目。Vue很是容易上手,有些简单的需求,就让后端同窗代劳了。

      一直以来,都有一个争议点,Vue适合作小型项目,React适合开发大型项目。之前,Vue饱受诟病的是数据流管理,实际上,如今Vue2和React已经相差不大了,借助v-model和vuex,Vue甚至比React更胜一筹。将来Vue3会用Typescript写,构建项目将会更加稳健。

二. 5种数据流管理方式

1. props + emit回调

react和vue都在用的组件通讯方式之一,简约又简单。

依赖组件父子关系。

以下,子组件实例化过程当中,若是发现父组件订阅了子组件的事件,就会把订阅的事件添加到events列表里,以此容许开发者来发布事件,即$emit事件。

添加订阅者到event队列。

若是是祖孙级组件和兄弟级组件,它们之间的通讯就须要不少个emit回调在组件之间传递。这种通讯方式会组件耦合性太强,程序稳定性下降。

2. props + eventBus

适用于全部组件,不依赖组件之间的嵌套关系。

在EventBus里,实例化一个Vue实例做为一个观察者,全部的组件都做为订阅者。

// EventBus.js
import Vue from 'vue';

export default new Vue();
复制代码
// 添加订阅者
eventBus.$on('reset-preview', this.closeHandler);
// 发布通知
eventBus.$emit('preview');
复制代码

Vue内部实现了on和emit两个方法,on方法添加订阅者到队列里,emit在事件变化时,发布通知给订阅者。

EventBus虽然不依赖组件嵌套关系,可是数据流向是随意的,对于复杂的业务需求,难以支撑。因此须要一个中心化的观察者,观察数据变化,自上而下组件响应数据变化,自下而上更新数据变化到观察者中心。

emit回调和eventBus的区别是什么?

emit回调是强调组件关系,父组件是订阅者,子组件是发布者。 eventBus不关心组件关系,eventBus实例化的一个实例是发布者,组件都是订阅者。

3. vuex

优势:

  1. 解决全部祖孙级和父子级组件嵌套的数据流问题
  2. 缓存数据,减小http请求次数
  3. 单向数据流,数据流向更清晰
  4. 减小props和回调函数,组件之间解耦

使用vuex须要注意的点

  1. 与后台约束性强的数据,不宜写到vuex里。

vuex和vue同样都是经过劫持setter/getter追踪数据变化的,因此vuex也不能检测到array和object的增删。

例如表单型的数据,须要增长字段,向后台提交。vuex没法处理属性增删,$store.commit到store里,store里的数据不会更新。

  1. 在数据生命周期结束时,清空store里缓存的数据。

例如,一个复杂类型的数据,从服务端请求出来存到vuex里,而后在多个组件之间传递,在数据处理结束以后,又提交给服务端。此时,应该清除vuex保存的数据,以便在下一次打开页面时,vuex里的数据是干净的。

通常,在组件的created生命周期检查vuex缓存是否存在,没有缓存,则从新拉数据。在组件生命周期结束前,重置store,清除数据缓存。

什么状况下适合用vuex呢?

  1. 在须要缓存数据的时候
  2. emit实在解决不了时候

只有在用React实在解决不了的时候,才用Redux。--------Redux做者

过分使用vuex,将使项目变得臃肿,组件之间耦合度增长。

4. route

传统型,适用于各页面之间的数据传递。对于一些须要粘贴url让其余人访问的需求,须要在router里加上必须参数,而不能在vuex里。

好比筛选列表页,媒体流量桶管理页。这种页面结构相同,种类又众多。不能拆分红多个页面,但又须要独立的页面展现效果,适合把数据保存在route里。

使用router传参时,须要注意:

  1. 组件生命周期钩子(确保必需的route参数) 1.1 对于依赖router保存数据的页面,注意在vue组件生命周期钩子里加校验 1.2 父子组件的渲染顺序是,父created ---> 子created ---> 子mounted ---> 父mounted,因此在created生命周期里校验route参数,确保组件render之后有正确的数据显示。

1.3 watch route变化,及时更新数据。

5. 组件实例方法调用

用于嵌套的表单型组件

好比建立父任务时,父任务表单里同时又能够添加子任务,子认为有分别能够添加不一样的任务奖励规则。 好比建立订单时,能够添加广告组、广告计划、广告创意,广告创意里能够添加各种素材。 这些嵌套复杂的表单型组件,比较适合用这个。

三. 组件拆分原则

1. 善用slot,开闭规则。

修改封闭,扩展开放。

2. 功能拆分,单一职责规则

功能组件仍是ui组件?简化使用。 好比pagnition处理total < 1时,不显示分页器 好比筛选列表组件,保留UI部分,把提交按钮用slot写进去 功能组件,好比upload组件

3. 最少知道原则

尽量减小外部依赖 组件内的功能点,对外界的依赖越少越好。 越简单越好。 参考设计的最高境界,Kiss规则。

四. 一些注意事项

1. 利用$next获取组件渲染以后的dom

2. v-for的key

3. 覆写复杂类型的props

4. ajax的封装

建立axios的实例对ajax的封装,减小处理回调的代码量。直接修改axios的拦截器会污染整个工程ajax调用规则。对于屡次import进来的文件,webpack只会打包一次。

相关文章
相关标签/搜索