成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

前端數(shù)據(jù)扁平化與持久化

dreamtecher / 1261人閱讀

摘要:與持久化工程師花了年時(shí)間打造,與同期出現(xiàn)。有持久化數(shù)據(jù)結(jié)構(gòu),如等,并發(fā)安全??偨Y(jié)篇幅有限,時(shí)間也比較晚了,關(guān)于前端數(shù)據(jù)的扁平化與持久化處理先講這么多了,有興趣的同學(xué)可以關(guān)注下,后面有時(shí)間會(huì)多整理分享。

(PS: 時(shí)間就像海綿里的水,擠到?jīng)]法擠,只能擠擠睡眠時(shí)間了~ 知識(shí)點(diǎn)還是需要整理的,付出總會(huì)有收獲,tired but fulfilled~)

前言

最近業(yè)務(wù)開(kāi)發(fā),從零搭建網(wǎng)頁(yè)生成器,支持網(wǎng)頁(yè)的可視化配置。為了滿足這種需求,需要將各種頁(yè)面抽象成類似地模塊,再將每個(gè)模塊抽象成各個(gè)可配置的組件,有些組件還包含一些小部件。這樣一來(lái),頁(yè)面配置的JSON數(shù)據(jù)就會(huì)深層級(jí)地嵌套,那么修改一個(gè)小組件的配置,要怎樣來(lái)更新頁(yè)面樹(shù)的數(shù)據(jù)?用id一層一層遍歷?這樣做法當(dāng)然是不推薦的,不僅性能差,代碼寫(xiě)起來(lái)也麻煩。因此,就考慮能否像數(shù)據(jù)庫(kù)一樣,把數(shù)據(jù)范式化,將嵌套的數(shù)據(jù)展開(kāi),每條數(shù)據(jù)對(duì)應(yīng)一個(gè)id,通過(guò)id直接操作。Normalizr 就幫你做了這樣一件事情。

另外考慮到頁(yè)面編輯,就需要支持 撤銷重做的功能,那么要怎樣來(lái)保存每一步的數(shù)據(jù)?頁(yè)面編輯的數(shù)據(jù)互相關(guān)聯(lián),對(duì)象的可變性會(huì)帶來(lái)很大的隱患。雖然JS中的const(es6)Object.freeze(es5) 可以防止數(shù)據(jù)被修改,但它們都是shallow處理,遇到嵌套多和深的結(jié)構(gòu)就需要遞歸處理,而遞歸又存在性能上的問(wèn)題。這時(shí),用過(guò)React的童鞋就知道了,React借助 Immutable 來(lái)減少DOM diff的比對(duì),它就能夠很好地解決上面這兩個(gè)問(wèn)題。Immutable 實(shí)現(xiàn)的原理是 Persistent Data Structure(持久化數(shù)據(jù)結(jié)構(gòu)),也就是使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時(shí),要保證舊數(shù)據(jù)同時(shí)可用且不變。

那么為什么在JS中,諸如對(duì)象這樣的數(shù)據(jù)類型是可變的呢?我們先來(lái)了解一下JS的數(shù)據(jù)類型。

JS數(shù)據(jù)類型

JS的數(shù)據(jù)類型包括基本類型和引用類型?;绢愋桶⊿tring、Number、 Boolean、Null、Undefined,引用類型主要是對(duì)象(包括Object、Function、Array、Data等)?;A(chǔ)類型的值本身無(wú)法被改變,而引用類型,如Object,是可以被改變的。本文討論的數(shù)據(jù)不可變,就是指保持對(duì)象的狀態(tài)不變。來(lái)看看下面的例子:

// 基本類型

var a = 1;
var b = a;
b = 3;
console.log(a); // 1
console.log(b); // 3

// 引用類型
var obj1 = {};
obj1.arr = [2,3,4];
var obj2 = obj1;
obj2.arr.push(5);

console.log(obj1.arr); // [2, 3, 4, 5]
console.log(obj2.arr); // [2, 3, 4, 5]

