[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 指令基于一个数组来渲染一个列表。可以用于遍历数组和对象** ```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.itqaq.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() 侦听器**