国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

《Vue.js 設(shè)計(jì)與實(shí)現(xiàn)》—— 02 框架設(shè)計(jì)核心要素

這篇具有很好參考價(jià)值的文章主要介紹了《Vue.js 設(shè)計(jì)與實(shí)現(xiàn)》—— 02 框架設(shè)計(jì)核心要素。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

框架設(shè)計(jì)并非僅僅實(shí)現(xiàn)功能那么簡(jiǎn)單,里面有很多學(xué)問(wèn)。例如:

  • 框架應(yīng)該給用戶(hù)提供哪些構(gòu)建產(chǎn)物?產(chǎn)物的模塊格式如何?

  • 當(dāng)用戶(hù)沒(méi)有以預(yù)期的方式使用框架時(shí),是否應(yīng)該打印合適的警告信息從而提供更好的開(kāi)發(fā)體驗(yàn),讓用戶(hù)快速定位問(wèn)題?

  • 開(kāi)發(fā)版本和生產(chǎn)版本的構(gòu)建有何區(qū)別?

  • 熱更新(hot module replacement,HMR)需要框架層面的支持,是否也應(yīng)該考慮?

  • 當(dāng)框架提供了多個(gè)功能,而用戶(hù)只需要其中幾個(gè)功能時(shí),用戶(hù)能否選擇關(guān)閉其他功能從而減少最終資源的打包體積?

1. 提升開(kāi)發(fā)體驗(yàn)

衡量一個(gè)框架是否足夠優(yōu)秀的指標(biāo)之一就是看它的開(kāi)發(fā)體驗(yàn)如何,以 Vue.js 3 為例:

createApp(App).mount('#not-exist')

當(dāng)創(chuàng)建一個(gè)組件并試圖將其掛載到一個(gè)不存在的 DOM 節(jié)點(diǎn)時(shí),就會(huì)收到一條警告信息:

[Vue warn]: Failed to mount app: mount target selector "#not-exist" returned null.

這條信息讓我們能夠清晰且快速地定位問(wèn)題。如果 Vue.js 內(nèi)部不做任何處理,那么很可能得到的是 JavaScript 層面的錯(cuò)誤信息,如 Uncaught TypeError: Cannot read property 'xxx' of null,而根據(jù)此信息很難知道問(wèn)題所在。

因此,在框架設(shè)計(jì)和開(kāi)發(fā)過(guò)程中,提供友好的警告信息至關(guān)重要。始終提供友好的警告信息不僅能夠幫助用戶(hù)快速定位問(wèn)題,節(jié)省用戶(hù)的時(shí)間,還能夠讓框架收獲良好的口碑,讓用戶(hù)認(rèn)可框架的專(zhuān)業(yè)性。

在 Vue.js 的源碼中,經(jīng)常能夠看到 warn 函數(shù)的調(diào)用,例如:

warn(`Failed to mount app: mount target selector "${container}" returned null.`)

除了提供必要的警告信息外,還有很多其他方面可以作為切入口,進(jìn)一步提升用戶(hù)的開(kāi)發(fā)體驗(yàn)。例如,在 Vue.js 3 中,當(dāng)在控制臺(tái)打印一個(gè) ref 數(shù)據(jù)時(shí):

const count = ref(0)
console.log(count)

打印結(jié)果是一個(gè) ref 對(duì)象,很不直觀,但調(diào)用 count.value 后,得到的就是響應(yīng)式對(duì)象的值,變得非常直觀。

那么有沒(méi)有辦法在直接打印 count 時(shí)讓輸出的信息更友好呢?當(dāng)然可以,瀏覽器允許我們編寫(xiě)自定義的 formatter,從而自定義輸出形式。

在 Vue.js 3 的源碼中,有一個(gè)名為 initCustomFormatter 的函數(shù),用來(lái)在開(kāi)發(fā)環(huán)境下初始化自定義 formatter。

以 Chrome 為例,打開(kāi) DevTools 的設(shè)置,然后勾選 “Console” -> “Enable custom formatters” 選項(xiàng),如下:

《Vue.js 設(shè)計(jì)與實(shí)現(xiàn)》—— 02 框架設(shè)計(jì)核心要素

然后刷新瀏覽器并查看控制臺(tái),會(huì)發(fā)現(xiàn)輸出內(nèi)容變得非常直觀,如下:

《Vue.js 設(shè)計(jì)與實(shí)現(xiàn)》—— 02 框架設(shè)計(jì)核心要素

2. 控制代碼體積

框架的大小也是衡量框架的標(biāo)準(zhǔn)之一。在實(shí)現(xiàn)同樣功能的情況下,代碼越少越好,這樣體積就會(huì)越小,最后瀏覽器加載資源的時(shí)間也就越少。前面說(shuō)到,框架提供越完善的警告信息越好,但這意味著要編寫(xiě)更多的代碼,那么如何在這個(gè)基礎(chǔ)上實(shí)現(xiàn)代碼體積的控制呢?