上面例子中,b的值改變后,a的值不會(huì)隨著改變;而obj2.arr被修改后,obj1.arr的值卻跟著變化了。這是因?yàn)镴S對(duì)象中的賦值是“引用賦值”,即在賦值的過(guò)程中,傳遞的是在內(nèi)存中的引用。這也是JS中對(duì)象為什么有深拷貝和淺拷貝的用法,只有深拷貝后,對(duì)新對(duì)象的修改才不會(huì)改變?cè)瓉?lái)的對(duì)象。
淺拷貝只會(huì)將對(duì)象的各個(gè)屬性進(jìn)行依次復(fù)制,并不會(huì)進(jìn)行遞歸復(fù)制,而 JavaScript 存儲(chǔ)對(duì)象都是存地址的。上面代碼中,只是執(zhí)行了淺拷貝,結(jié)果導(dǎo)致 obj1obj2指向同一塊內(nèi)存地址。所以修改obj2.arrobj1.arr的值也變了。如果是深拷貝(如Lodash的cloneDeep)則不同,它不僅將原對(duì)象的各個(gè)屬性逐個(gè)復(fù)制出去,而且將原對(duì)象各個(gè)屬性所包含的對(duì)象也依次采用深拷貝的方法遞歸復(fù)制到新對(duì)象上,也就不會(huì)存在上面 obj1obj2 中的 arr 屬性指向同一個(gè)內(nèi)存對(duì)象的問(wèn)題。

為了更清晰地理解這個(gè)問(wèn)題,還是得來(lái)了解下javascript變量的存儲(chǔ)方式。

數(shù)據(jù)類型的存儲(chǔ)

程序的運(yùn)行都需要內(nèi)存,JS語(yǔ)言把數(shù)據(jù)分配到內(nèi)存的棧(stack)和堆(heap)進(jìn)行各種調(diào)用(注:內(nèi)存中除了棧和堆,還有常量池)。JS這樣分配內(nèi)存,與它的垃圾回收機(jī)制有關(guān),可以使程序運(yùn)行時(shí)占用的內(nèi)存最小。

在JS中,每個(gè)方法被執(zhí)行時(shí),都會(huì)建立自己的內(nèi)存棧,這個(gè)方法內(nèi)定義的變量就會(huì)一一被放入這個(gè)棧中。等到方法執(zhí)行結(jié)束,它的內(nèi)存棧也自然地銷毀了。因此,所有在方法中定義的變量都是放在棧內(nèi)存中的。當(dāng)我們?cè)诔绦蛑袆?chuàng)建一個(gè)對(duì)象時(shí),這個(gè)對(duì)象將被保存到運(yùn)行時(shí)數(shù)據(jù)區(qū)中,以便反復(fù)利用(因?yàn)閷?duì)象的創(chuàng)建成本通常較大),這個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)就是堆內(nèi)存。堆內(nèi)存中的對(duì)象不會(huì)隨方法的結(jié)束而銷毀,即使方法結(jié)束后,這個(gè)對(duì)象還可能被另一個(gè)引用變量所引用。只有當(dāng)一個(gè)對(duì)象沒(méi)有任何引用變量引用它時(shí),系統(tǒng)的垃圾回收機(jī)制才會(huì)在核實(shí)的時(shí)候回收它。

總的來(lái)說(shuō),棧中存儲(chǔ)的是基礎(chǔ)變量以及一些對(duì)象的引用變量,基礎(chǔ)變量的值是存儲(chǔ)在棧中,而引用變量存儲(chǔ)在棧中的是指向堆中的對(duì)象的地址,這就是修改引用類型總會(huì)影響到其他指向這個(gè)地址的引用變量的原因。堆是運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的,存取速度較慢,棧的優(yōu)勢(shì)是存取速度比堆要快,并且棧內(nèi)的數(shù)據(jù)可以共享,但是棧中數(shù)據(jù)的大小與生存期必須是確定的,缺乏靈活性。

Normalizr與范式化

