组件
基本概述
使用子组件
- 导入,无需注册,直接使用
 - 编译时,区分大小写
 - 可使用 
/>关闭标签 
传递 props
- 需要再组件上声明注册 
defineProps宏 
<script setup> 
    cosnt props = defineProps(['title'])
	props.title
 
 
监听事件
子组件通过$emit方法,抛出一个事件
父组件通过v-on 监听子组件抛出的事件
difineEmits 宏
- 声明了一个组件可能触发的所有事件
 - 可以对事件的参数进行验证。
 - 可以让vue 避免将他们作为原生事件监听隐式地应用于子组件地更目录
 
其他
- 大小写区分:HTML 标签和属性名称是不分大小写的,所以浏览器会把任何大写的字符解释为小写。
 - 闭合标签: Vue 的模板解析器支持任意标签使用 
/>作为标签关闭的标志 - 元素位置限制: ul、ol、table、select
 
深入组件
注册
全局注册
# app.component() 方法
 
 
缺点
- 全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”)。
 - 全局注册在大型项目中使项目的依赖关系变得不那么明确。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。
 
局部注册
组件名格式
使用 PascalCase (帕斯卡命名法) 的理由:
- PascalCase 时合法的 JavaScript 标识符
 <PascalCase/>更显著的表名了这是vue组件
props
声明 deineProps()
const props = deineProps(['foo'])
const props = defineProps({title: string,})
 
 
Prop 名字格式
- camelCase 驼峰名字格式
 - 父组件向子组件传递值时,可使用 camelCase 和 camel-case犯法
 
单向数据流
-  
所有的 props 都遵循单向绑定原则
 -  
避免子组件意外修改父组件的状态,导致数据流将很容易变得混乱而难以理解。
 
更改对象 / 数组类型的 props
- JavaScript 的对象和数组是按引用传递
 - 虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。
 - 对 Vue 来说,禁止这样的改动,虽然可能生效,但有很大的性能损耗,比较得不偿失。
 
prop 校验
defineProps({
    a: Number,//a:[String, Number]
    a: {type: String, reuqired: true, default: 100},
    a:{validator(value) {return ['success','warning', 'danger'].includs(value)}}
})
 
 
Boolean类型转换
- 有一种边缘情况——只有当 
Boolean出现在String之前时,Boolean转换规则才适用: 
<MyComponent disabled />
    
// disabled 将被转换为 true
defineProps({
  disabled: [Number, Boolean]
})
  
// disabled 将被解析为空字符串 (disabled="")
defineProps({
  disabled: [String, Boolean]
})
 
事件
tip: 事件声明时可选的,还是推荐你完整地声明所有要触发的事件
触发与监听事件
-  
事件的名字也提供了自动的格式转换,camelCase形式命名事件
 -  
所有传入
$emit()的额外参数都会被直接传向监听器。举例来说,$emit('foo', 1, 2, 3)触发后,监听器函数将会收到这三个参数值。 
$emit('incremnetClick',1,2,3,4)
 
声明触发的事件defineEmits()
defineEmits(['inFocus', 'sumbit'])
function buttonClick() {
  emit('submit')
}
 
Emits验证
- 支持对象语法:对触发事件的参数进行验证
 
const emit = defineEmites({
    submit(payload){
        // 通过返回值为 `true` 还是为 `false` 来判断
    	// 验证是否通过
    }
})
 
组件v-model
<Hello :modelValue = 'searchText' @update:modelValue='newvalue => searchText = newvalue'/>
 
内部方法一
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>
 
 
内部方法二
使用一个可写的,同时具有 getter 和 setter 的 computed 属性。
import { computed } from 'vue'
let props = defineProps(['modelValue'])
let emit = defineEmits(['update:modelValue'])
let value = computed({
    get() {return props.modelValue},
    set(value) {emit('update:modelValue', value)}
})
<input v-model="value">
 
v-model参数
默认情况:v-model再组件上使用的时modelValue、update:modelValue
自定义
例如:v-mode:title=“text” ==== title、update:title
v-mode:title="text"
 
多个v-model 绑定
 
处理 v-model 修饰符
 
自定义修饰符
不带参的 v-model
 
<MyComponent v-model.aaa="myText" />
const props = defineProps({
    modelVlaue: Stirng,
    modelModifiers:{default: () => ({})}
})
handleClick() {
    if(props.modelModifiers.aaa){}//判断是否有某个修饰符
    emit('update:modelValue', '值')
}
 
