③ vue 组件
vue组件
1 组件的定义及复用性 局部组件和全局组件
// 局部组件的定义:要注册之后才能使用,建议大写字母单词,驼峰命名
const Counter2 = {
data() {
return {
count: 1
}
},
template: `<div @click="count+=2">{{count}}</div>`
}
const app = Vue.createApp({
components: { Counter2 },
// 组件具有复用性
template: `<div>
<counter />
<counter />
<counter2 />
</div>`
})
// 全局组件的定义:建议小写字母单词,中间横杆连接
app.component('counter', {
data() {
return {
count: 1
}
},
template: `<div @click="count++">{{count}}</div>`
})
const vm = app.mount('#root')
2 组件间传值及传值校验
2.1 组件间传值
-
静态传值
-
动态传值
const app = Vue.createApp({
data() {
return { num: 123 }
},
template: `<div> <test :content="num" /> </div>`
})
app.component('test', {
props: [ 'content' ],
template: `<div>{{content}}</div>`
})
const vm = app.mount('#root')
2.2 传值校验
类型 type
-
String
-
Boolean
-
Array
-
Object
-
Function
-
Symbol
深度校验 validator
const app = Vue.createApp({
data() {
return { num: 123 }
},
template: `<div> <test :content="num" /> </div>`
})
app.component('test', {
props: {
content: {
type: Number,
default: () => {
return 789
},
validator: value => {
return value > 1000
},
required: true
}
},
template: `<div>{{content}}</div>`
})
const vm = app.mount('#root')
3 单向数据流的理解
3.1 多个字段传入子组件
v-bind="params"
const app = Vue.createApp({
data() {
return {
params: {
content: 1234,
a: 123,
b: 456,
c: 789
}
}
},
/** <test v-bind="params" /> 相当于
* <test :content="params.content" :a="params.a" :b="params.b" :c="params.c" />
*/
template: `<div>
<test v-bind="params" />
</div>`
})
app.component('test', {
props: [ 'content', 'a', 'b', 'c' ],
template: `<div>{{content}}-{{a}}-{{b}}-{{c}}</div>`
})
const vm = app.mount('#root')
3.2 大小写传参
-
标签上用
:xxx-xxx
传递 -
props上用
xxxXxx
接收,插值表达式使用xxxXxx
3.3 单向数据流
- 子组件可以使用父组件传递过来的数据,但是不能修改传递过来的数据
const app = Vue.createApp({
data() {
return { num: 1 }
},
template: `<div>
<counter :count="num" />
</div>`
})
app.component('counter', {
props: [ 'count' ],
data() {
return {
myCount: this.count
}
},
template: `<div @click="myCount+=1">{{myCount}}</div>`
})
const vm = app.mount('#root')
为什么要有“单向数据流”的限制
- 避免组件之间数据耦合,子组件A修改了父组件的数据x,会导致B组件接收到的数据x也被修改
4 Non-Props 属性是什么
4.1 Non-Props 属性
- 父组件向子组件传值,子组件不接收的情况下,底层会将父组件传递的内容添加到子组件的最外层 DOM 上作为其属性
4.2 Non-Props 属性应用场景
-
给子组件添加类名
-
修改子组件样子
4.3 子组件拒绝 Non-Props 属性
inheritAttrs: false
const app = Vue.createApp({
template: `<div>
<counter msg="hello" />
</div>`
})
app.component('counter', {
inheritAttrs: false,
template: `<div>counter</div>`
})
const vm = app.mount('#root')
4.4 子组件获取 Non-Props 属性
4.4.1 标签上
- 子组件多个根节点时,使用
v-bind="$attrs"
将父组件传递的 Non-Props 属性放置到指定元素上
const app = Vue.createApp({
template: `<div>
<counter msg="hello" msg1="hello1"/>
</div>`
})
app.component('counter', {
inheritAttrs: false,
template: `
<div>counter</div>
<div v-bind="$attrs">counter</div>
<div :msg="$attrs.msg">counter</div>
`
})
const vm = app.mount('#root')
4.4.2 js上
this.$attrs.msg
5 父子组件间如何通过事件进行通信
5.1 props
+ this.$emit()
-
父组件通过
v-bind
传递参数,子组件通过props
接收父组件传递的参数 -
父组件调用子组件时使用
v-on
绑定事件,子组件通过this.$emit()
触发父组件事件
const app = Vue.createApp({
data() {
return { count: 1}
},
methods: {
handleAdd(param) {
this.count += param
}
},
template: `<div> <counter :count="count" @add="handleAdd" /> </div>`
})
app.component('counter', {
props: ['count'],
methods: {
handleClick() {
this.$emit('add', 2)
}
},
template: `
<div @click="handleClick">{{count}}</div>
`
})
const vm = app.mount('#root')
6 组件间双向绑定高级内容
6.1 v-model + props: ['modelValue']
-
父组件调用子组件时使用
v-model
绑定参数 -
子组件通过
props: ['modelValue']
接收参数 -
子组件通过
this.$emit('update:modelValue', param)
修改modelValue
const app = Vue.createApp({
data() {
return { count: 1}
},
template: `<div>
<counter v-model="count" />
</div>`
})
app.component('counter', {
props: ['modelValue'],
methods: {
handleClick() {
this.$emit('update:modelValue', this.modelValue + 3)
}
},
template: `<div @click="handleClick">{{modelValue}}</div>`
})
const vm = app.mount('#root')
6.2 v-model:xxx + props: ['xxx']
-
父组件调用子组件时使用
v-model:xxx
绑定参数 -
子组件通过
props: ['xxx']
接收参数 -
子组件通过
this.$emit('update:xxx', param)
修改xxx
const app = Vue.createApp({
data() {
return { count: 1}
},
template: `<div>
<counter v-model:app="count" />
</div>`
})
app.component('counter', {
props: ['app'],
methods: {
handleClick() {
this.$emit('update:app', this.app + 3)
}
},
template: `<div @click="handleClick">{{app}}</div>`
})
const vm = app.mount('#root')
6.3 使用 props:['modelModifiers'] 接收 v-model的修饰符
app.component('counter', {
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
},
},
methods: {
handleClick() {
let newVal = this.modelValue + 'b'
if(this.modelModifiers.uppercase) {
newVal = newVal.toUpperCase()
}
this.$emit('update:modelValue', newVal )
} ,
},
template: `
<div @click="handleClick">{{modelValue}}</div>
`
})
const vm = app.mount('#root')
7 使用插槽和具名插槽解决组件内容传递问题
7.1 slot 中使用的数据作用域的问题
-
父模板里调用的数据属性,使用的都是父模板里的数据
-
子模板里调用的数据属性,使用的都是子模板里的数据
const app = Vue.createApp({
data() {
return {
text: '提交'
}
},
template: `<div>
<myform>
<div>{{text}}</div>
</myform>
<myform>
<button>{{text}}</button>
</myform>
</div>`
})
app.component('myform', {
template: `
<div>
<input />
<span><slot /></span>
</div>
`
})
const vm = app.mount('#root')
7.2 slot 默认值
app.component('myform', {
template: `
<div>
<input />
<span><slot>default value</slot></span>
</div>
`
})
7.3 具名插槽
const app = Vue.createApp({
template: `<layout>
<template v-slot:header>
<div>header</div>
</template>
<template v-slot:footer>
<div>footer</div>
</template>
</layout>`
})
app.component('layout', {
template: `
<div>
<slot name="header" />
<div>contain</div>
<slot name="footer" />
</div>
`
})
const vm = app.mount('#root')
7.4 插槽简写
const app = Vue.createApp({
template: `<layout>
<template #header>
<div>header</div>
</template>
<template #footer>
<div>footer</div>
</template>
</layout>`
})
8 作用域插槽
const app = Vue.createApp({
// template: `<list v-slot="slotProps">
// <div>{{slotProps.item}}</div>
// </list>`
template: `<list v-slot="{item}">
<div>{{item}}</div>
</list>`
})
app.component('list', {
data() {
return {
list: [1, 2, 3]
}
},
template: `
<div>
<slot v-for="item in list" :item="item" />
</div>
`
})
const vm = app.mount('#root')
9 动态组件和异步组件
9.1 动态组件
- 根据数据变化,结合
component
标签 +:is
属性来随时动态切换组件的实现
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,主要用于保留组件状态或者避免重新渲染
const app = Vue.createApp({
data() {
return {
currentItem: 'input-item'
}
},
methods: {
handleClick() {
this.currentItem = this.currentItem === 'input-item' ? 'common-item' : 'input-item'
}
},
template: `
<keep-alive>
<component :is="currentItem" />
</keep-alive>
<button @click="handleClick">切换</button>
`
})
app.component('input-item', {
template: `<input />`
})
app.component('common-item', {
template: `<div>hello world</div>`
})
const vm = app.mount('#root')
9.2 异步组件
- 异步执行某些组件的逻辑
Vue.defineAsyncComponent()
+return Promise
const app = Vue.createApp({
template: `
<div>
<common-item />
<async-common-item />
</div>
`
})
app.component('common-item', {
template: `<div>hello world</div>`
})
app.component('async-common-item', Vue.defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
template: `<div> this is an async component</div>`
})
}, 2000)
})
}))
const vm = app.mount('#root')
10 基础语法知识点查缺补漏
10.1 v-once
- 元素标签只被渲染一次,即使相关的响应式属性发生变化
![](https://img2022.cnblogs.com/blog/1891336/202203/1891336-20220316114621246-1181547539.png)
const app = Vue.createApp({
data() {
return { count: 1 }
},
template: `
<div @click="count+=1" v-once>
{{count}}
</div>
`
})
const vm = app.mount('#root')
10.2 ref
1. 获取 dom 节点用的语法
const app = Vue.createApp({
data() {
return { count: 1 }
},
mounted() {
console.log(this.$refs.count.innerHTML="hello kitty");
},
template: `
<div>
<div ref="count">{{count}}</div>
</div>
`
})
const vm = app.mount('#root')
2. 获取 组件引用 的语法
const app = Vue.createApp({
data() {
return { count: 1 }
},
mounted() {
console.log(this.$refs.common.sayHello());
},
template: `
<div>
<common-item ref="common">
</div>
`
})
app.component('common-item', {
methods: {
sayHello() {
alert('hello')
}
},
template: `<div>hello world</div>`
})
const vm = app.mount('#root')
10.3 provide + inject
- 跨组件传值 -- 一次性传递(非响应式)
const app = Vue.createApp({
data() {
return { count: 1 }
},
// provide: {
// count: 1
// },
provide() {
return {
count: this.count
}
},
template: `
<div>
<child :count="count" />
</div>
`
})
app.component('child', {
template: `<child-child />`
})
app.component('child-child', {
inject: ['count'],
template: `<div>{{ count }}</div>`
})
const vm = app.mount('#root')