使用组件属性的优势
- 可重用性:使得组件变得可被定制,进而能被重用
- 数据一致性:保证single source of true,只有一个唯一的数据源。
使用属性
在组件中定义属性
<template>
<div>{{ prop1 }}</div>
</template>
<script>
export default {
props: ['prop1', 'prop2']
}
</script>
传递属性给组件
<MyComponent prop1="prop1 value"/>
数据绑定data binding
也可以绑定当前组件的属性进而动态获得值:
<template>
<MyComponent :prop1="p1"/>
</template>
<script>
// ...
data() {
p1: 'test value'
}
</script>
如果想要传递非字符串类型的属性值时,可以使用数据绑定(data binding):
更多的时候是在script中定义/获取数据,然后通过数据绑定将这些数据传递给子组件:
<template>
<MyComponent :header="header" />
</template>
<script>
import MyComponent from './components/MyComponent.vue'
export default {
components: {MyComponent},
data() {
return {
header: 'This is the header'
}
}
}
</script>
通过数据绑定实现不同主题
在调用组件时:
<MyComponent theme="dark"/>
在组件中:
<template>
<div class="message_box" :class="theme === 'dark' ? 'dark' : 'light'">
</template>
<script>
export default {
props: ["theme"],
};
</script>
<style>
.dark {
background: black;
}
.light {
background: lightgrey;
}
</style>
使用对象类型属性
像前面例子一样,我们可以使用数组来定义组件的props。但同时也可以使用对象来定义这些props,这样就能在定义prop的同时声明其属性:
export default {
name: 'Student',
props: {
name: String,
score: Number,
registered: Boolean,
}
}
这样在调用这个组件的时候,Vue就会检查传递过来的属性类型,因此这时需要使用 :score 来声明这个属性不是字符串类型的。
<Student name="Paul" :score="90" :registered="true" />
验证属性
必填属性及默认值
props: {
name: {
type: String,
required: true,
default: 'YOUR NAME'
}
}
非prop属性
针对非prop属性,Vue的默认行为
Vue的一个默认行为就是,对于任何非prop的属性,会被自动添加到该组件的唯一根元素上。如果根元素不唯一,则不会添加该属性到任何元素上。
比如:在Student组件中没有定义id属性,但如果传递了id属性的话:
<Student id="9901"/>
则选然后的Studenet组件中,根元素上会被添加一个id属性:
<div data-v-xxxxxxxx id="9901">
自定义非prop属性的绑定行为
在组件中,可以通过$atts来自己指定想要把传递过来的非prop属性绑定到哪个元素上:
<template>
<div>title</div>
<div v-bind="$atts">{{ name }}</div>
</template>
还可以禁止Vue的默认行为,这样就不会自动将非prop属性绑定到根元素上。
export default {
// ...
inheritAttrs: false
}
Provide / Inject API
在一个多级组件中,可以通过props来从上到下传递信息。但这就涉及到很多不必要的属性传递。通过Provide/Inject API,就可以实现在Vue组件中传递属性,而不需在组件层级结构中显式传递。这就有些点像React中的Context API。
其涉及到的操作包括:
- 在上级组件中,比如App中提供 (Provide) 相应值
- 在下级组件中注入 (Inject) 对应的值
In App.vue:
export default {
name: 'Student',
data() {
//
},
provide: {
authenticated: true
}
}
In sub component, e.g. Header.vue:
export default {
inject: ['authenticated']
}
但目前存在一个小问题,在App中无法直接访问authenticated的值。如果要解决的话:
export default {
name: 'Student',
data() {
return {
authenticated: true
}
},
provide() {
return {
authenticated: this.authenticated,
}
}
}
通过自定义事件从子组件传递值到父组件
我们已经知道可以通过props将值从父组件传递到子组件。那么如何从子组件传递信息到父组件呢?在Vue中可以通过自定义事件来实现。
自定义事件
比如在子组件Dialog中:
<template>
<button @click="$emit('close')">关闭对话框</button>
</template>
<script>
export default {
name: 'Dialog',
emits: ['close']
}
</script>
在父组件中:
<Dialog v-show="showDialog" @close="showDialog = false" />
在自定义事件中传递参数
子组件中:
<button @click="$emit('close', 'lcoding')">关闭对话框</button>
父组件中:
<Dialog v-show="showDialog" @close="closeDialog" />
<script>
// ...
methods: {
closeDialog(tag) {
this.showDialog = false;
console.log(tag);
}
}
</script>
定义自定义事件参数的验证规则
子组件中:
<template>
<input type="text" v-model="tag" />
<button @click="$emit('close', tag)">关闭对话框</button>
</template>
<script>
emits: {
close: (tag) => {
if (tag) {
return true;
} else {
return false;
}
}
},
data() {
return {
tag: '';
}
}
</script>
注意,上面的代码中,无论验证是否通过,都会发出close事件。关于这点,可以进行改进。比如:单击关闭按钮时,进行验证,如果验证通过,发出close事件,否则什么都不做。
methods: {
closeDialog() {
if (this.tag) {
this.$emit("close");
}
},
},
如何通过v-model将属性和自定义组件关联
问题描述
在默认情况下,如果通过v-model绑定了一个一般属性,则会实现UI和属性的双向绑定。但如果使用v-model绑定一个属性到自定义组件时,Vue不会实现这种双向绑定。比如:
有一个简单的Input组件:
<template>
<input type="text" />
</template>
在父组件中调用这个组件:
<Input v-model="name">
...
<script>
// ...
data() {
return name: '';
}
</script>
当输入框中的值发生变化时,App的name属性并不会随之发生变化。
解决方法
在子组件Input中:
<template>
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script>
export default {
name: "MyInput",
props: {
modelValue: String,
},
};
</script>
示例-模式对话框
创建ModalBox.vue
添加文件components/ModalBox.vue:
<template>
<div class="background">
<div class="modal">
<p>test content</p>
</div>
</div>
</template>
<style>
.background {
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.8);
width: 100%;
height: 100%;
}
.modal {
margin: 150px auto;
padding: 100px;
width: 600px;
background: white;
border-radius: 5px;
}
</style>
在父组件中调用ModalBox
在父组件App.vue中:
<template>
<ModalBox />
</template>
<script>
import ModalBox from "./components/ModalBox.vue";
export default {
name: "App",
components: {
ModalBox,
},
};
</script>