[Vue3] 第一課 認識前端框架和 Vue3

2024 年 1 月 21 日
文章摘要
FakeGPT
加載中...
此內容由人工不智慧生成。

什麼是前端框架

儘管我們的前端三件套(HTML、CSS和JavaScript)已經非常強大,能夠完成所有的前端開發工作,但在有的時候,一些開發過程也稍顯繁瑣。例如,我們有下面一個例子:

從後端請求到一個 list,需要把這個 list 中的每一項內容都渲染到頁面上。

這是一個極爲常見的需求。如果我們使用三件套來實現它,我們需要這樣去寫:

<div id="father"></div>
// 假設下面的變數list就是我們得到的 all
let list = ["小王", "小李", "小張", "小劉"]
let father_node = document.getElementById("father")
for (let i = 0;i < list.length;i ++) {
let child_node = document.createNode("div")
child_node.innerHTML = list[i]
father_node.appendChild(child_node)
}

這樣的邏輯顯然是明瞭的。

然而,讀者是否認爲這樣的步驟太過於繁瑣?我們爲了將一個列表動態渲染到頁面上,竟然要手動地一個一個創造節點、添加節點。儘管有 for 迴圈幫助我們,這也顯得過於繁瑣了些。

框架(framework)就是爲我們解決類似問題的一個解。

所謂的框架,就是將一些複雜但是沒有什麼親自書寫必要的步驟封裝起來,開發人員只需要聚焦於具體功能的實現,其餘的交給框架來做即可。框架的出現大大提高了開發效率,避免了開發人員將大量時間耗費在沒有意義的冗餘程式碼中。

比如上面的這個例子,如果使用框架來做,什麼獲取父節點,什麼創建子節點之類的步驟統統都可以省略掉。如果使用 Vue 框架,僅需如下程式碼即可:

<div id="father">
<div v-for="item in list">{{ item }}</div>
</div>
let list = reactive(["小王", "小李", "小張", "小劉"])

至於這段程式碼是什麼意思,讀者暫時不需要去了解它,在之後的學習中,筆者會詳細爲您講述。

從上面這個例子中,我們不難看出,使用框架前後的代碼開發難度簡直是改天換地的。這也是我們使用框架最重要的意義所在。

目前在前端比較流行的框架有 Vue、React、BootStrap 等。後端比較流行的框架有 Springboot(Java)、Gin(Go)、Flask(Python) 等。本系列課程使用的框架是Vue(音標/vju:/)。使用的框架版本是 Vue 3.0。

Vue3 框架簡介

在並不遙遠的東南亞,有一個神奇的國家叫馬來西亞,這個神奇的國家有兩塊土地,分別是半島部分和婆羅洲島部分——但這不重要。重要的是在這兩部分中間,夾着一個面積極小的國家,這個國家就是大名鼎鼎的新加坡。在這個小國家中,生活着一個偉大的人類,這個人類幾乎以一己之力賞了全球近一半的前端開發人員的飯碗。這個偉大的人類,名字叫做 Evan You,或者作爲中國人,我們更熟悉他的中文名——尤雨溪

尤雨溪,Vue 框架和 Vite 構建工具的作者,全球一半前端程序員心中祖師爺一般的存在。此人長期混跡於各大論壇,在中國社群媒體上極受歡迎。

他所發展的 Vue 框架一經問世即風靡全球。 其後推出的 Vue3 版本雖然基本上不相容之前的 Vue2,但得益於 Vue3 的強大特性和與 Vite 工具結合之後令人難以置信的構建速度,Vue3 正在快速搶佔前端程式設計師的心臟。

Vue3 框架非常簡單、高效、直觀且快速。Vue3 官網對它的自我介紹是“The Progressive JavaScript Framework”,即漸進式 JavaScript 框架。

Vue 官網主頁

上圖這個花裏胡哨像夜店一樣的主頁就是 Vue3 項目的主頁。由於尤雨溪大佬是新加坡華人,故 Vue3 的文檔也有很舒適的中文版本。在接下來的學習中,我們將主要使用 Vue3 的官方文檔。

Vue3 專案建立

