组件系统是vue的一个重要的概念。他可以让我们使用独立的,可复用的小组件来构建大的复杂的应用,任何应用都可以看作是组件树。组件可以看做是自定义的Html代码。可扩展的html,封装可重用的html。
使用组件的使用分为三步
创建组件构造器,调用Vue.extend()创建,这是Vue构造器的扩展。他有个选项对象。选项对象有个template属性,定义组件要渲染的html
注册组件 ,Vue.compontent()注册
使用组件(在Vue实例的作用范围内使用组件,必须挂载在实例下,否则不生效)
举例
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app1"> <my-component></my-component> </div> <div id="app2"> <my-component></my-component> </div> </body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"> </script> <script> var mycomponent = Vue.extend({ template:'<div>啊啊啊,苍天大地</div>' }); var app1 = new Vue({ el: '#app1', components : {'my-component' :mycomponent} }) //以下就没效果 var app1 = new Vue({ el: '#app2', }) </script> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <style> table tr td{ border:1px solid gray; padding:10px; } table{ border-collapse:collapse; width:800px; table-layout:fixed; } tr.firstLine{ background-color: lightGray; } </style> <body> <!--view层 --> <div id="app"> <parent-component></parent-component> </div> </body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"> </script> <script > /* Model层 */ var exampleData ={ exchange: 6.4, rmb:0 } //创建子组件 var Child =Vue.extend({ template:'<p>这是个子组件</p>' }) //创建父组件 var Parent =Vue.extend({ //在Parent组件里使用 <child-component>标签 template:'<p>这是个父组件 <child-component></child-component></p>', //局部注册子组件 components:{ 'child-component':Child } }) //全局注册下Parent组件 Vue.component('parent-component',Parent) new Vue({ //... el:'#app' }) </script> </html>
结果如下:
1,//局部注册子组件 components:{ 'child-component':Child }
这是在子组件注册到父组件中,并将子组件的标签设置为 child-component
2,template:'<p>这是个父组件 <child-component></child-component></p>',
这就是说 在父组件内以标签形式使用子组件。在页面用标签渲染父组件的内容,同时子组件的内容也被渲染出来。
上面的例子,子组件是在父组件里注册的,它只能在父组件里的template里使用。
下面示范下两种错误的使用子组件的例子
错误1:
<div id="app"> <parent-component> <child-component></child-component> </parent-component> </div>
解释下为什么无效呢?
当子组件注册到父组件下时候,Vue.js会编译好父组件的模板,模板的内容已经决定了父组件即将要渲染的HTML
<parent-component> ..... </parent-component>相当于他的子标签只能当做普通的HMTL执行,他不是普通的html标签,就会被忽略。
上面注册有点繁琐:
Vue简化了上面的步骤,如下举例子:
Vue.component('parent-component',{template:'<p>这是个父组件呢 <child-component></child-component></p>'})
第一个参数是标签名称,第二个是选项对象,使用选项对象的template属性定义组件模板。
这种方法Vue在背后自动调用Vue.extend()
但是呢,尽管上面简化了步骤,还是得在template里拼接html,依然麻烦。
Vue提供了两种方式将定义在js里的html模板分离出来。script标签和template标签
1.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <style> table tr td{ border:1px solid gray; padding:10px; } table{ border-collapse:collapse; width:800px; table-layout:fixed; } tr.firstLine{ background-color: lightGray; } </style> <body> <!--view层 --> <div id="app"> <my-component> </my-component> </div> <script type="text/x-template" id="my-component"> <div>This is my-component</div> </script> </body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"> </script> <script > /* Model层 */ //全局注册下Parent组件 Vue.component('my-component',{ //template选项现在不是html元素,而是一个id,Vue.js根据这id查找对应的元素,然后把这个元素内的html作为模板 //进行编译 template:'#my-component' }) new Vue({ //... el:'#app' }) </script> </html>
2,
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <style> table tr td{ border:1px solid gray; padding:10px; } table{ border-collapse:collapse; width:800px; table-layout:fixed; } tr.firstLine{ background-color: lightGray; } </style> <body> <!--view层 --> <div id="app"> <my-component> </my-component> </div> <template id="my-component"> <div>This is my-component</div> </template> </body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"> </script> <script > /* Model层 */ //全局注册下Parent组件 Vue.component('my-component',{ //template选项现在不是html元素,而是一个id,Vue.js根据这id查找对应的元素,然后把这个元素内的html作为模板 //进行编译 template:'#my-component' }) new Vue({ //... el:'#app' }) </script> </html>
上面两种效果近似。重点看script和template标签的差别。
传入Vue构造器的多数选项也可以在Vue.compontent()和Vue.extend()中使用。不过有两个例外。data和el选项例外。
Vue.js规定,定义组件的选项时,data和el选项必须使用函数。
不然会报错。但是测试下如下代码没报错。为什么?
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <style> table tr td{ border:1px solid gray; padding:10px; } table{ border-collapse:collapse; width:800px; table-layout:fixed; } tr.firstLine{ background-color: lightGray; } </style> <body> <!--view层 --> <div id="app"> <my-component> </my-component> </div> <template id="my-component"> <div>This is my-component</div> </template> </body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"> </script> <script > /* Model层 */ //全局注册下Parent组件 Vue.component('my-component',{ template:'#my-component', data:{ a:1 } }) new Vue({ //... el:'#app' }) </script> </html>
如果让组件的data选项指向一个对象,意味着所有的组件共用一个data。应该使用一个函数作为data选项,让这个函数返回一个新对象。
引子:虽然Vue常用于双向绑定,互相影响,但是有单向绑定怎么办呢?父组件影响子组件,子组件修改的影响不了父组件。
引出props选项:
props基础:
组件的作用域都是孤立的,这意味着不能且不应该直接在子组件的模板里直接用父组件的数据。用prop把数据传给子组件。
<html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <!--view层 --> <div id="app"> <my-component v-bind:my-name="name" v-bind:my-age="age"> </my-component> </div> <template id="mycomponent"> <table> <tr> <th colspan="2"> 子组件数据 </th> </tr> <tr> <td>my name</td> <td>{{ myName }}</td> </tr> <tr> <td>my age</td> <td>{{ myAge }}</td> </tr> </table> </template> </body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"> </script> <script > /* Model层 */ var vm =new Vue({ //... el:'#app', data:{ name:'小章', age:23 }, components:{'my-component':{ template:'#mycomponent', props:['myName','myAge'] } } }) </script> </html>
注意一点:
子组件的props选项时候需要是驼峰命名法。对应的HTML不分大小写,用-隔离。像上面用法。对应起来。
props单向影响联系。
下面例子演示影响修改父组件数据影响。