JS超級對象Objext:響應式、版本控制、數據鎖

NO IMAGE

Objext功能簡介

JS超級對象,在原生object的基礎上進行擴展,支持:

  • 通過keyPath獲取、設置數據
  • 響應式,可以通過watch監聽某個keyPath
  • 數據版本控制,通過commit和reset,可以隨時創建快照或恢復數據
  • 數據校驗
  • 數據鎖,鎖住之後,不能做任何數據修改

安裝和使用

安裝:

npm i objext

引入:

import Objext from 'objext'
const { Objext } = require('objext')
<script src="objext/dist/objext.js"></script>
<script>
const { Objext } = window.objext 
</script>

實例化:

const objx = new Objext({
name: 'tomy',
age: 10,
body: {
head: true,
feet: true,
},
get sex() {
return this.age > 8 ? 'male' : 'female'
},
say() {
alert('Hello~')
},
})

上面這個例子演示瞭如何創建一個超級對象,過程超級簡單,只需要將一個普通對象傳入new Objext作為參數即可,這樣得到的對象objx和原始的對象的結構是一模一樣,然而功能卻比原始對象高級N倍。

基於keyPath的數據操作

你可以像使用普通對象一樣使用objext實例,但是,這樣會造成一些問題,特別是不能使用delete,也不能直接賦值一個新屬性,這樣會丟失這些屬性的自動響應能力,如果你用過vue的話肯定對這個思路很瞭解。

通過Objext創建的對象擁有以下方法,這些方法全部以$開頭,並且是隱藏式的,不能通過for…in枚舉。

$get(keyPath)

keyPath是指獲取一個屬性節點的路徑,例如獲取objx.body.head.hair屬性,’body.head.hair’就是它的keyPath。

和普通對象不同的是,通過$get方法,可以直接用keyPath讀取一個屬性。用$get方法的好處是,不用擔心你去獲取一個undefined的屬性的子屬性,舉個例子,objx.body為undefined,那麼,你在讀取objx.body.head的時候,就會報錯。但是用objx.$get(‘body.head’)就可以避免這個問題。

$set(keyPath, value)

和$get的好處一樣,普通對象你不能讀取一個undefined屬性的子屬性,更別提給它賦值。而$set就可以做到,它可以為一個不存在的深層次的屬性進行賦值:

const objx = new Objext({})
objx.$set('body.head.hair', 'black')
// => { body: { head: { hair: 'black' } } }

更重要的是,只有通過$set方法,才能讓一個屬性具備可響應式能力。比如你直接objx.feet = 2,feet這個屬性不是響應式的,你不能用$watch去監聽它。但是你objx.$set(‘feet’, 2)之後,它就是響應式了。

$remove(keyPath)

用來移除某個屬性,替代delete操作。

響應式數據

Objext創建到對象是響應式的,和vue的那種模式一樣,當一個屬性發生變化時,是可以被監測到的。

$watch(keyPath, callback, deep)

和angular的$watch使用很像,它用來監聽一個屬性發生變化:

objx.$watch('body.head.hair', (e, newValue, oldValue) => {
// e: 包含一些信息,你可以用來進行判斷 
// newValue: 新值
// oldValue: 老值
// 任何對body.head.hair的修改都會觸發callback,即使newValue===oldValue,因此,你必須在callback裡面自己做邏輯去判斷是否要執行一些代碼
})

我們看下e裡面都有些什麼:

  • path: 被修改的屬性的path信息
  • key: watch的第一個參數
  • target: 被監聽到的屬性所屬的對象
  • preventDefault(): 放棄剩下的所有監聽回調
  • stopPropagation(): 禁止冒泡,Objext的監聽採取冒泡模式,當一個節點的屬性發生變化時,先是這個節點的watcher被激活,然後往它的父級不斷廣播,直到最頂層

deep

當第三個參數deep設置為true的時候,表示深度觀察,它的深層級節點發生變動的時候也能監聽到。

keyPath為’*’

將keyPath設置為’*’表示監聽任何變化。

$unwatch(keyPath, callback)

$watch的反函數,取消某個監聽。

計算屬性

Objext支持計算屬性,傳入原生的計算屬性,之後可以得到一個響應式的計算屬性,並且具備緩存能力。注意,僅支持傳入getter,setter將被直接丟掉。

const objx = new Objext({
age: 10,
get height() {
return this.age * 12.5
},
})
objx.$watch('height', (e, newValue, oldValue) => {
console.log(newValue)
})
objx.age = 20
// 由於height屬性依賴age屬性,因此,當修改age屬性時,height也會同時被改變。

數據版本控制

一個數據,在通過Objext的方法進行修改時,它是基於當前的一個鏡像,如果你玩過docker的話,對鏡像應該比較熟悉。Objext提供了數據版本控制的能力,通過兩個方法,可以實現創建一個快照,和恢復一個快照的能力。

$commit(tag)

創建一個名為tag的快照。

$reset(tag)

恢復到名為tag的快照的數據。

objx.name = 'tomy'
objx.$commit('version1')
objx.name = 'jimy'
objx.$reset('version1')
console.log(objx.name) // => 'tomy'

數據校驗

通過設置校驗器,可以對數據進行校驗。它有兩種校驗方式,一種是在使用$set修改/添加屬性的時候,另外一種是直接調用$validate方法,對整個數據進行全量校驗。

這裡需要注意的是,通過手動修改/賦值屬性,是不能觸發校驗的,校驗只有在使用$set時被使用。

還有一點是,校驗是後置的,也就是說,你無法在實例化時校驗初始數據。

$formulate(validators)

添加校驗器。

它可以被多次執行,所有的校驗器都會被記錄下來。使用時應該注意,校驗器一般是在使用前設置,因為添加好校驗器之後,它們是不能被刪除的。

_validators_是一個數組,裡面包含了所有校驗器配置信息,每一個元素都是一個對象,即一個校驗器的配置信息。這個對象的格式如下:

{
path: 'body.head', // 要校驗的路徑
check: value => Boolean, // 校驗函數,返回boolean值
message: '格式不對', // 校驗失敗時返回的錯誤message信息
warn: error => {}, // 校驗失敗後要執行的函數,error包含了message信息,另外還包含value和path信息
}

所有的校驗器被放在一個隊列裡。在校驗時,一個校驗器未通過失敗時,就不會往下繼續校驗了。

$validate()

一次性校驗所有數據,校驗器隊列會被依次運行。

數據鎖

通過鎖開關,可以實現對數據的鎖定和解鎖。對象被鎖死之後,無法進行修改操作。

$lock()

鎖死數據。

objx.name = 'tomy'
objx.$lock()
objx.name = 'pina'
console.log(objx.name) // => 'tomy'

上鎖後,深層級的數據也被上鎖,無法被修改。

$unlock()

解鎖數據。

其他

另外,為了更方便的獲取對象的信息,Objext提供了幾個更便捷的方法。

$clone()

基於當前數據,克隆出一個新的Objext對象,在保持數據相同的情況下,和原對象沒有任何關係。

$$hash

這是一個屬性,可以獲取當前對象的hash值,在判斷兩個Objext對象的實際內容是否相等時,可以利用它來進行判斷。

valueOf()

快速獲取當前Objext對象的原生對象內容。

toString()

獲取原生對象值的字符串形式。

GitHub地址:https://github.com/tangshuang/objext 

作者博客:https://www.tangshuang.net/

寫項目不容易,打個賞唄~~~~~~~~~~~~~~~~~

相關文章

React中的雙向綁定

學不動系列:新前端框架Nautil,哇~

TySheMo前端數據管理模型

連續同源異步操作隊列