Vue笔记

时间:2024-03-20 16:57:04

Vue3 笔记

1. 环境搭建

  • 安装 18.0 或更高版本的 Node.js

  • 创建项目

    # 在指定目录下执行以下命令, 用来创建Vue项目
    npm create vue@latest
    
  • 运行项目

    # 进入Vue项目的根目录
    cd <your-project-name>
    # 安装依赖
    npm install
    # 运行项目
    npm run dev
    
  • 打包Vue项目,此命令会在 ./dist 文件夹中为你的应用创建一个生产环境的构建版本

    npm run build
    

2. Vue3目录结构

project/
|—— public/              # 于存放静态资源文件的目录,这些文件不会经过打包处理
|   |—— index.html
|—— src/
|   |—— assets/          # 放置静态资源文件,如图片、样式等
|   |—— components/      # 放置可复用的 Vue 组件
|   |—— views/           # 放置页面级组件
|   |—— router/          # 放置路由配置文件
|   |—— store/           # 放置 Vuex 状态管理文件
|   |—— services/        # 放置与后端交互的服务文件
		|—— ApiService.js # 用于定义后端 API 请求的方法,包括 GET、POST..
		|—— AuthService.js # 用于处理用户认证和授权相关的操作,比如登录、注册
|   |—— utils/           # 放置工具函数
|   |—— App.vue          # 根组件
|   |—— main.js          # 项目入口文件
|—— package.json         # 项目配置文件
|—— babel.config.js      # Babel 配置文件
|—— vue.config.js        # Vue CLI 配置文件
|—— index.html			 # 整个应用的入口文件

3. idnex.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div>
	<!-- 引入js入口文件 -->
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

4. main.js

// 从静态资源文件夹中导入了一个名为 main.css 的样式
import './assets/main.css'

// 从 Vue 库中导入了 createApp 函数,用于创建一个 Vue 应用实例
import { createApp } from 'vue'

// 从 Pinia 库中导入了 createPinia 函数,用于创建一个 Pinia 应用状态管理实例
import { createPinia } from 'pinia'

// 导入了根组件 App.vue,这是整个应用程序的根组件
import App from './App.vue'

// 导入了路由配置文件,用于设置 Vue Router (页面跳转)
// 导入router 文件夹中 export default router 暴露出来的实例对象
import router from './router'

// 使用 createApp 函数创建了一个 Vue 应用实例对象
const app = createApp(App)

// 调用 app.use 方法来安装并使用 Pinia 应用状态管理实例
app.use(createPinia())

// 调用 app.use 方法来安装并使用 Vue Router
app.use(router)

// 将 Vue 应用实例挂载到index.html页面 具有 id="app" 的 HTML 元素上
app.mount('#app')

5. App.vue

<!-- 根组件JS脚本 -->
<script setup>
	// 引入 vue-router 中的路由组件,用于页面跳转和显示
	import {
		// 它可以被用作普通的 <a> 标签来进行页面之间的导航
		RouterLink,
		// 用于渲染当前路由匹配到的组件的组件
		// 当用户访问不同的 URL 时,RouterView 会根据当前路由匹配到的组件来动态地渲染对应的内容
		RouterView
	} from 'vue-router'
	
	// 导入可复用组件 HelloWorld
	import HelloWorld from './components/HelloWorld.vue'
</script>

<!-- 根组件模板内容 -->
<template>
	<!-- 使用自定义的组件 -->
	<HelloWorld msg="dd did it!" />
	
	<!-- 使用路由跳转组件,相当于a标签 -->
	<!-- to属性的值为router文件夹中index.js注册的对应的组件路由-->
	<RouterLink to="/">Home</RouterLink>
	<!-- 使用路由跳转组件 -->
	<RouterLink to="about">About</RouterLink>
	
	<!-- 用于显示 RouterLink 跳转的页面内容 -->
	<RouterView />
</template>

<!-- 根组件样式 -->
<style scoped>
	
</style>

6. router/index.js

// 从 vue-router 包中导入了 createRouter 和 createWebHistory 函数
import { createRouter, createWebHistory } from 'vue-router'
// 导入自定义视图组件,用于在页面中显示一个区域的内容
import HomeView from '../views/HomeView.vue'

