一、Vue 基础之组件、组件传值、单项数据流、Non-props、父子组件通信、组件双向绑定、插槽、作用域插槽、动态组件、异步组件、provide 和 inject
- 组件的定义,组件具备复用性。全局组件,只要定义了,处处可以使用,性能不高,但是使用起来简单,名字建议,小写字母单词,中间用横线间隔。局部组件,定义了,要注册之后才能使用,性能比较高,使用起来有些麻烦,建议大写字母开头,驼峰命名。局部组件使用时,要做一个名字和组件间的映射对象,你不写映射,
Vue
底层也会自动尝试帮你做映射,如下所示:
const Counter = {
data() {
return (
count: 1
)
},
template: `<div @click="count += 1">{{count}}</div>`
}
const HelloWorld = {
template: `<div>hello world</div>`
}
const app = Vue.createApp({
components: {
Counter, HelloWorld
},
template: `
<div>
<hello-world />
<counter />
</div>
`
});
const vm = app.mount('#root');
vue
父子组件传值,父组件通过调用子组件的标签,通过标签上的属性向子组件传递一些值,子组件通过 props
接收对应父组件传递来的数据,然后在 props
接收好后就可以使用父组件的数据。静态传参不通过 v-bind
,不加冒号,只能传递字符串。动态传参通过 v-bind
带入,加冒号,可以传递 String、Boolean、Array、Object、Function、Symbol
等等。动态传参比静态传参更加灵活,数据类型更多。在 props
传递的属性中,type
是传递的类型,required
是必填,default
是默认值,validator
是校验,如下所示:
const app = Vue.createApp({
data() {
return { num: 1234 }
},
template: `
<div><test :content="num" /></div>
`
});
app.component('test', {
props: {
content: {
type: Number,
validator: function(value) {
return value < 1000;
},
default: function() {
return 456;
},
required: true
}
},
template: `<div>{{content}}</div>`
});
const vm = app.mount('#root');
- 单向数据流,子组件可以使用父组件传递过来的数据,但是绝对不能修改传递过来的数据,如下所示:
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');
Non-props
属性,父组件向子组件传值,但是子组件不接收的情况,会把父组件传递过来的内容放到子组件最外层的 DOM
标签下,变成子组件最外层 DOM
标签的属性。Non-props
属性的应用场景,在组件的样式修改情况下。如果不想继承 non-props
属性,需要添加 inheritAttrs
属性为 false
,如下所示:
const app = Vue.createApp({
template: `<div><counter msg="hello" msg1="hello1" /></div>`
});
app.component('counter', {
mounted() {
console.log(this.$attrs.msg);
},
template: `
<div :msg="$attrs.msg">Counter</div>
<div v-bind="$attrs">Counter</div>
<div :msg1="$attrs.msg1"">Counter</div>
`
});
const vm = app.mount('#root');
- 子组件给父组件传值,子组件通过
this.$emit
派发一个事件,并且也可以传递参数,父组件通过 v-on
监听对应的事件,并且绑定对应的事件处理。如果父子组件存在双向数据绑定的关系,可以可以使用 v-model
。父组件使用 v-model
传递属性,子组件的 props
通过 modelValue
接收,通过 this.$emit
以 update:modelValue
的形式派发事件,传递相应的参数。如果不想以 modelValue
的格式,那么传递的时候以加冒号别名的形式,如下所示:
const app = Vue.createApp({
data() {
return { count: 1 }
},
template: `<counter v-model="count" />`
});
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');
- 父子组件存在双向数据绑定的关系,父组件通过
v-model
传递修饰符,在 v-model
后面加 .和对应的修饰符名字,修饰符通过 props
传递给子组件,子组件通过 props
中的 modelModifiers
接收,接收的时候可以设置默认值为空对象,在做后面的事件触发时可以根据修饰符做对应的处理,如下所示:
const app = Vue.createApp({
data() {
return {
count: 'a',
}
},
template: `<counter v-model.uppercase="count" />`
});
app.component('counter', {
props: {
'modelValue': String,
'modelModifiers': {
default: () => ({})
}
},
methods: {
handleClick() {
let newValue = this.modelValue + 'b';
if (this.modelModifiers.uppercase) {
newValue = newValue.toUppercase();
}
this.$emit('update:modelValue', newValue);
},
},
template: `<div @click="handleClick">{{modelValue}}</div>`
});
const vm = app.mount('#root');
slot
插槽,如果父组件想往子组件中传递 DOM
节点或元素标签的时候,没必要通过属性方式去传,直接把元素写在标签中间,子组件通过 slot
调用传递过来的内容。slot
中使用的数据,作用域的问题,父模版里调用的数据属性,使用的都是父模版里的数据。子模版里调用的数据属性,使用的都是子模版里的数据。具名插槽,如下所示:
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"></slot>
<div>content</div>
<slot name="footer"></slot>
</div>
`
});
const vm = app.mount('#root');
- 作用域插槽,如下所示:
const app = Vue.createApp({
template: `
<list v-slot="slotProps">
<div>{{slotProps.item}}</div>
</list>
`
});
app.components('list', {
data() { return { list: [1,2,3]}},
template: `
<div><slot v-for="item in list" :item="item" /></div>
`
});
const vm = app.mount('#root');
- 动态组件,根据数据的变化,结合
component
这个标签,来随时动态切换组件的实现。keep-alive
,当动态组件第一次渲染的时候,会把里面输入的状态、变更的情况都记录下来,当再次用这个组件的时候,会从缓存中将之前的数据拿出来填充上,具有缓存的特性,所以动态组件有时候会和 keep-alive
一起使用。异步组件,是异步执行某些组件的逻辑,这叫做异步组件,如下所示:
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>`
})
}, 4000)
})
}));
const vm = app.mount('#root');
v-once
让某个元素标签只渲染一次,ref
实际上是获取 DOM
节点或组件引用的一个语法。如果涉及多层组件传值,可以使用 provide / inject
,provide
提供数据,inject
跨越多层组件注入使用,如下所示:
const app = Vue.createApp({
data() {
return {
count: 1
}
},
provide() {
return {
count: this.count
}
},
template: `
<div>
<child :count="count" />
<button @click="count += 1">Add</button>
</div>
`
});
app.component('child', {
template: `<child-child />`
});
app.component('child-child', {
inject: ['count'],
template: `<div>{{count}}</div>`
});
const vm = app.mount('#root');