里氏替换原则(Liskov Substitution Principle,LSP)规定,子类对象必须能够替换父类对象,并且程序的行为保持不变。
在Vue 3中,这意味着我们在创建可替换的组件时,应该确保子组件能够完全替代父组件,而不会引发错误或不一致的行为。
示例:EmailInput 组件
1. 创建基本输入组件 BaseInput.vue
这个组件是一个基本的输入框组件,可以接受任何类型的输入。
<!-- BaseInput.vue -->
<template>
<div class="base-input">
<label :for="id">{{ label }}</label>
<input :id="id" :type="type" v-model="internalValue" />
</div>
</template>
<script>
export default {
name: 'BaseInput',
props: {
id: {
type: String,
required: true
},
label: {
type: String,
required: true
},
type: {
type: String,
default: 'text'
},
modelValue: {
type: [String, Number],
default: ''
}
},
computed: {
internalValue: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value);
}
}
}
};
</script>
<style scoped>
.base-input {
margin-bottom: 10px;
}
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
</style>
2. 创建电子邮件输入组件 EmailInput.vue
这个组件扩展了 BaseInput
,添加了电子邮件格式验证和错误提示功能。
<!-- EmailInput.vue -->
<template>
<div class="email-input">
<BaseInput
:id="id"
:label="label"
type="email"
v-model="emailValue"
@update:modelValue="validateEmail"
/>
<p v-if="error" class="error">{{ error }}</p>
</div>
</template>
<script>
import BaseInput from './BaseInput.vue';
export default {
name: 'EmailInput',
components: {
BaseInput
},
props: {
id: {
type: String,
required: true
},
label: {
type: String,
required: true
},
modelValue: {
type: String,
default: ''
}
},
data() {
return {
emailValue: this.modelValue,
error: ''
};
},
watch: {
emailValue(newValue) {
this.$emit('update:modelValue', newValue);
this.validateEmail();
}
},
methods: {
validateEmail() {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(this.emailValue)) {
this.error = '无效的邮箱地址';
} else {
this.error = '';
}
}
}
};
</script>
<style scoped>
.email-input {
margin-bottom: 10px;
}
.error {
color: red;
font-size: 0.875em;
}
</style>
3. 使用组件
在父组件中无缝替换 BaseInput
和 EmailInput
。
<!-- App.vue -->
<template>
<div id="app">
<!-- 使用BaseInput -->
<BaseInput id="username" label="Username" v-model="username" placeholder="请输入用户名" />
<!-- 使用EmailInput替换BaseInput -->
<br>
<EmailInput id="email" label="Email" v-model="email" placeholder="邮箱地址" />
</div>
</template>
<script>
import BaseInput from './components/BaseInput.vue';
import EmailInput from './components/EmailInput.vue';
export default {
name: 'App',
components: {
BaseInput,
EmailInput
},
data() {
return {
username: '',
email: ''
};
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
总结
上面的示例中,首先创建了一个通用输入组件 BaseInput
,它可以接受任何类型的输入。
然后,创建了一个电子邮件输入组件 EmailInput
,通过扩展 BaseInput
来专门处理电子邮件输入,并增加了电子邮件格式验证和错误提示功能。
EmailInput
继承了 BaseInput
的所有属性和方法,同时增加了对电子邮件格式的验证。
如果输入的电子邮件格式不正确,会显示错误提示信息。
这样,我们可以在需要电子邮件输入的地方无缝替换 BaseInput
为 EmailInput
,而不需要修改其他代码。
通过这种方式,我们遵循了里氏替换原则,确保子组件能够替代父组件而不改变程序的行为。