// 使用 createRouter 函数创建了一个新的路由实例对象
const router = createRouter({
	// 指定了路由的历史模式为基于浏览器 history 的模式
	// 并且传入了 import.meta.env.BASE_URL 作为基础 URL
	// 这将影响路由的根路径和路由的匹配规则
  history: createWebHistory(import.meta.env.BASE_URL),
	// 定义了应用程序中的路由规则
  routes: [
    {
			// RouterLink 的 to 属性对应的url,会匹配到 component 对应的组件
      path: '/',
			// 路由名称,RouterLink 的 to 属性也可以使用name属性作为值跳转到对应的组件
			// 但是必须这样写 :to="{ name: 'home' }"
      name: 'home',
			// 绑定路径对应的组件
      component: HomeView
    },
    {
      path: '/about',
      name: 'about',
			// 绑定组件的另一种方式,箭头函数体 里面返回需要导入的组件
      component: () => import('../views/AboutView.vue')
    }
  ]
})

// 导出了这个创建好的路由实例对象,以便在应用程序的其他地方进行使用
export default router

7. stores/counter.js

// 引入了 ref 和 computed 这两个用于定义响应式数据和计算属性的函数
import { ref, computed } from 'vue'
// 引入了 Pinia 库中的 defineStore 方法,用于定义一个状态管理模块
import { defineStore } from 'pinia'

// 定义了一个名为 useCounterStore 的状态管理模块,
// 并使用 defineStore 方法对其进行定义
// 实例化一个状态管理对象,里面的属性和方法,会保存数据状态
// 最后通过 export 直接导出这个状态管理对象
// 其他地方可以通过 import { useCounterStore } from '...' 来引入这个模块
export const useCounterStore = defineStore('counter', () => {
	// 定义一个常量引用,他的值是一个对象,初始化了一个数据  (类似于浏览器session)
	// ref函数可以传递任意类型的数据,比如传递对象初始化数据 
	// const person = ref({ name: 'Alice', age: 30 })
	// 可以通过访问 count.value 属性来获取或修改这些引用的值
  const count = ref(0)
	// 定义一个常量引用,用于计算响应式引用的值
	// 当 count 方法变化时,computed() 函数会自动自行,并将值返回给计算常量引用
  // computed() 方法接收一个函数,这个函数的返回值会赋值给 doubleCount 常量
	const doubleCount = computed(() => count.value * 2)
	// 定义一个方法,方调用这个方法时 count对象里属性的值加1
  function increment() {
    count.value++
  }
	// 将这些属性和方法返回
  return { count, doubleCount, increment }
})


// // 其他页面导入时 import { useCounterStore } from './counter.js'
// export { useCounterStore }
// // 其他页面导入时 import useCounterStore from './counter.js'
// export default useCounterStore 

8. views/AboutView.vue

<!-- 使用 <script setup> 时,你可以直接声明变量、引入组件选项,定义响应式状态等 -->
<script setup>
	import { useCounterStore } from '../stores/counter.js'
	import { ref } from 'vue' 
	// 实例化状态管理对象
	const constStore = useCounterStore()
	// 将状态里面的值再次封装为常量引用
	const counts = ref(constStore.count)
	
	// 增加一个方法,按钮点击事件
	const increment = () => {
		// 调用状态管理对象的累加方法,对状态里面的 count值进行修改
		constStore.increment()
		// 将状态管理对象中的常量引用,赋值给本地的常量引用,以响应式更新页面的数据
		counts.value = constStore.count
	}
</script>

<template>
  <div class="about">
    <h1>This is an about page</h1>
		<!-- 页面中可以直接获得常量引用的值不用加 .value -->
		<h2>{{ counts }}</h2>
		<!-- 添加Vue单击事件 -->
		<button @click="increment">加一</button>
  </div>
</template>

<style>
@media (min-width: 1024px) {
  .about {
    min-height: 100vh;
    display: flex;
    align-items: center;
  }
}
</style>

