vue3学习笔记
笔记写完后,要复制到博客上的
一、基础语法
1、创建一个Vue应用:
-
应用实例:每个 Vue 应用都是通过
createApp函数创建一个新的 应用实例。import { createApp } from 'vue' const app = createApp({ /* 根组件选项 */ }) -
根组件:我们传入
createApp的对象实际上是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。
如果你使用的是单文件组件,我们可以直接从另一个文件中导入根组件。import { createApp } from 'vue' // 从一个单文件组件中导入根组件 import App from './App.vue' const app = createApp(App) -
挂载应用:
应用实例必须在调用了
.mount()方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串。 -
应用配置:
应用实例会暴露一个
.config对象允许我们配置一些应用级的选项,例如定义一个应用级的错误处理器,用来捕获所有子组件上的错误:app.config.errorHandler = (err) => { /* 处理错误 */ } -
多个应用实例:【基本不用】
2、模板语法
-
文本插值:
-
原始HTML:双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用
v-html指令: -
Attribute 绑定:
-
同名简写:如果 attribute 的名称与绑定的 JavaScript 值的名称相同,那么可以进一步简化语法,省略attribute 值:
<!-- 与 :id="id" 相同 --> <div :id></div> <!-- 这也同样有效 --> <div v-bind:id></div> -
动态绑定多个值:
果你有像这样的一个包含多个 attribute 的 JavaScript 对象:const objectOfAttrs = { id: 'container', class: 'wrapper', style: 'background-color:green' }通过不带参数的
v-bind,你可以将它们绑定到单个元素上:<div v-bind="objectOfAttrs"></div>
-
-
使用JavaScript表达式:
- 仅支持表达式
- 调用函数
- 受限的全局访问:
模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如Math和Date。
没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在window上的属性。然而,你也可以自行在app.config.globalProperties上显式地添加它们,供所有的 Vue 表达式使用。
-
指令 Directives
-
参数 Arguments:某些指令会需要一个“参数”,在指令名后通过一个冒号隔开做标识。
例如:v-bind:href="url",v-bind冒号后面的href就是v-bind指令的参数。<a v-bind:href="url"> ... </a> -
动态参数:
同样在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内:<a v-bind:[attributeName]="url"> ... </a>注意:指令中动态参数值是有限制的,值应当是一个字符串。语法上也有一些限制。
-
修饰符:修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。
例如.prevent修饰符会告知v-on指令对触发的事件调用event.preventDefault():<form @submit.prevent="onSubmit">...</form>
-
3、响应式基础:
-
ref():-
<script setup>:在setup()函数中手动暴露大量的状态和方法非常繁琐。单文件组件中,我们可以使用<script setup>来大幅度地简化代码:<script setup> import { ref } from 'vue' const count = ref(0) function increment() { count.value++ } </script> <template> <button @click="increment"> {{ count }} </button> </template>
-
reactive():- 为
reactive()标注类型 <script setup>:和ref一样,自动暴露顶层变量。
- 为
-
DOM更新时机:当你修改了响应式状态时,DOM 会被自动更新。但是需要注意的是,DOM 更新不是同步的。Vue 会在“next tick”更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。
import { nextTick } from 'vue' async function increment() { count.value++ await nextTick() // 现在 DOM 已经更新了 }
4、计算属性:
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
- 为计算属性标注类型
- 可写计算属性
- 获取上一个值:
如果需要,可以通过访问计算属性的 getter 的第一个参数来获取计算属性返回的上一个值。
5、侦听器:watch数据监听
<script setup>
import { ref, watch } from 'vue'
const question = ref('')
// 可以直接侦听一个 ref
watch(question, async (newQuestion, oldQuestion) => {
})
</script>
-
侦听数据源类型:
watch的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组。 -
深层侦听器:
直接给watch()传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:const obj = reactive({ count: 0 }) watch(obj, (newValue, oldValue) => { // 在嵌套的属性变更时触发 }) obj.count++ -
即时回调的侦听器:(立刻执行)
watch默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。watch( source, (newValue, oldValue) => { // 立即执行,且当 `source` 改变时再次执行 }, { immediate: true } // 设置 立即调用 ) -
一次性侦听器:
每当被侦听源发生变化时,侦听器的回调就会执行。如果希望回调只在源变化时触发一次,请使用once: true选项。watch( source, (newValue, oldValue) => { // 当 `source` 变化时,仅触发一次 }, { once: true } ) -
watchEffect():watchEffect()允许我们自动跟踪回调的响应式依赖。
即,watchEffect是watch的简化。watchEffect不需要明确写出依赖的响应式源,自动把回调中使用到的响应式源作为依赖源。默认就是立即执行的,所以不需要指定immediate: true。watchEffect(async () => { const response = await fetch( `https://jsonplaceholder.typicode.com/todos/${todoId.value}` ) data.value = await response.json() }) -
回调的触发时机:
- Post Watchers
- 同步侦听器:
-
停止侦听器:
在
setup()或<script setup>中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。因此,在大多数情况下,你无需关心怎么停止一个侦听器。一个关键点是,侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。如下方这个例子:
<script setup> import { watchEffect } from 'vue' // 它会自动停止 watchEffect(() => {}) // ...这个则不会! setTimeout(() => { watchEffect(() => {}) }, 100) </script>
6、模板引用:DOM引用或组件引用
<input ref="input">
-
访问模板引用:
要在组合式 API 中获取引用,我们可以使用辅助函数useTemplateRef()<script setup> import { useTemplateRef, onMounted } from 'vue' // 第一个参数必须与模板中的 ref 值匹配 const input = useTemplateRef('my-input') onMounted(() => { input.value.focus() }) </script> <template> <input ref="my-input" /> </template>vue3.5版本以前是没有useTemplateRef函数的。所以3.5以前的用法是,声明一个与模板里 ref attribute 匹配的引用:
<script setup> import { ref, onMounted } from 'vue' // 声明一个 ref 来存放该元素的引用 // 必须和模板里的 ref 同名 const input = ref(null) onMounted(() => { input.value.focus() }) </script> <template> <input ref="input" /> </template> -
v-for中的模板引用
当在v-for中使用模板引用时,对应的 ref 中包含的值是一个数组,它将在元素被挂载后包含对应整个列表的所有元素:<script setup> import { ref, useTemplateRef, onMounted } from 'vue' const list = ref([ /* ... */ ]) const itemRefs = useTemplateRef('items') onMounted(() => console.log(itemRefs.value)) </script> <template> <ul> <li v-for="item in list" ref="items"> {{ item }} </li> </ul> </template> -
函数模板引用
<input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">- ref还可以绑定为一个函数,会在每次组件更新时都被调用。该函数会收到元素引用作为其第一个参数。
- 当绑定的元素被卸载时,函数也会被调用一次,此时的
el参数会是null。你当然也可以绑定一个组件方法而不是内联函数。
-
组件上的ref:
模板引用也可以被用在一个子组件上。这种情况下引用中获得的值是组件实例
7、组件基础
- 传递 props
- 监听事件
- 通过插槽来分配内容
- 动态组件
8、生命周期
二、深入组件
2.1、组件注册
-
全局注册:
我们可以使用 Vue 应用实例的
.component()方法,让组件在当前 Vue 应用中全局可用。app.component( // 注册的名字 'MyComponent', // 组件的实现 { /* ... */ } )如果是单文件组件,可以注册被导入的.vue文件:
import MyComponent from './App.vue' app.component('MyComponent', MyComponent).component()方法可以被链式调用:app .component('ComponentA', ComponentA) .component('ComponentB', ComponentB) .component('ComponentC', ComponentC) -
局部注册:
在使用<script setup>的单文件组件中,导入的组件可以直接在模板中使用,无需注册:<script setup> import ComponentA from './ComponentA.vue' </script> <template> <ComponentA /> </template>如果没有使用
<script setup>,则需要使用components选项来显式注册:import ComponentA from './ComponentA.js' export default { components: { ComponentA }, setup() { // ... } }
2.2、Props
-
Props声明:
在使用<script setup>的单文件组件中,props 可以使用defineProps()宏来声明:<script setup> const props = defineProps(['foo']) console.log(props.foo) </script>在没有使用
<script setup>的组件中,props 可以使用props选项来声明:export default { props: ['foo'], setup(props) { // setup() 接收 props 作为第一个参数 console.log(props.foo) } }注意传递给
defineProps()的参数和提供给props选项的值是相同的,两种声明方式背后其实使用的都是 props 选项。 -
响应式 Props 解构:
const { foo } = defineProps(['foo']) watchEffect(() => { // 在 3.5 之前只运行一次 // 在 3.5+ 中在 "foo" prop 变化时重新执行 console.log(foo) })可以使用 JavaScript 原生的默认值语法声明 props 默认值。这在使用基于类型的 props 声明时特别有用。
const { foo = 'hello' } = defineProps<{ foo?: string }>()难点:这块了解有点 费劲,下次再补充下
-
传递 prop 的细节
2.3、组件事件
-
触发与监听事件:同vue2的
<button @click="$emit('someEvent')">Click Me</button> -
事件参数:同vue2的
-
声明触发的事件:
我们在<template>中使用的$emit方法不能在组件的<script setup>部分中使用,但defineEmits()会返回一个相同作用的函数供我们使用:<script setup> const emit = defineEmits(['inFocus', 'submit']) function buttonClick() { emit('submit') } </script>显式地使用了
setup函数而不是<script setup>,则事件需要通过emits选项来定义,emit函数也被暴露在setup()的上下文对象上:export default { emits: ['inFocus', 'submit'], setup(props, ctx) { ctx.emit('submit') } }说明:$emit可以在模板中直接使用,但在setup中不能直接使用,需要先声明。
-
事件校验:
和对 props 添加类型校验的方式类似,所有触发的事件也可以使用对象形式来描述。
2.4、组件 v-model
v-model可以在组件上使用以实现双向绑定。
-
基本用法:
从 Vue 3.4 开始,推荐的实现方式是使用defineModel()宏:<!-- Child.vue --> <script setup> const model = defineModel() function update() { model.value++ } </script> <template> <div>Parent bound v-model is: {{ model }}</div> <button @click="update">Increment</button> </template><!-- Parent.vue --> <Child v-model="countModel" /> -
v-model 的参数:

浙公网安备 33010602011771号