范式化(Normalization)是數(shù)據(jù)庫(kù)設(shè)計(jì)中的一系列原理和技術(shù),以減少數(shù)據(jù)庫(kù)中數(shù)據(jù)冗余,增進(jìn)數(shù)據(jù)的一致性。直觀地描述就是尋找對(duì)象之間的關(guān)系,通過(guò)某種方式將關(guān)系之間進(jìn)行映射,減少數(shù)據(jù)之間的冗余,優(yōu)化增刪改查操作。Normalizr庫(kù)本身的解釋就是Normalizes nested JSON according to a schema),一種類似于關(guān)系型數(shù)據(jù)庫(kù)的處理方法,通過(guò)建表建立數(shù)據(jù)關(guān)系,把深層嵌套的數(shù)據(jù)展開(kāi),更方便靈活的處理和操作數(shù)據(jù)。

來(lái)看個(gè)官網(wǎng)的例子,理解一下:

{
  "id": "123",
  "author": {
    "id": "1",
    "name": "Paul"
  },
  "title": "My awesome blog post",
  "comments": [
    {
      "id": "324",
      "commenter": {
        "id": "2",
        "name": "Nicole"
      }
    }
  ]
}

這是一份博客的數(shù)據(jù),一篇文章article有一個(gè)作者author, 一個(gè)標(biāo)題title, 多條評(píng)論,每條評(píng)論有一個(gè)評(píng)論者commenter,每個(gè)commenter又有自己的id和name。這樣如果我們要獲取深層級(jí)的數(shù)據(jù),如commenter時(shí),就需要層層遍歷。這時(shí)候,如果使用Normalizr,就可以這樣定義Schema:

import { schema } from "normalizr";  

const user = new schema.Entity("users");

const comment = new schema.Entity("comments", {
  commenter: user
});

const article = new schema.Entity("articles", {
  author: user,
  comments: [comment]
});

然后調(diào)用一下 Normalize,就可以得到扁平化后的數(shù)據(jù),如下:

{
  "entities": {
    "users": {
      "1": {
        "id": "1",
        "name": "Paul"
      },
      "2": {
        "id": "2",
        "name": "Nicole"
      }
    },
    "comments": {
      "324": {
        "id": "324",
        "commenter": "2"
      }
    },
    "articles": {
      "123": {
        "id": "123",
        "author": "1",
        "title": "My awesome blog post",
        "comments": ["324"]
      }
    }
  },
  "result": "123"
}

這樣每個(gè)作者、每條評(píng)論、每篇文章都有對(duì)應(yīng)的id, 我們就不需要遍歷,可以直接拿對(duì)應(yīng)的id進(jìn)行修改。

再來(lái)看下我們?cè)陧?xiàng)目中的示例代碼:

分別定義element、section 和 page三張表,并指定它們之間的關(guān)系。這樣范式化后,想對(duì)某個(gè)頁(yè)面某個(gè)模塊或者某個(gè)元素進(jìn)行增刪查改,就直接拿對(duì)應(yīng)的id,不需要再耗性能去遍歷了。

Immutable與持久化

Facebook工程師Lee Byron花了3年時(shí)間打造Immutable,與 React 同期出現(xiàn)。Immutable Data,維基百科上是這樣定義的:

In computing, a persistent data structure is a data structure that always preserves the previous version of itself when it is modified. Such data structures are effectively immutable, as their operations do not (visibly) update the structure in-place, but instead always yield a new updated structure.

簡(jiǎn)單來(lái)說(shuō),Immutable Data 就是一旦創(chuàng)建,就不能再被更改的數(shù)據(jù)。對(duì) Immutable 對(duì)象的任何修改或添加刪除操作都會(huì)返回一個(gè)新的 Immutable 對(duì)象。Immutable 實(shí)現(xiàn)的原理是 Persistent Data Structure(持久化數(shù)據(jù)結(jié)構(gòu)),也就是使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時(shí),要保證舊數(shù)據(jù)同時(shí)可用且不變。Immutable 使用了 Structural Sharing(結(jié)構(gòu)共享),即如果對(duì)象樹(shù)中一個(gè)節(jié)點(diǎn)發(fā)生變化,只修改這個(gè)節(jié)點(diǎn)和受它影響的父節(jié)點(diǎn),其它節(jié)點(diǎn)則進(jìn)行共享,這樣就避免了深拷貝帶來(lái)的性能損耗。

