前端答疑-對象引用-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

相關文章
相關標籤/搜索