带参数 v-model
 
<MyComponent v-model:title.capitalize="myText">
    const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }
 
透传 Attributes
- 属性 和监听器 默认透传到子组件的根元素上。
 - 如果子组件的根元素有点击事件,父组件也透传了点击事件,则,这个父级和子级的事件都会被触发
 
禁用 Attributes 继承
在子组件中配置i选项
<script setup> 
    defineOptions({inheritAttrs: false})
 
场景:attribute需要应用在根节点以外的其他元素上。
这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs访问
<span>Fallthrough attribute: {{ $attrs }}</span>
 
这个 $attrs 对象包含了除组件所声明的 props 和 emits 之外的所有其他 attribute,例如 class,style,v-on 监听器等等。
需要注意
- 透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像 
foo-bar这样的一个 attribute 需要通过$attrs['foo-bar']来访问。 - 像 
@click这样的一个v-on事件监听器将在此对象下被暴露为一个函数$attrs.onClick。 
多根节点的 Attributes 继承
有着多个根节点的组件没有自动 attribute 透传行为
如果 $attrs 没有被显式绑定,将会抛出一个运行时警告。
在 JavaScript 中访问透传 Attributes
import { useAttrs } from 'vue'
const attrs = useAttrs()
 
注意:
虽然这里的 attrs 对象总是反映为最新的透传 attribute,但它并不是响应式的 (考虑到性能因素)。你不能通过侦听器去监听它的变化。
插槽Slots
具名插槽
子组件:
父组件:使用 v-slot=“header” 指令 或者简写为 #header
<template #header> #等价于 <template v-slot:header>
    <h1>Here might be a page title</h1>
</template>
 
作用域插槽
场景:插槽中的内容需要用大父组件域内或子组件域内的数据。
<slot :text="greetingMessage" :count="1"></slot>
<aaa v-slot="slotProps">{{slotProps.text}}</aa>
 
 
具名作用域插槽
<slot name="header" :text="greetingMessage" :count="1"></slot>
<aaa v-slot:header="slotProps">{{slotProps.text}}</aa>
 
依赖注入
prop 逐级透传问题
实现跨父子孙组件传值
provide(提供)
- 接受两个参数: 
  
- 第一个:注入名,可以是一个字符串或是一个 
Symbol。 - 第二个:提供的值,任意类型包括响应状态, ref
 
 - 第一个:注入名,可以是一个字符串或是一个 
 - 一个组件可多次调用 provide() 使用不同的注入名
 
应用层 Provide
app.provide('message', 'hello')
 
 
inject(注入)
import {reject} from 'vue'
// const props = defineProps()
 
 
注入默认值
const value = inject('message', '这是默认值')
 
在一些场景中,默认值可能需要通过调用一个函数或初始化一个类来取得。为了避免在用不到默认值的情况下进行不必要的计算或产生副作用,我们可以使用工厂函数来创建默认值:
js
const value = inject('key', () => new ExpensiveClass(), true)
 
第三个参数表示默认值应该被当作一个工厂函数。
和响应式数据配合使用
建议尽可能将任何对响应式状态的变更都保持在供给方组件(父)中
import { provide, ref } from 'vue'
function updateLocation() { location.value = 'South Pole' }
provide('location', { location, updateLocation})
const { location, updateLocation } = inject('location')
<button @click="updateLocation">{{ location }}</button>
 
使用 Symbol 作注入名
// keys.js
export const myInjectionKey = Symbol()
// 在供给方组件中
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, { /*
  要提供的数据
*/ });
// 注入方组件
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'
const injected = inject(myInjectionKey)
 
异步组件
基本用法
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => {
    return new Promise((resolve, reject) => {
        resolve(/*获取到的组件*/)
    })
})
# 或者
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)
 
AsyncComp 是一个外层包装过的组件,仅在页面需要它渲染时才会调用加载内部实际组件的函数。它会将接收到的 props 和插槽传给内部组件,所以你可以使用这个异步的包装组件无缝地替换原始组件,同时实现延迟加载。
- 与普通组件一样,异步组件可以使用 
app.component() 
app.component('MyComponent', defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
))
 
- 可以直接在父组件中直接定义它们
 
<script setup>
import { defineAsyncComponent } from 'vue'
const AdminPage = defineAsyncComponent(() =>
  import('./components/AdminPageComponent.vue')
)
</script>
<template>
  <AdminPage />
</template>










