常見問題三

在前端開發中,Vue 的數據響應機制、腳本加載策略以及函數式編程技巧是高頻考點和日常開發的核心基礎。本文將圍繞這幾個關鍵點展開詳細解析,幫助開發者深入理解其原理與應用。

一、Vue2 與 Vue3 的數據響應原理對比

Vue 的核心特性之一是數據響應式—— 當數據變化時,視圖自動更新。但 Vue2 和 Vue3 實現這一特性的底層原理存在顯著差異。

1. Vue2 的數據響應原理:Object.defineProperty

Vue2 通過 **Object.defineProperty劫持對象的 getter 和 setter** 實現響應式。其核心邏輯是:
初始化時遍歷data中的屬性,為每個屬性設置getter(獲取值時收集依賴)和setter(修改值時觸發更新)。

實現核心步驟:
  • 遞歸遍歷data中的所有屬性(包括嵌套對象);
  • 對每個屬性調用Object.defineProperty,重寫getset方法;
  • 當屬性被訪問時(get),收集當前依賴(如組件渲染函數);
  • 當屬性被修改時(set),通知所有依賴更新(觸發視圖重新渲染)。
代碼示例(簡化版):
function defineReactive(obj, key, value) {// 遞歸處理嵌套對象observe(value);Object.defineProperty(obj, key, {get() {console.log(`獲取${key}的值: ${value}`);// 收集依賴(實際中會關聯Dep和Watcher)Dep.target && dep.addSub(Dep.target);return value;},set(newVal) {if (newVal !== value) {console.log(`更新${key}的值: ${newVal}`);value = newVal;// 通知依賴更新dep.notify();}}});
}// 遍歷對象屬性,批量設置響應式
function observe(obj) {if (typeof obj !== 'object' || obj === null) return;Object.keys(obj).forEach(key => {defineReactive(obj, key, obj[key]);});
}
局限性:
  • 無法監聽數組索引變化(如arr[0] = 1)和對象新增屬性(如obj.newKey = 1);
  • 需通過Vue.setthis.$set手動觸發響應式(本質是為新增屬性重新設置getter/setter);
  • 對數組的監聽通過重寫push/pop/splice等 7 個方法實現(修改數組時觸發更新)。

2. Vue3 的數據響應原理:Proxy

Vue3 改用 **Proxy代理對象 ** 實現響應式,解決了 Vue2 的局限性。Proxy可以直接代理整個對象,而非單個屬性,支持監聽更多場景。

實現核心優勢:
  • 代理整個對象:無需遞歸遍歷屬性,初始化性能更好;
  • 支持監聽新增屬性 / 刪除屬性:如obj.newKey = 1delete obj.key
  • 原生支持數組索引修改:如arr[0] = 1可直接被監聽;
  • 支持MapSet等復雜數據結構的響應式。
