[TOC] #### 1. Vue 框架介紹 --- **一、什么是 Vue ?** Vue 官網(wǎng): [https://cn.vuejs.org](https://cn.vuejs.org) Vue 是一款用于構(gòu)建用戶界面的 JavaScript 框架,它基于 HTML,CSS 和 JavaScript 構(gòu)建,并提供了一套聲明式的、組件化的編程模型,幫助你高效的開發(fā)用戶界面 **二、Vue 是漸進式框架 ?** 以前我們使用 html,css,js 開發(fā)項目,當項目比較大,比較復雜的話,使用 js 來寫的話,是沒有問題的,但是會比較困難,任務(wù)量比較大。所以呢,出現(xiàn)了 Vue 這個框架,來幫助我們開發(fā)項目更加簡單,更加的方便。 假設(shè)以前我們使用的 js 開發(fā)的項目,現(xiàn)在想要使用 vue 進行重構(gòu),如果一下子將項目改為 vue,工作量是非常大的。項目中有很多頁面,我們可以先在某些頁面中引入 vue,一點一點的使用 vue 重構(gòu),這就是 **漸進式** 的概念 #### 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 時,不涉及“構(gòu)建步驟”。這使得設(shè)置更加簡單,并且可以用于增強靜態(tài)的 HTML 或與后端框架集成。但是,你將無法使用單文件組件 (SFC) 語法 ```html <!-- 借助 script 標簽直接通過 CDN 來使用 Vue --> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> ``` 使用示例: 使用 `Vue.createApp()` 創(chuàng)建應(yīng)用,并且通過 `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 構(gòu)建 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 開發(fā)構(gòu)建工具,類似于 webpack,比 webpack 更快,以閃電般的速度啟動和編譯 在命令行中執(zhí)行以下命令: ``` # project-name 項目名稱, 可省略 npm init vue@latest <project-name> ``` 將會按照并執(zhí)行 [create-vue](https://github.com/vuejs/create-vue) ,它是 Vue 官方的項目腳手架工具,執(zhí)行中有交互操作,提示安裝一些依賴,一路回車即可 ![](https://img.itqaq.com/art/content/e63c7c625976473b40289a99ee250612.png) #### 3. Vue3 模板語法 --- Vue 使用一種基于 HTML 的模板語法,使我們能夠聲明式地將其組件實例的數(shù)據(jù)綁定到呈現(xiàn)的 DOM 上。Vue 會將模板編譯成高度優(yōu)化的 JavaScript 代碼。結(jié)合響應(yīng)式系統(tǒng),當應(yīng)用狀態(tài)變更時,Vue 能夠智能地推導出需要重新渲染的組件的最少數(shù)量,并應(yīng)用最少的 DOM 操作 **文本插值** 文本插值是最基本的數(shù)據(jù)綁定形式,使用的是 Mustache 語法(即雙大括號),是開發(fā)中使用最頻繁的模板語法之一 ```html <span>Message: {{ msg }}</span> ``` **原始 HTML** 文本插值會將數(shù)據(jù)渲染為純文本,所以數(shù)據(jù)中即使有 html 標簽也不會被解析。若想插入 html,需要使用 `v-html` 指令: 這里遇到了一個新的概念: 指令,`v-html` 屬性被稱為一個指令。在 vue 中,以 `v-` 作為前綴的屬性,稱為 vue 的指令,表明它們是一些由 vue 提供的特殊屬性 ```html <div v-html="msg"></div> ``` **屬性綁定** 雙大括號不能在 HTML 屬性中使用,想要響應(yīng)式的綁定一個屬性,應(yīng)該使用 `v-bind` 指令: `v-bind` 指令將元素的 id 屬性和組件的 uid 屬性保持一致。綁定的值是 null 或 undefined,該屬性將會從渲染的元素上移除 ```html <div v-bind:id="uid"></div> ``` 因為 `v-bind` 在開發(fā)中使用非常頻繁,所以 Vue 官方提供了簡寫語法: ```html <div :id="uid"></div> ``` **布爾型屬性** 布爾型屬性根據(jù) `true/false` 值來決定屬性是否應(yīng)該存在于該元素上 當 isDisabled 的值為真值或空字符串時,元素會包含 disabled 屬性 需要特別注意的是值為空字符串時 disabled 屬性也存在,其他假值則 disabled 屬性不存在 ```html <button :disabled="isDisabled">Button</button> ``` **動態(tài)綁定多個值** 如果有一個這樣包含多個屬性的 JS 對象 ```javascript const objectOfAttrs = { id: 'container', class: 'wrapper' } ``` 通過不帶參數(shù)的 `v-bind`,可以將它們綁定到單個元素上 ```html <div v-bind="objectOfAttrs"></div> ``` 頁面渲染后 Vue 將多個屬性添加到了元素上: ```html <div id="container" class="wrapper"></div> ``` **使用 JavaScript 表達式** Vue 數(shù)據(jù)綁定中都支持完整的 JavaScript 表達式 在 Vue 模板中,表達式可以被使用在 `文本插值(雙大括號)` 和 `任何 Vue 指令屬性(以v-開頭的特殊屬性)` 的值中 ``` {{ number + 10 }} {{ ok ? 'yes' : 'no' }} {{ array.join(',') }} <div :id="`list-${uid}`"></div> ``` **調(diào)用函數(shù)** 可以在表達式中使用組件暴露的方法 ```html <span :title="toTitleDate(date)"> {{ formatDate(date) }} </span> ``` **指令 Directives** Vue 指令是帶有 `v-` 前綴的特殊屬性。 Vue 提供了很多[內(nèi)置指令](https://cn.vuejs.org/api/built-in-directives.html) ,包含上面提到的 `v-bind` 和 `v-html` #### 4. 組件的 data 屬性 --- 組件的 data 選項必須是一個函數(shù),它的返回值必須是一個對象 Vue 在創(chuàng)建新組件實例的過程中調(diào)用此函數(shù),通過響應(yīng)式系統(tǒng)將其包裹起來 #### 5. 計算屬性和方法 --- **計算屬性 computed** 模板中的表達式雖然方便,但也只能用來做簡單的操作。如果在模板中寫太多邏輯,會讓模板變得臃腫,難以維護。此時可以使用**計算屬性**描述依賴響應(yīng)式狀態(tài)的復雜邏輯 ```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 中的方法不要定義為剪頭函數(shù),因為箭頭函數(shù)中沒有 this。如果是普通函數(shù),Vue 自動為 methods 綁定 this,并且 this 始終指向 vue 實例 ```javascript export default { data() { return { count: 1, } }, methods: { add() { this.count++ }, sub(num) { this.count -= num } } } ``` #### 6. 偵聽器的使用 --- 在有些情況下,我們需要在狀態(tài)變化后執(zhí)行一些操作,例如: 更改 DOM,或根據(jù)異步操作的結(jié)果去修改另一處的狀態(tài) 在選項式 API 中,我們可以使用 `watch()` 選項監(jiān)聽響應(yīng)式數(shù)據(jù),發(fā)生變化時觸發(fā)一個函數(shù) ```javascript export default { data() { return { msg: "你好嗎?", count: 0, user: { name: "liang", age: 18, gender: 1, }, }; }, created() { setTimeout(() => { this.count = 10; }, 1000); }, // 監(jiān)聽數(shù)據(jù)的變化 watch: { // 當msg發(fā)生變化時,觸發(fā)這個函數(shù) // newVal,oldVal 修改前和修改后的值 msg(newVal, oldVal) { console.log("msg:", { newVal, oldVal }); // 可以執(zhí)行異步操作,或復雜代碼 // 實際開發(fā)中經(jīng)常在 watch 中調(diào)用 methods 方法 this.showMsg(); }, // 即時回調(diào)的偵聽器 count: { // 初始化的時候調(diào)用 immediate: true, // oldVal 是可選參數(shù),可省略不寫 handler(newVal) { console.log("count:", { newVal }); }, }, // 深層偵聽器(監(jiān)聽對象中的所有屬性) // user: { // // 深度偵聽 // // 深度偵聽會一層層的向下遍歷,給每個對象屬性都加上偵聽器 // deep: true, // handler(newVal) { // console.log("user: ", { newVal }); // }, // }, // 深層偵聽器(監(jiān)聽對象中的某個屬性) // 使用字符串的形式進行優(yōu)化,只會單獨監(jiān)聽對象中對應(yīng)的屬性 "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> ``` 綁定的對象并不一定需要寫成內(nèi)聯(lián)字面量的形式,也可以直接綁定一個對象: ```javascript data() { return { custom: { nav: true, 'text-success': true } } } ``` ```html <!-- 模板語法 --> <div :class="custom">liang</div> <!-- 頁面渲染 --> <div class="nav text-success">liang</div> ``` 也可以綁定一個返回對象的計算屬性,這是項目開發(fā)中很常見且很有用的技巧 ```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 屬性,又動態(tài)綁定了 class 屬性也是可以的,那么 vue 會將動態(tài)綁定的和本身的合并 ```html <div class="nav" :class="classObject">liang</div> ``` #### 8. class 類名綁定數(shù)組 --- 我們可以給 `:class` 綁定一個數(shù)組來渲染多個 css 類名【實際開發(fā)中綁定數(shù)組用的不多】 ```javascript data() { return { activeClass: 'active', errorClass: 'text-danger' } } ``` ```html <!-- 模板語法 --> <div :class="[activeClass, errorClass]">liang</div> <!-- 頁面渲染 --> <div class="active text-danger">liang</div> ``` 數(shù)組和對象結(jié)合: ```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` 支持綁定一個對象值,對應(yīng)的是 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 樣式綁定數(shù)組 --- 還可以給 `:style` 綁定一個數(shù)組,數(shù)組元素是包含多個樣式的對象,這些對象會被合并,然后再進行渲染 ```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` 用于條件性的渲染一塊內(nèi)容,當表達式為真值時才被渲染 ```html <div v-if="score >= 60">及格</div> <div v-if="score >= 60">及格</div> <div v-else>不及格</div> <div v-if="score > 90">優(yōu)秀</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> ``` 經(jīng)典問題: v-if 和 v-show 有什么區(qū)別 ? 當條件為假值時,`v-if` 并不會在 dom 渲染保留元素,而 `v-show` 會渲染元素,只是設(shè)置 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 指令基于一個數(shù)組來渲染一個列表。可以用于遍歷數(shù)組和對象** ```javascript data() { return { object: { name: 'liang', age: 18 }, array: [{ message: 'Foo' }, { message: 'Bar' }], } } ``` ```html <!-- array 源數(shù)據(jù)數(shù)組 item 迭代項的別名,即數(shù)組元素 index 數(shù)據(jù)下標 --> <li v-for="item in array"> {{ item.message }} </li> <!-- object 源數(shù)據(jù)對象 value 屬性值 key 屬性 index 下標 --> <li v-for="(value, key, index) in object"> {{ key }} : {{ value }} </li> ``` **在 v-for 中使用范圍值** v-for 可以接收一個整數(shù)值。在這種用例中,該模板基于 `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 優(yōu)點: 將同一個邏輯關(guān)注點相關(guān)代碼收集在一起 組合式 API 是一系列 API 的集合,使我們可以使用函數(shù)而不是聲明選項式的方式書寫 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 包裹數(shù)據(jù)是為了讓變量具有響應(yīng)式 // 基本數(shù)據(jù)類型使用 ref 方法包裹 // 引用數(shù)據(jù)類型使用 reactive 方法包裹 // toRefs 讓解構(gòu)后的數(shù)據(jù)具有響應(yīng)式(使用...解構(gòu)出來的屬性不是響應(yīng)式的) Vue.createApp({ // 組件被創(chuàng)建之前執(zhí)行,不需要使用 this setup() { /* msg 邏輯代碼 */ let msg = ref('hello vue') function updateMsg() { msg.value = 'm.waterflosserreview.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() 偵聽器**