Vue 官方推薦使用自家的 Vite 構建工具來搭建 Vue3 專案,筆者亦推薦這種方式。因爲這種方式非常便捷,且搭建出來的項目使用 Vite 構建工具,該工具以其高速準確的構建水平而享譽世界。搭建過程如下:

  1. 檢驗 npm 安裝:
Terminal window
npm -v

如果有輸出 npm 的版本號碼,證明 npm 安裝成功。這一步相信讀者已經完成。

  1. 建立 Vue3 專案:
Terminal window
npm create vite@latest

輸入專案名稱,然後選擇 vue 這個專案模板。我們暫時不使用 TypeScript,而是繼續使用 JavaScript 進行開發。

  1. 進入專案內部,執行npm install
Terminal window
# 進入某個目錄
cd <專案名>
npm install

這樣,一個 Vue3 專案就已經被 new 了出來。

專案目錄解析

建立好專案之後,我們會得到以下專案目錄:

Vue 專案檔案結構

下面我們來具體來看一下目錄結構。

flake.lock 和 flake.nix

這兩個檔案是筆者使用的作業系統 Nix OS 特有的,用於配置該專案在 Nix OS 上的開發環境,讀者不會有這兩個檔案,忽略即可。

index.html

這個檔案我們已經非常熟悉了,這是最後放到瀏覽器裏面的 HTML 檔案。在 Vue 中,我們是透過Mount這個操作,將Vue框架的程式碼的編譯結果交給 HTML 去執行。因此,我們需要在這裏明白一件事情:任何框架,都只是簡化了開發過程,而不是改變三件套的底層邏輯。 也就是說,瀏覽器根本不認識 Vue 框架,它只認識前端三件套。Vue 框架的程式碼必須編譯成前端三件套,才能夠被瀏覽器識別到。所以在這個專案結構中,纔會有 index.html 檔案的出現。

package.json 和 package-lock.json

這兩個檔案讀者不應該陌生。它們是 node.js 的依賴管理檔案。當我們使用 npm install 的時候,依賴會自動寫入這兩個檔案,並鎖定版本,確保專案的可復現性。大家可以將其理解成一個依賴索引,之後在不同的電腦上重建專案的時候,只需要按照索引去 npm 倉庫裏面找依賴就可以了。

public 目錄

這個目錄用來存放一些 static 的資源,例如圖片、影片等。

README.md

專案自述文件,可以暫時忽略。

vite.config.js

Vite 構建工具的配置檔案,構建過程中的一些 option 可以在其中定義。

src 目錄

項目的主要目錄! 我們大部分時間都將在這個目錄中書寫我們的程式碼。

src/App.vue

項目的根 component,在學習 Vue 的前期,我們主要在這個檔案中深耕,書寫我們的程式碼。

src/assets

也用於存放 static 檔案,在前期不推薦讀者使用這個目錄,因其在構建過程中會出現路徑問題,故需要特別配置。在讀者對 Vue 和 JS 足夠熟悉之前,建議大家直接使用 public 目錄。該目錄可以直接刪除。

src/components

component 目錄。在 Vue 中,一個極其重要的思想是:一切皆component。關於 component 的概念,將在後面的文章中詳細介紹。

src/main.js

專案的主要配置檔案。在這裏我們可以定義全局 component,管理 npm 軟體包的全局使用等等。

src/style.css

專案的全局 css,可以用於所有的 component。

專案啓動

我們可以透過:

Terminal window
npm run dev

來啓動一個開發伺服器。該伺服器預設運行在 5173 連接埠。修改將自動熱更新,不需要手動更新頁面,也不需要手動啓停伺服器。

Vue3 基礎部分

模板語法

Vue3 第一個強大的點在與其擁有一套高效且簡介的模板語法。我們之前的那個例子就是應用了這套模板語法。

通俗來講,所謂模板語法,就是能夠將 JavaScript 中的資料快速動態渲染到 HTML 中。

Vue3 提供的模板語法包括下面幾類:

  1. 文字插值:
<span> {{ msg }} </span>

