vue3学习-1(基础)
- 电脑硬件
- 2025-09-16 07:48:02

vue3学习-1(基础) 1. 开始API 风格选项式 API (Options API)组合式 API (Composition API) 快速创建个应用 2.基础1. 创建个应用2.模板语法3.响应式基础`reactive()` 的局限性[]( cn.vuejs.org/guide/essentials/reactivity-fundamentals.html#limitations-of-reactive)4.计算属性5. 组件上使用 `v-for`6. 事件处理内联事件处理器方法事件处理器 7. 表单输入绑定8.侦听器深层侦听器即时回调的侦听器一次性侦听器`watchEffect`副作用清理同步侦听器停止侦听器 9. 模板引用10.组件基础定义一个组件传递 props监听事件插槽动态组件DOM 内模板解析注意事项 11.生命周期钩子注册周期钩子生命周期图示 3.深入组件4.逻辑复用5.内置组件6.应用规模化7.最佳实践8.进阶主题 1. 开始 API 风格
选项式 API 和组合式 API。
选项式 API (Options API) <script> export default { // data() 返回的属性将会成为响应式的状态 // 并且暴露在 `this` 上 data() { return { count: 0 } }, // methods 是一些用来更改状态与触发更新的函数 // 它们可以在模板中作为事件处理器绑定 methods: { increment() { this.count++ } }, // 生命周期钩子会在组件生命周期的各个不同阶段被调用 // 例如这个函数就会在组件挂载完成后被调用 mounted() { console.log(`The initial count is ${this.count}.`) } } </script> <template> <button @click="increment">Count is: {{ count }}</button> </template> 组合式 API (Composition API) <script setup> import { ref, onMounted } from 'vue' // 响应式状态 const count = ref(0) // 用来修改状态、触发更新的函数 function increment() { count.value++ } // 生命周期钩子 onMounted(() => { console.log(`The initial count is ${count.value}.`) }) </script> <template> <button @click="increment">Count is: {{ count }}</button> </template> 在生产项目中: 当你不需要使用构建工具,或者打算主要在低复杂度的场景中使用 Vue,例如渐进增强的应用场景,推荐采用选项式 API。当你打算用 Vue 构建完整的单页应用,推荐采用组合式 API + 单文件组件。 快速创建个应用安装 18.3 或更高版本的 Node.js,使用nvm最好,可以切换版本,就算是老项目的vue2也可以不影响
npm create vue@latest cd <your-project-name> npm install npm run dev # npm run build 如果是生产 需要build出个文件,一般编译好的文件是放在dist目录使用全局构建版本,写个html直接打开就行
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>Page Title</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" type="text/css" media="screen" href="main.css" /> <script src="main.js"></script> </head> <body></body> <script src=" unpkg /vue@3/dist/vue.global.js"></script> <div id="app">{{ message }}</div> <script> const { createApp, ref } = Vue; createApp({ setup() { const message = ref('Hello vue!'); return { message, }; }, }).mount('#app'); </script> </html>使用 ES 模块构建版本,启用 Import maps,拆分模块,这些都不太重要不影响主流程看不看都可以以下是链接, cn.vuejs.org/guide/quick-start.html
2.基础 1. 创建个应用每个 Vue 应用都是通过 createApp 函数创建一个新的 应用实例
import { createApp } from 'vue' const app = createApp({ /* 根组件选项 */ })我们传入 createApp 的对象实际上是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。
如果你使用的是单文件组件,我们可以直接从另一个文件中导入根组件。
import { createApp } from 'vue' // 从一个单文件组件中导入根组件 import App from './App.vue' const app = createApp(App)项目中整体结构大概会是这种组件树的形式
App (root component) ├─ TodoList │ └─ TodoItem │ ├─ TodoDeleteButton │ └─ TodoEditButton └─ TodoFooter ├─ TodoClearButton └─ TodoStatistics挂在应用
html <div id="app"> <button @click="count++">{{ count }}</button> </div>js
import { createApp } from 'vue' const app = createApp({ data() { return { count: 0 } } }) app.mount('#app')多个应用实例
const app1 = createApp({ /* ... */ }) app1.mount('#container-1') const app2 = createApp({ /* ... */ }) app2.mount('#container-2') 如果你正在使用 Vue 来增强服务端渲染 HTML,并且只想要 Vue 去控制一个大型页面中特殊的一小部分,应避免将一个单独的 Vue 应用实例挂载到整个页面上,而是应该创建多个小的应用实例,将它们分别挂载到所需的元素上去。 2.模板语法 # 文本插值 <span>Message: {{ msg }}</span> # 原始 HTML html插值很容易XSS 漏洞 少用 <p>Using v-html directive: <span v-html="rawHtml"></span></p> # Attribute 绑定 # v-bind 指令 <div v-bind:id="dynamicId"></div> # 简写 <div :id="dynamicId"></div> # 同名简写 仅支持 3.4 版本及以上 <!-- 与 :id="id" 相同 --> <div :id></div> # 布尔型 Attribute <button :disabled="isButtonDisabled">Button</button> # 动态绑定多个值 const objectOfAttrs = { id: 'container', class: 'wrapper', style: 'background-color:green' } <div v-bind="objectOfAttrs"></div> # 可以在绑定的表达式中使用一个组件暴露的方法:toTitleDate是组件中的方法 <time :title="toTitleDate(date)" :datetime="date"> {{ formatDate(date) }} </time> # 指令是带有 v- 前缀的特殊 attribute 3.响应式基础在组合式 API 中,推荐使用 ref() 函数来声明响应式状态:ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回:
import { ref } from 'vue' const count = ref(0) console.log(count) // { value: 0 } console.log(count.value) // 0 count.value++ console.log(count.value) // 1要在组件模板中访问 ref,请从组件的 setup() 函数中声明并返回它们:
import { ref } from 'vue' export default { // `setup` 是一个特殊的钩子,专门用于组合式 API。 setup() { const count = ref(0) // 将 ref 暴露给模板 return { count } } } <div>{{ count }}</div>在模板中使用 ref 时,我们不需要附加 .value。为了方便起见,当在模板中使用时,ref 会自动解包 (有一些注意事项)。
在 setup() 函数中手动暴露大量的状态和方法非常繁琐。幸运的是,我们可以通过使用单文件组件 (SFC) 来避免这种情况。我们可以使用 <script setup> 来大幅度地简化代码:
<script setup> import { ref } from 'vue' const count = ref(0) function increment() { count.value++ } </script> <template> <button @click="increment"> {{ count }} </button> </template>Ref 会使它的值具有深层响应性。这意味着即使改变嵌套对象或数组时,变化也会被检测到
还有另一种声明响应式状态的方式,即使用 reactive() API。与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性:
import { reactive } from 'vue' const state = reactive({ count: 0 }) <button @click="state.count++"> {{ state.count }} </button> reactive() 的局限性reactive() API 有一些局限性:
有限的值类型:它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型。
不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:
js
let state = reactive({ count: 0 }) // 上面的 ({ count: 0 }) 引用将不再被追踪 // (响应性连接已丢失!) state = reactive({ count: 1 })对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
js
const state = reactive({ count: 0 }) // 当解构时,count 已经与 state.count 断开连接 let { count } = state // 不会影响原始的 state count++ // 该函数接收到的是一个普通的数字 // 并且无法追踪 state.count 的变化 // 我们必须传入整个对象以保持响应性 callSomeFunction(state.count)由于这些限制,我们建议使用 ref() 作为声明响应式状态的主要 API。
4.计算属性 可以缓存,如果属性无变化不会重复执行,比函数性能好,Getter 不应有副作用(不要改变其他状态、在 getter 中做异步请求或者更改 DOM!),避免直接修改计算属性值
<script setup> import { ref, computed } from 'vue' const count = ref(2) // 这个计算属性在 count 的值小于或等于 3 时,将返回 count 的值。 // 当 count 的值大于等于 4 时,将会返回满足我们条件的最后一个值 // 直到 count 的值再次小于或等于 3 为止。 const alwaysSmall = computed({ get(previous) { if (count.value <= 3) { return count.value } return previous }, set(newValue) { count.value = newValue * 2 } }) </script> # 类样式绑定 :class :style # 条件渲染 v-if # 一个 v-else 元素必须跟在一个 v-if 或者 v-else-if 元素后面,否则它将不会被识别。 v-else v-else-if # v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。 # v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用。 v-show总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。
当 v-if 和 v-for 同时存在于一个元素上的时候,v-if 会首先被执行
const items = ref([{ message: 'Foo' }, { message: 'Bar' }]) <li v-for="item in items"> {{ item.message }} </li> <li v-for="(value, key, index) in myObject"> {{ index }}. {{ key }}: {{ value }} </li> <!-- 这会抛出一个错误,因为属性 todo 此时 没有在该实例上定义,避免v-if和v-for在一个标签中使用,v-if在-v-for内部使用 --> <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo.name }} </li> <template v-for="todo in todos"> <li v-if="!todo.isComplete"> {{ todo.name }} </li> </template> 5. 组件上使用 v-for <MyComponent v-for="(item, index) in items" :item="item" :index="index" :key="item.id" />:item="item":将当前迭代项的值绑定到子组件的 item 属性上。
:index="index":将当前迭代项的索引绑定到子组件的 index 属性上。
替换一个数组,当遇到的是非变更方法时,我们需要将旧的数组替换为新的:
// `items` 是一个数组的 ref items.value = items.value.filter((item) => item.message.match(/Foo/)) 6. 事件处理 内联事件处理器 const count = ref(0) <button @click="count++">Add 1</button> <p>Count is: {{ count }}</p> 方法事件处理器 const name = ref('Vue.js') function greet(event) { alert(`Hello ${name.value}!`) // `event` 是 DOM 原生事件 if (event) { alert(event.target.tagName) } } <!-- `greet` 是上面定义过的方法名 --> <button @click="greet">Greet</button> 7. 表单输入绑定 # 不使用v-model <input :value="text" @input="event => text = event.target.value"> # 使用v-model <input v-model="text"> # 注意在 <textarea> 中是不支持插值表达式的。请使用 v-model 来替代: <!-- 错误 --> <textarea>{{ text }}</textarea> <!-- 正确 --> <textarea v-model="text"></textarea> # 复选框 <input type="checkbox" id="checkbox" v-model="checked" /> <label for="checkbox">{{ checked }}</label> # 复选框多值 const checkedNames = ref([]) <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> # 单选按钮 <div>Picked: {{ picked }}</div> <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> # 选择器 <div>Selected: {{ selected }}</div> <select v-model="selected"> <option disabled value="">Please select one</option> <option>A</option> <option>B</option> <option>C</option> </select> # 选择器的选项可以使用 v-for 动态渲染: const selected = ref('A') const options = ref([ { text: 'One', value: 'A' }, { text: 'Two', value: 'B' }, { text: 'Three', value: 'C' } ]) <select v-model="selected"> <option v-for="option in options" :value="option.value"> {{ option.text }} </option> </select> <div>Selected: {{ selected }}</div> # 值绑定 <!-- `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> # 但有时我们可能希望将该值绑定到当前组件实例上的动态数据。这可以通过使用 v-bind 来实现。 # 复选框 <input type="checkbox" v-model="toggle" true-value="yes" false-value="no" /> # true-value 和 false-value 是 Vue 特有的 attributes,仅支持和 v-model 配套使用。这里 toggle 属性的值会在选中时被设为 'yes',取消选择时设为 'no'。你同样可以通过 v-bind 将其绑定为其他动态值: <input type="checkbox" v-model="toggle" :true-value="dynamicTrueValue" :false-value="dynamicFalseValue" /> # 输入自动转为数字 <input v-model.number="age" /> # 自动去除两端的空格 <input v-model.trim="msg" /> 8.侦听器 const x = ref(0) const y = ref(0) // 单个 ref watch(x, (newX) => { console.log(`x is ${newX}`) }) // getter 函数 watch( () => x.value + y.value, (sum) => { console.log(`sum of x + y is: ${sum}`) } ) // 多个来源组成的数组 watch([x, () => y.value], ([newX, newY]) => { console.log(`x is ${newX} and y is ${newY}`) }) const obj = reactive({ count: 0 }) // 错误,因为 watch() 得到的参数是一个 number watch(obj.count, (count) => { console.log(`Count is: ${count}`) }) // 提供一个 getter 函数 watch( () => obj.count, (count) => { console.log(`Count is: ${count}`) } ) 深层侦听器直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:
const obj = reactive({ count: 0 }) watch(obj, (newValue, oldValue) => { // 在嵌套的属性变更时触发 // 注意:`newValue` 此处和 `oldValue` 是相等的 // 因为它们是同一个对象! }) obj.count++相比之下,一个返回响应式对象的 getter 函数,只有在返回不同的对象时,才会触发回调:
watch( () => state.someObject, () => { // 仅当 state.someObject 被替换时触发 } )你也可以给上面这个例子显式地加上 deep 选项,强制转成深层侦听器:
watch( () => state.someObject, (newValue, oldValue) => { // 注意:`newValue` 此处和 `oldValue` 是相等的 // *除非* state.someObject 被整个替换了 }, { deep: true } )在 Vue 3.5+ 中,deep 选项还可以是一个数字,表示最大遍历深度——即 Vue 应该遍历对象嵌套属性的级数。
即时回调的侦听器watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。
watch( source, (newValue, oldValue) => { // 立即执行,且当 `source` 改变时再次执行 }, { immediate: true } ) 一次性侦听器 仅支持 3.4 及以上版本每当被侦听源发生变化时,侦听器的回调就会执行。如果希望回调只在源变化时触发一次,请使用 once: true 选项。
watch( source, (newValue, oldValue) => { // 当 `source` 变化时,仅触发一次 }, { once: true } ) watchEffect const todoId = ref(1) const data = ref(null) watch( todoId, async () => { const response = await fetch( ` jsonplaceholder.typicode /todos/${todoId.value}` ) data.value = await response.json() }, { immediate: true } )特别是注意侦听器是如何两次使用 todoId 的,一次是作为源,另一次是在回调中。
我们可以用 watchEffect 函数 来简化上面的代码。watchEffect() 允许我们自动跟踪回调的响应式依赖。上面的侦听器可以重写为:
watchEffect(async () => { const response = await fetch( ` jsonplaceholder.typicode /todos/${todoId.value}` ) data.value = await response.json() }) 副作用清理有时我们可能会在侦听器中执行副作用,例如异步请求:
watch(id, (newId) => { fetch(`/api/${newId}`).then(() => { // 回调逻辑 }) })但是如果在请求完成之前 id 发生了变化怎么办?当上一个请求完成时,它仍会使用已经过时的 ID 值触发回调。理想情况下,我们希望能够在 id 变为新值时取消过时的请求。
我们可以使用 onWatcherCleanup() API 来注册一个清理函数,当侦听器失效并准备重新运行时会被调用:
import { watch, onWatcherCleanup } from 'vue' watch(id, (newId) => { const controller = new AbortController() fetch(`/api/${newId}`, { signal: controller.signal }).then(() => { // 回调逻辑 }) onWatcherCleanup(() => { // 终止过期请求 controller.abort() }) }) watchPostEffect(() => { /* 在 Vue 更新后执行 */ }) 同步侦听器 import { watchSyncEffect } from 'vue' watchSyncEffect(() => { /* 在响应式数据变化时同步执行 */ }) 停止侦听器在 setup() 或 <script setup> 中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。因此,在大多数情况下,你无需关心怎么停止一个侦听器。
一个关键点是,侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。如下方这个例子:
<script setup> import { watchEffect } from 'vue' // 它会自动停止 watchEffect(() => {}) // ...这个则不会! setTimeout(() => { watchEffect(() => {}) }, 100) </script> const unwatch = watchEffect(() => {}) // ...当该侦听器不再需要时 unwatch()注意,需要异步创建侦听器的情况很少,请尽可能选择同步创建。如果需要等待一些异步数据,你可以使用条件式的侦听逻辑:
// 需要异步请求得到的数据 const data = ref(null) watchEffect(() => { if (data.value) { // 数据加载后执行某些操作... } }) 9. 模板引用访问模板引用
useTemplateRef() 3.5+
<script setup> import { useTemplateRef, onMounted } from 'vue' // 第一个参数必须与模板中的 ref 值匹配 const input = useTemplateRef('my-input') onMounted(() => { input.value.focus() }) </script> <template> <input ref="my-input" /> </template>v-for 中的模板引用
<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
<script setup> import { useTemplateRef, onMounted } from 'vue' import Child from './Child.vue' const childRef = useTemplateRef('child') onMounted(() => { // childRef.value 将持有 <Child /> 的实例 }) </script> <template> <Child ref="child" /> </template>如果一个子组件使用的是选项式 API 或没有使用 <script setup>,被引用的组件实例和该子组件的 this 完全一致,这意味着父组件对子组件的每一个属性和方法都有完全的访问权。大多数情况下,你应该首先使用标准的 props 和 emit 接口来实现父子组件交互。
使用了 <script setup> 的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露:
<script setup> import { ref } from 'vue' const a = 1 const b = ref(2) // 像 defineExpose 这样的编译器宏不需要导入 defineExpose({ a, b }) </script>请注意,defineExpose 必须在任何 await 操作之前调用。否则,在 await 操作后暴露的属性和方法将无法访问。
10.组件基础组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成一个层层嵌套的树状结构:
定义一个组件当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的 .vue 文件中,这被叫做单文件组件 (简称 SFC):
<script setup> import { ref } from 'vue' const count = ref(0) </script> <template> <button @click="count++">You clicked me {{ count }} times.</button> </template>当不使用构建步骤时,一个 Vue 组件以一个包含 Vue 特定选项的 JavaScript 对象来定义:
import { ref } from 'vue' export default { setup() { const count = ref(0) return { count } }, template: ` <button @click="count++"> You clicked me {{ count }} times. </button>` // 也可以针对一个 DOM 内联模板: // template: '#my-template-element' }要使用一个子组件,我们需要在父组件中导入它。假设我们把计数器组件放在了一个叫做 ButtonCounter.vue 的文件中,这个组件将会以默认导出的形式被暴露给外部。
<script setup> import ButtonCounter from './ButtonCounter.vue' </script> <template> <h1>Here is a child component!</h1> <ButtonCounter /> </template>通过 <script setup>,导入的组件都在模板中直接可用。
如果你是直接在 DOM 中书写模板 (例如原生 <template> 元素的内容),模板的编译需要遵从浏览器中 HTML 的解析行为。在这种情况下,你应该需要使用 kebab-case 形式并显式地关闭这些组件的标签。
<!-- 如果是在 DOM 中书写该模板 --> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter> 传递 propsProps 是一种特别的 attributes,你可以在组件上声明注册。要传递给博客文章组件一个标题,我们必须在组件的 props 列表上声明它。这里要用到 defineProps 宏:
<!-- BlogPost.vue --> <script setup> defineProps(['title']) </script> <template> <h4>{{ title }}</h4> </template>defineProps 是一个仅 <script setup> 中可用的编译宏命令,并不需要显式地导入。声明的 props 会自动暴露给模板。defineProps 会返回一个对象,其中包含了可以传递给组件的所有 props
const props = defineProps(['title']) console.log(props.title)如果你没有使用 <script setup>,props 必须以 props 选项的方式声明,props 对象会作为 setup() 函数的第一个参数被传入:
export default { props: ['title'], setup(props) { console.log(props.title) } }一个组件可以有任意多的 props,默认情况下,所有 prop 都接受任意类型的值。
当一个 prop 被注册后,可以像这样以自定义 attribute 的形式传递数据给它:
<BlogPost title="My journey with Vue" /> <BlogPost title="Blogging with Vue" /> <BlogPost title="Why Vue is so fun" />在实际应用中,我们可能在父组件中会有如下的一个博客文章数组:
const posts = ref([ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ])这种情况下,我们可以使用 v-for 来渲染它们:
<BlogPost v-for="post in posts" :key="post.id" :title="post.title" />使用 v-bind 语法 (:title="post.title") 来传递动态 prop 值的。当事先不知道要渲染的确切内容时,这一点特别有用。
监听事件组件实例提供了一个自定义事件系统。父组件可以通过 v-on 或 @ 来选择性地监听子组件上抛的事件,就像监听原生 DOM 事件那样:
<BlogPost ... @enlarge-text="postFontSize += 0.1" />子组件可以通过调用内置的 $emit 方法,通过传入事件名称来抛出一个事件:
<!-- BlogPost.vue, 省略了 <script> --> <template> <div class="blog-post"> <h4>{{ title }}</h4> <button @click="$emit('enlarge-text')">Enlarge text</button> </div> </template>因为有了 @enlarge-text="postFontSize += 0.1" 的监听,父组件会接收这一事件,从而更新 postFontSize 的值。
我们可以通过 defineEmits 宏来声明需要抛出的事件:
<!-- BlogPost.vue --> <script setup> defineProps(['title']) const emit = defineEmits(['enlarge-text']) //emit 函数 emit('enlarge-text') </script>如果你没有在使用 <script setup>,你可以通过 emits 选项定义组件会抛出的事件。你可以从 setup() 函数的第二个参数,即 setup 上下文对象上访问到 emit 函数:
export default { emits: ['enlarge-text'], setup(props, ctx) { ctx.emit('enlarge-text') } } 插槽 <AlertBox> Something bad happened. </AlertBox>可以通过 Vue 的自定义 <slot> 元素来实现:
Something bad happened.会插入到strong标签后
<!-- AlertBox.vue --> <template> <div class="alert-box"> <strong>This is an Error for Demo Purposes</strong> <slot /> </div> </template> <style scoped> .alert-box { /* ... */ } </style> 动态组件 <script setup> import Home from './Home.vue' import Posts from './Posts.vue' import Archive from './Archive.vue' import { ref } from 'vue' const currentTab = ref('Home') const tabs = { Home, Posts, Archive } </script> <template> <div class="demo"> <button v-for="(_, tab) in tabs" :key="tab" :class="['tab-button', { active: currentTab === tab }]" @click="currentTab = tab" > {{ tab }} </button> <component :is="tabs[currentTab]" class="tab"></component> </div> </template> <!-- currentTab 改变时组件也改变 --> <component :is="tabs[currentTab]"></component>在上面的例子中,被传给 :is 的值可以是以下几种:
被注册的组件名导入的组件对象你也可以使用 is attribute 来创建一般的 HTML 元素。
当使用 <component :is="..."> 来在多个组件间作切换时,被切换掉的组件会被卸载。我们可以通过 `` 组件强制被切换掉的组件仍然保持“存活”的状态。
DOM 内模板解析注意事项大小写区分:HTML 标签和属性名称是不分大小写的,所以浏览器会把任何大写的字符解释为小写,无论是 PascalCase 形式的组件名称、camelCase 形式的 prop 名称还是 v-on 的事件名称,都需要转换为相应等价的 kebab-case (短横线连字符) 形式
闭合标签:在 DOM 内模板中,我们必须显式地写出关闭标签。由于 HTML 只允许一小部分特殊的元素省略其关闭标签,最常见的就是 <input> 和 <img>。对于其他的元素来说,如果你省略了关闭标签,原生的 HTML 解析器会认为开启的标签永远没有结束。
元素位置限制:某些 HTML 元素对于放在其中的元素类型有限制,例如 <ul>,<ol>,<table> 和 <select>,相应的,某些元素仅在放置于特定元素中时才会显示,例如 <li>,<tr> 和 <option>。
这将导致在使用带有此类限制元素的组件时出现问题。例如:
<table> <blog-post-row></blog-post-row> </table>自定义的组件 <blog-post-row> 将作为无效的内容被忽略,因而在最终呈现的输出中造成错误。我们可以使用特殊的 is attribute 作为一种解决方案:
<table> <tr is="vue:blog-post-row"></tr> </table>一些小例子:示例
11.生命周期钩子每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。
注册周期钩子举例来说,onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后运行代码:
<script setup> import { onMounted } from 'vue' onMounted(() => { console.log(`the component is now mounted.`) }) </script>还有其他一些钩子,会在实例生命周期的不同阶段被调用,最常用的是 onMounted、onUpdated 和 onUnmounted。所有生命周期钩子的完整参考及其用法请参考 API 索引。
当调用 onMounted 时,Vue 会自动将回调函数注册到当前正被初始化的组件实例上。这意味着这些钩子应当在组件初始化时被同步注册。例如,请不要这样做:
js
setTimeout(() => { onMounted(() => { // 异步注册时当前组件实例已丢失 // 这将不会正常工作 }) }, 100)注意这并不意味着对 onMounted 的调用必须放在 setup() 或 <script setup> 内的词法上下文中。onMounted() 也可以在一个外部函数中调用,只要调用栈是同步的,且最终起源自 setup() 就可以。
生命周期图示有关所有生命周期钩子及其各自用例的详细信息,请参考生命周期钩子 API 索引。
3.深入组件 4.逻辑复用 5.内置组件 6.应用规模化 7.最佳实践 8.进阶主题vue3学习-1(基础)由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“vue3学习-1(基础)”