如果去看 Vue.js 3 的源碼,就會(huì)發(fā)現(xiàn)每一個(gè) warn 函數(shù)的調(diào)用都會(huì)配合 __DEV__ 常量的檢查,例如:

if (__DEV__ && !res) { // 打印警告信息的前提時(shí) __DEV__ 為 true
  warn(
    `Failed to mount app: mount target selector "${container}" returned null.`
  )
}

Vue.js 使用 rollup.js 對(duì)項(xiàng)目進(jìn)行構(gòu)建,這里的 __DEV__ 常量實(shí)際上是通過(guò) rollup.js 的插件配置來(lái)預(yù)定義的,其功能類(lèi)似于 webpack 中的 DefinePlugin 插件。

Vue.js 在輸出資源的時(shí)候,會(huì)輸出兩個(gè)版本,其中一個(gè)用于開(kāi)發(fā)環(huán)境,如 vue.global.js,另一個(gè)用于生產(chǎn)環(huán)境,如 vue.global.prod.js。

當(dāng) Vue.js 構(gòu)建用于開(kāi)發(fā)環(huán)境的資源時(shí),會(huì)把 __DEV__ 常量設(shè)置為 true;當(dāng)構(gòu)建生產(chǎn)環(huán)境的資源時(shí),會(huì)把 __DEV__ 常量設(shè)置為 false。

因?yàn)樯a(chǎn)環(huán)境下判斷條件始終為假,這段永遠(yuǎn)不會(huì)執(zhí)行的代碼稱(chēng)為 dead code,它不會(huì)出現(xiàn)在最終產(chǎn)物中,在構(gòu)建資源時(shí)就會(huì)被移除。這樣就做到了在開(kāi)發(fā)環(huán)境中為用戶(hù)提供友好的警告信息的同時(shí),不會(huì)增加生產(chǎn)環(huán)境代碼的體積。

3. 良好的 Tree-Shaking

僅僅通過(guò) __dev__ 變量控制代碼量是遠(yuǎn)遠(yuǎn)不夠的。還以 Vue.js 為例,其內(nèi)建了很多組件,例如 <Transition> 組件,如果項(xiàng)目中沒(méi)有用到該組件,其對(duì)應(yīng)的代碼就不需要也不應(yīng)該包含在最終的構(gòu)建資源中。那么如何做到這一點(diǎn)呢?答案就是 Tree-Shaking。

在前端領(lǐng)域,這個(gè)概念是由 rollup.js 普及的。簡(jiǎn)單地說(shuō),Tree-Shaking 指的就是消除那些永遠(yuǎn)不會(huì)被執(zhí)行的代碼,也就是排除 dead code,現(xiàn)在無(wú)論是 rollup.js 還是webpack,都支持 Tree-Shaking

想要實(shí)現(xiàn) Tree-Shaking,必須滿(mǎn)足一個(gè)條件,即模塊必須是 ESM(ES Module),因?yàn)?Tree-Shaking 依賴(lài) ESM 的靜態(tài)結(jié)構(gòu)。以 rollup.js 為例看看 Tree-Shaking 如何工作,其目錄結(jié)構(gòu)如下:

- demo
	- package.json
	- input.js
	- utils.js

首先安裝 rollup.js:

yarn add rollup -D # 或 npm install rollup -D

input.js 和 utils.js 文件的內(nèi)容如下:

// input.js
import { foo } from './utils.js'
foo()

// utils.js
export function foo(obj) {
    obj && obj.foo
}
export function bar(obj) { // bar 函數(shù)未被使用
    obj && obj.bar
}

接著,執(zhí)行如下命令進(jìn)行構(gòu)建:

npx rollup input.js -f esm -o bundle.js

構(gòu)建后,輸出的 bundle.js 的內(nèi)容為:

export function foo(obj) {
    obj && obj.foo
}
foo()

這說(shuō)明 Tree-Shaking 起了作用,我們并沒(méi)有使用 bar 函數(shù),因此它作為 dead code 被刪除了。但是仔細(xì)觀察會(huì)發(fā)現(xiàn),foo 函數(shù)的執(zhí)行也沒(méi)有什么意義,僅僅是讀取了對(duì)象的值,所以它的執(zhí)行似乎沒(méi)什么必要。既然把這段代碼刪了也不會(huì)對(duì)程序產(chǎn)生影響,那為什么 rollup.js 不把這段代碼也作為 dead code 移除呢?

