项目代码地址:https://github.com/chenfenbgin/vue-component/tree/master
两种方式: 数组 和 对象类型
注意:带有默认值的对象,对象或数组默认值必须从一个工厂函数中获取
,因为如果有多次引用该组件,防止他指向的对象不变。(引用赋值的关系),这里需要不同的对象。
父组件:
<template>
<div id="app">
<!-- 这里可以传递多个 -->
<!-- 这里传的非props的属性,会被添加到子组件的根节点上(在有根组件的前提下继承) -->
<show-message
class="chen"
id="idchen"
title="哈哈哈"
content="我是哈哈哈"
></show-message>
<show-message title="嘻嘻嘻" content="我是嘻嘻嘻"></show-message>
<show-message :title="title" :content="content"></show-message>
<!-- 可以有两种方式:
一种就是messsage.title;
另一种就是v-bind='对象'
-->
<!-- 方式一 -->
<show-message
:title="message.title"
:content="message.content"
></show-message>
<!-- 方式二, 传递的是一个对象 -->
<show-message v-bind="message"></show-message>
</div>
</template>
<script type="text/javascript">
import ShowMessage from "./ShowMessage.vue";
export default {
data() {
return {
title: "嘻嘻嘻",
content: "嘻嘻嘻",
message: {
title: "嘿嘿嘿",
content: "我是嘿嘿嘿",
},
};
},
components: {
ShowMessage,
},
};
</script>
<style scoped></style>
子组件:
<template>
<!-- 父组件传递过来的非props属性 -->
<!-- <div class="chen"> -->
<div>
<!-- 父组件传递过来的title 和 content -->
<!-- <h2>{{ title }}</h2> -->
<!-- 从非props取出class只给h2标签 -->
<!-- <h2 :class="$attrs.class">{{ title }}</h2> -->
<!-- 如果这里也有id,class属性的话,可以直接使用v-bind -->
<h2 :="$attrs">{{ title }}</h2>
<p>{{ content }}</p>
</div>
</template>
<script type="text/javascript">
export default {
name: "ShowMessage",
inheritAttrs: false, //不希望组件的根元素继承attribute
/**
* 父组件之间通信
* Props: 是你可以在组件上注册一些自定义的 attribute
* 父组件给这些 attribute赋值,子组件通过attribute 的名称获取对应的值
*
* 用法:
* 方式一: 字符串数组
* 方式二: 对象数组
*/
// 父传子方式一: 数组
// props: ["title", "content"],
// 父传子方式二: 对象
props: {
title: String,
content: {
type: String, //类型: String Number Boolean Array Object Date Function Symbol
required: true, //必传
default: "2134", // 默认值
},
message: [String, Number], //多个可能的类型,
messageObject: {
type: Object,
// 对象或者数组必须从一个工厂函数获取, 因为使用多个组件,其中一个把值改了,那就不是我们要的效果了
default() {
return {
messageObject: "hello messageObject",
};
},
},
//自定义验证函数
validatorFunction: {
validator(value) {
return ["success", "warning", "danger"].includes(value);
},
},
// 具有默认值的函数
propG: {
type: Function,
default() {
return "Default function";
},
},
},
data() {
return {};
},
components: {},
};
</script>
<style scoped></style>
父组件:
<template>
<div id="app">
<h2>当前计数: {{ counter }}</h2>
<!-- emtis第三步:监听事件,使用的是v-on对事件监听,@add='要操作的函数' -->
<counter-operation
@add="increment"
@sub="decrement"
@addN="incrementN"
></counter-operation>
</div>
</template>
<script type="text/javascript">
import CounterOperation from "./CounterOperatin.vue";
export default {
name: "app",
data() {
return {
counter: 100,
};
},
components: {
CounterOperation,
},
methods: {
increment() {
this.counter++;
},
decrement() {
this.counter--;
},
incrementN(num, name, age) {
console.log(name, age);
this.counter += num;
},
},
};
</script>
<style scoped></style>
子组件:
<template>
<div id="app">
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<input type="text" v-model.number="num" />
<button @click="incrementN">+n</button>
</div>
</template>
<script type="text/javascript">
export default {
name: "CounterOperation",
// emits第一种写法:数组写法, vue3里面的写法: 注册要触发的事件,子组件需要触发的事件
// 第1步: 注册事件, vue3中,需要定义会触发那些事件,emits: []
// emits: ["add", "sub", "addN"],
// emits第二种写法: 对象写法, 对象写法的目的是为了进行参数的验证
emits: {
add: null, //没有参数,空就是不需要验证
sub: null,
addN: (num, name, age) => {
//一个参数写为payload
if (num > 10) {
// 大于10才给它传过去,其实都会传过去,只是报一个警告
return true;
}
console.log(num, name, age);
return false;
},
},
data() {
return {
num: 3,
};
},
components: {},
methods: {
increment() {
console.log("+1");
// 第2步: 触发事件: this.$emit("")
this.$emit("add");
},
decrement() {
console.log("-1");
this.$emit("sub");
},
incrementN() {
this.$emit("addN", this.num, "chen", 24);
},
},
};
</script>
<style scoped></style>
父组件:
<template>
<div id="app">
<tab-control :titles="titles" @titleClick="titleClick"></tab-control>
<h2>{{ content[currentIndex] }}</h2>
</div>
</template>
<script type="text/javascript">
import TabControl from "./TabControl.vue";
export default {
name: "app",
data() {
return {
titles: ["衣服", "鞋子", "裤子"],
content: ["衣服页面", "鞋子页面", "裤子页面"],
currentIndex: 0,
};
},
components: {
TabControl,
},
methods: {
titleClick(index) {
console.log(index);
this.currentIndex = index;
},
},
};
</script>
<style scoped>
</style>
子组件:
<template>
<div class="tab-control">
<div
:class="{ active: currentIndex === index }"
class="tab-control-item"
v-for="(item, index) of titles"
:key="index"
@click="itemClick(index)"
>
<span> {{ item }} </span>
</div>
</div>
</template>
<script type="text/javascript">
export default {
name: "TabControl",
emits: ["titleClick"],
props: {
titles: {
type: Array,
default: () => {
return [];
},
},
},
data() {
return {
currentIndex: 0,
};
},
components: {},
methods: {
itemClick(index) {
this.currentIndex = index;
this.$emit("titleClick", index);
},
},
};
</script>
<style scoped>
.tab-control {
display: flex;
}
.tab-control-item {
/* 3个占比都是1份 */
flex: 1;
text-align: center;
}
.tab-control-item.active {
color: red;
}
.tab-control-item.active span {
border-bottom: 5px solid red;
padding: 5px 5px;
}
</style>
注:Vuex也是可以使用的(复杂数据使用)。provide和inject主要用在子孙组件
App.vue:
<template>
<div>
<home></home>
<button @click="addName">+name</button>
</div>
</template>
<script type="text/javascript">
import Home from "./Home.vue";
import { computed } from "vue";
// 对于有模块化的组件来说,这里的this是undefined
// console.log(this); // undefined
export default {
name: "App",
/**
* 父组件: 有一个provide选项来提供数据
* 子组件: 有一个inject选项来开始使用这些数据
*/
// 对象的提供者
// provide: {
// name: 'chen',
// age: 23,
// },
// 我们把provide改成函数写法,这里this,在源码中会被绑定到组件实例上
provide() {
console.log(this); //组件实例
return {
name: "chen",
age: 23,
// 如果有引用data里面的数据,需要将provide写成函数形式返回
// length: this.names.length,
// 这里this,箭头函数时不绑定的。computed返回的是一个ref对象,需要使用.value
length: computed(() => this.names.length), //改为响应式(如果names改变了,就可以这么使用)
};
},
data() {
return {
names: ["abc", "dfb", "dfdr"],
};
},
components: {
Home,
},
methods: {
addName() {
this.names.push("cen"); // 这里provide里面的name是不会改变的,需要将其改为响应式的
console.log(this.names);
},
},
};
</script>
<style scoped></style>
Home.vue :
<template>
<div>
<home-content></home-content>
</div>
</template>
<script type="text/javascript">
import HomeContent from "./HomeContent.vue";
export default {
name: "Home",
data() {
return {};
},
components: {
HomeContent,
},
};
</script>
<style scoped></style>
HomeContent.vue:
<template>
<div>HomeContent: {{ name }}-{{ age }} - {{ length.value }}</div>
</template>
<script type="text/javascript">
export default {
name: "HomeContent",
// 将App.vue中provide的属性进行注入
inject: ["name", "age", "length"],
data() {
return {};
},
components: {},
};
</script>
<style scoped></style>
注:我们希望父组件中访问子组件的data,需要使用作用域插槽。
如果是简单的东西,就没有必要使用路由了呢, 应该使用动态组件。
注:一般配合router一起使用。
注:一般使用异步组件也是配合路由使用异步
开发中异步组件一般和suspens一起使用。
异步组件的使用是为了做代码分包。