9. main.js 中实例化一个Vue应用

  • 每个 Vue 应用都是通过 createApp函数创建一个新的 应用实例

  • 传入 createApp 的对象实际上是一个组件

  • createApp API 允许你在同一个页面中创建多个共存的 Vue 应用

    import { createApp } from 'vue'
    // 导入了根组件 App.vue,这是整个应用程序的根组件
    import App from './App.vue'
    
    // 实例化vue应用并传参
    const app = createApp(App)
    
    // 将 Vue 应用实例挂载到index.html页面 具有 id="app" 的 HTML 元素上
    app.mount('#app')
    
  • 根组件的模板通常是组件本身的一部分

    但也可以直接通过在挂载容器内编写模板来单独提供

    <div id="app">
      <button @click="count++">{{ count }}</button>
    </div>
    
    import { createApp } from 'vue'
    
    const app = createApp({
      data() {
        return {
          count: 0
        }
      }
    })
    
    app.mount('#app')
    

10. 模版语法

  • 最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号)

  • 双大括号标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新

    <span>Message: {{ msg }}</span>
    
  • 属性名也可以是动态的 只需要加上中括号接口绑定为动态的变量

    <!-- 绑定事件名称 -->
    <a @[eventName]="doSomething"> ... </a>
    
    <!-- 绑定属性名称 -->
    <a :[attributeName]="url"> ... </a>
    

11. 响应式基础

  • 声明响应式状态 ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回

  • Ref 可以持有任何类型的值,包括深层嵌套的对象、数组等

    import { ref } from 'vue'
    
    const count = ref(0)
    
  • 示例

    <script setup>
      // 使用 ref 创建响应式变量
      import { ref, onMounted } from 'vue';
      // 初始化响应数据
      var count = ref(1);
        
      // 定义方法  相当于在vue2 的method里面编写 
      const increment = () => {
        count.value++;
      };
    
      // 在 onMounted 生命周期钩子中执行 mounted 回调函数
      onMounted(() => {
        // 打印 count 的值
        console.log(count); // => 1
        // 修改 count 的值
        count.value = 3;
        // 调用方法
    	increment();
      });
    </script>
    
    <template>
      Count is: {{ count }}
    </template>
    
  • js文件中编写, 示例

    import { ref } from 'vue'
    
    // 导入对象
    export default {
      setup() {
        const count = ref(0)
    
        function increment() {
          // 在 JavaScript 中需要 .value
          count.value++
        }
    
        // 不要忘记同时暴露 increment 函数
        return {
          count,
          increment
        }
      }
    }
    

另一种声明响应式状态的方式,即使用 reactive() API

  • reactive() 将使对象本身具有响应性

  • 有限的值类型:它只能传入对象类型存储 (对象、数组和如 MapSet 这样的集合类型)

    <template>
        <button @click="state.count++">
          {{ state.count }}
        </button>
    </template>
    
    <script setup>
    import { reactive } from 'vue'
    // 初始化响应式数据,和ref不同的是将使对象本身也具有响应性
    const state = reactive({ count: 0 })
    </script>
    

12. 计算属性

概念

  • 如果在模板中写太多逻辑,会让模板变得臃肿,难以维护

  • 所以引入一个计算属性用于计算响应式变量的变化

  • 禁止改变其他状态、getter 中做异步请求或者更改 DOM

  • 一个计算属性的声明中描述的是如何根据其他值派生一个值

  • 因此 getter 的职责应该仅为计算和返回该值

    <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'
      ]
    })
    
    // 使用computed方法用于计算响应式数据的值返回不同的内容
    // 只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果, 而不用重复执行 getter 函数
    const publishedBooksMessage = computed(() => {
      return author.books.length > 0 ? 'Yes' : 'No'
    })
    </script>
    
    <template>
      <p>Has published books:</p>
      <span>{{ publishedBooksMessage }}</span>
    </template>
    
  • 可以通过同时提供 getter 和 setter 来创建 计算属性,以修改计算属性的值

  • 现在当你再运行 fullName.value = 'John Doe' 时,setter 会被调用而 firstNamelastName 会随之更新

    <script setup>
    import { ref, computed } from 'vue'
    
    const firstName = ref('John')
    const lastName = ref('Doe')
    
    const fullName = computed({
      // getter
      get() {
        return firstName.value + ' ' + lastName.value
      },
      // 避免直接修改计算属性值 (只为了解)
      set(newValue) {
        // 注意:我们这里使用的是解构赋值语法
        [firstName.value, lastName.value] = newValue.split(' ')
      }
    })
    </script>
    

