基本示例
- 在Vue中,组件是一个很强大的功能,组件可以扩展HTML元素,封装可重用的代码。比如在页面中的某一部分需要在多个场景中使用,那么我们可以将需要部分提取出来,从而提高代码的复用率。
- 所有的Vue组件都是Vue的实列,因此它可以接受Vue中的所有的生命周期钩子。
示例代码:<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>组件基础</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script> </head> <body> <div id="app"> <v-btn></v-btn> <hr /> <v-btn></v-btn> </div> <script type="text/javascript"> // 自定义组件 Vue.component('v-btn', { data: function () { return { count: 0 } }, template: ` <div> <h1>{{ count }}</h1> <button @click='count++'>点击我试试</button> </div> ` }) // 实例化 new Vue({ el: '#app' }); </script> </body> </html>
data 必须是一个函数
在上面的示例代码中,我们可以看到有一段代码是这样的:
data: function () {
return {
count: 0
}
},
那么,为什么data
中的数据需要使用函数return
回来,而不直接使用Object
形式呢?
vue
组件中data
值不能为对象,因为对象是引用类型,组件可能会被多个实例同时引用。如果data
值为对象,将导致多个实例共享一个对象,其中一个组件改变data
属性值,其它实例也会受到影响。data
为函数,通过return
返回对象的拷贝,致使每个实例都有自己独立的对象,实例之间可以互不影响的改变data
属性值。
例子一【引用类型】:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>例子</title>
</head>
<body>
<div id="app"></div>
<script>
const fn = function() {};
fn.prototype.data = { a: 1 };
const demo01 = new fn();
const demo02 = new fn();
demo01.data.a = 5;
document.getElementById('app').innerHTML = `
<div>[demo02.data.a]:${demo02.data.a}</div>
`;
</script>
</body>
</html>
打开浏览器运行上面的代码:可以看到页面显示内容为:
[demo02.data.a]:5
为什么demo02.data.a
不是1
呢?
因为每个组件的data都在内存的同一个地址中,一个数据改变了其他也会跟着改变。
例子二【函数】:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>例子</title>
</head>
<body>
<div id="app"></div>
<script>
const fn = function() {
this.data = this.data();
};
fn.prototype.data = function() {
return { a: 1 };
};
const demo01 = new fn();
const demo02 = new fn();
demo01.data.a = 5;
document.getElementById('app').innerHTML = `
<div>[demo02.data.a]:${demo02.data.a}</div>
`;
</script>
</body>
</html>
打开浏览器运行上面的代码:可以看到页面显示内容为:
[demo02.data.a]:1
为什么demo02.data.a
是1
呢?
因为data
是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。
通过 Prop 向子组件传递数据
模版写法:
Vue.component('v-h1', {
props: ['msg'],
template: `<h1>{{ msg }}</h1>`
});
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件基础</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<v-h1 msg="我是标题"></v-h1>
</div>
<script type="text/javascript">
Vue.component('v-h1', {
props: ['msg'],
template: `<h1>{{ msg }}</h1>`
});
new Vue({ el: '#app' });
</script>
</body>
</html>
监听子组件事件
在我们开发组件时,它的一些功能可能要求我们和父级组件进行沟通。
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件基础</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div :style="{ fontSize: fontSize + 'em' }">
<v-p @big-font="fontSize++"></v-p>
</div>
</div>
<script type="text/javascript">
Vue.component('v-p', {
template: `
<div>
<p>我是标题</p>
<button @click="$emit('big-font')">放大文字</button>
</div>
`
});
new Vue({
el: '#app',
data() {
return {
fontSize: 1
}
}
});
</script>
</body>
</html>
通过上面的代码可以发现,子组件可以通过调用内建的$emit
方法并传入事件名称来触发一个事件,代码片段:
<!-- 子组件 -->
<button @click="$emit('big-font')">放大文字</button>
<!-- 父组件 -->
<v-p @big-font="fontSize++"></v-p>
使用事件抛出一个值
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件基础</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div :style="{ fontSize: fontSize + 'em' }">
<v-p @big-font="fontSize += $event"></v-p>
</div>
</div>
<script type="text/javascript">
Vue.component('v-p', {
template: `
<div>
<p>我是标题</p>
<button @click="$emit('big-font', 0.1)">放大文字</button>
</div>
`
});
new Vue({
el: '#app',
data() {
return {
fontSize: 1
}
}
});
</script>
</body>
</html>
代码片段:
<!-- 子组件 -->
<button @click="$emit('big-font', 0.1)">放大文字</button>
<!-- 父组件 -->
<v-p @big-font="fontSize += $event"></v-p>
跟上面的例子做对比可以发现,使用事件抛出一个值时,只需在$emit
上添加多一个参数即可,接收时使用$event
进行接收。
通过插槽分发内容
在编写组件的过程中,有时我们希望组件里面能自己定义一些HTML
元素,这时我们可以使用<slot>
实现。
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件基础</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<v-demo>
<p style="color: red;">我是内容</p>
</v-demo>
</div>
<script type="text/javascript">
Vue.component('v-demo', {
template: `
<div>
<h1>我是标题</h1>
<slot></slot>
</div>
`
});
new Vue({ el: '#app' });
</script>
</body>
</html>
定义组件名
两种方式:
// 使用 kebab-case
Vue.component('my-component-name', { /* ... */ })
// 使用 PascalCase
Vue.component('MyComponentName', { /* ... */ })
组件的注册
组件的注册分为全局注册
和局部注册
两种。
全局注册
代码模版:
Vue.component('ComponentName', {...});
new Vue({ el: '#app' })
在组件基础中的所有代码都为全局注册。
局部注册
代码模版:
// 编写 v-demo 组件
var VDemo = {
template: `<h1>我是标题</h1>`
};
// 注册 v-demo 组件
new Vue({
el: '#app',
components: {
'v-demo': VDemo
}
});
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>深入了解组件</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<v-demo></v-demo>
</div>
<script type="text/javascript">
var VDemo = {
template: `<h1>我是标题</h1>`
};
new Vue({
el: '#app',
components: {
'v-demo': VDemo
}
});
</script>
</body>
</html>
全局注册和局部注册的区别
全局注册往往是不够理想的。比如,如果你使用一个像 webpack
这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript
的无谓的增加。