msg 爲需要插入到這個地方的 JS 變數值。同時,每次該值更新,該處內容也會同步自動更新。

  1. 屬性綁定:

文本插值是將 JS 變數直接插入到對應的 HTML 標籤之間。那麼標籤的屬性有沒有可能使用 JS 變數動態去調整呢?當然也是可以的。語法如下:

<span :id="id_js"></span>

我們可以看到,只要在屬性名稱之前加一個:,就可以在後面使用 JS 變數作爲屬性的值。同樣的,變數值改變,屬性值也會同步改變。

這個模板語法被大量使用,其中有一種是極爲常用的,那就是 Boolean 值的綁定。例如,我們想要一個按鈕在 variable(Boolean) 變數值爲真的時候啓用,其餘情況禁用。我們就可以這樣來做:

<button :disabled="!variable"></button>
  1. 使用 JavaScript 表達式

至此,我們僅在模板中綁定了一些簡單的屬性名稱。但是 Vue 實際上在所有的資料綁定中都支援完整的 JavaScript 表達式:

{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>

每個綁定只支援單一表達式,也就是一段能夠被求值的 JavaScript 程式碼。一個簡單的判斷方法是是否可以合法地寫在return關鍵字後面。

<!-- 這是一個語句,而非表達式 -->
{{ var a = 1 }}
<!-- 不支援條件控制,請使用三元表達式 -->
{{ if (ok) { return message } }}
  1. 呼叫函式
<time :title="toTitleDate(date)" :datetime="date">
{{ formatDate(date) }}
</time>

其中 title 的值就是 toTitleDate() 這個函式的返回結果。

ref()和reactive()

渲染到 HTML 模板上的 JS 變數,必須使用 ref()reactive() 包圍。我們稱之爲響應式變數 。只有使用 ref()reactive() 包圍的變數,才能夠在其發生改變時,自動觸發 HTML 模板的同步改變。

一般來講,當所渲染的資料類型爲比較簡單的類型,譬如字串、數字、Boolean 等的時候,我們使用 ref() 包圍;當其比較複雜,譬如陣列、物件的時候,我們使用 reactive() 包圍。但是在初級階段,筆者推薦所有需要渲染的變數均使用 ref() 來渲染

接下來我們來看一個例子:

import { ref } from 'vue';
let variable = ref("hello, world.")
const show_var = () => {
// 在 JavaScript 部分使用 variable 變數的值,必須使用 .value 來取到!!
console.log(variable.value)
}
<!-- 在 HTML 模板部分使用模板語法,不能使用 .value -->
<div>{{ variable }}</div>

計算屬性

模板中的表達式雖然方便,但也只能用來做簡單的操作。如果在模板中寫太多邏輯,會讓模板變得臃腫,難以維護。比如說,我們有這樣一個包含巢狀陣列的物件:

const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})

我們想根據 author 是否已有一些書籍來展示不同的資訊:

<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>

如果在模板中需要不只一次這樣的計算,我們可不想將這樣的程式碼在模板裡重複好多遍。

因此我們建議使用計算屬性來描述依賴響應式狀態的複雜邏輯。

重構之後的程式碼如下:

<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一個計算屬性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>

綁定 class 和 style

前面我們講到了模板語法中存在着對屬性的綁定,而 class 和 style 嚴格來講,當然也是 HTML 標籤的屬性。但是這兩個屬性同一般的屬性來講有所不同。

class 的動態綁定

通常用於動態綁定某個類別。即在某個條件滿足的情況下綁定該類別,而不滿足的情況下則不綁定。語法如下:

<div :class="{ class_name : isActive }"></div>

其中,class_name 是需要綁定的類別,而 isActive 則是一個 Boolean 類型的響應式 JavaScript 變數。在 isActivetrue 時,渲染如下:

<div class="class_name"></div>

同理,在 isActivefalse 時,渲染如下:

<div></div>

style 的動態綁定

style 屬性的動態綁定則通常用於希望某個 css 屬性的值是動態的 JavaScript 變數

例如,我們希望將如下 div 的 style 中 color 同 JS 變數 color_div 綁定起來,我們可以這樣做:

<div :style="{ 'color' : color_div }"></div>

這樣,當 color_div 的值發生改變,該 div 的 style 中 color 值也會相應改變。

條件渲染

請讀者的回想一下,以下場景是否在之前的開發過程中經常遇到:希望某一個元素在需要的時候顯示,但在不需要的時候消失?

答案是肯定的。例如,我們的結業功課中,有一項要求是:當 click 某個按鈕之後,頁面中的某個元素消失掉,重新 click 那個按鈕,元素又回來。

在之前的開發過程中,讀者想到了五花八門的方法來解決這個問題,有使用 JS 動態添加和刪除元素的(這也是我們推薦的標準方法),有使用 JS 處理 style,讓元素移出屏幕之外的(很不錯的想法,在實際開發中也經常這樣使用),甚至還有寫另一個沒有該元素的頁面和當前頁面互相切換的(這個方法有點差,但的確是劍走偏鋒)。在 Vue 框架中,我們使用一種更爲簡便的方法來實現這一目標,那就是條件渲染。

條件渲染的標誌是 v-ifv-else。例如,我們需要在 isActive 變數爲 true 時顯示 div,而在其爲 false 時不顯示它,我們只需要這樣寫:

<div v-if="isActive"></div>

如果我們想要讓這個 div 不顯示的同時顯示另一個元素,我們當然可以這樣寫:

<div v-if="isActive"></div>
<div v-if="!isActive"></div>

這樣,當第一個 div 不顯示的時候,第二個 div 將會顯示;當第一個 div 顯示的時候,第二個 div 將不會顯示。

如果這兩個 div 緊緊靠在一起,那我們就可以這樣簡化之:

<div v-if="isActive"></div>
<div v-else></div>

使用 v-else 的要求是:使用 v-else 的元素必須緊跟在使用 v-if 的標籤之後。因爲 Vue 透過這種方式判斷這個 v-else 屬於哪個 v-if。這很類似 JS 中的 if-else 語句,else 關鍵字也是跟在 if 後面的。

列表渲染

列表渲染適用於什麼樣的場景呢?適用於我們這節課最開始的那個例子:現在有一個 list,需要把這個 list 中的每一項內容都渲染到頁面上。

爲了方便讀者觀察,我再次把之前的程式碼貼到這裏來:

// 假設下面的變數 all 就是我們得到的 all
let list = ["小王", "小李", "小張", "小劉"]
let father_node = document.getElementById("father")
for (let i = 0;i < list.length;i ++) {
let child_node = document.createNode("div")
child_node.innerHTML = list[i]
father_node.appendChild(child_node)
}

以上是使用原生三件套實現該任務所執行的程式碼。

Vue 中,實現這個任務再簡單不過:使用 v-for。例如上面的例子:

<div v-for="item in list">{{ item }}</div>

我們來解釋一下:

v-for 關鍵字表示列表渲染,即將 list 中的每一項渲染到 HTML 中。

item in list 表達式:item 是臨時的一個變數,名字可以隨便起,就像定義一個變數一樣。它僅在此列表渲染中有效,在被渲染元素和該元素的所有子元素中適用,代表列表中的每一個列表項。in 關鍵字很直觀,不多解釋。list 即爲需要渲染的 list 的變數名稱。

{{ item }}是模板語法,表示將 item 這個臨時變數的值渲染到這個位置。

上述列表渲染的結果是:

<div>小王</div>
<div>小李</div>
<div>小張</div>
<div>小劉</div>

事件處理

對應原生 JS 中的事件處理。在原生 JS 中,我們是透過 addEventListener() 函式來監聽某個元素觸發的事件,例如 click、mouseenter、mouseleave 等。在 Vue 中,我們可以直接在元素上添加監聽,並將該監聽綁定到某個函式上:

<div @click="func"></div>
const func = () => {
// func
}

按鍵修飾符

滑鼠的 click 事件可以對應着三種情況:click 左鍵,click 右鍵,click 中間鍵。當然,讀者要是非說 Apple 的滑鼠只有一個按鍵,那我也不能多說什麼(笑)。

