2021-09-16 vue3文檔閱讀筆記

今天有空就把vue3文檔從頭到尾看了一遍。就當做對vue從頭開始的學習和補漏,理解了一些用法改變的原理。最明顯的地方就是響應式的改變,vue2用的是Object.defineProperty(),vue3用的是Proxy。vue3還新增了組合式API等等。
這次記錄主要是記錄一些基礎的,面試常被問到的,查漏補缺。

應用&組件實例

應用實例

每個 Vue 應用都是通過用 createApp 函數創建一個新的應用實例開始的:

const app = Vue.createApp({ /* 選項 */ })

該應用實例是用來在應用中注冊“全局”組件的。我們將在后面的指南中詳細討論,簡單的例子:

const app = Vue.createApp({})
app.component('SearchInput', SearchInputComponent)
app.directive('focus', FocusDirective)
app.use(LocalePlugin)

應用實例暴露的大多數方法都會返回該同一實例,允許鏈式:

Vue.createApp({})
  .component('SearchInput', SearchInputComponent)
  .directive('focus', FocusDirective)
  .use(LocalePlugin)

根組件實例

傳遞給 createApp 的選項用于配置根組件。當我們掛載應用時,該組件被用作渲染的起點。

一個應用需要被掛載到一個 DOM 元素中。例如,如果我們想把一個 Vue 應用掛載到 <div id="app"></div>,我們應該傳遞 #app