我們通過(guò)圖片來(lái)理解一下:

Immutable 內(nèi)部實(shí)現(xiàn)了一套完整的持久化數(shù)據(jù)結(jié)構(gòu),有很多易用的數(shù)據(jù)類型,如Collection、List、Map、Set、Record、Seq(Seq是借鑒了Clojure、Scala、Haskell這些函數(shù)式編程語(yǔ)言,引入的一個(gè)特殊結(jié)構(gòu))。它有非常全面的map、filter、groupBy、reduce、find等函數(shù)式操作方法。它的Api很強(qiáng)大,大家有興趣可以去看下。這里簡(jiǎn)單列舉 updateIn/getIn 來(lái)展示它帶來(lái)的一些便捷操作:

var obj = {
  a: {
    b: {
      list: [1, 2, 3]
    }
  }
};
var map = Immutable.fromJS(obj); // 注意 fromJS這里實(shí)現(xiàn)了深轉(zhuǎn)換
var map2 = Immutable.updateIn(["a", "b", "list"], (list) => {
  return list.push(4);
});

console.log(map2.getIn(["a", "b", "list"]))
// List [ 1, 2, 3, 4 ]

代碼中我們要改變數(shù)組List的值,不必一層一層獲取數(shù)據(jù),而是直接傳入對(duì)應(yīng)的路徑修改就行。這種操作在數(shù)據(jù)嵌套越深時(shí),優(yōu)勢(shì)更加明顯。來(lái)看下我們業(yè)務(wù)代碼的示例吧。

這里在多個(gè)頁(yè)面的模塊配置中,要更新某個(gè)頁(yè)面的某個(gè)模塊的數(shù)據(jù),我們只需要在updateIn傳入對(duì)應(yīng)的path和value,就可以達(dá)到預(yù)想的效果。篇幅有限,更多的示例請(qǐng)自行查看api。

熟悉React的同學(xué)也基于它結(jié)構(gòu)的不可變性共享性,用它來(lái)能夠快速進(jìn)行數(shù)據(jù)的比較。原本React中使用PureRenderMixin來(lái)做DOM diff比較,但只是淺比較,當(dāng)數(shù)據(jù)結(jié)構(gòu)比較深的時(shí)候,依然會(huì)存在多余的diff過(guò)程。這里只提個(gè)點(diǎn),不深入展開(kāi)了,感興趣的同學(xué)可以自行g(shù)oogle。

與 Immutable.js 類似的,還有個(gè)seamless-immutable,它的代碼庫(kù)非常小,壓縮后下載只有 2K。而 Immutable.js 壓縮后下載有16K。大家各取所需,根據(jù)實(shí)際情況,自己斟酌下使用哪個(gè)比較適合。

優(yōu)缺點(diǎn)

什么事物都有利弊,代碼庫(kù)也不例外。這里列舉下它們的優(yōu)缺點(diǎn),大家權(quán)衡利弊,一起來(lái)看下:

