注册组件语法糖
之前的注册组件的方式,可能有些繁琐。
Vue 为了简化整个过程,提供了注册的语法糖。
主要是省去了调用 Vue.extend() 的步骤,而是可以直接使用一个对象代替。
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1. 全局组件注册的语法糖
// 1. 创建组件构造器
// const cpn1 = Vue.extend();
// 2. 注册组件
// 全局组件
Vue.component('cpn1', {
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容,哈哈哈</p>
</div>
`
});
const app = new Vue({
el: "#app",
data: {
message: "hello"
},
components: {
// 局部组件
cpn2: {
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容,嘻嘻嘻</p>
</div>
`
}
}
});
</script>
模板分离的写法
我们会发现,我们使用了语法糖后,代码中出现了大量的 HTML 代码,特别不方便阅读。所以我们需要把模板抽离出来,写在外面,让代码好阅读。
<div id="app">
<cpn></cpn>
<cpn2></cpn2>
</div>
<!-- 1. script 标签,类型必须是 text/x-template -->
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题1</h2>
<p>我是内容,哈哈哈</p>
</div>
</script>
<!-- 2. template 标签 -->
<template id="cpn2">
<div>
<h2>我是标题2</h2>
<p>我是内容,嘻嘻嘻</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1. 注册一个全局组件
Vue.component('cpn', {
template: '#cpn'
})
const app = new Vue({
el: "#app",
data: {
message: "hello"
},
components: {
cpn2: {
template: '#cpn2'
}
}
});
</script>
- 使用 script 标签 type 属性为 text/x-template。
- 使用 template 标签。
都需要通过 id 来找到到模板!
还有一点需要注意,在定义模板时,我们需要一个明确的根元素,否则 Vue 可能会报错,也就是我们需要用一个标签把模板内容包裹起来,在上面就是 div 标签。
组件访问 Vue 实例数据
组件可以访问 Vue 实例数据嘛?
<div id="app">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "hello"
},
components: {
cpn: {
// Vue 会报错提示找不到 message
template: `
<div>
<h2>{{message}}</h2>
<p>我是内容,嘻嘻嘻</p>
</div>
`
}
}
});
</script>
通过上面代码,我们可以发现组件是不能直接访问到 Vue 实例里面的数据,
结论:Vue 组件应该有自己保存数据的地方。
通过 data 访问数据
我们可以给组件也添加 data 属性,如下
components: {
cpn: {
// Vue 会报错提示找不到 message
template: `
<div>
<h2>{{message}}</h2>
<p>我是内容,嘻嘻嘻</p>
</div>
`,
data: {
message: 'hello'
}
}
}
但是,会发现 Vue 给我们报错了
The "data" option should be a function that returns a per-instance value in component definitions.
原来,组件中的 data 不是一个对象,而是一个函数,然后通过函数返回实例对象,所以我们进行修改。
data() {
// 返回实例对象
return {
message: 'hello'
}
}
就会发现,可以正常访问到 message 了,所以我们知道了。
- 组件对象也有一个 data 属性(也可以有 methods 等属性,后面会用到)
- 只是这个 data 属性必须是一个函数。
- 而且这个函数返回一个对象,对象内部保存着数据。
为什么 data 必须是一个函数
我们通过一个案例来引出,我们先把之前做的计数器,封装成一个组件
<div id="app">
<h2>当前计数:{{counter}}</h2>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
counter: 0
},
method: {
add: function () {
this.counter++;
},
sub: function () {
this.counter--;
}
}
});
</script>
封装后:
<!-- 组件实例对象 -->
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>当前计数:{{counter}}</h2>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1. 注册组件
Vue.component('cpn', {
template: `#cpn`,
data() {
return {
counter: 0
}
},
methods: {
add() {
this.counter++;
},
sub() {
this.counter--;
}
}
})
const app = new Vue({
el: "#app",
data: {
message: "hello"
}
});
</script>
我们实例了3个 cpn 实例,那么我们思考一个问题,这3个实例是不是都有 counter 属性,他们之间的 counter 属性是同一个嘛?或者说,他们的 data 对象是同一个嘛?
很显然,如果他们的 data 对象是同一个,那么我们对其中一个组件进行按了 + 号,其他的值也会发生改变,我们测试就会发现很显然不是一个 data。
并且,我们是通过一个函数,返回了一个 data 对象,每次返回的都不是同一个对象,这也可以验证。
所以我们知道了为什么 data 是用函数返回一个对象,而不是直接使用对象。
这就好像类有静态成员和非静态成员,每个实例出来的对象共享的是静态成员,而非静态成员是每个对象自己特有的属性,他们的值不一定相同,也互不影响。如果组件中的 data 不是返回一个对象,那么很显然,每个组件共享一个 counter 这显然不是我们想要的组件。
组件应该是可以复用的,每一个实例出来的组件都是独立的互不影响的,所以 data 是使用函数返回一个对象。可以保证每个组件维护自己的 counter 而不干扰其他组件的 counter 。
类 —> 组件
类的成员变量 —> 组件的 data
类的方法 —> 组件的函数
类的实例 —> 实例组件