组件的使用分为个步骤:
下面按上面步骤实现简易组件:
<body>
<div class="container">
<my-comp>my-comp>
div>
<script src="./vue.js">script>
<script>
// 1.创建组件构造器
const comp = Vue.extend({
template: `
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
`,
});
// 2.注册组件
Vue.component("my-comp", comp);
var app = new Vue({
el: ".container",
data: {},
methods: {},
});
script>
body>
讲解:
注意:组件一定要在vue实例中使用才有效,上面的container容器就是一个vue实例。
有了语法糖,我们这里就不需要使用组件构造器函数了。
<body>
<div class="container1">
<my-comp>my-comp>
div>
<script src="./vue.js">script>
<script>
Vue.component("my-comp", {
template: `
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
`,
});
// 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来创建组件。
以上就实现了简易的组件构建与使用。
全局组件与局部组件如何区分?
1.当组件在vue实例中注册时,则是局部组件,只能在该vue实例中使用。
2.当组件不是在实例中注册时,则是全局组件,在任意的vue实例都可以使用。
我们在上面定义的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: `
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
`,
});
// 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组件:
在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: `
这个世界只在乎你是否在到达了一定的高度,而不在乎你是踩在巨人的肩膀上上去的,还是踩在垃圾上上去的。
`,
});
// vue实例1
var app1 = new Vue({
el: ".container1",
// 注册组件
components: {
"my-comp": comp,
},
});
// vue实例2
var app2 = new Vue({
el: ".container2",
});
script>
body>
上面我只在app1中注册了组件:
很明显,app2实例没有注册组件,所以无法使用该组件,这就是局部组件。
在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>
简单来说:父子组件是嵌套关系,子组件被包含在父组件中。子组件在父组件中“注册”。
<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>
特别注意:vue实例也是一个组件,而且是根组件,因此father组件也是根组件的子组件。
什么是动态数据?
上面我们的数据是直接写死的,我们都知道标签元素中要想内容是动态的就使用胡子语法,属性想要是动态的就是用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属性了吗?
特别注意:
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对象的属性值的。
在实际开发中,往往一些数据需要从上层传递到下层:
如何进行父子组件间的通信呢?
注意:vue实例是一个根组件,这里使用根组件与一个自定义组件形成父子组件。
props基本用法:
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: {
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"],
},
<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>
实例:
<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的属性值,它使用的是默认属性值,所以显示的是小黄熊,而且也在浏览器中报错。
实例:我们从子组件传给父组件,用户鼠标所点击图片对应的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属性定义一个函数,然后返回一个对象。
子传父思路:
<template id="component">
<div>
<img
v-for="item in imgData"
v-bind:src="item.src"
v-on:click="returnData(item.id)"
/>
div>
template>
methods: {
returnData(id) {
//发射事件
this.$emit("son", id);
},
},
<div class="container1">
<my-component v-on:son="getData">my-component>
div>
methods: {
getData(id) {
console.log(`点击了id==${id}的图片`);
},
},
注意:v-on:son="getData"的getData函数不能加括号以及写入参数,它会默认给父组件传递 来自this.$emit(“son”, 参数2)中的参数2,如果数据很复杂,可以使用对象或数组。