前端答疑-对象引用-vue共享数据源的三种方式

事情发生在上周(2019-06-06)团队技术分享的时候。
原由在于一个问题:vue 中多个组件如何使用同一个变量,咱们叫这个变量为 baseConfig 吧。
说实话我没想到那么多人不理解其中的知识。今天我整理一下发出来。html

方案一:VUEX

什么是 Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 能够帮助咱们管理共享状态,并附带了更多的概念和框架。这须要对短时间和长期效益进行权衡。前端

状态自管理应用包含如下几个部分:vue

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入致使的状态变化。

如下是一个表示“单向数据流”理念的简单示意:clipboard.pngvuex

Vuex 的应用

其实看完了上面的介绍,咱们就明白,这是一个很是符合咱们需求的工具。那么咱们就来看看怎么去应用。编程

  • mapState 辅助函数 当一个组件须要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,咱们可使用 mapState 辅助函数帮助咱们生成计算属性。
  • 从下面的例子能够看到,咱们子组件中的值是经过计算属性来从 store 中获取的。这样在经过 mutations 等方式改变以后,咱们的值也会动态更新。
  • 固然,你的页面简单的时候,也不须要再去使用 Vuex,考虑一下后面的方案吧。
const store = new Vuex.Store({
  state: {
    baseConfig: {
        server_name: 'lilnong.top'
    }
  }
})
// 建立一个 User 组件
const Serv = {
  template: `<div>{{ server_name }}</div>`,
  computed: {
    server_name() {
      // this.$store
      return store.state.baseConfig.server_name
    }
  }
}
const app = new Vue({
  el: '#app',
  store, // 这样能够把 store 的实例注入全部的子组件
  components: { Serv },
  template: `
    <div class="app">
      <serv></serv>
    </div>
  `
})

方案二:父子组件传参

父传参的方式

baseConfig 须要定义在最外面,而后给全部的子组件都传递进去,当有改变的时候,子组件也会跟着改变。数组

{
    data: {
        baseConfig: {
            server_name: 'lilnong.top'
        }
    }
}
<child :server_name="baseConfig.server_name" :baseConfig="baseConfig">

子组件接受参数的方式

每一个子组件都须要接收。微信

{
    props:['server_name', 'baseConfig'],//这种是无默认值,无类型检查的,正常使用不推荐这种写法
}

父子组件方案的优缺点

  1. 全部的父组件都须要传递参数,全部的子组件都要接收参数。
  2. 传入的问题能够经过,传入一个对象的全部参数来解决

方案二:路由组件传参

该方案也叫方案二,并非我写错了,是由于他们的场景是同样。
在 Vue Router 的路由中,咱们把组件配置在 routes 中,致使咱们没法在模板之中传递参数。
这里咱们须要使用他提供的 props 属性来传参,文档地址jsRun测试地址lilnong.top测试地址app

const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]
const router = new VueRouter({
  routes // (缩写) 至关于 routes: routes
})
const app = new Vue({
  router
}).$mount('#app')
<div id="app">
  <h1>Hello App!</h1>
  <p>
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <router-view></router-view>
</div>

方案三:全局对象(store 模式)

简单状态管理起步使用 --官方文档框架

全局对象

就是以下使用,定义一个全局对象,而后修改这个全局对象就行了异步

const sourceOfTruth = {}
const vmA = new Vue({
  data: sourceOfTruth
})
const vmB = new Vue({
  data: sourceOfTruth
})

store 模式

原理上来说,仍是全局对象,可是经过简单的规定,来明确数据流向

var store = {
  debug: true,
  state: {
    message: 'Hello!'
  },
  setMessageAction (newValue) {
    if (this.debug) console.log('setMessageAction triggered with', newValue)
    this.state.message = newValue
  },
  clearMessageAction () {
    if (this.debug) console.log('clearMessageAction triggered')
    this.state.message = ''
  }
}
var vmA = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})

var vmB = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})

clipboard.png

争论核心:全局对象方案究竟行不行?原理?

好了,三种方案这里就已经介绍完了。那开始看看咱们的争论:全局放个对象的方式不行(对方观点),数据更新时组件不会自动更新

先说原理,内存地址

vue 数据绑定的原理你们都懂吧?经过 defineProperty 来劫持,Dep 收集依赖等等。
对于对象类型的数据,咱们变量里面保存的实际上是一个指向堆的地址,咱们来看下面的这个例子。

var obj = {};//定义了一个对象,`obj` 存放的是一个地址
obj.a = 1;//经过 `obj` 的地址,找到对象,而后给对象里面放了 `a=1` ;
var obj1 = obj;//把 `obj` 的地址,给 `obj1` 复制了一下
obj1.a = 2;//经过 `obj1` 的地址,找到对象,而后给对象里面放了 `a=2` ;
//这个时候,对象里面存放的就是{a:2}//console.log(obj, obj1)
//这里引出了另外一个问题 深拷贝与浅拷贝

这里也就是个人核心原理。

  1. 定义一个对象(保存在 window 上)
  2. 经过对象变量保存的是地址的原理
  3. 咱们在其余组件都用 window 上的对象以此来达到目的。

再说场景,细化问题

是否是看到上面的原理好简单?可是每每不是这么简单,下面我们分析一下状况

  1. window 上没绑对象,而是其余类型这么办?好比说 nullundefined
    我也没辙,大哥,看好上面的原理,主要原理就是地址引用
  2. 对象覆盖,就是以下的这个赋值场景。
    其实我理解你是想给 obj 从新都赋值一下。

    obj={};
    obj2 = obj;
    obj.a = 1;
    obj2 = {a:2,b:3};//这里把 obj2 的地址换成了新的一个对象
    //console.log(obj, obj2)

    可是不能这样写,正确操做以下:

    1. Object.assign(obj2, {a:2,b:3})
    2. 一个一个值的写 obj2.a=2;obj2.b=3;
  3. 后添加的属性,没有计入 vue 的数据观察队列(新手常常犯的错误

    1. 对象变动检测注意事项

      于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
      var vm = new Vue({
       data: {
         a: 1
       }
      })
      // `vm.a` 如今是响应式的
      
      vm.b = 2
      // `vm.b` 不是响应式的

      对象解决方案 Vue.set(object, propertyName, value)
      对象解决方案(实例内) this.$set(object, propertyName, value)

    2. 数组更新检测
      将一些方法进行了封装 push()、pop()、shift()、unshift()、splice()、sort()、reverse()
      经过上面的方法来改变数组能够监听到改变。
      因为 JavaScript 的限制,Vue 不能检测如下数组的变更:

      1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
      2. 当你修改数组的长度时,例如:vm.items.length = newLength

总结

共享数据三种方法

  1. vuex
    大而全,使用复杂
  2. 组件值传递
    原生自带,使用复杂,适合组件相关数据
  3. store
    简单,不适合复杂项目。工程的话,仍是推荐 vuex

对象引用须要注意的地方

  1. 不能给变量二次赋值obj2={}
  2. 只有对象类型才是存地址, ArrayObject
    StringNull等不包括在内
  3. 增长数据要注意是否被观察到

    1. 对象:注意 Vue.set
    2. 数组:使用规定方法

测试地址,采用 setTimeout 来模拟异步操做。当时苦的一批,完了还没保存。性感码农,在线编程
成功的说服了在场的兄弟们,而后周四就拖堂了。

资料

  1. VUEX 中文站
  2. 状态管理 --vue官网

微信公众号:前端linong

clipboard.png