当前位置: 首页 > news >正文

vue3学习-1(基础)

vue3学习-1(基础)

    • 1. 开始
      • API 风格
        • 选项式 API (Options API)
        • 组合式 API (Composition API)
      • 快速创建个应用
    • 2.基础
      • 1. 创建个应用
      • 2.模板语法
      • 3.响应式基础
      • `reactive()` 的局限性[](https://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="https://unpkg.com/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,拆分模块,这些都不太重要不影响主流程看不看都可以以下是链接,https://cn.vuejs.org/guide/quick-start.html

2.基础

1. 创建个应用

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

    import { createApp } from 'vue'const app = createApp({/* 根组件选项 */
    })
    
  2. 我们传入 createApp 的对象实际上是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。

    如果你使用的是单文件组件,我们可以直接从另一个文件中导入根组件。

    import { createApp } from 'vue'
    // 从一个单文件组件中导入根组件
    import App from './App.vue'const app = createApp(App)
    
  3. 项目中整体结构大概会是这种组件树的形式

    App (root component)
    ├─ TodoList
    │  └─ TodoItem
    │     ├─ TodoDeleteButton
    │     └─ TodoEditButton
    └─ TodoFooter├─ TodoClearButton└─ TodoStatistics
    
  4. 挂在应用

    1. html
    <div id="app"><button @click="count++">{{ count }}</button>
    </div>
    
    1. js

      import { createApp } from 'vue'const app = createApp({data() {return {count: 0}}
      })app.mount('#app')
      
  5. 多个应用实例

    const app1 = createApp({/* ... */
    })
    app1.mount('#container-1')const app2 = createApp({/* ... */
    })
    app2.mount('#container-2')
    
    1. 如果你正在使用 Vue 来增强服务端渲染 HTML,并且只想要 Vue 去控制一个大型页面中特殊的一小部分,应避免将一个单独的 Vue 应用实例挂载到整个页面上,而是应该创建多个小的应用实例,将它们分别挂载到所需的元素上去。

2.模板语法

  1. # 文本插值
    <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.响应式基础

  1. 在组合式 API 中,推荐使用 ref() 函数来声明响应式状态:ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回:

    import { ref } from 'vue'const count = ref(0)console.log(count) // { value: 0 }
    console.log(count.value) // 0count.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 有一些局限性:

    1. 有限的值类型:它只能用于对象类型 (对象、数组和如 MapSet 这样的集合类型)。它不能持有如 stringnumberboolean 这样的原始类型。

    2. 不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:

      js

      let state = reactive({ count: 0 })// 上面的 ({ count: 0 }) 引用将不再被追踪
      // (响应性连接已丢失!)
      state = reactive({ count: 1 })
      
    3. 对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:

      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-ifv-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

<MyComponentv-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 来实现。
# 复选框 
<inputtype="checkbox"v-model="toggle"true-value="yes"false-value="no" />
# true-value 和 false-value 是 Vue 特有的 attributes,仅支持和 v-model 配套使用。这里 toggle 属性的值会在选中时被设为 'yes',取消选择时设为 'no'。你同样可以通过 v-bind 将其绑定为其他动态值:
<inputtype="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(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)data.value = await response.json()},{ immediate: true }
)

特别是注意侦听器是如何两次使用 todoId 的,一次是作为源,另一次是在回调中。

我们可以用 watchEffect 函数 来简化上面的代码。watchEffect() 允许我们自动跟踪回调的响应式依赖。上面的侦听器可以重写为:

watchEffect(async () => {const response = await fetch(`https://jsonplaceholder.typicode.com/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>
传递 props

Props 是一种特别的 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 来渲染它们:

<BlogPostv-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"><buttonv-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>

还有其他一些钩子,会在实例生命周期的不同阶段被调用,最常用的是 onMountedonUpdatedonUnmounted。所有生命周期钩子的完整参考及其用法请参考 API 索引。

当调用 onMounted 时,Vue 会自动将回调函数注册到当前正被初始化的组件实例上。这意味着这些钩子应当在组件初始化时被同步注册。例如,请不要这样做:

js

setTimeout(() => {onMounted(() => {// 异步注册时当前组件实例已丢失// 这将不会正常工作})
}, 100)

注意这并不意味着对 onMounted 的调用必须放在 setup()<script setup> 内的词法上下文中。onMounted() 也可以在一个外部函数中调用,只要调用栈是同步的,且最终起源自 setup() 就可以。

生命周期图示

在这里插入图片描述

有关所有生命周期钩子及其各自用例的详细信息,请参考生命周期钩子 API 索引。

3.深入组件

4.逻辑复用

5.内置组件

6.应用规模化

7.最佳实践

8.进阶主题

相关文章:

vue3学习-1(基础)

vue3学习-1&#xff08;基础&#xff09; 1. 开始API 风格选项式 API (Options API)组合式 API (Composition API) 快速创建个应用 2.基础1. 创建个应用2.模板语法3.响应式基础reactive() 的局限性[](https://cn.vuejs.org/guide/essentials/reactivity-fundamentals.html#limi…...

deepseek使用记录18——文化基因之文化融合

文明长河中的生命浪花 在洛阳白马寺的银杏树下&#xff0c;年轻母亲指着"农禅并重"碑刻给孩子讲述祖辈耕作的故事&#xff1b;在哔哩哔哩的直播间里&#xff0c;00后女孩穿着汉服跳起街舞&#xff0c;弹幕飘过"这才是文化缝合怪"。当文明交融的宏大叙事照…...

Hadoop简介

1. Hadoop简介 官网&#xff1a;http://hadoop.apache.org 1.1 Hadoop架构 Hadoop由三个模块组成&#xff1a;分布式存储HDFS、分布式计算MapReduce、资源调度引擎YARN 1.2 Hadoop历史 Hadoop作者Doug Cutting Apache Lucene是一个文本搜索系统库 Apache Nutch作为前者的一部…...

密码学(哈希函数)

4.1 Hash函数与数据完整性 数据完整性&#xff1a; 检测传输消息&#xff08;加密或未加密&#xff09;的修改。 密码学Hash函数&#xff1a; 构建某些数据的简短“指纹”&#xff1b;如果数据被篡改&#xff0c;则该指纹&#xff08;以高概率&#xff09;不再有效。Hash函数…...

谈谈单例模式中通过Htools包的SpringUtil.getBean获取Bean的好处

目录 优势 解决依赖注入失效问题&#xff1a; 典型应用场景&#xff1a; 好处 1. 实例化时序问题 2. 延迟获取解决空指针 3. 设计模式与 Spring 的权衡 代码对比&#xff1a;错误 vs 正确 错误示例&#xff08;空指针&#xff09;&#xff1a; 正确实现&#xff08;延…...

本地部署大语言模型-DeepSeek

DeepSeek 是国内顶尖 AI 团队「深度求索」开发的多模态大模型&#xff0c;具备数学推理、代码生成等深度能力&#xff0c;堪称"AI界的六边形战士"。 Hostease AMD 9950X/96G/3.84T NVMe/1G/5IP/RTX4090 GPU服务器提供多种计费模式。 DeepSeek-R1-32B配置 配置项 规…...

adb的安装

1、概念 &#xff08;1&#xff09;adb&#xff08;android debug bridge&#xff09;安卓调试桥&#xff0c;用于完成电脑和手机之间的通信控制。 &#xff08;2&#xff09;xcode来完成对于ios设备的操控&#xff0c;前提是有个mac电脑。 2、adb的安装 &#xff08;1&…...

Python 如何实现 Markdown 记账记录转 Excel 存储

文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons&#xff1a;JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram&#xff0c;自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 &#xff1f; 5 IDEA必装的插件&…...

随机播放音乐 伪随机

import java.util.*;/*** https://cloud.tencent.com.cn/developer/news/1045747* 伪随机播放音乐*/ public class MusicPlayer {private List<String> allSongs; // 所有歌曲列表private List<String> playedSongs; // 已经播放过的歌曲列表private Map<String…...

latex 环境配置

编译器可选 miktex和 tex live ① miktex 下载地址 Portable 版本用的也是 Installer版的安装程序 basic-miktex-24.1-x64.exe&#xff0c;但是需要修改文件名为 miktex-portable.exe ├──texmfs │ ├─config │ ├─data │ └─install │ └─miktex/…...

fortify安全扫描Access Control: Database问题解决

概述 Access Control: Database说白了就是权限控制。在访问数据库(sql和nosql)需要加入当前用户的权限控制。不然会被fortify扫描出来&#xff0c;认为客户端可能不挟持和假冒&#xff0c;从而导致数据被泄露。 但是这个并不是任何时候都需要的&#xff0c;有的接口本来…...

Java 设计模式:软件开发的精髓与艺

目录 一、设计模式的起源二、设计模式的分类1. 创建型模式2. 结构型模式3. 行为型模式三、设计模式的实践1. 单例模式2. 工厂模式3. 策略模式四、设计模式的优势五、设计模式的局限性六、总结在软件开发的浩瀚星空中,设计模式犹如一颗颗璀璨的星辰,照亮了开发者前行的道路。它…...

初学者如何用 Python 写第一个爬虫?

?? 欢迎来到我的博客&#xff01; 非常高兴能在这里与您相遇。在这里&#xff0c;您不仅能获得有趣的技术分享&#xff0c;还能感受到轻松愉快的氛围。无论您是编程新手&#xff0c;还是资深开发者&#xff0c;都能在这里找到属于您的知识宝藏&#xff0c;学习和成长。 ?? …...

Cocos Creator3.8.6拖拽物体的几种方式

文章目录 前言一、第一种通过UILocation二、第二种通过UIDelta实现总结 前言 在游戏开发中&#xff0c;拖拽物体是一个非常常见的交互功能&#xff0c;无论是用于UI元素的拖动&#xff0c;还是场景中物体的移动&#xff0c;拖拽操作都能极大地提升用户体验。Cocos Creator 3.8…...

分布式Session

我用「餐厅点餐代码实战」帮你彻底搞懂分布式Session&#xff0c;看完不仅能应对面试&#xff0c;还能直接应用到实际开发。先记住这个核心矛盾&#xff1a;多服务员如何记住同一顾客的喜好&#xff1f; 一、从生活场景理解Session的本质 传统单机场景&#xff08;小餐馆&…...

Kotlin 运算符重载

在Kotlin中&#xff0c;常用的运算符重载函数名如下&#xff1a; 1.算术操作符&#xff1a; 加法&#xff1a;plus 减法&#xff1a;minus 乘法&#xff1a;times 除法&#xff1a;div 取模&#xff1a;rem 或 mod 整数除法&#xff1a;floorDiv 求幂&#xff1a;pow 自增&…...

OpenHarmony4.1-轻量与小型系统ubuntu开发环境

因OpenHarmony官网提供包含轻量、小型与标准系统的全量代码非常宠大&#xff0c;解包后大概需要70G以上硬盘空间&#xff0c;如要编译标准系统则需要140G以上空间。 如硬盘空间有限与只使用轻量/小型OpenHarmony系统&#xff0c;则可以下载并直接使用本人裁剪源码过的ubuntu硬盘…...

AVR 单片机硬件供电处理

摘自AVR 单片机应用笔记&#xff1a;AN2519 - AVR Microcontroller Hardware Design Considerations。 2. 供电 供电设计是任何硬件设计的关键一环&#xff0c;直接影响到系统的性能。在设计供电时&#xff0c;有两个重要的方面需要考虑&#xff1a;ESD 防护和噪声干扰。这些内…...

LeetCode 27 移除元素

LeetCode 27 - 移除元素&#xff08;Remove Element&#xff09;是一个简单但经典的双指针问题&#xff0c;主要考察数组操作的基本功。虽然问题容易&#xff0c;但掌握多种解法以及衍生的变体问题对解决更复杂的操作数组问题有帮助。 题目描述 输入&#xff1a;整数数组 nums…...

对“预训练”的理解

预训练有什么用 传统的机器学习是偏数学的&#xff0c;对数据的量不做过多要求&#xff0c;而深度学习的项目通常是有大量的数据可供使用。 在平常的任务或者项目中&#xff0c;我们可能并没有大量数据&#xff0c;只有少量数据&#xff0c;在这时我们就可以通过“借用”有大…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

软件工程 期末复习

瀑布模型&#xff1a;计划 螺旋模型&#xff1a;风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合&#xff1a;模块内部功能紧密 模块之间依赖程度小 高内聚&#xff1a;指的是一个模块内部的功能应该紧密相关。换句话说&#xff0c;一个模块应当只实现单一的功能…...