Vue学习笔记(十) - 组件

组件

概述

所谓组件,在我看来,是一段自定义的可复用的 HTML 代码(包括内容、样式和行为)。

也就是说,一个组件应该要有以下部分:

  • 样式 - style
  • 内容 - template
  • 行为 - script

使用

使用 Vue 的组件,要按照以下步骤进行:

  1. 定义组件,也就是创建一个组件构造器
  2. 注册组件
  3. 使用组件

<!-- HTML part -->
<div id="app">
<simple-component> test1 </simple-component>
</div>
<simple-component> test2 </simple-component>
// JS part
// 定义一个组件,创建构造器
var SimpleComponent = Vue.extend({
template: '<div>先做一个最简单的组件</div>'
});
// 注册组件
Vue.component('simple-component', SimpleComponent);
// 使用组件
var vm = new Vue({
el: '#app'
});

只有当挂载到某个 Vue 实例下,组件才会生效。
从上图可以看出,simple-component 组件在 #app 下才生成,直接放置则没有。

全局注册&局部注册

当依照上述流程来生成一个组件,既通过 Vue.component() 来注册组件的话,这个组件是全局的,每个 Vue 实例都可以插入该组件。

如果不需要全局注册的话,或者只允许某些组件使用,用 Vue 实例化的选项对象的 component 属性来实现局部注册。

<!-- HTML part -->
<div class="group">
<p>局部注册</p>
<p>只允许组件my-component在app1中使用,app2不允许</p>
<div id="app1">
<my-component>生成指令时这句话就会替换掉</my-component>
</div>
<div id="app2">
<my-component>生成指令时这句话就会替换掉</my-component>
</div>
</div>
// JS part
// 局部注册
// 定义一个组件,创建构造器
var MyComponent = Vue.extend({
template: '<div>我是my-component</div>'
});
// 使用组件
var vm1 = new Vue({
el: '#app1',
components: {
'my-component': MyComponent
}
});
var vm2 = new Vue({
el: '#app2'
});

父子组件

父子组件,顾名思义,就是组件之间构成了父子关系,即在组件内部定义并使用了其他组件。

实现方法:在父组件的构造器定义时,在选项对象中加入 components 属性,属性的值为一个对象,自定义标签名为 key,组件构造器为 value。

<!-- HTML part -->
<div id="app">
<parent-component></parent-component>
</div>
// JS part
// 定义子组件
var ChildComponent = Vue.extend({
template: '<span style="background: pink;">这里是子组件</span>'
});
// 定义父组件
var ParentComponent = Vue.extend({
// 把子组件以标签的形式嵌入到当前这个父组件中使用
template: '<div style="background-color:lightgreen;padding:10px">'
+'这里是父组件'
+'<child-component></child-component>'
+'</div>',
// 把 ChildComponent 组件注册到 ParentComponent 中
components: {
'child-component': ChildComponent
}
});
// 注册父组件
Vue.component('parent-component', ParentComponent);
// 生成实例
new Vue({
el: '#app'
});

由于子组件是在父组件中注册的,因此决定了它不能在父组件以外的组件内部使用。子组件只能在父组件的 template 中使用

因此下面的使用方式是错误的:

  1. 直接在 HTML 文件中以子元素的形式在父组件的标签中使用
    <div id="app">
    <parent-component>
    <child-component></child-component>
    </parent-component>
    </div>

不管是否有在父组件设置了 template 属性,都不允许。因为在组件渲染的时候,HTML 的内容已经确定,子标签因为不是标准的 HTML 标签,因此直接被忽略。

  1. 在父组件标签外部使用子组件
<div id="app">
<parent-component></parent-component>
<child-component></child-component>
</div>

Script 和 Template

在上面的代码中,对于所有组件,它们的模版 template 的属性值都是拼接的 HTML 代码串,这样会使 HTML 代码和 JavaScript 代码高度耦合,不利于维护。

要解决这个问题,可以使用 script 标签或者 template 标签将组件的模版内容放在 HTML 文件中。

  • script

    <!-- HTML -->
    <script type="text/x-template" id="myComponent">
    <div>这里是模版内容</div>
    </script>
  • template

    <!-- HTML -->
    <template id="myComponent">
    <div>这里是模版内容</div>
    </template>

使用模版只需要将模版标签的 id 值赋给 template 属性:

Vue.extend({
template: "#myComponent"
})

使用props

每个组件实例其作用域都是独立的,这样也意味着,无法在子组件直接引用父组件的数据。反之亦然。使用 props 可以将父组件的数据传给子组件。

(为什么文档没有说父组件怎么引用子组件?)

直接传值

直接传值的话,就是说在定义子组件的组件构造器时,添加一个 props 属性,其值为一个数组,数组的元素为子组件用来绑定父组件变量的变量名(string)

// 增加了几个输入框,检测父子组件的变量的变化关系
var cc = Vue.extend({
template: '<p style="color:red;">{{myLyric}}</p>'
+'<p><input type="text" v-model="myLyric"/></p>',
props: ['myLyric'] // 设置和父组件绑定的变量名
});
var cp = Vue.extend({
template: '<p>{{lyric}}</p>'
+'<input type="text" v-model="lyric">'
+'<cc-child v-bind:my-lyric="lyric"></ccc-child>', // 在这里绑定父子组件的变量
components: {
'cc-child': cc
},
data: function(){ // 定义组件内的变量,组件内data属性值应该用函数返回一个对象,而不直接设置为一个对象
return {
lyric: "如日记内的一根倒刺"
}
}
});
var vm2 = new Vue({
el: '#app2',
components: {
'cc-parent': cp
}
});

在这个栗子中,这是一个单向绑定。父组件的变量改变时,子组件绑定的变量会相应改变。反之不然,子组件的变量改变时,父组件的不会改变。

注意:在子组件中定义prop时,使用了camelCase命名法。由于HTML特性不区分大小写,camelCase的prop用于特性时,
需要转为 kebab-case(短横线隔开)。例如,在prop中定义的myName,在用作特性时需要转换为my-name。

假如要实现父子组件的变量双向绑定的话呢?很简单。只要在绑定变量时加上 .sync 后缀修饰符就可以了。

e.g. :my-param .sync =”param”

也可以实现单次绑定

e.g. :my-param .once =”param”

参考资料:http://www.cnblogs.com/keepfool/p/5625583.html#h2_6