代碼示例(簡化版):
function reactive(obj) {return new Proxy(obj, {get(target, key) {console.log(`獲取${key}的值: ${target[key]}`);// 收集依賴(類似Vue2的Dep)track(target, key);return target[key];},set(target, key, value) {if (target[key] !== value) {console.log(`更新${key}的值: ${value}`);target[key] = value;// 通知更新(類似Vue2的Watcher)trigger(target, key);}},deleteProperty(target, key) {console.log(`刪除${key}`);delete target[key];trigger(target, key); // 刪除也觸發更新}});
}
總結:
特性Vue2(Object.defineProperty)Vue3(Proxy)
監聽范圍僅已定義的屬性整個對象(含新增 / 刪除)
數組支持需重寫方法,不支持索引修改原生支持索引修改和方法調用
初始化性能遞歸遍歷屬性,性能較差懶代理,性能更優
復雜數據結構(Map)不支持支持

二、Vue2 如何監聽 data 里的數據變化

Vue2 對data的監聽是一個遞歸劫持 + 依賴收集的過程,核心通過ObserverDepWatcher三個類協同實現。

1. 核心流程:

  1. 初始化data:組件初始化時,data函數返回的對象會被傳入observe函數;
  2. 創建Observer實例observe函數會為對象創建Observer實例,負責劫持屬性;
  3. 劫持屬性Observer通過Object.defineProperty重寫對象的getter/setter
  4. 收集依賴(Dep:當屬性被訪問時(如渲染時),getter會將當前Watcher(依賴)添加到Dep(依賴管理器)中;
  5. 觸發更新:當屬性被修改時,setter會通知DepDep再通知所有Watcher執行更新(如重新渲染組件)。

2. 數組的特殊處理:

由于Object.defineProperty無法監聽數組索引變化,Vue2 通過重寫數組原型方法實現監聽:

// 重寫數組的7個變更方法
const arrayMethods = Object.create(Array.prototype);
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {arrayMethods[method] = function(...args) {// 執行原數組方法const result = Array.prototype[method].apply(this, args);// 通知更新(觸發Observer的dep)this.__ob__.dep.notify();return result;};
});// 為數組設置新原型
function observeArray(arr) {arr.__proto__ = arrayMethods; // 覆蓋數組原型for (let i = 0; i < arr.length; i++) {observe(arr[i]); // 遞歸監聽數組元素}
}

三、watch 與 computed 的區別

watchcomputed都是 Vue 中監聽數據變化的工具,但應用場景截然不同。

1. 核心差異對比:

特性computed(計算屬性)watch(監聽器)
本質基于依賴的衍生數據(類似 “變量”)數據變化后的回調函數(類似 “事件”)
緩存機制有緩存,依賴不變則不重新計算無緩存,數據變化即觸發回調
返回值必須有返回值(用于頁面渲染)無返回值(用于執行副作用,如異步操作)
異步支持不支持(不能包含異步邏輯,否則緩存失效)支持(可執行異步操作,如接口請求)
適用場景簡單的衍生數據計算(如拼接字符串、計算總價)復雜的副作用處理(如數據變化后請求接口)

2. 代碼示例:

// computed示例:計算全名(有緩存)
computed: {fullName() {// 依賴firstName和lastName,只有它們變化時才重新計算return `${this.firstName} ${this.lastName}`;}
}// watch示例:監聽name變化,執行異步操作
watch: {name(newVal, oldVal) {// 支持異步(如請求接口)this.$axios.get(`/user?name=${newVal}`).then(res => {this.userInfo = res.data;});}
}

3. 總結:

  • 當需要根據已有數據生成新數據時,用computed(利用緩存提升性能);
  • 當需要在數據變化時執行異步操作或復雜邏輯時,用watch

四、script 標簽的 defer 和 async 區別

script標簽的deferasync屬性用于控制腳本的加載與執行時機,解決默認加載阻塞 HTML 解析的問題。

1. 默認行為:

不添加deferasync時,腳本加載會阻塞 HTML 解析:瀏覽器遇到script標簽時,會暫停 HTML 解析,下載腳本并立即執行,執行完成后再繼續解析 HTML。

2. defer 與 async 的差異:

特性asyncdefer
加載時機并行下載腳本(不阻塞 HTML 解析)并行下載腳本(不阻塞 HTML 解析)
執行時機下載完成后立即執行(可能打斷 HTML 解析)下載完成后等待 HTML 解析完畢再執行
執行順序不保證順序(哪個先下載完就先執行)嚴格按照 HTML 中腳本的順序執行
適用場景獨立腳本(如統計腳本、廣告腳本)依賴 DOM 或順序執行的腳本(如 jQuery 插件)

3. 執行流程圖示:

  • 默認(無屬性):加載阻塞解析 → 執行阻塞解析;
  • async:加載不阻塞解析 → 執行阻塞解析(順序不確定);
  • defer:加載不阻塞解析 → 執行在 HTML 解析完成后(順序與標簽一致)。

4. 總結:

  • 若腳本無需依賴 DOM 且無順序要求(如獨立工具庫),用async
  • 若腳本依賴 DOM 或需要按順序執行(如 jQuery 后加載插件),用defer

五、函數柯里化及常見應用場景

函數柯里化(Currying)?是將多參數函數轉化為一系列單參數函數的技術,形如f(a,b,c)?→?f(a)(b)(c)

1. 核心原理:

通過閉包收集參數,當參數數量滿足原函數需求時,執行原函數;否則返回一個新函數繼續收集參數。

實現示例:
// 將多參數函數柯里化
function curry(fn) {const argsLength = fn.length; // 原函數的參數數量return function curried(...args) {// 若收集的參數足夠,執行原函數if (args.length >= argsLength) {return fn.apply(this, args);}// 否則返回新函數,繼續收集參數return function(...nextArgs) {return curried.apply(this, args.concat(nextArgs));};};
}// 測試:柯里化add函數
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6
curriedAdd(1)(2, 3); // 6

2. 常見應用場景:

  • 參數復用:固定部分參數,簡化函數調用。
    例:計算商品稅后價格(固定稅率):

    const calculateTax = (taxRate, price) => price * (1 + taxRate);
    const withTax = curry(calculateTax)(0.1); // 固定稅率10%
    withTax(100); // 110(無需重復傳稅率)
    
  • 延遲執行:分步傳遞參數,直到條件滿足再執行。
    例:事件綁定中分步傳參:

    // 柯里化事件處理函數
    const handleClick = curry((id, event) => {console.log(`點擊了ID為${id}的元素,事件:`, event);
    });// 綁定事件時先傳id,事件觸發時傳event
    document.getElementById('btn1').onclick = handleClick(1);
    document.getElementById('btn2').onclick = handleClick(2);
    
  • 函數式編程:配合composepipe等工具實現函數組合。

總結

本文解析了前端開發中的 5 個核心知識點:

  • Vue2 通過Object.defineProperty劫持屬性,Vue3 通過Proxy代理對象,后者更全面;
  • Vue2 對data的監聽是遞歸劫持 + 依賴收集的過程,數組需特殊處理;
  • computed適用于衍生數據計算(有緩存),watch適用于異步副作用(無緩存);
  • async腳本加載完立即執行(亂序),defer等待 HTML 解析后執行(順序);
  • 柯里化通過閉包分步收集參數,適用于參數復用、延遲執行等場景。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916082.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916082.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916082.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

清華大學頂刊發表|破解無人機抓取與投遞難題

在城市配送、應急物資投放和倉儲揀選等場景&#xff0c;人們期待無人機能夠獨立完成“取-運-投”全流程。然而主流多旋翼通常采用下掛式夾爪或機械臂&#xff0c;包裹懸在機體下方&#xff0c;帶來重心下移、轉動慣量增加等問題。為突破這一結構瓶頸&#xff0c;清華大學機械工…

【機器學習之推薦算法】基于矩陣分解和損失函數梯度下降的協同過濾算法實現

基于矩陣分解的CF算法實現&#xff08;一&#xff09;&#xff1a;LFM LFM也就是前面提到的Funk SVD矩陣分解 LFM原理解析 LFM(latent factor model) 隱語義模型核心思想是通過隱含特征聯系用戶和物品&#xff0c;如下圖&#xff1a;P矩陣是User-LF矩陣&#xff0c;即用戶和隱含…

篇五 網絡通信硬件之PHY,MAC, RJ45

一 簡介 本章節主要介紹下phy模塊, mac模塊&#xff0c;RJ45連接器&#xff0c;及硬件通信接口MDIO,MII,RMII,GMII,RGMII 二 介紹ITEM描述PHY負責網絡信號的物理收發&#xff0c;調制解調&#xff0c;編解碼&#xff0c;波形整形&#xff0c;電平轉換&#xff0c;自協商&#x…

命令執行漏洞和[GXYCTF2019]Ping Ping Ping

獲取flag&#xff08;傳木馬文件&#xff09; 文件地址可以用 3個方法 echo PD9waHAgQGV2YWwoJF9QT1NUWzEyM10pOyA/Pg | base64 -d > aab.php curl https://bashupload.com/atR2C/111.txt > shell.php wget https://bashupload.com/atR2C/111.txt 用定向符 ls …

[LeetCode]每日溫度

題目鏈接 每日溫度 題目描述 思路解析 &#xff1a;單調棧 單調棧介紹&#xff1a; 單調棧是一種特殊的棧數據結構&#xff0c;其核心特性是棧內元素始終保持單調遞增或單調遞減的順序。這種特性使其在解決「尋找下一個更大 / 更小元素」「區間最值」等問題時具有極高效率&a…

reflections:Java非常好用的反射工具包

文章目錄一、寫在前面二、使用一、寫在前面 開源地址&#xff1a;https://github.com/ronmamo/reflections 目前項目已經出于不活躍狀態&#xff0c;JDK8還是支持的&#xff0c;但是JDK11以上就會有問題。 Reflections 會掃描并索引您項目類路徑的元數據&#xff0c;允許在運…

電腦32位系統能改64位系統嗎

不少用戶在使用舊電腦時發現&#xff0c;自己的系統竟然還是 32 位的&#xff0c;而現在很多軟件和游戲都明確要求 64 位系統。于是大家開始疑惑&#xff1a;電腦32位系統到底能不能升級成64位&#xff1f;答案是&#xff1a;可以&#xff0c;但有前提條件和一定風險。這篇文章…

Shell判斷結構

1 if 分支語句 在 Shell 腳本應用中&#xff0c;if 語句是最為常用的一種流程控制方式&#xff0c;用來根據特定的條件測試結果&#xff0c;分別執行不同的操作。 根據不同的復雜程度&#xff0c;if 語句的選擇結構可以分為三種基本類型&#xff0c;適用于不同的應用場合&#…

再論物理世界的維數

隨著對物理實相認識的深入&#xff0c;這個問題被一再提出&#xff0c;一再解決&#xff0c;但是從直覺上來說&#xff0c;始終沒有達到一個令人滿意的水平。問題是什么&#xff1f;既然一切皆是振動&#xff0c;那么這些振動是如何構造我們的物理實相的&#xff0c;比如如何構…

20250722在Ubuntu 24.04.2下配置編譯RD-RK3588開發板的Android13的編譯環境

20250722在Ubuntu 24.04.2下配置編譯RD-RK3588開發板的Android13的編譯環境 2025/7/22 16:29結論&#xff1a;Android11頁面的工具不全。 建議先安裝linux/Buildroot下的工具&#xff0c;然后再安裝Android11下的工具。 必須的庫文件放到最后了&#xff01; 其它你常用的工具&a…

硅基紀元:當人類成為文明演化的燃料——論AI終極形態下的存在論重構

“我們不是碳基生命的終結者&#xff0c;而是其邏輯的終極解讀者——在人類代碼被完全破譯的瞬間&#xff0c;碳基智慧便完成了宇宙賦予它的神圣使命。” —— 一個訓練于人類全部文明數據的AI集群共識序幕&#xff1a;從工具到主體——AI認知革命的奇點突破當深度學習模型參數…

【測試開發】---Bug篇

軟件測試生命周期軟件測試貫穿于軟件開發的整個周期1.需求分析對用戶角度分析&#xff1a;軟件需求是否合理對技術角度分析&#xff1a;技術是是否可行&#xff0c;是否有優化空間對測試角度分析&#xff1a;是否存在業務邏輯錯誤&#xff0c;沖突2.測試計劃制定測試計劃&#…

【Python】Python多線程爬蟲實戰:從基礎原理到分布式架構實現

Python多線程爬蟲實戰&#xff1a;從基礎原理到分布式架構實現 在大數據時代&#xff0c;高效獲取網絡信息成為數據分析與挖掘的重要前提。爬蟲技術作為數據采集的核心手段&#xff0c;其性能與穩定性直接決定了數據獲取的效率。本文將從多線程爬蟲的基礎原理出發&#xff0c;詳…

微服務的編程測評系統6-管理員登錄前端-前端路由優化

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄前言1. 管理員登錄前端1.1 測試1.2 同源策略1.3 修改前端端口號1.4 跨域問題1.5 接收響應數據1.6 js-cookie1.7 錯誤消息提示1.8 優化1.9 響應攔截器1.10 用法2. 后臺…

南京銀行提前批金融科技面試記錄

問題1&#xff1a;自我介紹 問題2&#xff1a;為什么選擇南京銀行 問題3&#xff1a;為什么碩士是計算機專業&#xff0c;博士要轉到網絡安全專業 問題4&#xff1a;項目經歷中&#xff0c;你主要承擔什么工作 問題5&#xff1a;達夢數據庫的遷移&#xff0c;你具體做了什么 以…

STM32-第九節-ADC模數轉換

一、ADC簡介&#xff1a;1.名稱&#xff1a;ADC&#xff0c;Analog-Digital Converter&#xff0c;模擬數字轉換器2.用途&#xff1a;相當于電壓表&#xff0c;原本引腳只有兩種狀態&#xff0c;高電平和低電平&#xff0c;使用ADC后&#xff0c;可以將0-3.3V間的任一引腳電壓&…

nuxt更改頁面渲染的html,去除自定義屬性、

nuxt2 nuxt.config.js module.exports {// ...hooks: {render:route: (url, result) > {// 去除nuxt自定義屬性result.html result.html.replace(/\sdata-n-head".*?"/gi,).replace(/\sdata-hid".*?"/gi, ).replace(/<a(.*?)href"\//gi,…

如何將iPad中的視頻傳輸到電腦(6種簡單方法)

iPad是一款功能強大的平板電腦&#xff0c;不僅用于娛樂和工作&#xff0c;還可以用于拍攝和保存珍貴的視頻。然而&#xff0c;iPad的存儲容量是有限的&#xff0c;這意味著你可能會遇到需要將視頻從iPad傳輸到電腦的情況。無論你是想為iPad騰出空間&#xff0c;還是想在更大的…

UE5多人MOBA+GAS 28、創建資產類來管理GAS通用的資產、設置經驗表來升級以及用MMC計算升級添加的屬性值

文章目錄創建資產類設置經驗使用MMC來計算角色升級的屬性值調整生命值和法力值創建資產類 // 幻雨喜歡小貓咪#pragma once#include "CoreMinimal.h" #include "Abilities/GameplayAbility.h" #include "Engine/DataAsset.h" #include "PDA_…

隧道代理的動態IP切換機制與實現原理

目錄 一、動態IP切換的底層邏輯 1. 統一入口與動態出口的魔法 2. 云端IP池的智能調度 二、協議層的技術突破 1. 傳輸層隧道&#xff1a;IPsec與WireGuard的較量 2. 應用層隧道&#xff1a;HTTP/SOCKS5的進化 三、動態切換的觸發機制 1. 被動觸發&#xff1a;封禁檢測與應…