在 《Clean Architecture》里第 6 章介绍 functional programming ,有提到一个很重要的观念 - 不可变动性 (immutable)。
这是什麽意思呢?在 JavaScript 中,对於复杂型别 (常见的型别是 Object 或 Array) 的修改,会选择替换掉物件本身,就能做到 immutable。
那这件事和 watch 有什麽关系呢?
在 Vue.js 的画新画面逻辑中「资料改变,画面就改变」,要怎麽样才是「正确的资料改变」呢?
在官网文件中的 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) { /* ... */ }
}
}
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
被替换。
把上述的例子,拿来 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 会查觉到记忆体位址修改,所以要更新画面。
有时候,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
绘制自己铁人赛文章资料所构成的图表 最後一天就来做个本次文章系列的总结吧,我用puppteer爬取了...
上次的画完系统流程图後,接续开始着手画系统架构图。因之前是资讯管理系毕业,专题中有画过系统架构图,所...
新手在刚开始学习时,在工作上往往会遇到许多的困难,而在这边我有一些建议可以给新手 1. 学习怎麽Go...
今天久违的去运动了一下 回家居然晕眩到不行 决定今天的可成先分个1/2来看QQ 函式:程序码包装在一...
A new day!!今天要来讲的内容比较简单,篇幅甚至有点短ヽ(✿゚▽゚)ノ,但知识量可不会减少喔...