文章目录
1.注册组件的基本步骤
1.1 原始的创建方法
组件的使用分为个步骤:
- 创建组件构造器
- 注册组件
- 使用组件
下面按上面步骤实现简易组件:
<body>
<div class="container">
<!-- 使用组件 my-comp -->
<my-comp></my-comp>
</div>
<script src="./vue.js"></script>
<script>
// 1.创建组件构造器
const comp = Vue.extend({
template: `<div>
<p>
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
</p>
<img
src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1689507601,2788277149&fm=26&gp=0.jpg"
/>
</div>`,
});
// 2.注册组件
Vue.component("my-comp", comp);
var app = new Vue({
el: ".container",
data: {},
methods: {},
});
</script>
</body>
组件能正常使用:
讲解:
- Vue.extend()中参数是一个对象,里面有很多属性。这里只使用了template属性,定义了组件的模板,这就实现了创建组件构造器。
- Vue.component(参数1,参数2):参数1定义的是使用组件的标签名,参数2传入的是第一步创建的组件构造器。
- 直接使用第二部的参数1使用组件:例如< my-comp ></ my-comp >。
注意:组件一定要在vue实例中使用才有效,上面的container容器就是一个vue实例。
1.2语法糖创建方法
有了语法糖,我们这里就不需要使用组件构造器函数了。
<body>
<div class="container1">
<!-- 使用组件 -->
<my-comp></my-comp>
</div>
<script src="./vue.js"></script>
<script>
Vue.component("my-comp", {
template: `<div>
<p>
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
</p>
<img
src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
/>
</div>`,
});
// vue实例1
var app1 = new Vue({
el: ".container1",
});
</script>
</body>
注意:直接使用Vue.component()来实现了组件创建,在Vue.component()内部调用了Vue.extend()。这是vue帮我们实现了。
在实际开发中,我们不是在Vue.component()的第二个参数来装载模板的,一个组件的元素并不像我们上面的那么简单,如果都写在参数中,就显得js代码特别臃肿。
开发中这样写:
<body>
<template id="component">
<div>
<p>
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
</p>
<img
src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
/>
</div>
</template>
<div class="container1">
<!-- 使用组件 -->
<my-comp></my-comp>
</div>
<script src="./vue.js"></script>
<script>
Vue.component("my-comp", {
template: component
});
// vue实例1
var app1 = new Vue({
el: ".container1"
});
</script>
</body>
单独利用template标签来装载模板内容,然后设置id,在Vue.component()中使用这个id来创建组件。
以上就实现了简易的组件构建与使用。
2.全局组件与局部组件
全局组件与局部组件如何区分?
1.当组件在vue实例中注册时,则是局部组件,只能在该vue实例中使用。
2.当组件不是在实例中注册时,则是全局组件,在任意的vue实例都可以使用。
2.1 全局组件
我们在上面定义的my-comp组件就是全局组件,他可以在任意vue实例使用:
<body>
<div class="container1">
<!-- 使用组件 -->
<my-comp></my-comp>
</div>
<div class="container2">
<!-- 使用组件 -->
<my-comp></my-comp>
</div>
<script src="./vue.js"></script>
<script>
// 1.创建组件构造器
const comp = Vue.extend({
template: `<div>
<p>
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
</p>
<img
src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
/>
</div>`,
});
// 2.注册组件
Vue.component("my-comp", comp);
// vue实例1
var app1 = new Vue({
el: ".container1",
data: {},
methods: {},
});
// vue实例2
var app2 = new Vue({
el: ".container2",
});
</script>
</body>
上面定义了app1、app2两个vue实例,都使用了my-comp组件:
2.2 局部组件
在vue实例中注册组件:
<body>
<div class="container1">
<!-- 使用组件 -->
<my-comp></my-comp>
</div>
<div class="container2">
<!-- 使用组件 -->
<my-comp></my-comp>
</div>
<script src="./vue.js"></script>
<script>
// 1.创建组件构造器
const comp = Vue.extend({
template: `<div>
<p>
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
</p>
<img
src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
/>
</div>`,
});
// vue实例1
var app1 = new Vue({
el: ".container1",
// 注册组件
components: {
"my-comp": comp,
},
});
// vue实例2
var app2 = new Vue({
el: ".container2",
});
</script>
</body>
上面我只在app1中注册了组件:
很明显,app2实例没有注册组件,所以无法使用该组件,这就是局部组件。
2.3语法糖局部组件(最简写法)
在vue实例中注册组件使用的是components属性,key是组件标签名,value是组件构造器。
<body>
<template id="component">
<div>
<p>哈哈哈</p>
</div>
</template>
<div class="container1">
<!-- 使用组件 -->
<my-component></my-component>
</div>
<script src="./vue.js"></script>
<script>
// vue实例1
var app1 = new Vue({
el: ".container1",
data: {},
components: {
// 在vue实例注册组件,形成父子关系,局部组件
"my-component": {
template: "#component",
},
},
});
</script>
</body>
3. 父子组件
简单来说:父子组件是嵌套关系,子组件被包含在父组件中。子组件在父组件中“注册”。
<body>
<template id="father">
<div>
<p>
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
</p>
<img
src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg"
/>
<my-son></my-son>
</div>
</template>
<template id="son">
<div>
<p>
爱你哟
</p>
<img
src="https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2346251443,2005527457&fm=26&gp=0.jpg"
/></div
></template>
<div class="container1">
<!-- 使用组件 -->
<my-father></my-father>
</div>
<script src="./vue.js"></script>
<script>
// 1.子组件构造器
const son = Vue.extend({
template: "#son",
});
// 2.父组件构造器
const father = Vue.extend({
template: "#father",
// 注意:在父组件中注册子组件
components: {
"my-son": son,
},
});
// 3.父组件注册
Vue.component("my-father", father);
// vue实例1
var app1 = new Vue({
el: ".container1",
});
</script>
</body>
注意:
- 子组件的“注册”是发生在父组件的构造器中利用components属性注册的。
- 在父组件的template模板中引用子组件的标签。
- 子组件是局部组件,只能在父组件中被引用。
特别注意:vue实例也是一个组件,而且是根组件,因此father组件也是根组件的子组件。
4.组件中的动态数据
什么是动态数据?
上面我们的数据是直接写死的,我们都知道标签元素中要想内容是动态的就使用胡子语法,属性想要是动态的就是用v-bind,这里我们重点关注元素标签中的数据是在何处定义以及如何定义。
<body>
<template id="component">
<div>
<p>{{content}}</p>
<img v-bind:src="imgSrc" />
</div>
</template>
<div class="container1">
<!-- 使用组件 -->
<my-component></my-component>
</div>
<script src="./vue.js"></script>
<script>
Vue.component("my-component", {
template: "#component",
data: function () {
return {
content:
"这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
imgSrc:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
};
},
});
// vue实例1
var app1 = new Vue({
el: ".container1",
});
</script>
</body>
正常显示:
注意这段代码:
Vue.component("my-component", {
template: "#component",
data: function () {
return {
content:
"这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
imgSrc:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
};
},
});
看到我们的data属性了吗?
特别注意:
- 我们组件中要使用的数据不是在vue实例的data属性中定义的,而是在Vue.component()中利用data属性定义的。
- 组件中data属性是一个函数,且返回一个对象,对象中包含组件想要的数据,而vue实例的data属性直接就是一个对象。
a.为什么组件的data属性必须是一个函数,再通过函数返回一个对象?
倘若我们data属性就是一个对象,会有“数据共享”的问题。
一个组件的封装可以使我们在很多地方复用组件,那么我们复用组件时,组件显示的内容往往是不一样的,假如data属性是一个对象,那么我们在复用组件时,都是显示同一组数据,从而无法实现显示不同内容的效果。
为什么会数据共享呢?
这得根据我们的内存来谈,对象是“栈堆存储”
即存储对象时,存储了变量名和一个地址,当我们调用变量时,是通过地址去找到相应的数据,同一个对象只有一个地址。因此,假如data属性是一个对象,则只要调用该对象,都是指向那个地址的同一数据,所以每个组件的data都是相同的。不符合我们实际。
举例说明:(非函数返回的对象)
const obj = {
name: "大白",
age: 20,
};
let a = obj;
let b = obj;
let c = obj;
console.log("修改前:", a.name, b.name, c.name);
a.name = "小白";
console.log("修改后:", a.name, b.name, c.name);
组件中的data属性如果不是通过函数返回对象,就会像上面的obj对象一样,无论复用多少组件,数据都一样,而且修改一个实例,其它实例的数据也同样被改变。这就是为什么组件中的data属性是一个函数返回的对象的原因。
b.为什么使用函数返回一个对象就不会数据共享呢?
我们应该知道函数是有自己独立的作用域的,每个函数实例都有一个独立的作用域。
看实例:
function test() {
return {
name: "大白",
age: 22,
};
}
let a = test();
let b = test();
let c = test();
console.log("修改前:", a.name, b.name, c.name);
a.name = "小白";
console.log("修改后:", a.name, b.name, c.name);
a、b、c分别是test函数返回的对象,它们都有自己的独立作用域,所以在修改对象a时,它是不会影响b、c对象的属性值的。
5. 父子组件传值
在实际开发中,往往一些数据需要从上层传递到下层:
- 比如在一个页面中,我们从服务器中请求到很多数据
- 其中一部分数据,并非都是整个页面的大组件来显示的,而是需要里面的子组件进行显示
- 这时,我们并不会在子组件再发送一次网络请求,而是让父组件将数据传递给子组件。
如何进行父子组件间的通信呢?
- 通过props向子组件传递数据
- 通过事件向父组件发送消息
5.1 父传子
注意:vue实例是一个根组件,这里使用根组件与一个自定义组件形成父子组件。
props基本用法:
- 在组件中,使用选项props来声明需要从父级接收到的数据。
- props的值有两种方式:
1. 字符串数组,数组中的字符串就是传递时在子组件使用数据的名称。
2. 对象,对象可以设置传递时的类型,也可以设置默认值等。
a.字符串数组格式:
<body>
<template id="component">
<div>
<p>{{content1}}</p>
<img v-bind:src="img1" />
</div>
</template>
<div class="container1">
<!-- 使用组件 -->
<my-component
v-bind:content1="content"
v-bind:img1="imgSrc"
></my-component>
</div>
<script src="./vue.js"></script>
<script>
// vue实例1
var app1 = new Vue({
el: ".container1",
data: {
content:
"这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
imgSrc:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
},
components: {
// 在vue实例注册组件,形成父子关系,局部组件
"my-component": {
template: "#component",
props: ["content1", "img1"],
},
},
});
</script>
</body>
使用思路:
- 在父组件的data属性中定义传给子组件的数据
data: {
content:
"这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
imgSrc:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
},
- 在注册子组件 中使用props属性使用数组 来定义在子组件使用数据的变量名
components: {
// 在vue实例注册组件,形成父子关系,局部组件
"my-component": {
template: "#component",
props: ["content1", "img1"],
},
- 在使用子组件标签中,使用动态绑定属性格式来传递数据,属性名是props属性数组中的变量名,值是父组件中的变量名。
<my-component
v-bind:content1="content"
v-bind:img1="imgSrc"
></my-component>
- 在组件模板中使用动态绑定属性使用关于属性类型的数据,而标签内容则使用胡子语法
<template id="component">
<div>
<p>{{content1}}</p>
<img v-bind:src="img1" />
</div>
</template>
b.对象格式:
看代码:
<body>
<template id="component">
<div>
<p>{{content1}}</p>
<img v-bind:src="img1" />
</div>
</template>
<div class="container1">
<!-- 使用组件 -->
<my-component
v-bind:content1="content"
v-bind:img1="imgSrc"
></my-component>
</div>
<script src="./vue.js"></script>
<script>
// vue实例1
var app1 = new Vue({
el: ".container1",
data: {
content:
"这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
imgSrc:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
},
components: {
// 在vue实例注册组件,形成父子关系,局部组件
"my-component": {
template: "#component",
props: {
content1: {
type: String,
default: "我是默认值",
},
img1: {
type: String,
required: true,//必须,即务必给组件标签传值,否则报错
},
},
},
},
});
</script>
</body>
对象形式可设置的属性更多:
- default:设置默认值
- required:该数据是否是必须传给子组件
实例:
<body>
<template id="component">
<div>
<p>{{content1}}</p>
<img v-bind:src="img1" />
</div>
</template>
<div class="container1">
<!-- 使用组件 -->
<my-component v-bind:content1="content"></my-component>
</div>
<script src="./vue.js"></script>
<script>
// vue实例1
var app1 = new Vue({
el: ".container1",
data: {
content:
"这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。",
imgSrc:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=852066810,2008608422&fm=26&gp=0.jpg",
},
components: {
// 在vue实例注册组件,形成父子关系,局部组件
"my-component": {
template: "#component",
props: {
content1: {
type: String,
default: "我是默认值",
},
img1: {
type: String,
default:
"https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3743194253,688329487&fm=26&gp=0.jpg",
required: true, //
},
},
},
},
});
</script>
</body>
认真看代码:
<my-component v-bind:content1="content"></my-component>
我们并没有给子组件传递img的属性值,它使用的是默认属性值,所以显示的是小黄熊,而且也在浏览器中报错。
5.2 子传父($emit)
实例:我们从子组件传给父组件,用户鼠标所点击图片对应的id号
<body>
<template id="component">
<div>
<img
v-for="item in imgData"
v-bind:src="item.src"
v-on:click="returnData(item.id)"
/>
</div>
</template>
<div class="container1">
<!-- 使用组件 -->
<my-component v-on:son="getData"></my-component>
</div>
<script src="./vue.js"></script>
<script>
// vue实例1
var app1 = new Vue({
el: ".container1",
methods: {
getData(id) {
console.log(`点击了id==${id}的图片`);
},
},
components: {
// 在vue实例注册组件,形成父子关系,局部组件
"my-component": {
template: "#component",
data: function () {
return {
imgData: [
{
id: 1,
src:
"https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=121777500,749621780&fm=26&gp=0.jpg",
},
{
id: 2,
src:
"https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=272736508,2842806735&fm=26&gp=0.jpg",
},
{
id: 3,
src:
"https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2064238529,2898748334&fm=26&gp=0.jpg",
},
],
};
},
methods: {
returnData(id) {
//发射事件
this.$emit("son", id);
},
},
},
},
});
</script>
</body>
在上面动图中,我分别点击了第三、第二、第一的图片,查看右边的检查,打印出id,已经正常获得子组件传过来的id。
这个例子中我直接在子组件定义数据,而不是父组件传给子组件数据:
data: function () {
return {
imgData: [
{
id: 1,
src:
"https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=121777500,749621780&fm=26&gp=0.jpg",
},
{
id: 2,
src:
"https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=272736508,2842806735&fm=26&gp=0.jpg",
},
{
id: 3,
src:
"https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2064238529,2898748334&fm=26&gp=0.jpg",
},
],
};
},
上面我们已经讲过,组件自定义数据是通过data属性定义一个函数,然后返回一个对象。
子传父思路:
- 在子组件模板中定义好鼠标点击触发事件(或其它触发事件):(v-on:click=“returnData(item.id)”)
<template id="component">
<div>
<img
v-for="item in imgData"
v-bind:src="item.src"
v-on:click="returnData(item.id)"
/>
</div>
</template>
- 由于我们在子组件中触发函数,所以returnData()(自定义函数名)函数在子组件定义:
methods: {
returnData(id) {
//发射事件
this.$emit("son", id);
},
},
- 当鼠标点击触发上面函数,函数内部通过$emit来向父组件发射一个自定义函数和要发送的数据
- 既然子组件发射函数,那么我们就需要监听接收它发射的函数,在子组件标签接收函数:(v-on:son)son是发射器自定义发射的变量名
<div class="container1">
<!-- 使用组件 -->
<my-component v-on:son="getData"></my-component>
</div>
- getData()是在父组件自定义的处理来自子组件数据的函数:
methods: {
getData(id) {
console.log(`点击了id==${id}的图片`);
},
},
注意:v-on:son="getData"的getData函数不能加括号以及写入参数,它会默认给父组件传递 来自this.$emit(“son”, 参数2)中的参数2,如果数据很复杂,可以使用对象或数组。