用 watch 搭配服用 immutable

在 《Clean Architecture》里第 6 章介绍 functional programming ,有提到一个很重要的观念 - 不可变动性 (immutable)。

这是什麽意思呢?在 JavaScript 中,对於复杂型别 (常见的型别是 Object 或 Array) 的修改,会选择替换掉物件本身,就能做到 immutable。

那这件事和 watch 有什麽关系呢?

在 Vue.js 的画新画面逻辑中「资料改变,画面就改变」,要怎麽样才是「正确的资料改变」呢?

Vue.js 的 watch

官网文件中的 API 介绍中,有完整的 watch 说明。

在此将它的介绍拆成两个部份,会有助於观念上的厘清。
对於四种简单型别的 watch 方式。

第一种就是直接用一个和 data 同名的 watch 。让资料在 set 的同时,可以呼叫一次这个放在 watch 的 function

export default {
  data: {
    a: 1
  },
  watch: {
    a: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    }
  }
}

第二种,就是将 method 放到 watch 上面,因为有时候有些 method 会主动触发,有时又要被动触发,就必须这样安排,不过另一种做法也可以放在第一种方法的 function 中再呼叫 someMethod

export default {
  data: {
    b: 2
  },
  watch: {
    // string method name
    b: 'someMethod',
  },
  methods: {
    someMethod() {
      // do something...
    }
  }
}

第三种做法,watch 里放的就不只是 method 了。而是一个 config ,注册了一个 handler 并且设定它要 deep:true 的这样触发。

第四种做法就出现 immediate 这个关键字,并且也是设定成 true。

export default {
  data: {
    c: 3,
    d: 4
  },
  watch: {
    // the callback will be called whenever any of the watched object properties change regardless of their nested depth
    c: {
      handler: function (val, oldVal) { /* ... */ },
      deep: true
    },
    // the callback will be called immediately after the start of the observation
    d: {
      handler: 'someMethod',
      immediate: true
    }
  }
}

最後一做法,在范例程序码中,有出现较深的资料结构。
如果 watch 较深的资料时,可以直接用 e.f 来监聪深度的结构改变。

export default {
  data: {
    e: {
      f: {
        g: 5
      }
    }
  },
  watch: {
    // you can pass array of callbacks, they will be called one-by-one
    e: [
      'handle1',
      function handle2 (val, oldVal) { /* ... */ },
      {
        handler: function handle3 (val, oldVal) { /* ... */ },
        /* ... */
      }
    ],
    // watch vm.e.f's value: {g: 5}
    'e.f': function (val, oldVal) { /* ... */ }
  }
}

用替换了哪个记忆体位址了解 immutable

let user = {
  name: 'Chris',
  age: 18,
};

画成记忆体图,如下图 (假设 JavaScript 是以 call by sharing 实作的话)

修改的话,可以画成下图,接下来就来解释两种修改方式。

如果是 mutable (可变的,就直接改掉 user 的成员变数所指向的位置),将 user.age 改成 19 的动作,就是指向 0xFF0400 指向 0xFF0408

程序码如下

let user = {
  name: 'Chris',
  age: 18,
};

user.age = 19

对於修改记忆体位址的位置来说,就不是 user 被替换,而是 user.age 的位置被替换。

如果要 immutable 就是换掉 user 为目标。(修改的是蓝色线的指向,要指向新的一个物件),将 user 改成 另一个 object 的动作,就是指向 0xFF0200 指向 0xFF0210

程序码如下

let user = {
  name: 'Chris',
  age: 18,
};

user = {
  name: 'Chris',
  age: 19,
}

对於修改记忆体位址的位置来说,就是 user 被替换。

那 immutable 和 Vue 的 watch 有什麽关系?

把上述的例子,拿来 vue 里面放,就像这样,也许会放在 vuex 不过意思差不多就是这样。

有个 data 里面有一个深一点的资料结构,也许是 object 也许是 array (在此用 object 当例子)

export default {
  data () {
    return {
      user {
        name: 'Chris',
        age: 18
      }
    }
  }
}

Vue.js 可以监听的范围,基本上只要是 return {} 这一层里面的变数 watch 都可以正常连动。以这个例子来说,就是修改 user 就可以正常连动,但是有时候我们只是想要修改 age 怎办?

就可以准备一个已经改好的物件,{ name: 'Chris', age: 19} 来替换掉 user 原本的物件就可以了。这样一来,对 user 来说就不是修改,而是替换,也可以让 vue.js 保证 watch 可以连动,不需要查询这麽特别又复杂的使用方式。

export default {
  data () {
    return {
      user {
        name: 'Chris',
        age: 18
      }
    }
  },
  watch: {
    user() {
      console.log('update other data');
    }
  }
  methods: {
    updateUserAge() {
      this.user = {
        ...this.user,
        age: 19
      }
    }
  }
}

updateUserAge 就是一个简单的做法,让你在执行时, watch 的部份保证会连动到。Vue.js 会查觉到记忆体位址修改,所以要更新画面。

Vue.js 的连动更新画面

有时候,vuex 的资料结构太深,mutate 只修改成员变数时,也会造成无法正确触发 getters 的问题,导致画面没有正确更新

const store = new Vuex.Store({
  state: {
    user: {
      name: 'Chris',
      age: 18
    }
  },
  mutations: {
    userName (state, { name }) {
      // 这样可能 (我是说可能) 就会出现问题
      state.user.name = name
    }
  }
})

但是,只要保持 immutable 的观念,让更新。在官网的介绍中,叫 Reactivity Rules

const store = new Vuex.Store({
  state: {
    user: {
      name: 'Chris',
      age: 18
    }
  },
  mutations: {
    userName (state, { name }) {
      // 这样一定可以更新
      state.user = {
        ...state.user,
        name
      }
    }
  }
})

<<:  任务开发检讨

>>:  Day22 Lab 2 - Object storage的RAID实作1

Not only MordernWeb,But also Data Club—阅读建议、文章索引、结语

绘制自己铁人赛文章资料所构成的图表 最後一天就来做个本次文章系列的总结吧,我用puppteer爬取了...

个人笔记 维修单派工 系统架构图

上次的画完系统流程图後,接续开始着手画系统架构图。因之前是资讯管理系毕业,专题中有画过系统架构图,所...

Day 7. 关於.NET新手遇到问题,我是这样建议

新手在刚开始学习时,在工作上往往会遇到许多的困难,而在这边我有一些建议可以给新手 1. 学习怎麽Go...

D12 - 彭彭的课程# Python 函式基础:定义并呼叫函式(1)

今天久违的去运动了一下 回家居然晕眩到不行 决定今天的可成先分个1/2来看QQ 函式:程序码包装在一...

[Day 16] v-text和v-html

A new day!!今天要来讲的内容比较简单,篇幅甚至有点短ヽ(✿゚▽゚)ノ,但知识量可不会减少喔...