那麼如何表示這三種情況呢?這就需要使用到按鍵修飾符的概念。例如:

<div @click.left="clickLeft"></div>
<div @click.right="clickRight"></div>
<div @click.middle="clickMiddle"></div>

還有許多按鍵修飾符,讀者可以自行參考 Vue 官方文檔。

表單輸入綁定

當我們輸入一段文字、點選一個按鈕,或者選擇一個抽屜的時候,我們會將選擇的結果放到 JS 變數中去。我們希望這個過程是自動的。例如,我們在輸入文字的時候,總是希望每輸入一個字元,對應的JS變數都會自動更新爲這個值。在原生三件套中,我們只能夠這樣寫:

let val = ""
let input_box = document.getElementById("input_box")
input_box.addEventListener("input", (e) => {
val = e.target.innerHTML
})
<input id="input_box">

如果我們需要雙向綁定呢?即當 val 的值發生改變的時候,input 輸入框中的內容也可以放生改變。坦白講,筆者也不會寫。但是筆者找到了一個看起來還算 OK 的解決方案,讀者可以感受一下:

雙向綁定的一個解決方案

這顯然是極爲恐怖的。而在 Vue 下,框架用一個屬性—— v-model 即幫你實現了表單元素與JS變數的雙向綁定:

<input v-model="val">

這樣,當 input 有輸入的時候, val 將會同步更新爲輸入的內容;當 val 發生改變的時候,input 輸入框中的內容也會同步更新。

關於其他表單元素的雙向綁定,請讀者自行查閱 Vue 官方文檔。

偵聽器

我們有的時候希望在某個變數的值發生變化的時候,自動執行某個函式。那麼我們即可使用 Vue 提供的偵聽器—— watch()。下面的程式碼表示偵聽變數 variable,當其值發生改變的時候,自動將新值列印出來。

watch(variable, (newValue) => {
console.log(newValue)
})

watch() 函式包括兩個引數:第一個是要偵聽的對象,第二個是一個回呼函式,代表偵聽對象發生改變的時候需要執行的動作。

生命週期

這個概念十分重要!!!

我們先來了解一下頁面從生到死的全過程:

  • 創建(create):指某個頁面剛剛被創建出來,元素還沒有開始渲染, JS 剛剛開始執行。
  • 掛載(mount):指頁面中元素渲染的過程。
  • 更新(update):頁面中元素渲染完成後某些元素髮生更新的過程。
  • 卸載(unmount):指頁面中元素解除渲染的過程。
  • 消亡(destroy):指頁面完全消亡。

這一整個過程,包含了這個頁面從生到死的全過程,稱之爲這個頁面的生命週期

我們可以將我們的函式添加到這個生命週期的任意位置,並且在該時刻執行。下圖表示了 Vue 中頁面的生命週期:

Vue 生命週期

結語

Vue 的基礎部分到這裏基本就講得蠻全面了。在這裏給讀者提幾點學習 Vue 的建議:

  1. 熟讀 Vue 官方文檔,由於官方文檔有順暢的中文版本,所以這是我們閱讀很大的優勢。Vue 官方文檔永遠都是最新、最權威、最完整的 Vue 學習資料。當我們遇到問題的時候,去翻一翻官方文檔,說不定就找到了解決辦法。

  2. 和原生三件套的寫法做對比。Vue 作爲前端框架,自然簡化了大量原生的開發工作。因此在學習一個 Vue 特性的時候,我們腦中可以舉一個例子,然後分別使用原生三件套和 Vue 來實現,這樣我們就可以很清楚地知道 Vue 到底可以應用到什麼地方,又簡化了什麼。

  3. 多造訪一些著名的論壇。遇到問題,一找官方文檔,二找 Google,三找論壇,四找 GPT,這也應該成爲讀者經過訓練之後的一個條件反射。

[Vue3] 第一課 認識前端框架和 Vue3
https://blog.kynix.tw/posts/1731034797125/
作者
Adrian Chen
建檔時間
2024 年 1 月 21 日
協議
BY-NC-SA 4.0
姓名標示-非商業性-相同方式分享 4.0 國際