Observable

observable use proxy to return a proxy object, the proxy object can be monitored by observe.

1
import { observable } from "dob"

Usage

1
2
3
4
5
6
7
8
// es5
observable(obj)

// esnext
@observable
class Obj {
someField = ".."
}

Features

observable support native objectarrayMapWeakMapSetWeakSet, support undefined variables, dynamic conditional branches.

Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const obj = observable({
user: {
name: "jeck",
age: 25,
}
})
observe(() => {
console.log(`${obj.user.name}, ${obj.user.age}`)
})
obj.user.name = "bob"
obj.user = {
name: "well",
age: 16,
}
delete obj.user.name

Array

Only when the variables used are modified, the action will be triggered again.

1
2
3
4
5
6
7
8
9
let arr = observable([1, 2, 3, 4, 5])
observe(() => {
console.log(`${arr.map(i => i)}`)
})
arr[0] = 10
arr.push(6)
Action(()=>{ arr.shift() })
Action(()=>{ arr.splice(2, 1) })
delete arr[0]

Because of JS achieve shift splice by divided into multiple steps, so use Action can be aggregated into an action.

.length will trigger action when any change, which will change array’s length:

1
2
3
4
5
6
let arr = observable([1, 2, 3, 4, 5])
observe(() => {
console.log(`${arr.length}`)
})
arr.push(6)
arr.splice(2, 1)

Map

listen size:

1
2
3
4
5
6
7
const map = observable(new Map())
observe(() => {
console.log(`${map.size}`)
})
map.set("banana", 5)
map.set("apple", 3)
map.clear()

listen single key:

1
2
3
4
5
const map = observable(new Map())
observe(() => {
console.log(`${map1.get("apple")}`)
})
map1.set("apple", 6)

WeakMap

Same with Map.

Set

listen size:

1
2
3
4
5
6
7
const set = observable(new Set())
observe(() => {
console.log(`${set.size}`)
})
set.add("banana")
set.add("apple")
set.clear()

WeakSet

Same with Set.

Expand thinking

All of the basic type as shown above can be used in object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj = observable({
simpleNumber: 1,
simpleString: "text~",
simpleArray: [1, 2, 3, 4, 5],
map: new Map([ ["a", 1], ["b", 2], ["c", 3] ]),
deepObj: {
user: {
articles: [{
title: "nodejs"
}]
}
},
show: false
})

Dynamic binding

For uninitialized variables, can also be tracked.

1
2
3
4
5
const obj = observable({})
observe(() => {
log('#demo-dynamic-binding div', `${obj.a}`)
})
obj.a = 3

Conditional branch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const objCon = observable({
useA: true,
a: 1,
b: 2
})

observe(() => {
if (objCon.useA) {
console.log(objCon.a)
}
})

objCon.a = 3
objCon.useA = false
objCon.a = 4 // not work