收藏好這篇,別再只說「資料劫持」了

2021-09-11 12:59:42 字數 1719 閱讀 8020

最近接觸了一些面試者,當我問起「vue 如何實現資料雙向繫結」時,會脫口而出「資料劫持」,然後呢?然後就沒有然後了╮(╯_╰)╭。確實,「資料劫持」是基礎,但遠不是面試官想聽到的答案,不如花個十分鐘看看本文,下次照著回答就好了

要解答問題,首先要理解問題:資料雙向繫結是一種模式,web語境下一般指資料從dom到js物件之間的自動同步。dom 與 js 被隔離在兩個不同的執行時上,互相之間需要通過命令式的 dom介面 溝通:dom 需要正確觸發事件,將資訊傳輸給js程式;而js也需要在狀態變更後,有意識地呼叫適當的介面,改變dom內容。這種方式會引起兩個問題:

狀態的管理與展現是完全剝離開的兩套不同邏輯,需要刻意保持同步,這是很高的開發成本

dom 規範定義了不少介面,而且有相容性問題,這是很高的學習成本

雙向繫結通過各種各樣的設計,將資料從 dom 到 js 或者從 js 到 dom 的同步過程,封裝在框架本身,上層**脫離了對底層介面的依賴,只需要關注狀態管理邏輯。

我們要討論的第乙個問題是,如何檢測 js 物件屬性發生的變更?最簡單粗暴的方法是「髒檢查」,舊版本的angular就是用的這種方法,在各種可能引發狀態變更的事件後,啟動一次髒檢查。這種方法很直觀,但實現上需要考慮很多問題:

髒檢查過程中可能會觸發新的資料變更,也就進入死迴圈了

髒檢查的實現必須保留新舊兩份資料的副本

髒檢查必須預知所有可能觸發狀態變更的時機,也就意味著需要對部分原生介面進行包裹(包括settimeoutrequestnextanimationframe等)

... vue 則採用元程式設計介面object.defineproperty實現的。 在元件初始化,會呼叫該介面,將物件屬性包裝為getset函式,將**「埋入」屬性是「獲取」、「修改」行為中。看個簡單例子,直觀感受下:

const person = {};

// 嘿嘿,誰都改不了我的名字

object.defineproperty(person, 'name', ,

set(v)

});console.log(person.name);

// van

person.name = 'tec';

// they want change my name

console.log(person.name);

// van

複製**

object.defineproperty只是解決了狀態變更後,如何觸發通知的問題,那要通知誰呢?誰會關心那些屬性發生了變化呢?在 vue 中,使用 dep 解耦了依賴者與被依賴者之間關係的確定過程。簡單來說:

對應下圖:

注意,vue 元件中的render函式,我們可以單純將其視為一種特殊的computed函式,在它所對應的watcher物件發生變化時,觸發執行render,生成新的 virutal-dom 結構,再交由 vue 做diff,更新檢視。

vue 使用資料劫持作為底層支撐,又設計了一套精妙的依賴管理方案解耦依賴。但資料劫持方案也有其難以解決的痛點:

只能應用於簡單物件

對陣列無效,所以需要包裝陣列方法

屬性劫持的出發點是「變」,所以vue無法很好接入「immutable」模式

前端一些常用的正則驗證,收藏好!

validate.js 如下 export const checkvaild str,type test str case tel 座機return 0 d d d test str case card 身份證return d d d d x x test str case pwd 密碼以字母開頭,...

掌握CSS選擇器收藏這篇文章就夠了

在 css 中,選擇器是一種模式,用於選擇需要新增樣式的元素。選擇器示例 學習css的教程 型別選擇器 h1型別選擇器 通配選擇器 通配選擇器 類選擇器 box 類選擇器 id選擇器 unique id選擇器 標籤屬性選擇器 a title 標籤屬性選擇器 後代選擇器 article p 後代選擇器...