Normalizr 可以將數(shù)據(jù)扁平化處理,方便對(duì)深層嵌套的數(shù)據(jù)進(jìn)行增刪查改,但是文檔不是很清晰,大家多查多理解,引入庫(kù)文件也會(huì)增大。Immutable 有持久化數(shù)據(jù)結(jié)構(gòu),如List/Map等,并發(fā)安全。其次,它支持結(jié)構(gòu)共享,比cloneDeep 性能更優(yōu),節(jié)省內(nèi)存。第三,它借鑒了Clojure、Scala、Haskell這些函數(shù)式編程語(yǔ)言,引入了特殊結(jié)構(gòu)Seq,支持Lazy operation。Undo/Redo,Copy/Paste,甚至?xí)r間旅行這些功能對(duì)它來(lái)說(shuō)都是小菜一碟。缺點(diǎn)方面,Immutable源文件過(guò)大,壓縮后有15kb。而且它侵入性強(qiáng),與原生api容易混淆。此外,類型轉(zhuǎn)換比較繁瑣,尤其是與服務(wù)器交互頻繁時(shí),這種缺點(diǎn)就更加明顯。當(dāng)然,也可以根據(jù)業(yè)務(wù)需求,衡量下是否用seamless-immutable,它使用 Object.defineProperty (因此只能在 IE9 及以上使用) 擴(kuò)展了 JavaScript 的 Array 和 Object 對(duì)象來(lái)實(shí)現(xiàn),只支持 Array 和 Object 兩種數(shù)據(jù)類型。但是代碼庫(kù)非常小,壓縮后下載只有 2K。

總結(jié)

篇幅有限,時(shí)間也比較晚了,關(guān)于前端數(shù)據(jù)的扁平化與持久化處理先講這么多了,有興趣的同學(xué)可以關(guān)注下,后面有時(shí)間會(huì)多整理分享。

參考資料

前端數(shù)據(jù)范式化

Immutable詳解及React中實(shí)踐

為什么需要Immutable.js

facebook immutable.js 意義何在,使用場(chǎng)景?

一些鏈接, 關(guān)于不可變數(shù)據(jù)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/97315.html

相關(guān)文章

  • 前端入坑體驗(yàn)分享

    摘要:同源策略同源策略是一種約定,由公司年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到等攻擊。 一、Vue變化檢測(cè) 背景 初始化對(duì)象,屬性未知;某些事件觸發(fā)時(shí),對(duì)象改變(新增屬性),Vue監(jiān)聽(tīng)不到 原因 Vue.js 不能檢測(cè)到對(duì)象屬性的添加或刪除,因?yàn)閂ue.js 在初始化實(shí)例時(shí)將屬性轉(zhuǎn)為 getter/setter,所以屬性必須在 dat...

    hss01248 評(píng)論0 收藏0
  • 微服務(wù)架構(gòu)入門(mén)

    摘要:故障處理設(shè)計(jì)微服務(wù)架構(gòu)所帶來(lái)的一個(gè)后果就是必須考慮每個(gè)服務(wù)的失敗容錯(cuò)機(jī)制。因此,微服務(wù)非常重視建立架構(gòu)及相關(guān)業(yè)務(wù)指標(biāo)的實(shí)時(shí)監(jiān)控和日志機(jī)制。 微服務(wù)架構(gòu)入門(mén) 1. 微服務(wù)簡(jiǎn)介 微服務(wù)是一種架構(gòu)風(fēng)格,一個(gè)大型的復(fù)雜軟件由一個(gè)或多個(gè)微服務(wù)組成。系統(tǒng)中每個(gè)微服務(wù)都可以被獨(dú)立部署,各個(gè)微服務(wù)之間是松耦合的。每個(gè)微服務(wù)僅關(guān)注于完成一件任務(wù)并很好地完成任務(wù)。在所有情況下,每個(gè)任務(wù)代表這一個(gè)小的業(yè)務(wù)能...

    ninefive 評(píng)論0 收藏0
  • 前端芝士樹(shù)】如何完成數(shù)組的平化 Array flattern?

    摘要:前端芝士樹(shù)如何完成數(shù)組的扁平化問(wèn)題描述輸入一個(gè)嵌套型數(shù)組輸出扁平化后的數(shù)組如果只是兩層的數(shù)據(jù)如果是多層嵌套的數(shù)組 【前端芝士樹(shù)】如何完成數(shù)組的扁平化 Array flattern? 問(wèn)題描述 輸入:一個(gè)嵌套型數(shù)組輸出:扁平化后的數(shù)組 let array = [1, [2, 3, 4]]; let arrayDeeper = [1, [2, [3, 4]]]; 如果只是兩層的數(shù)據(jù) fun...

    2shou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<