這涉及 Tree-Shaking 中的第二個(gè)關(guān)鍵點(diǎn) —— 副作用。如果一個(gè)函數(shù)調(diào)用會(huì)產(chǎn)生副作用,那么就不能將其移除。

簡(jiǎn)單地說(shuō),副作用就是,當(dāng)調(diào)用函數(shù)的時(shí)候會(huì)對(duì)外部產(chǎn)生影響,例如修改了全局變量。

但是,上面的代碼只讀取對(duì)象的值,怎么會(huì)產(chǎn)生副作用呢?其實(shí)是有可能的,如果 obj 對(duì)象是一個(gè)通過(guò) Proxy 創(chuàng)建的代理對(duì)象,那么讀取對(duì)象屬性時(shí),就會(huì)觸發(fā)代理對(duì)象的 get 夾子(trap),在 get 夾子中是可能產(chǎn)生副作用的。至于到底會(huì)不會(huì)產(chǎn)生副作用,只有代碼真正運(yùn)行的時(shí)候才能知道,JavaScript 本身是動(dòng)態(tài)語(yǔ)言,因此想要靜態(tài)地分析哪些代碼是 dead code 很有難度。

即然靜態(tài)地分析代碼很困難,所以像 rollup.js 這類(lèi)工具都會(huì)提供一個(gè)機(jī)制,讓我們手動(dòng)明確地告訴 rollup.js 該段代碼是一個(gè)純函數(shù),不會(huì)產(chǎn)生副作用,可以移除它。具體實(shí)現(xiàn)如下:

import {foo} from './utils'

/*#__PURE__*/ foo() // 前面的 __PURE__ 告知是一個(gè)純函數(shù),不會(huì)產(chǎn)生副作用,可以移除

此時(shí)再次執(zhí)行構(gòu)建命令并查看 bundle.js 文件,就會(huì)發(fā)現(xiàn)它的內(nèi)容是空的。

因此,我們?cè)诰帉?xiě)框架的時(shí)候需要合理使用 /*#__PURE__* 注釋。Vue.js 3 的源碼里面大量使用了該注釋。

那么,這會(huì)不會(huì)對(duì)編寫(xiě)代碼造成很大的心智負(fù)擔(dān)呢?其實(shí)不會(huì),因?yàn)橥ǔ.a(chǎn)生副作用的代碼都是模塊內(nèi)函數(shù)的頂級(jí)調(diào)用。

什么是頂級(jí)調(diào)用呢?如下:

foo() // 頂級(jí)調(diào)用

function bar() {
    foo() // 函數(shù)內(nèi)調(diào)用 -- 沒(méi)有副作用,除非 bar() 頂級(jí)調(diào)用
}

/*#__PURE__*/ 注釋不僅僅作用于函數(shù),它可以應(yīng)用于任何語(yǔ)句上。該注釋也不是只有 rollup.js 才能識(shí)別,webpack 以及壓縮工具(如 terser)都能識(shí)別它。

4. 輸出構(gòu)建產(chǎn)物

前面說(shuō)到 Vue.js 會(huì)為開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境輸出不同的包,如 vue.global.js 用于開(kāi)發(fā)環(huán)境,它包含必要的警告信息,而 vue.global.prod.js 用于生產(chǎn)環(huán)境,不包含警告信息。實(shí)際上,Vue.js 的構(gòu)建產(chǎn)物除了有環(huán)境上的區(qū)分之外,還會(huì)根據(jù)使用場(chǎng)景的不同而輸出其他形式的產(chǎn)物。

不同類(lèi)型的產(chǎn)物一定有對(duì)應(yīng)的需求背景,因此需要從需求講起。首先我們希望用戶(hù)可以直接在 HTML 頁(yè)面中使用 <script> 標(biāo)簽引入框架并使用:

<body>
  <script src="/path/to/vue.js"></script>
    <script>
    const { createApp } = Vue
    // ...
  </script>
</body>

為了實(shí)現(xiàn)這個(gè)需求,需要輸出一種叫作 IIFE 格式的資源。IIFE 的全稱(chēng)是 Immediately Invoked Function Expression,即“立即調(diào)用的函數(shù)表達(dá)式”,易于用 JavaScript 來(lái)表達(dá):

(function () {
    // ...
})

如以上代碼所示,這是一個(gè)立即執(zhí)行的函數(shù)表達(dá)式。實(shí)際上,vue.global.js 文件就是 IIFE 形式的資源,它的代碼結(jié)構(gòu)如下所示:

var Vue = (function(exports){
  // ...
  exports.createApp = createApp;
  // ...
  return exports
}({}))

這樣當(dāng)我們使用 <script> 標(biāo)簽直接引入 vue.global.js 文件后,全局變量 Vue 就是可用的了。在 rollup.js 中,可以通過(guò)配置 format: 'iife' 來(lái)輸出這種形式的資源:

// rollup.config.js
const config = {
  input: 'input.js',
  output: {
    file: 'output.js',
    format: 'iife' // 指定模塊形式
  }
}

export default config

隨著技術(shù)的發(fā)展和瀏覽器的支持,現(xiàn)在主流瀏覽器對(duì)原生 ESM 的支持都不錯(cuò),所以用戶(hù)除了能夠使用 <script> 標(biāo)簽引用 IIFE 格式的資源外,還可以直接引入 ESM 格式的資源,例如 Vue.js 3 還會(huì)輸出 vue.esm-browser.js 文件,用戶(hù)可以直接用 <script type="module"> 標(biāo)簽引入:

<script type="module" src="/path/to/vue.esm-browser.js"></script>

為了輸出 ESM 格式的資源,rollup.js 的輸出格式需要配置為:format: 'esm'。

為什么 vue.esm-browser.js 文件中會(huì)有 -browser 字樣?其實(shí)對(duì)于 ESM 格式的資源來(lái)說(shuō),Vue.js 還會(huì)輸出一個(gè) vue.esm-bundler.js 文件,其中 -browser 變成了 -bundler。為什么這么做呢?我們知道,無(wú)論是 rollup.js 還是 webpack,在尋找資源時(shí),如果 package.json 中存在 module 字段,那么會(huì)優(yōu)先使用 module 字段指向的資源來(lái)代替 main 字段指向的資源。

可以打開(kāi) Vue.js 源碼中的 packages/vue/package.json 文件看一下:

{
  "main": "index.js",
  "module": "dist/vue.runtime.esm-bundler.js",
}

其中 module 字段指向的是 vue.runtime.esm-bundler.js 文件,意思是說(shuō),如果項(xiàng)目是使用webpack 構(gòu)建的,那么你使用的 Vue.js 資源就是 vue.runtime.esm-bundler.js,也就是說(shuō),帶有 -bundler 字樣的 ESM 資源是給 rollup.js 或 webpack 等打包工具使用的,而帶有 -browser 字樣的 ESM 資源是直接給 <script type="module"> 使用的。它們之間有何區(qū)別?這就不得不提到上文中的 __DEV__ 常量。當(dāng)構(gòu)建用于 <script> 標(biāo)簽的 ESM 資源時(shí),如果是用于開(kāi)發(fā)環(huán)境,那么 __DEV__ 會(huì)設(shè)置為 true;如果是用于生產(chǎn)環(huán)境,那么 __DEV__ 常量會(huì)設(shè)置為 false,從而被 Tree-Shaking 移除。但是當(dāng)我們構(gòu)建提供給打包工具的 ESM 格式的資源時(shí),不能直接把 __DEV__ 設(shè)置為 truefalse,而要使用(process.env.NODE_ENV !== 'production')替換 __DEV__ 常量。例如下面的源碼:

if (__DEV__) {
  warn(`useCssModule() is not supported in the global build.`)
}

在帶有 -bundler 字樣的資源中會(huì)變成:

if ((process.env.NODE_ENV !== 'production')) {
  warn(`useCssModule() is not supported in the global build.`)
}

這樣做的好處是,用戶(hù)可以通過(guò) webpack 配置自行決定構(gòu)建資源的目標(biāo)環(huán)境,但是最終效果其實(shí)一樣,這段代碼也只會(huì)出現(xiàn)在開(kāi)發(fā)環(huán)境中。用戶(hù)除了可以直接使用 <script> 標(biāo)簽引入資源外,我們還希望用戶(hù)可以在 Node.js 中通過(guò) require 語(yǔ)句引用資源,例如:

const Vue = require('vue')

為什么會(huì)有這種需求呢?因?yàn)楫?dāng)進(jìn)行服務(wù)端渲染時(shí),Vue.js 的代碼是在 Node.js 環(huán)境中運(yùn)行的。在 Node.js 環(huán)境中,資源的模塊格式應(yīng)該是 CommonJS,簡(jiǎn)稱(chēng) cjs。為了能夠輸出 cjs 模塊的資源,可以通過(guò)修改 rollup.config.js 的配置 format: 'cjs' 來(lái)實(shí)現(xiàn):

5. 特征開(kāi)關(guān)