13. Class与Style绑定

概念

  • 可以给 :class (v-bind:class 的缩写) 传递一个对象来动态切换 class 样式

    <!-- :class="{ css样式名称: 是否使用这个css样式(布尔值) }" -->
    <div :class="{ active: isActive }"></div>
    
    <script setup>
    import { ref } from 'vue';
    // 响应式属性
    const isActive = ref(true)
    </script>
    
  • 绑定的对象并不一定需要写成内联字面量的形式,也可以直接绑定一个对象

    <div :class="classObject"></div>
    
    <script setup>
    import { reactive } from 'vue';
    // 响应式对象
    const classObject = reactive({
      active: true,  // 决定是否使用 css样式 active
      // 对与类名有特殊字符的需要加上引号
      'text-danger': false  // 不适用 css样式 text-danger
    })
    </script>
    
  • 可以绑定一个返回对象的计算属性

    <div :class="classObject"></div>
    
    <script setup>
    import { ref,computed } from 'vue';
    const isActive = ref(true)
    const error = ref(null)
    
    const classObject = computed(() => ({
      // 当isActive的值为true,并且error的值为空时,使用这个css样式
      active: isActive.value && !error.value,
      'text-danger': error.value && error.value.type === 'fatal'
    }))
    </script>
    
  • 可以给 :class 绑定一个数组来渲染多个 CSS class

    <div :class="[activeClass, errorClass]"></div>
    
    <script setup>
    import { ref } from 'vue';
    const activeClass = ref('active')
    const errorClass = ref('text-danger')
    </script>
    
  • 如果你也想在数组中有条件地渲染某个 class,你可以使用三元表达式

    <div :class="[isActive ? activeClass : '', errorClass]"></div>
    
    <script setup>
    import { ref } from 'vue';
    const isActive = ref(true)
    const activeClass = ref('active')
    const errorClass = ref('text-danger')
    </script>
    

Style绑定内联样式绑定

  • :style 支持绑定 JavaScript 对象值,对应的是 html的style属性

    <!-- :style="{ color: 响应式属性, fontSize: 响应式属性 + 'px' }" -->
    <div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
    
    <script setup>
    import { ref } from 'vue';
    const activeColor = ref('red')
    const fontSize = ref(30)
    </script>
    
  • 直接绑定一个样式对象

    <div :style="styleObject"></div>
    
    <script setup>
    import { reactive } from 'vue';
    // 响应式对象
    const styleObject = reactive({
      color: 'red',
      fontSize: '13px'
    })
    </script>
    
  • 返回样式对象的计算属性

    <div :style="styleObject"></div>
    
    <script setup>
    import {reactive,computed} form 'vue'
    // 计算属性返回的样式对象
    const styleObject = computed(() => ({
      color: 'red',
      fontSize: '13px'
    }))
    </script>
    
  • 绑定数组 ::style 绑定一个包含多个样式对象的数组

    <div :style="[baseStyles, overridingStyles]"></div>
    
    <script setup>
    import { ref } from 'vue';
    const baseStyles = ref({color: 'red',fontSize: '16px'})
    const overridingStyles = ref({fontWeight: 'bold' })
    </script>
    

14. 条件渲染