const RootComponent = { /* 選項 */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')

與大多數應用方法不同的是,mount 不返回應用本身。相反,它返回的是根組件實例

雖然沒有完全遵循 MVVM 模型,但是 Vue 的設計也受到了它的啟發。因此在文檔中經常會使用 vm (ViewModel 的縮寫) 這個變量名表示組件實例。

計算屬性&偵聽器

計算屬性緩存 vs 方法

你可能已經注意到我們可以通過在表達式中調用方法來達到同樣的效果:

<p>{{ calculateBooksMessage() }}</p>
// 在組件中
methods: {
  calculateBooksMessage() {
    return this.author.books.length > 0 ? 'Yes' : 'No'
  }
}

我們可以將同一函數定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。然而,不同的是計算屬性是基于它們的反應依賴關系緩存的。計算屬性只在相關響應式依賴發生改變時它們才會重新求值。這就意味著只要author.books 還沒有發生改變,多次訪問 publishedBookMessage 計算屬性會立即返回之前的計算結果,而不必再次執行函數。

這也同樣意味著下面的計算屬性將不再更新,因為 Date.now () 不是響應式依賴

computed: {
  now() {
    return Date.now()
  }
}

相比之下,每當觸發重新渲染時,調用方法將總會再次執行函數。

我們為什么需要緩存?假設我們有一個性能開銷比較大的計算屬性 list,它需要遍歷一個巨大的數組并做大量的計算。然后我們可能有其他的計算屬性依賴于
list。如果沒有緩存,我們將不可避免的多次執行 list 的 getter!如果你不希望有緩存,請用 method 來替代

計算屬性的 Setter

計算屬性默認只有 getter,不過在需要時你也可以提供一個 setter:

// ...
computed: {
  fullName: {
    // getter
    get() {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set(newValue) {
      const names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

現在再運行 vm.fullName = 'John Doe' 時,setter 會被調用,vm.firstNamevm.lastName 也會相應地被更新。

Class與Style綁定

如果你的組件有多個根元素,你需要定義哪些部分將接收這個類。可以使用 $attrs 組件屬性執行此操作:

<div id="app">
  <my-component class="baz"></my-component>
</div>
const app = Vue.createApp({})

app.component('my-component', {
  template: `
    <p :class="$attrs.class">Hi!</p>
    <span>This is a child component</span>
  `
})

條件渲染

v-if vs v-show

v-if 是“真正”的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷毀和重建。

v-if 也是惰性的:如果在初始渲染時條件為假,則什么也不做——直到條件第一次變為真時,才會開始渲染條件塊。

相比之下,v-show 就簡單得多——不管初始條件是什么,元素總是會被渲染,并且只是簡單地基于 CSS 進行切換。

一般來說,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用 v-show 較好;如果在運行時條件很少改變,則使用 v-if 較好

v-if 與 v-for 一起使用

提示 不推薦同時使用 v-if 和 v-for

v-ifv-for 一起使用時,v-if 具有比 v-for 更高的優先級。請查閱列表渲染指南以獲取詳細信息。

當它們處于同一節點,v-if 的優先級比 v-for 更高,這意味著 v-if 將沒有權限訪問 v-for 里的變量:

<!-- This will throw an error because property "todo" is not defined on instance. -->

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

可以把 v-for 移動到 <template> 標簽中來修正:

<template v-for="todo in todos">
  <li v-if="!todo.isComplete">
    {{ todo }}
  </li>
</template>

表單綁定輸入

基礎用法

v-model 在內部為不同的輸入元素使用不同的 property 并拋出不同的事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段將 value 作為 prop 并將 change 作為事件。

組件基礎

基本實例

這里有一個 Vue 組件的示例:

// 創建一個Vue 應用
const app = Vue.createApp({})

// 定義一個名為 button-counter 的新全局組件
app.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `
    <button @click="count++">
      You clicked me {{ count }} times.
    </button>`
})

組件的復用

你可以將組件進行任意次數的復用
每個組件都會各自獨立維護它的data。因為你每用一次組件,就會有一個它的新組件實例被創建

在組件上使用 v-model

自定義事件也可以用于創建支持 v-model 的自定義輸入組件。記住:

<input v-model="searchText" />
等價于:

<input :value="searchText" @input="searchText = $event.target.value" />
當用在組件上時,v-model 則會這樣:

<custom-input
  :model-value="searchText"
  @update:model-value="searchText = $event"
></custom-input>

為了讓它正常工作,這個組件內的 <input> 必須:

將其 value attribute 綁定到一個名叫 modelValue 的 prop 上
在其 input 事件被觸發時,將新的值通過自定義的 update:modelValue 事件拋出
寫成代碼之后是這樣的:

app.component('custom-input', {
  props: ['modelValue'],
  template: `
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    >
  `
})

現在 v-model 就應該可以在這個組件上完美地工作起來了:

<custom-input v-model="searchText"></custom-input>

深入組件

非 Prop 的 Attribute

Attribute 繼承

當組件返回單個根節點時,非 prop attribute 將自動添加到根節點的 attribute 中。例如,在 <date-picker> 組件的實例中:

app.component('date-picker', {
  template: `
    <div class="date-picker">
      <input type="datetime" />
    </div>
  `
})

如果我們需要通過 data status property 定義 <date-picker> 組件的狀態,它將應用于根節點 (即 div.date-picker)。

<!-- 具有非prop attribute的Date-picker組件-->
<date-picker data-status="activated"></date-picker>

<!-- 渲染 date-picker 組件 -->
<div class="date-picker" data-status="activated">
  <input type="datetime" />
</div>

同樣的規則適用于事件監聽器:

<date-picker @change="submitChange"></date-picker>
app.component('date-picker', {
  created() {
    console.log(this.$attrs) // { onChange: () => {}  }
  }
})

當有一個 HTML 元素將 change 事件作為 date-picker 的根元素時,這可能會有幫助。

app.component('date-picker', {
  template: `
    <select>
      <option value="1">Yesterday</option>
      <option value="2">Today</option>
      <option value="3">Tomorrow</option>
    </select>
  `
})

在這種情況下,change 事件監聽器從父組件傳遞到子組件,它將在原生 select 的 change 事件上觸發。我們不需要顯式地從 date-picker 發出事件:

<div id="date-picker" class="demo">
  <date-picker @change="showChange"></date-picker>
</div>
const app = Vue.createApp({
  methods: {
    showChange(event) {
      console.log(event.target.value) // 將記錄所選選項的值
    }
  }
})

禁用 Attribute 繼承

如果你不希望組件的根元素繼承 attribute,你可以在組件的選項中設置 inheritAttrs: false。例如:

禁用 attribute 繼承的常見情況是需要將 attribute 應用于根節點之外的其他元素。

通過將 inheritAttrs 選項設置為 false,你可以訪問組件的 $attrs property,該 property 包括組件 propsemits property 中未包含的所有屬性 (例如,classstylev-on 監聽器等)。

app.component('date-picker', {
  inheritAttrs: false,
  template: `
    <div class="date-picker">
      <input type="datetime" v-bind="$attrs" />
    </div>
  `
})

有了這個新配置,data status attribute 將應用于 input 元素!

<!-- Date-picker 組件 使用非 prop attribute -->
<date-picker data-status="activated"></date-picker>

<!-- 渲染 date-picker 組件 -->
<div class="date-picker">
  <input type="datetime" data-status="activated" />
</div>

多個根節點上的 Attribute 繼承

與單個根節點組件不同,具有多個根節點的組件不具有自動 attribute 回退行為。如果未顯式綁定 $attrs,將發出運行時警告。

<custom-layout id="custom-layout" @click="changeValue"></custom-layout>
// 這將發出警告
app.component('custom-layout', {
  template: `
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  `
})

// 沒有警告,$attrs被傳遞到<main>元素
app.component('custom-layout', {
  template: `
    <header>...</header>
    <main v-bind="$attrs">...</main>
    <footer>...</footer>
  `
})

自定義事件

處理 v-model 修飾符

在 2.x 中,我們對組件 v-model 上的 .trim 等修飾符提供了硬編碼支持。但是,如果組件可以支持自定義修飾符,則會更有用。在 3.x 中,添加到組件 v-model 的修飾符將通過 modelModifiers prop 提供給組件:

讓我們創建一個示例自定義修飾符 capitalize,它將 v-model 綁定提供的字符串的第一個字母大寫。

添加到組件 v-model 的修飾符將通過 modelModifiers prop 提供給組件。在下面的示例中,我們創建了一個組件,其中包含默認為空對象的 modelModifiers prop。

請注意,當組件的 created 生命周期鉤子觸發時,modelModifiers prop 包含 capitalize,其值為 true——因為它被設置在 v-model 綁定 v-model.capitalize="bar"

<my-component v-model.capitalize="bar"></my-component>
app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  template: `
    <input type="text" 
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)">
  `,
  created() {
    console.log(this.modelModifiers) // { capitalize: true }
  }
})

現在我們已經設置了 prop,我們可以檢查 modelModifiers 對象鍵并編寫一個處理器來更改發出的值。在下面的代碼中,每當 <input/> 元素觸發 input 事件時,我們都將字符串大寫。

<div id="app">
  <my-component v-model.capitalize="myText"></my-component>
  {{ myText }}
</div>
const app = Vue.createApp({
  data() {
    return {
      myText: ''
    }
  }
})

app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  },
  template: `<input
    type="text"
    :value="modelValue"
    @input="emitValue">`
})

