目录
挂载全局属性和方法
v-bind一次绑定多个值
v-bind用在样式中
Vue指令绑定值
Vue指令绑定属性
动态属性的约束
Dom更新时机
”可写的“计算属性
v-if与v-for不建议同时使用
v-for遍历对象
数组变化检测
事件修饰符
v-model用在表单类标签上
v-model还可以绑定一个数组
复选框设置真假值
v-model的修饰符
组件上v-model的参数
组件绑定多个v-model
自定义v-model修饰符
侦听器
数据来源
侦听异步数据
Ref
v-for 中的模板引用
ref的值可以是一个函数
ref用在组件上
组件部分
子组件接收属性
props是只读的
props检验
子组件抛出事件
动态组件
穿透
1. 组件内只有一个根标签
2.组件内有多个跟标签
禁用穿透
扩展
插槽
默认插槽
具名插槽
动态插槽名
作用域插槽
依赖注入
提供者
消费者
注入默认值
案例
修改数据应尽量控制在提供方
异步组件(了解)
异步组件对象形式
挂载属性和方法可以使用globalProperties
属性来进行挂载
main.ts
import * as echarts from 'echarts'
// 例如挂载echarts到全局
app.config.globalProperties.$echarts = echarts
相当于
只不过使用v-bind
绑定对象比较方便,适合绑定的属性比较多时
v-bind
其实还有一个妙用,就是可以用在css
样式中。v-bind(js变量)
js
表达式可以被使用的应用场景:插值表达式中和Vue
指令中(这个很重要,记死)
什么是js
表达式呢?很简单,只要结果返回的是一个值,能被函数return
出去,则它就是个js
表达式
属性绑定中的值,如果是需要计算得出来的,则可以使用es6
的模板字符串进行处理和拼接,上面箭头标注的地方,这样大大提升了属性绑定的灵活性。当然也可以通过计算属性计算所得,甚至是三元表达式处理所得。直接绑定个方法都可以
唯一需要注意的一点:就是绑定函数时,每次组件更新时都会重新调用一次,所以尽可能避免在绑定的函数中出现副作用(副作用就是除了函数实现自身主要功能以外的操作,例如操作dom
,ajax
请求等)
案例
父组件
函数getRes执行次数为:{{ num }}
隐藏/显示组件
子组件
about
null
。不支持在里面计算,如果复杂的,最好用计算属性注意:直接在动态属性中写模板字符串,也是支持的。因为它也是个字符串(只不过里面可以写变量)
也可以直接在动态属性中写个函数,前提是函数的返回值要满足计算属性名的类型
函数返回值需要满足计算属性名的类型(上面的限制中说只能是字符串或者是null
,但是写数字类型也是能出来的)
$nexttick
函数中包裹的逻辑会在下一个Dom
声明周期中调用,可以把它理解成一个延迟的回调函数
最典型的用法就是给组件绑定一个v-model
父组件
父组件内:{{ data }}
子组件
上面的步骤看起来很绕,其实步骤分好,就清楚了
v-model
,其实就相当于下面的写法 data = newValue"
/>
_data
,get
函数返回父组件传来的最新的值。而set
函数则负责向父组件抛事件(子向父组件传值)。利用父组件的update:model-value
函数对data
进行赋值(emits
传来的可是子组件_data
的值)。所以此时父组件的data
就是子组件_data
改变后的值。data
发生改变了,则子组件接收的值肯定也就发生改变了。所以父子组件就产生了数据联动官网链接:组件 v-model | Vue.js (vuejs.org)
Vue3
中,如果v-if
和v-for
都用在了同一个标签上,则v-if
会优先执行v-for
推荐绑定一个key
值,key
值最好是字符串或者数字v-for
中可以使用对象结构
-
{{ name }} -- {{ age }}
总结起来就是:js
数组方法中,如果会改变原数组,则就不需要重新赋值。如果不会改变原数组,则就需要重新赋值
这点很重要,开发中大部分时候都是处理数组数据的
有时候我们不想改变原数组,但是又需要使用触发响应式的方法。则可以对原数组进行一下深拷贝,然后用深拷贝的值进行操作
.stop
:阻止冒泡.prevent
:阻止默认行为.self
:只有自己能触发.capture
:使用捕获模式(冒泡是由小到大,而捕获则是由大到小).once
:只触发一次.passive
:不阻止默认行为
...
checkbox
,下拉选择selected
多选状态下v-model
,后面其实都是有对应的参数的,只不过默认带上了
input
标签和textarea
标签绑定v-model
。其实就是v-model:value
checkbox
标签和radio
标签绑定v-model
。其实就是v-model:checked
select
标签绑定v-model
。其实就是v-model:value
v-model
都可以,只要参数不同,能区分开就行v-model
后面.
的部分就是修饰符。默认提供的有lazy
,number
,trim
下面我们自己写一个:
父组件:
子组件:注意注释的内容部分
子组件内容
v-model
修饰符的有无做出相应的判断啦把上面的子组件改造一下:
子组件内容
那么既有参数,又有修饰符的,子组件的defineProps
改怎么接收呢?(用的很少,了解下就行,用到了就查文档)
可以侦听的一共有5
种:
ref
声明的数据:直接写,不需要 () =>
返回,也不需要.value
() =>
返回,也不需要.value
getter
函数(只要里面的某一项发生改变了都会触发),需要 () =>
返回,也需要.value
() =>
返回,也不需要.value
。如果是侦听对象中的某个属性,那需要 () =>
返回,也需要.value
如果侦听整个对象,则对象内的属性的值变化时,是不会侦听到的
改变name{{ obj.name }}
watch
的第三个参数,可以写一些配置项:deep
(深度侦听),immediate
(立即侦听,通常用于页面刚一出来,就需要执行的情况),flush: post
(在侦听器回调中能访问被 Vue
更新之后的 DOM。类似于nexttick
)
改变name{{ obj.name }}
点击num+1: {{ num }}
ref
绑定的函数父组件:
defineExpose
向外导出的方法和变量子组件:
子组件内容...
只会出现子组件中导出的方法和变量
vue3
中可以直接使用defineProps
进行属性的接收,内置就有,不需要导入props
是单向流的,即数据只能自顶向下。下面的不能修改上面的,只能下面的通知上面的进行数据修改父组件:
子组件:
子组件内容...{{ data }}
如果不声明defineProps
接收,则都会被$attrs
进行接收(起个兜底的作用)
在模板中可以直接使用$attrs
进行传递过来的属性的调用
子组件内容...{{ $attrs.data }}
如果想在script
标签内使用,则需要useAttrs
这个函数
子组件内容...{{ $attrs.data }}
useAttrs
这个方法总是反映为最新的穿透 attribute
,但它并不是响应式的。也就是说,侦听器是侦听不到接收数据的变化的
父组件:
改变data
子组件:
子组件内容
如果一定要侦听attrs
对象中属性数据的变化,可以使用onUpdated
生命周期
对上面的子组件进行改造
子组件内容
props
是只读的。子组件如果想要修改传递过来的props
值,则需要进行重新声明,把props
传递过来的值作为初始值或者计算属性进行处理
特别需要注意的一点:就是子组件对父组件传递过来的数组或者对象进行修改,会影响父组件数据的
父组件:
{{ list }}
子组件:
子组件内容
- {{ item.name }}
改变zs的名字
结果截图:有时候父子组件耦合度比较高的话,其实用这个也挺好的(省事了,不用子组件再往父组件抛事件了)
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// Number 类型的默认值
propD: {
type: Number,
default: 100
},
// 对象类型的默认值
propE: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propG: {
type: Function,
// 不像对象或数组的默认,这不是一个
// 工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})
Boolean
外的(布尔的会给个false
的默认值)未传递的可选 prop
将会有一个默认值 undefined
常见类型:
String
Number
Boolean
Array
Object
Date
Function
Symbol
类型也可以是自定义的类或构造函数,Vue
将会通过 instanceof
来检查类型是否匹配(用的很少)
vue3
中可以直接使用defineEmits
进行自定义事件的声明,然后在需要的地方进行事件的抛出。内置就有,不需要导入子组件:
向父组件发送值
父组件:
当然也可以直接在模板中进行事件的抛出,需要使用$emit
子组件:
向父组件发送值
父组件不变
defineEmits
还支持对象语法
true
则校验通过,返回false
则校验不通过向父组件发送事件对象event
或者这样
is
的值可以是组件名,还可以是标签名组件名:
插槽内容
标签名:
div中的内容
main.ts
import About from './views/About.vue'
const app = createApp(App)
// 注册一个全局组件About
app.component('About', About)
Test.vue
注意:这里is
属性不需要动态绑定,直接指定组件名即可
插槽内容
多学一招:注册全局组件的另一种方法(利用插件的方法)
找个地方创建一个ts
文件
index.ts
// 1. 导入组件
import About from './About.vue'
// 2. 进行全局组件的注册
const component = {
install: (app, options) => {
// 插件代码
app.component('About', About)
}, //'About'这就是后面可以使用的组件的名字,install是默认的一个方法
}
// 导出该组件
export default component
然后再main.ts
中引入文件并使用app.use()
进行全局注册
main.ts
// 引入
import component from './views/index'
const app = createApp(App)
// 全局注册
app.use(component)
attribute
”指的是传递给一个组件,却没有被该组件声明为props
或emits
的 attribute
或者 v-on
事件监听器。最常见的例子就是 class
、style
和 id
。分好两种情况
透传的 attribute 会自动被添加到根元素上。并且style和class会进行合并
父组件:
子组件:
子组件内容
则子组件div
标签上会接收所有穿透过来的属性。并且class
和style
会进行合并
事件也会被子组件的跟标签继承
父组件:
子组件:
子组件内容
这时点击子组件,会打印两次
深层组件穿透
翻译后:
额外的非道具属性(类、样式、liData)已传递给组件,但由于组件呈现片段或文本根节点,因此无法自动继承。
封装组件时,可能会遇到这种问题
有的时候,我们可能不希望父组件传递过来的属性应用在跟标签上,想应用在跟标签内的标签上,则就可以禁用穿透。然后就可以使用$attrs
在需要的地方进行使用
这个 $attrs
对象包含了除组件所声明的 props
和 emits
之外的所有其他 attribute
,例如 class
,style
,v-on
监听器等等
禁用穿透
举例说明:
父组件:
子组件:
子组件内容
- {{ $attrs.liData }}
展示效果:
会发现,不加穿透禁用,都会继承到跟标签上
加了以后的效果(把上面子组件内的内容注释解开)
就会发现,父组件穿透过来的属性都没有加到跟标签上了。而是全被$attrs
接收了。就可以通过$attrs
随便在子组件任意标签上使用穿透的属性
对子组件进行下改造
子组件内容
- {{ $attrs.liData }}
此时的页面效果:
需要注意的地方:
v-model
更好)父组件:
{{ num }}
子组件:
子组件内容 -- {{ n }}
点击改变父组件传来的num值
子组件也可以换成下面的方式,会更灵活一点
子组件内容 -- {{ n }}
点击改变父组件传来的num值
插槽一共分为3
种:默认插槽,具名插槽和作用域插槽
插槽传递的是模板内容(也就是html
,css
,js
组合起来的一段结构)
写在组件标签内的内容,就是插槽内容
父组件:
Click me!
子组件:
最终子组件渲染出来的结果
插槽分为插槽内容(父组件)和插槽出口(子组件),它们是一一对应的(用name做区分)
插槽的作用域
name
为default
的插槽,即不写名字的插槽(不写name
默认就是default
)父组件:
默认插槽的内容
子组件:
子组件内容
插槽默认内容
default
)的插槽。也就是name名字你需要指定name
一样的就是一对。适合需要多个插槽的情况v-slot
可以简写为#
号默认作用域插槽
name
为default
的作用域插槽父组件:
{{ record }}
子组件:
子组件内容
插槽默认内容
具名作用域插槽
name
不为default
的作用域插槽。就比上面的多了一个name
父组件:
{{ record }}
子组件:
子组件内容
插槽默认内容
注意:
v-slot
只能用在template
标签和组件标签上provide
(祖先组件,负责提供数据)和消费者inject
(子孙组件,负责消费数据)。提供的值是响应式的provide
函数:
提供应用级别的数据:需要写到main.ts
中,这样整个应用都能够进行使用(用的很少,通常写插件时提供数据用它。或者一些数据,整个应用都频繁的使用到,也可以使用应用层provide
提供整个应用数据)
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
inject
函数:
注意:
ref
,注入进来的会是该 ref
对象,而不会自动解包为其内部的值。这使得注入方组件能够通过 ref
对象保持了和供给方的响应性链接祖先组件:
改变num值
子孙组件:
子组件内容 -- {{ num }}
祖先组件:
{{ num }}
子孙组件:
子组件内容 -- {{ num }}
num+10
就是异步加载的组件(defineAsyncComponent
),它只会在需要访问的时候才会进行加载,里面可以传入一个promise
Es
模块动态导入(import(...)
)也会返回一个Promise
,大多数情况下会将它和defineAsyncComponent
搭配使用
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
AsyncComp
是一个外层包装过的组件,仅在页面需要它渲染时才会调用加载内部实际组件的函数。它会将接收到的 props
和插槽传给内部组件,所以你可以使用这个异步的包装组件无缝地替换原始组件,同时实现延迟加载与普通组件一样,异步组件可以使用 app.component()
全局注册
app.component('MyComponent', defineAsyncComponent(() =>
import('./components/MyComponent.vue')
))
也可以直接在父组件中直接定义它们:
promise
,而且还支持对象形式const AsyncComp = defineAsyncComponent({
// 加载的组件
loader: () => import('./Foo.vue'),
// 加载异步组件时使用的组件
loadingComponent: LoadingComponent,
// 展示加载组件前的延迟时间,默认为 200ms。因为加载时显示的组件和加载后显示的组件之间直接切换的话,会有闪白出现
delay: 200,
// 加载失败后展示的组件
errorComponent: ErrorComponent,
// 如果提供了一个 timeout 时间限制,并超时了
// 也会显示这里配置的报错组件,默认值是:Infinity
timeout: 3000
})