概念

  • v-if 是一个指令,他必须依附于某个元素

  • v-if 指令用于条件性地渲染一块内容, 当表达式返回真值时才被渲染

  • 注意事项:当 v-ifv-for 同时存在于一个元素上的时候,v-if 会首先被执行 ,所有不推荐在同一个元素上使用这两个指令

    <h1 v-if="awesome">Vue is awesome!</h1>
    
    <script setup>
    import { ref } from 'vue';
    const awesome = ref(true)
    </script>
    
  • 可以使用 v-elsev-if 添加一个“else 区块”

    <!-- 每当点击按钮时 awesome 的值取反 -->
    <button @click="awesome = !awesome">Toggle</button>
    <h1 v-if="awesome">Vue is awesome!</h1>
    <h1 v-else>Oh no</h1>
    
    <script setup>
    import { ref } from 'vue';
    const awesome = ref(true)
    </script>
    
  • v-else-if 提供的是相应于 v-if 的“else if 区块”

    <div v-if="type === 'A'">
      A
    </div>
    <div v-else-if="type === 'B'">
      B
    </div>
    <div v-else-if="type === 'C'">
      C
    </div>
    <div v-else>
      Not A/B/C
    </div>
    
    <script setup>
    import { ref } from 'vue';
    const type = ref('B')
    </script>
    
  • 按条件显示一个元素的指令是 v-show

  • v-show 会在 DOM 渲染中保留该元素

  • v-show 仅切换了该元素上名为 display 的 CSS 属性

  • v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用

  • 无论 v-show 的条件是 true 还是 false,元素都会被渲染到 DOM 中,只是通过 display: none; 样式来控制显示与隐藏

    <h1 v-show="true">Hello!</h1>
    

15. v-for

概念

  • 使用 v-for 对数据进行动态渲染

  • Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()
  • 使用 v-for 的标签也会被循环生成

    <!-- 循环生成多个 li -->
    <li v-for="item in items">
      {{ item.message }}
    </li>
    
    <script setup>
    const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
    </script>
    
  • v-for 也支持使用可选的第二个参数表示当前项的位置索引

    <li v-for="(item, index) in items">
      {{ index }} - {{ item.message }}
    </li>
    
    <script setup>
    const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
    </script>
    
  • 对于多层嵌套的 v-for,每个 v-for 作用域都可以访问到父级作用域

    <li v-for="item in items">
      <span v-for="childItem in item.children">
        {{ item.message }} {{ childItem }}
      </span>
    </li>
    
  • 循环渲染对象数据

    <script setup>
    import { ref } from 'vue'
    const items = ref({message: 'Foo' , fdf: 'Bar'})
    </script>
    
    <template>
    	<ul>
            <li v-for="(value, key, index) in items">
            {{ key }} - {{ value }}
            </li>
    	</ul>
    </template>
    
  • v-for 可以直接接受一个整数值,注意此处 n 的初值是从 1 开始而非 0

    <span v-for="n in 10">{{ n }}</span>
    

通过 key 管理状态

  • 给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素

  • 需要为每个元素对应的块提供一个唯一的 key属性

    <div v-for="item in items" :key="item.id">
      <!-- 内容 -->
    </div>
    

16. 事件处理

概念

  • 内联事件处理器:事件被触发时执行的内联 JavaScript 语句

    <button @click="count++">Add 1</button>
    <p>Count is: {{ count }}</p>
    
    <script setup>
    const count = ref(0)
    </script>
    
  • 绑定事件函数

    <script setup>
    import { ref } from 'vue'
    
    const name = ref('Vue.js')
    
    const greet = (event) => {
      alert(`Hello ${name.value}!`)
      // `event` is the native DOM event
      if (event) {
        alert(event.target.tagName)
      }
    }
    </script>
    
    <template>
    	<button @click="greet">Greet</button>
    </template>
    
  • 在内联处理器中直接调用方法

    <script setup>
    function say(message) {
      alert(message)
    }
    </script>
    
    <template>
    	<button @click="say('hello')">Say hello</button>
    	<button @click="say('bye')">Say bye</button>
    </template>
    
  • 在内联事件处理器中访问原生 DOM 事件

    <!-- 将DOM原生的时间对象作为参数传给 warn方法 -->
    <button @click="warn('Form cannot be submitted yet.', $event)">
      Submit
    </button>
    
    <script setup>
    function warn(message, event) {
      // 这里可以访问原生事件
      if (event) {
        event.preventDefault()
      }
      alert(message)
    }
    </script>
    
  • v-on 提供了事件修饰符,修饰符是用 . 表示的指令后缀

    • .stop 单击事件将停止传递
    • .prevent 提交事件将不再重新加载页面
    • .self
    • .capture
    • .once 一次性按钮
    • .passive
    <!-- 单击事件将停止传递 -->
    <a @click.stop="doThis"></a>
    
    <!-- 提交事件将不再重新加载页面 -->
    <form @submit.prevent="onSubmit"></form>
    
    <!-- 修饰语可以使用链式书写 -->
    <a @click.stop.prevent="doThat"></a>
    
    <!-- 也可以只有修饰符 -->
    <form @submit.prevent></form>
    
    <!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
    <!-- 例如:事件处理器不来自子元素 -->
    <div @click.self="doThat">...</div>
    
    <!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
    <!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
    <div @click.capture="doThis">...</div>
    
    <!-- 点击事件最多被触发一次 -->
    <a @click.once="doThis"></a>
    
    <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
    <!-- 以防其中包含 `event.preventDefault()` -->
    <div @scroll.passive="onScroll">...</div>
    
    <!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
    <input @keyup.enter="submit" />
    