app.mount('#app')

對于帶參數的 v-model 綁定,生成的 prop 名稱將為 arg + "Modifiers"

<my-component v-model:foo.capitalize="bar"></my-component>
app.component('my-component', {
  props: ['foo', 'fooModifiers'],
  template: `
    <input type="text" 
      :value="foo"
      @input="$emit('update:foo', $event.target.value)">
  `,
  created() {
    console.log(this.fooModifiers) // { capitalize: true }
  }
})
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • vue3 特點 vue3 支持 vue2 的大多數特性 性能提升:打包大小減少41%初次渲染快55%,更新快133...
    歡欣的膜笛閱讀 990評論 0 1
  • 前言 本文是閱讀vue文檔時的收獲和心得,與vue文檔大部分不符,閱讀vue文檔請移步Vue.js 創建一個 Vu...
    極奏閱讀 651評論 0 1
  • vue學習 箭頭函數 js位置 JavaScript代碼可以直接嵌在網頁的任何地方,不過通常我們都把JavaScr...
    RobinZZX閱讀 216評論 0 0
  • 基礎功能 v-bind 綁定屬性 v-on 綁定事件 v-model 表單輸入和應用狀態之間的雙向綁定 v-if ...
    16325閱讀 332評論 0 0
  • 16宿命:用概率思維提高你的勝算 以前的我是風險厭惡者,不喜歡去冒險,但是人生放棄了冒險,也就放棄了無數的可能。 ...
    yichen大刀閱讀 6,076評論 0 4