[TOC] #### 1. Vue 框架介紹 --- **一、什么是 Vue ?** Vue 官網: [https://cn.vuejs.org](https://cn.vuejs.org) Vue 是一款用于構建用戶界面的 JavaScript 框架,它基于 HTML,CSS 和 JavaScript 構建,并提供了一套聲明式的、組件化的編程模型,幫助你高效的開發用戶界面 **二、Vue 是漸進式框架 ?** 以前我們使用 html,css,js 開發項目,當項目比較大,比較復雜的話,使用 js 來寫的話,是沒有問題的,但是會比較困難,任務量比較大。所以呢,出現了 Vue 這個框架,來幫助我們開發項目更加簡單,更加的方便。 假設以前我們使用的 js 開發的項目,現在想要使用 vue 進行重構,如果一下子將項目改為 vue,工作量是非常大的。項目中有很多頁面,我們可以先在某些頁面中引入 vue,一點一點的使用 vue 重構,這就是 **漸進式** 的概念 #### 2. Vue3 安裝方式 --- **一、通過 CDN 使用 Vue** 官方文檔: [https://cn.vuejs.org/guide/quick-start.html#using-vue-from-cdn](https://cn.vuejs.org/guide/quick-start.html#using-vue-from-cdn) 通過 CDN 使用 Vue 時,不涉及“構建步驟”。這使得設置更加簡單,并且可以用于增強靜態的 HTML 或與后端框架集成。但是,你將無法使用單文件組件 (SFC) 語法 ```html <!-- 借助 script 標簽直接通過 CDN 來使用 Vue --> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> ``` 使用示例: 使用 `Vue.createApp()` 創建應用,并且通過 `mount()` 掛載到 `#app` 上 ```html <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <div id="app">{{ message }}</div> <script> Vue.createApp({ data() { return { message: 'Hello Vue !' } } }).mount('#app') </script> ``` **二、使用 Vite 構建 Vue 項目** 使用前提: 已安裝 16.0 或更高版本的 [Node.js](https://nodejs.org) 官方文檔 : [https://cn.vuejs.org/guide/quick-start.html#creating-a-vue-application](https://cn.vuejs.org/guide/quick-start.html#creating-a-vue-application) [Vite](https://vitejs.dev) 是一個 web 開發構建工具,類似于 webpack,比 webpack 更快,以閃電般的速度啟動和編譯 在命令行中執行以下命令: ``` # project-name 項目名稱, 可省略 npm init vue@latest <project-name> ``` 將會按照并執行 [create-vue](https://github.com/vuejs/create-vue) ,它是 Vue 官方的項目腳手架工具,執行中有交互操作,提示安裝一些依賴,一路回車即可  #### 3. Vue3 模板語法 --- Vue 使用一種基于 HTML 的模板語法,使我們能夠聲明式地將其組件實例的數據綁定到呈現的 DOM 上。Vue 會將模板編譯成高度優化的 JavaScript 代碼。結合響應式系統,當應用狀態變更時,Vue 能夠智能地推導出需要重新渲染的組件的最少數量,并應用最少的 DOM 操作 **文本插值** 文本插值是最基本的數據綁定形式,使用的是 Mustache 語法(即雙大括號),是開發中使用最頻繁的模板語法之一 ```html <span>Message: {{ msg }}</span> ``` **原始 HTML** 文本插值會將數據渲染為純文本,所以數據中即使有 html 標簽也不會被解析。若想插入 html,需要使用 `v-html` 指令: 這里遇到了一個新的概念: 指令,`v-html` 屬性被稱為一個指令。在 vue 中,以 `v-` 作為前綴的屬性,稱為 vue 的指令,表明它們是一些由 vue 提供的特殊屬性 ```html <div v-html="msg"></div> ``` **屬性綁定** 雙大括號不能在 HTML 屬性中使用,想要響應式的綁定一個屬性,應該使用 `v-bind` 指令: `v-bind` 指令將元素的 id 屬性和組件的 uid 屬性保持一致。綁定的值是 null 或 undefined,該屬性將會從渲染的元素上移除 ```html <div v-bind:id="uid"></div> ``` 因為 `v-bind` 在開發中使用非常頻繁,所以 Vue 官方提供了簡寫語法: ```html <div :id="uid"></div> ``` **布爾型屬性** 布爾型屬性根據 `true/false` 值來決定屬性是否應該存在于該元素上 當 isDisabled 的值為真值或空字符串時,元素會包含 disabled 屬性 需要特別注意的是值為空字符串時 disabled 屬性也存在,其他假值則 disabled 屬性不存在 ```html <button :disabled="isDisabled">Button</button> ``` **動態綁定多個值** 如果有一個這樣包含多個屬性的 JS 對象 ```javascript const objectOfAttrs = { id: 'container', class: 'wrapper' } ``` 通過不帶參數的 `v-bind`,可以將它們綁定到單個元素上 ```html <div v-bind="objectOfAttrs"></div> ``` 頁面渲染后 Vue 將多個屬性添加到了元素上: ```html <div id="container" class="wrapper"></div> ``` **使用 JavaScript 表達式** Vue 數據綁定中都支持完整的 JavaScript 表達式 在 Vue 模板中,表達式可以被使用在 `文本插值(雙大括號)` 和 `任何 Vue 指令屬性(以v-開頭的特殊屬性)` 的值中 ``` {{ number + 10 }} {{ ok ? 'yes' : 'no' }} {{ array.join(',') }} <div :id="`list-${uid}`"></div> ``` **調用函數** 可以在表達式中使用組件暴露的方法 ```html <span :title="toTitleDate(date)"> {{ formatDate(date) }} </span> ``` **指令 Directives** Vue 指令是帶有 `v-` 前綴的特殊屬性。 Vue 提供了很多[內置指令](https://cn.vuejs.org/api/built-in-directives.html) ,包含上面提到的 `v-bind` 和 `v-html` #### 4. 組件的 data 屬性 --- 組件的 data 選項必須是一個函數,它的返回值必須是一個對象 Vue 在創建新組件實例的過程中調用此函數,通過響應式系統將其包裹起來 #### 5. 計算屬性和方法 --- **計算屬性 computed** 模板中的表達式雖然方便,但也只能用來做簡單的操作。如果在模板中寫太多邏輯,會讓模板變得臃腫,難以維護。此時可以使用**計算屬性**描述依賴響應式狀態的復雜邏輯 ```javascript export default { data() { return { users: [{ id: 1, name: 'html' },{ id: 2, name: 'css' }] } }, computed: { getUsersName() { return this.users.reduce((total, item) => total += item.name, '') } } } ``` **方法 methods** 通過組件的 `methods` 選項向組件實例添加方法,它是一個包含所需方法的對象,在對象中定義方法 需要注意的是 methods 中的方法不要定義為剪頭函數,因為箭頭函數中沒有 this。如果是普通函數,Vue 自動為 methods 綁定 this,并且 this 始終指向 vue 實例 ```javascript export default { data() { return { count: 1, } }, methods: { add() { this.count++ }, sub(num) { this.count -= num } } } ``` #### 6. 偵聽器的使用 --- 在有些情況下,我們需要在狀態變化后執行一些操作,例如: 更改 DOM,或根據異步操作的結果去修改另一處的狀態 在選項式 API 中,我們可以使用 `watch()` 選項監聽響應式數據,發生變化時觸發一個函數 ```javascript export default { data() { return { msg: "你好嗎?", count: 0, user: { name: "liang", age: 18, gender: 1, }, }; }, created() { setTimeout(() => { this.count = 10; }, 1000); }, // 監聽數據的變化 watch: { // 當msg發生變化時,觸發這個函數 // newVal,oldVal 修改前和修改后的值 msg(newVal, oldVal) { console.log("msg:", { newVal, oldVal }); // 可以執行異步操作,或復雜代碼 // 實際開發中經常在 watch 中調用 methods 方法 this.showMsg(); }, // 即時回調的偵聽器 count: { // 初始化的時候調用 immediate: true, // oldVal 是可選參數,可省略不寫 handler(newVal) { console.log("count:", { newVal }); }, }, // 深層偵聽器(監聽對象中的所有屬性) // user: { // // 深度偵聽 // // 深度偵聽會一層層的向下遍歷,給每個對象屬性都加上偵聽器 // deep: true, // handler(newVal) { // console.log("user: ", { newVal }); // }, // }, // 深層偵聽器(監聽對象中的某個屬性) // 使用字符串的形式進行優化,只會單獨監聽對象中對應的屬性 "user.name": { deep: true, handler(newVal) { console.log("user.name: ", { newVal }); }, }, }, methods: { showMsg() { return this.msg; // how are you ? }, changeCount() { setTimeout(() => { this.count += 10; }, 1000); }, }, }; ``` #### 7. class 類名綁定對象 --- class 值 active 是否存在,取決于組件的 data 選項中 isActive 的真假值 ```html <div :class="{ active: isActive }">liang</div> ``` 可以在對象中寫多個字段來操作多個 class。補充: 當 class 的名稱不是 js 合法屬性名時,需要使用引號包裹 ```html <div :class="{ active: isActive, 'text-danger': hasError }">liang</div> ``` 綁定的對象并不一定需要寫成內聯字面量的形式,也可以直接綁定一個對象: ```javascript data() { return { custom: { nav: true, 'text-success': true } } } ``` ```html <!-- 模板語法 --> <div :class="custom">liang</div> <!-- 頁面渲染 --> <div class="nav text-success">liang</div> ``` 也可以綁定一個返回對象的計算屬性,這是項目開發中很常見且很有用的技巧 ```javascript data() { return { isActive: true, error: null } }, computed: { classObject() { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } } ``` ```html <div :class="classObject"></div> ``` 如果本身有 class 屬性,又動態綁定了 class 屬性也是可以的,那么 vue 會將動態綁定的和本身的合并 ```html <div class="nav" :class="classObject">liang</div> ``` #### 8. class 類名綁定數組 --- 我們可以給 `:class` 綁定一個數組來渲染多個 css 類名【實際開發中綁定數組用的不多】 ```javascript data() { return { activeClass: 'active', errorClass: 'text-danger' } } ``` ```html <!-- 模板語法 --> <div :class="[activeClass, errorClass]">liang</div> <!-- 頁面渲染 --> <div class="active text-danger">liang</div> ``` 數組和對象結合: ```javascript data() { return { isActive: true, textClass: 'text-info' } } ``` ```html <!-- 模板語法 --> <div :class="[textClass, { active: isActive }]">liang</div> <!-- 頁面渲染 --> <div class="text-info active">liang</div> ``` #### 9. style 樣式綁定對象 --- `:style` 支持綁定一個對象值,對應的是 html 的 style 屬性值 ```javascript data() { return { activeColor: 'red', fontSize: 30 } } ``` ```html <!-- 模板語法 --> <div :style="{ color: activeColor, fontSize: fontSize + 'px' }">liang</div> <!-- 頁面渲染 --> <div style="color: red; font-size: 30px;">liang</div> ``` 眾所周知,css 有很多 kebab-cased 命名法的屬性,比如 `font-size`,這種屬性名在綁定樣式時**要么使用引號包裹**,**要么使用 camelCase 命名代替 kebab-cased 命名** 下面兩種寫法都是可以的,Vue 官方推薦使用 camelCase 命名形式 ```html <!-- camelCase 命名法【vue官方推薦】 --> <div :style="{ fontSize: fontSize + 'px' }">liang</div> <!-- kebab-cased 命名法 --> <div :style="{ 'font-size': fontSize + 'px' }">liang</div> ``` 直接綁定一個樣式對象通常是一個好方式,這樣可以使模板更加簡潔: ```javascript data() { return { styleObject: { color: 'red', fontSize: '13px' } } } ``` ```html <div :style="styleObject">liang</div> ``` 同 class 綁定,若樣式對象需要復雜的邏輯,可以使用返回對象的計算屬性【下面樣式對象邏輯并不復雜,只為演示用法】 ```javascript data() { return { color: 'blue', isActive: true } }, computed: { getStyles() { return this.isActive ? { color: this.color } : {} } } ``` ```html <div :style="getStyles">liang</div> ``` #### 10. style 樣式綁定數組 --- 還可以給 `:style` 綁定一個數組,數組元素是包含多個樣式的對象,這些對象會被合并,然后再進行渲染 ```javascript data() { return { stylesObject1: { color: 'red' }, stylesObject2: { fontSize: '25px' }, } } ``` ```html <!-- 模板語法 --> <div :style="[stylesObject1, stylesObject2]">liang</div> <!-- 頁面渲染 --> <div style="color: red; font-size: 25px;">liang</div> ``` `style` 和 `:style` 都存在時,樣式會進行合并,如果相同的樣式,誰在后面誰生效,也就是屬性靠右的生效 ```html <div style="color: blue" :style="stylesObject2">liang</div> <div style="color: blue" :style="[stylesObject1, stylesObject2]">liang</div> ``` #### 11. 條件渲染 v-if 和 v-show --- `v-if` 用于條件性的渲染一塊內容,當表達式為真值時才被渲染 ```html <div v-if="score >= 60">及格</div> <div v-if="score >= 60">及格</div> <div v-else>不及格</div> <div v-if="score > 90">優秀</div> <div v-else-if="score >= 60">及格</div> <div v-else>不及格</div> ``` 在包裝器元素 `<template>`上使用 `v-if` 條件渲染分組 因為 v-if 是一個指令,他必須依附于某個元素。 但如果想要切換的不止一個元素呢?這種情況下可以使用不可見的包裝器元素 `<template>` 將多個元素包裹起來 ```html <template v-if="true"> <view></view> <view></view> </template> <template v-else> <view></view> <view></view> </template> ``` v-show 也可以按照條件來決定是否顯示一個元素,用法和 v-if 基本一樣 ``` <h1 v-show="ok">Hello!</h1> ``` 經典問題: v-if 和 v-show 有什么區別 ? 當條件為假值時,`v-if` 并不會在 dom 渲染保留元素,而 `v-show` 會渲染元素,只是設置 css 屬性了 `display: none;` ```html <!-- 模板語法 --> <view v-if="false">liang</view> <view v-show="false">itqaq</view> <!-- 頁面渲染 --> <!--v-if--> <view style="display: none;">itqaq</view> ``` #### 12. 列表渲染 v-for --- **v-for 指令基于一個數組來渲染一個列表??梢杂糜诒闅v數組和對象** ```javascript data() { return { object: { name: 'liang', age: 18 }, array: [{ message: 'Foo' }, { message: 'Bar' }], } } ``` ```html <!-- array 源數據數組 item 迭代項的別名,即數組元素 index 數據下標 --> <li v-for="item in array"> {{ item.message }} </li> <!-- object 源數據對象 value 屬性值 key 屬性 index 下標 --> <li v-for="(value, key, index) in object"> {{ key }} : {{ value }} </li> ``` **在 v-for 中使用范圍值** v-for 可以接收一個整數值。在這種用例中,該模板基于 `1...n` 的取值范圍重復多次 ```html <!-- n 從 1 開始,而不是 0 --> <span v-for="n in 10">{{ n }}</span> ``` `<template>` 上的 `v-for` 可以使用包裝器元素 `<template>` 包裹多個元素的塊,用 v-for 進行遍歷,這樣可以使代碼更加直觀 ```html <template v-for="n in 10"> <div></div> <div></div> <div></div> </template> ``` #### 13. Vue3 的組合式 API --- Vue3 組件可以按照兩種不同的 API 風格書寫: **選項式 API** 和 **組合式 API** **什么是選項式 API ?** **選項式 API** 又稱為 **聲明式渲染** 選項式 API 就是 Vue2 中的編寫風格,使用 data,methods,computed,watch 等選項的 API 風格。 **什么是組合式 API ?** 組合式 API 優點: 將同一個邏輯關注點相關代碼收集在一起 組合式 API 是一系列 API 的集合,使我們可以使用函數而不是聲明選項式的方式書寫 Vue 組件 setup() 鉤子 : [https://cn.vuejs.org/api/composition-api-setup.html](https://cn.vuejs.org/api/composition-api-setup.html) ```html <div id="app"> <div>{{ msg }}</div> <div>{{ user }}</div> <button @click="updateMsg">修改 Msg</button> <button @click="updateUser">修改 User</button> </div> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <script> const { ref, reactive, toRefs } = vue // 使用 ref,reactive 包裹數據是為了讓變量具有響應式 // 基本數據類型使用 ref 方法包裹 // 引用數據類型使用 reactive 方法包裹 // toRefs 讓解構后的數據具有響應式(使用...解構出來的屬性不是響應式的) Vue.createApp({ // 組件被創建之前執行,不需要使用 this setup() { /* msg 邏輯代碼 */ let msg = ref('hello vue') function updateMsg() { msg.value = 'www.g-eproperties.com' } /* user 邏輯代碼 */ const user = reactive({ name: 'liang', age: 18 }) function updateUser() { user.name = 'wang' } return { msg, updateMsg, user, updateUser, ...toRefs(user) } } }).mount('#app') </script> ``` **在 setup 中使用 watch() 偵聽器**