17. 表单输入绑定

概念

  • 实现表单元素与数据的双向绑定,简化数据状态管理

  • 自动处理不同表单元素的输入事件,避免手动编写事件监听器

  • 提高开发效率,减少重复的 DOM 操作

  • 通过使用 v-model,可以更轻松地管理表单数据和页面视图之间的关系

    <script setup>
    import { ref } from 'vue'
    
    const message = ref('')
    </script>
    
    <template>
      <p>Message is: {{ message }}</p>
    	<!-- v-model 相当于value属性  -->
    	<input v-model="message" placeholder="edit me" />
    </template>
    
  • 单一复选框绑定 决定是否选中

    对于复选框而言,当复选框被选中时,ss 数据属性的值会变为 true;当复选框未被选中时,ss 数据属性的值会变为 false

    <script setup>
    import { ref } from 'vue'
    
    const ss = ref('dd')
    </script>
    
    <template>
    	<input type="checkbox" id="checkbox" v-model="ss" />
    	<label for="checkbox">{{ ss }}</label>
    </template>
    
  • 多个复选框也可以绑定同一个数据,到的全选的效果

    <script setup>
    import { ref } from 'vue'
    
    const checkedNames = ref([])
    </script>
    
    <template>
      <div>Checked names: {{ checkedNames }}</div>
    
      <input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
      <label for="jack">Jack</label>
     
      <input type="checkbox" id="john" value="John" v-model="checkedNames" />
      <label for="john">John</label>
     
      <input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
      <label for="mike">Mike</label>
    </template>
    
  • 单选框绑定数据

    v-model绑定数据,当单选框被选中是,该单选框的value值会赋值为绑定的变量

    <script setup>
    import { ref } from 'vue'
    
    const picked = ref('One')
    </script>
    
    <template>
      <div>Picked: {{ picked }}</div>
    	<!-- v-model绑定数据,当单选框被选中是,该单选框的value值会赋值为绑定的变量 -->
    	<input type="radio" id="one" value="One" v-model="picked" />
    	<label for="one">One</label>
    
    	<input type="radio" id="two" value="Two" v-model="picked" />
      <label for="two">Two</label>
    </template>
    
  • 下拉框绑定数据

    当下拉选项被选中是,它的文本内容会赋值给绑定的变量

    <script setup>
    import { ref } from 'vue'
    
    const selected = ref('')
    </script>
    
    <template>
      <span> Selected: {{ selected }}</span>
    
      <select v-model="selected">
        <option disabled value="">Please select one</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
      </select>
    </template>
    
  • 下拉框绑定数组 (多选)

    <script setup>
    import { ref } from 'vue'
    
    const selected = ref([])
    </script>
    
    <template>
      <div>Selected: {{ selected }}</div>
    
      <select v-model="selected" multiple>
        <option>A</option>
        <option>B</option>
        <option>C00</option>
      </select>
    </template>
    
    <style>
    select[multiple] {
      width: 100px;
    }
    </style>
    
  • 值绑定

    <!-- `picked` 在被选择时是字符串 "a" -->
    <input type="radio" v-model="picked" value="a" />
    
    <!-- `toggle` 只会为 true 或 false -->
    <input type="checkbox" v-model="toggle" />
    
    <!-- `selected` 在第一项被选中时为字符串 "abc" -->
    <select v-model="selected">
      <option value="abc">ABC</option>
    </select>
    
    <input
      type="checkbox"
      v-model="toggle"
      :true-value="dynamicTrueValue"
      :false-value="dynamicFalseValue" />
    
    <input type="radio" v-model="pick" :value="first" />
    <input type="radio" v-model="pick" :value="second" />
    
    <select v-model="selected">
      <!-- 内联对象字面量 -->
      <option :value="{ number: 123 }">123</option>
    </select>
    
  • 修饰符

    <!-- 在 回车 事件后同步更新而不是 输入时 -->
    <input v-model.lazy="msg" />
    
    <!-- 输入自动转换为数字 -->
    <input v-model.number="age" />
    
    <!-- 去除用户输入内容中两端的空格 -->
    <input v-model.trim="msg" />
    