在設(shè)計(jì)框架時(shí),框架會(huì)給用戶(hù)提供諸多特性(或功能),例如提供 A、B、C 三個(gè)特性給用戶(hù),同時(shí)還提供了 a、b、c 三個(gè)對(duì)應(yīng)的特性開(kāi)關(guān),用戶(hù)可以通過(guò)設(shè)置 a、b、c 為 truefalse 來(lái)代表開(kāi)啟或關(guān)閉對(duì)應(yīng)的特性,這將會(huì)帶來(lái)很多益處,如:

  • 對(duì)于用戶(hù)關(guān)閉的特性,可以利用 Tree-Shaking 機(jī)制讓其不打包在最終的資源中。
  • 該機(jī)制為框架設(shè)計(jì)帶來(lái)了靈活性,可以通過(guò)特性開(kāi)關(guān)任意為框架添加新的特性,而不用擔(dān)心資源體積變大。
  • 當(dāng)框架升級(jí)時(shí),也可以通過(guò)特性開(kāi)關(guān)來(lái)支持遺留 API,這樣新用戶(hù)可以選擇不使用遺留 API,從而使最終打包的資源體積最小化。

那如何實(shí)現(xiàn)特性開(kāi)關(guān)呢?其原理和前面提到的 __DEV__ 常量一樣,本質(zhì)上是利用 rollup.js 的預(yù)定義常量插件來(lái)實(shí)現(xiàn)。拿 Vue.js 3 源碼中的一段 rollup.js 配置來(lái)說(shuō):

{
  __FEATURE_OPTIONS_API__: isBundlerESMBuild ? `__VUE_OPTIONS_API__` : true,
}

其中 __FEATURE_OPTIONS_API__ 類(lèi)似于 __DEV__。在 Vue.js 3 的源碼中搜索,可以找到很多類(lèi)似于如下代碼的判斷分支:

// support for 2.x options
if (__FEATURE_OPTIONS_API__) {
  currentInstance = instance
  pauseTracking()
  applyOptions(instance, Component)
  resetTracking()
  currentInstance = null
}

當(dāng) Vue.js 構(gòu)建資源時(shí),如果構(gòu)建的資源是供打包工具使用的(即帶有 -bundler 字樣的資源),那么上面的代碼在資源中會(huì)變成:

// support for 2.x options
if (__VUE_OPTIONS_API__) { // 這里不一樣
  currentInstance = instance
  pauseTracking()
  applyOptions(instance, Component)
  resetTracking()
  currentInstance = null
}

其中 __VUE_OPTIONS_API__ 是一個(gè)特性開(kāi)關(guān),用戶(hù)可以通過(guò)設(shè)置 __VUE_OPTIONS_API__ 預(yù)定義常量的值來(lái)控制是否要包含這段代碼。通常用戶(hù)可以使用 webpack.DefinePlugin 插件來(lái)實(shí)現(xiàn):

// webpack.DefinePlugin 插件配置
new webpack.DefinePlugin({
  __VUE_OPTIONS_API__: JSON.stringify(true) // 開(kāi)啟特性
})

最后解釋一下 __VUE_OPTIONS_API__ 開(kāi)關(guān)有什么用。在 Vue.js 2 中,我們編寫(xiě)的組件叫作Options API;在 Vue.js 3 中,推薦使用 Composition API 來(lái)編寫(xiě)代碼。為了兼容 Vue.js 2,在 Vue.js 3 中仍然可以使用 Options API 的方式編寫(xiě)代碼。但是如果明確知道自己不會(huì)使用選項(xiàng) API,用戶(hù)就可以使用 __VUE_OPTIONS_API__ 開(kāi)關(guān)來(lái)關(guān)閉該特性,這樣在打包的時(shí)候 Vue.js 的這部分代碼就不會(huì)包含在最終的資源中,從而減小資源體積。

6. 錯(cuò)誤處理

錯(cuò)誤處理是框架開(kāi)發(fā)過(guò)程中非常重要的環(huán)節(jié)??蚣苠e(cuò)誤處理機(jī)制的好壞直接決定了用戶(hù)應(yīng)用程序的健壯性,還決定了用戶(hù)開(kāi)發(fā)時(shí)處理錯(cuò)誤的心智負(fù)擔(dān)。

假設(shè)我們開(kāi)發(fā)了一個(gè)工具模塊,代碼如下:

// utils.js
export default {
    foo(fn) {
        fn && fn()
    }
}

該模塊導(dǎo)出一個(gè)對(duì)象,其中 foo 屬性是一個(gè)函數(shù),接收一個(gè)回調(diào)函數(shù)作為參數(shù),調(diào)用 foo 函數(shù)時(shí)會(huì)執(zhí)行該回調(diào)函數(shù),在用戶(hù)側(cè)使用時(shí):

import utils from 'utils.js'
utils.foo( () => {
    // ...
})

如果用戶(hù)提供的回調(diào)函數(shù)在執(zhí)行的時(shí)候出錯(cuò)了,怎么辦?有兩個(gè)辦法,第一個(gè)辦法是讓用戶(hù)自行處理,這需要用戶(hù)自己執(zhí)行 try ... catch

import utils from 'utils.js'
utils.foo( () => {
    try {
        // ...
    } catch(e) {
        // ...
    }
})

但是這會(huì)增加用戶(hù)的負(fù)擔(dān)。如果 utils.js 提供了幾十上百個(gè)類(lèi)似的函數(shù),那么用戶(hù)在使用的時(shí)候就需要逐一添加錯(cuò)誤處理程序。

第二個(gè)辦法是我們代替用戶(hù)統(tǒng)一處理錯(cuò)誤,如以下代碼所示:

// utils.js
export default {
    foo(fn) {
        try {
            fn && fn()
        } catch(e) {
            /* ... */
        }
    },
    bar(fn) {
        try {
            fn && fn()
        } catch(e) {
            /* ... */
        }
    },
    // ...
}

事實(shí)上,可以進(jìn)一步將錯(cuò)誤處理程序封裝在一個(gè)函數(shù)上,假設(shè)稱(chēng)為 callWithErrorHandling

// utils.js
export default {
    foo(fn) {
        callWithErrorHandling(fn)
    },
    bar(fn) {
        callWithErrorHandling(fn)
    },
    // ...
}

function callWithErrorHandling(fn) {
    try {
        fn && fn()
    } catch(e) {
        /* ... */
    } 
}

簡(jiǎn)潔還不是封裝函數(shù)的主要目的,我們能為用戶(hù)提供統(tǒng)一的錯(cuò)誤處理接口,如:

// utils.js
let handleError = null

export default {
  foo(fn) {
    callWithErrorHandling(fn)
  },
  // 用戶(hù)可以調(diào)用該函數(shù)注冊(cè)統(tǒng)一的錯(cuò)誤處理函數(shù)
  registerErrorHandler(fn) {
    handleError = fn
  }
}

function callWithErrorHandling(fn) {
  try {
    fn && fn()
  } catch (e) {
    // 將捕獲到的錯(cuò)誤傳遞給用戶(hù)的錯(cuò)誤處理程序
    handleError(e)
  }
}

這樣用戶(hù)側(cè)的代碼就會(huì)非常簡(jiǎn)潔且健壯:

import utils from 'utils.js'

// 注冊(cè)錯(cuò)誤處理程序
utils.registerErrorHandler((e) => {
  console.log(e)
})
utils.foo(() => {/*...*/})
utils.bar(() => {/*...*/})

這時(shí)錯(cuò)誤處理的能力完全由用戶(hù)控制,用戶(hù)既可以選擇忽略錯(cuò)誤,也可以調(diào)用上報(bào)程序?qū)㈠e(cuò)誤上報(bào)給監(jiān)控系統(tǒng)。實(shí)際上,這就是 Vue.js 錯(cuò)誤處理的原理,可以在源碼中搜索到 callWithErrorHandling 函數(shù)。另外,在 Vue.js 中,也可以注冊(cè)統(tǒng)一的錯(cuò)誤處理函數(shù):

import App from 'App.vue'

const app = createApp(App)
app.config.errorHandler = () => {
  // 錯(cuò)誤處理程序
}

7. 良好的 TS 支持

TypeScript 是由微軟開(kāi)源的編程語(yǔ)言,簡(jiǎn)稱(chēng) TS,它是 JavaScript 的超集,能夠?yàn)?JavaScript 提供類(lèi)型支持。使用 TS 的好處有很多,如代碼即文檔、編輯器自動(dòng)提示、一定程度上能夠避免低級(jí) bug、代碼的可維護(hù)性更強(qiáng)等。因此對(duì) TS 類(lèi)型的支持是否完善也成為評(píng)價(jià)一個(gè)框架的重要指標(biāo)。

如何衡量一個(gè)框架對(duì) TS 類(lèi)型支持的水平呢?這里有一個(gè)常見(jiàn)的誤區(qū),很多人以為只要是使用 TS 編寫(xiě)框架,就等價(jià)于對(duì) TS 類(lèi)型支持友好,其實(shí)這兩種完全不同。

舉例來(lái)說(shuō)。下面是使用 TS 編寫(xiě)的函數(shù):

function foo(val: any) {
    return val
}

這個(gè)函數(shù)直接將參數(shù)作為返回值,這說(shuō)明返回值的類(lèi)型是由參數(shù)決定的,如果參數(shù)是 number 類(lèi)型,那么返回值也是 number 類(lèi)型。但是,假設(shè)有下面的代碼:

const res = foo('str') // 參數(shù)為字符串類(lèi)型,理論上 res 也為字符串類(lèi)型,但是卻推斷成了 any 類(lèi)型

為了達(dá)到理想狀態(tài),只需要對(duì) foo 函數(shù)做簡(jiǎn)單的修改即可:

function foo<T extends any>(val: T): T {
    return val
}

const res = foo('str') // 這時(shí)就會(huì)將 res 推斷為 "str" 字符串字面量了

通過(guò)這個(gè)例子可以認(rèn)識(shí)到,使用 TS 編寫(xiě)代碼與對(duì) TS 類(lèi)型支持友好是兩件事。在編寫(xiě)大型框架時(shí),想要做到完善的 TS 類(lèi)型支持很不容易,可以查看 Vue.js 源碼中的 runtime-core/src/apiDefineComponent.ts 文件,整個(gè)文件里真正會(huì)在瀏覽器中運(yùn)行的代碼其實(shí)只有 3 行,但是全部的代碼接近 200 行,其實(shí)這些代碼都是在為類(lèi)型支持服務(wù)。由此可見(jiàn),框架想要做到完善的類(lèi)型支持,需要付出相當(dāng)大的努力。

更多文章可關(guān)注:GopherBlog、GopherBlog副站文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-438051.html

到了這里,關(guān)于《Vue.js 設(shè)計(jì)與實(shí)現(xiàn)》—— 02 框架設(shè)計(jì)核心要素的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Vue.js設(shè)計(jì)與實(shí)現(xiàn)閱讀-3

    Vue.js設(shè)計(jì)與實(shí)現(xiàn)閱讀-3

    前言 前面一章我們了解了,開(kāi)發(fā)體驗(yàn)是衡量一個(gè)框架的重要指標(biāo)之一。提供友好的警告信息至關(guān)重要,但是越詳細(xì)的警告信息,意味著框架體積越大。為了解決這一問(wèn)題,可以利用 Tree-Shaking 機(jī)制,配合構(gòu)建工具預(yù)定義常量,例如 __ DEV __ ,從而實(shí)現(xiàn)只在開(kāi)發(fā)環(huán)境中打印警告

    2024年02月02日
    瀏覽(16)
  • Vue.js設(shè)計(jì)與實(shí)現(xiàn)閱讀-2

    Vue.js設(shè)計(jì)與實(shí)現(xiàn)閱讀-2

    上一篇我們了解到了 命令式和聲明式的區(qū)別,前者關(guān)注過(guò)程,后者關(guān)注結(jié)果 了解了虛擬dom存在的意義,使找出差異這個(gè)過(guò)程消耗的性能最小化 學(xué)習(xí)了進(jìn)行時(shí)、編譯時(shí)、編譯時(shí)+進(jìn)行時(shí)的特點(diǎn),初步知道了vue3是采用編譯時(shí)+進(jìn)行時(shí)的框架 2、1 提升用戶(hù)體驗(yàn) 提供友好的警告信息

    2024年02月02日
    瀏覽(16)
  • vue.js畢業(yè)設(shè)計(jì),基于vue.js前后端分離在線小說(shuō)電子書(shū)閱讀小程序系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    vue.js畢業(yè)設(shè)計(jì),基于vue.js前后端分離在線小說(shuō)電子書(shū)閱讀小程序系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    用戶(hù)首次登陸系統(tǒng)需要注冊(cè)一個(gè)用戶(hù)作為賬號(hào),用戶(hù)在登錄平臺(tái)后,可以進(jìn)行平臺(tái)的操作。主要模塊包括以下幾點(diǎn): 登錄功能:注冊(cè)普通賬號(hào)登錄;登錄后可以修改用戶(hù)的基本信息,也可以退出。 資訊功能:后臺(tái)錄入資訊,在微信小程序在線電子書(shū)閱讀系統(tǒng)的資訊模板展示

    2024年02月07日
    瀏覽(23)
  • 前端框架Vue-Vue核心

    前端框架Vue-Vue核心

    目錄 第1章 Vue核心 1.1 Vue簡(jiǎn)介 1.1.2 Vue的特點(diǎn) 1.1.3 與其它 JS 框架的關(guān)聯(lián) 1.1.4 學(xué)習(xí)Vue之前要掌握的JavaScript基礎(chǔ)知識(shí) 1.1.5 Vue官網(wǎng) 1.1.6 Vue周邊庫(kù) 1.3 模塊語(yǔ)法 1.3.1 模板的理解 1.3.2 插值語(yǔ)法 1.3.3 指令語(yǔ)法 1.4 數(shù)據(jù)綁定 1.4.1. 單向數(shù)據(jù)綁定 1.4.2. 雙向數(shù)據(jù)綁定 1.4.3 el與data的兩種寫(xiě)法

    2024年02月13日
    瀏覽(62)
  • Vue.js的核心概念

    Vue.js 是一個(gè)流行的 JavaScript 框架,用于構(gòu)建交互式的 web 界面。 它的核心概念包括: 1. 響應(yīng)式系統(tǒng) :Vue 使用響應(yīng)式系統(tǒng)來(lái)確保 UI 與 underlying data 保持同步。當(dāng)數(shù)據(jù)變化時(shí),視圖自動(dòng)更新。 2. 組件系統(tǒng) :Vue 應(yīng)用由可復(fù)用的組件構(gòu)成。每個(gè)組件都有自己的視圖、業(yè)務(wù)邏輯和樣

    2024年01月18日
    瀏覽(31)
  • 使用JavaScript和Vue.js框架開(kāi)發(fā)的電子商務(wù)網(wǎng)站,實(shí)現(xiàn)商品展示和購(gòu)物車(chē)功能

    引言: 隨著互聯(lián)網(wǎng)的快速發(fā)展和智能手機(jī)的普及,電子商務(wù)行業(yè)正迎來(lái)一個(gè)全新的時(shí)代。越來(lái)越多的消費(fèi)者選擇網(wǎng)上購(gòu)物,而不再局限于傳統(tǒng)的實(shí)體店。這種趨勢(shì)不僅僅是改變了消費(fèi)者的習(xí)慣購(gòu)物,也給企業(yè)帶來(lái)了巨大的商機(jī)。為了不斷滿(mǎn)足消費(fèi)者的需求,電子商務(wù)網(wǎng)站需要

    2024年02月15日
    瀏覽(26)
  • 人工智能三個(gè)核心要素:算法、算力、數(shù)據(jù)

    人工智能三個(gè)核心要素:算法、算力、數(shù)據(jù)

    人工智能(Artificial Intelligence,簡(jiǎn)稱(chēng)AI)是指計(jì)算機(jī)系統(tǒng)模擬、模仿和執(zhí)行人類(lèi)智能的能力。它是一門(mén)研究如何使計(jì)算機(jī)能夠像人類(lèi)一樣思考、學(xué)習(xí)、推理和解決問(wèn)題的科學(xué)和技術(shù)領(lǐng)域。 人工智能通過(guò)利用大量的數(shù)據(jù)和強(qiáng)大的計(jì)算能力,以及各種算法和技術(shù),使計(jì)算機(jī)系統(tǒng)能

    2024年02月07日
    瀏覽(25)
  • Spring框架核心與設(shè)計(jì)思想

    Spring框架核心與設(shè)計(jì)思想

    我們一般所說(shuō)的Spring指的是Spring Framework(Spring 框架),它是一個(gè)開(kāi)源的框架,Spring支持廣泛的應(yīng)用場(chǎng)景,它可以讓Java企業(yè)級(jí)的應(yīng)用程序開(kāi)發(fā)變得更簡(jiǎn)單,官方一點(diǎn)的回答:spring是J2EE應(yīng)用程序框架,是輕量級(jí)的IoC和AOP的容器框架,主要是針對(duì)javaBean的生命周期進(jìn)行管理的輕量級(jí)

    2023年04月15日
    瀏覽(35)
  • 軟信天成:數(shù)據(jù)治理三大核心要素是什么?

    軟信天成:數(shù)據(jù)治理三大核心要素是什么?

    近年來(lái),信息技術(shù)的快速發(fā)展和深入應(yīng)用讓數(shù)據(jù)獲得了前所未有的增長(zhǎng),著名研究機(jī)構(gòu)IDC預(yù)測(cè):到2025年,全球數(shù)據(jù)預(yù)計(jì)增長(zhǎng)至175ZB。隨著數(shù)據(jù)價(jià)值的日益凸顯,無(wú)數(shù)企業(yè)開(kāi)始布局?jǐn)?shù)字化戰(zhàn)略轉(zhuǎn)型,如何從龐雜的企業(yè)數(shù)據(jù)體現(xiàn)出業(yè)務(wù)價(jià)值已經(jīng)被越來(lái)越多的企業(yè)所重視。 企業(yè)數(shù)據(jù)

    2024年02月05日
    瀏覽(18)
  • 擴(kuò)展檢測(cè)和響應(yīng):零信任安全的核心要素

    擴(kuò)展檢測(cè)和響應(yīng):零信任安全的核心要素

    面對(duì)不斷增長(zhǎng)的攻擊面,擴(kuò)展和增強(qiáng)威脅檢測(cè)和響應(yīng)能力是XDR在安全功效方面的主要結(jié)果。這一成果不僅有助于全面保護(hù),而且有助于更好地實(shí)施零信任安全。 默認(rèn)情況下,這種方法不信任任何用戶(hù)或任何設(shè)備,只允許訪問(wèn)需要的資源。為了更好地了解零信任和 XDR 的共同點(diǎn)

    2024年02月04日
    瀏覽(28)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包