18. 生命周期

  • onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后调用

    <script setup>
    import { ref, onMounted } from 'vue'
    const el = ref()
    onMounted(() => {
      // el.value 获得标签元素节点对象
      el.value.innerHTML = "Hello:)"
    })
    </script>
    
    <template>
      <!-- 将元素节点绑定到响应变量el中 -->
      <div ref="el"></div>
    </template>
    
  • onUpdated() 函数:组件因为响应式状态变更而更新其 DOM 树之后调用

    <script setup>
    import { ref, onUpdated } from 'vue'
    
    const count = ref(0)
    
    onUpdated(() => {
      // 当组件的数据发生变化时执行一次该函数,打印改变后的响应变量值
      console.log(count.value)
    })
    </script>
    
    <template>
      <button @click="count++">{{ count }}</button>
    </template>
    
  • onUnmounted 钩子函数只会在整个组件被移除(从 DOM 中移除)时触发,而不是在组件中的某个 HTML 标签被移除时触发

    <script setup>
    import { onMounted, onUnmounted } from 'vue'
    
    let intervalId
    onMounted(() => {
      // 组件挂载时执行一个计时器
      intervalId = setInterval(() => {
        // ...
      })
    })
    
    // 组件卸载时清除这个计时器对象
    onUnmounted(() => clearInterval(intervalId))
    </script>
    
  • onBeforeMount() 组件被挂载之前被调用

    onBeforeMount(() => {
      ...
    })
    
  • onBeforeUpdate() 在组件即将因为响应式状态变更而更新其 DOM 树之前调用

    onBeforeUpdate(() => {
      ...
    })
    
  • onBeforeUnmount() 在组件实例被卸载之前调用

    onBeforeUnmount(() => {
      ...
    })
    
  • onErrorCaptured() 在捕获了后代组件传递的错误时调用

    function onErrorCaptured(callback: ErrorCapturedHook): void
    
    type ErrorCapturedHook = (
      err: unknown,
      instance: ComponentPublicInstance | null,
      info: string
    ) => boolean | void
    
  • onRenderTracked() 当组件渲染过程中追踪到响应式依赖时调用

    function onRenderTracked(callback: DebuggerHook): void
    
    type DebuggerHook = (e: DebuggerEvent) => void
    
    type DebuggerEvent = {
      effect: ReactiveEffect
      target: object
      type: TrackOpTypes /* 'get' | 'has' | 'iterate' */
      key: any
    }
    

19. 侦听器

概念

  • 在状态变化时执行一些操作(计算属性中禁止的操作),例如更改 DOM,或是根据异步操作的结果去修改另一处的状态

  • watch 的第一个参数可以是不同形式的“数据源” (如ref)

  • 注意,你不能直接侦听响应式对象的属性值,因为 watch() 得到的参数是一个 具体值

    <script setup>
    import { ref, watch } from 'vue'
    
    const question = ref('')
    const answer = ref('Questions usually contain a question mark. ;-)')
    const loading = ref(false)
    
    // 可以直接侦听一个 ref
    watch(question, async (newQuestion, oldQuestion) => {
      if (newQuestion.includes('?')) {
        loading.value = true
        answer.value = 'Thinking...'
        try {
          const res = await fetch('https://yesno.wtf/a