细节点是什么?指一些平时可能没有太注意,但是用处很大的小地方,毕竟千里之堤毁于蚁穴。
使用is
有时候我们会遇到子组件填充表格数据的情况,这个时候我们需要先定义全局组件(row),然后把组件放到需要显示的位置
- <div id="root">
- <table>
- <tbody>
- <row></row>
- <row></row>
- <row></row>
- </tbody>
- </table>
- </div>
- <script>
- Vue.component('row',{
- template: '<tr><td>this is a row</td></tr>',
- })
- var vm = new Vue({
- el: '#root'
- })
- </script>
我们刷新页面会发现,效果实现了,但是我们查看页面代码会发现,原本3个tr标签应该被tbody所包裹,事实却是tr标签出现在和table标签同级的一层
HTML5的规范中要求table中有tbody,tbody中有tr,现在使用子组件代码写成row标签,所以浏览器解析row的时候就会出现问题,那么怎么解决呢?我们可以用is属性。
- <div id="root">
- <table>
- <tbody>
- <tr is="row"></tr>
- <tr is="row"></tr>
- <tr is="row"></tr>
- </tbody>
- </table>
- </div>
然后我们再次刷新页面,会发现现在表格结构为table包裹tbody,tbody包裹tr,一切尽在掌握=。=
注意
在使用ul等标签的时候,为避免出现bug也可以这样来做。
子组件定义data
我们想把子组件的内容放在data中存储,这样写
- <div id="root">
- <table>
- <tbody>
- <tr is="row"></tr>
- <tr is="row"></tr>
- <tr is="row"></tr>
- </tbody>
- </table>
- </div>
- <script>
- Vue.component('row',{
- data: {
- content: 'this is a row'
- },
- template: '<tr><td>{{content}}</td></tr>'
- })
- var vm = new Vue({
- el: '#root'
- })
- </script>
刷新页面会发现报错了,错误提示显示data应该是一个function而不应该是一个对象
那么为什么呢?因为我们在根组件可以直接通过对象定义,但是当我们在非根组件(子组件)中就不可以这样定义了,data要求后面的值必须是一个函数,并且这个函数要求返回一个对象,对象中包含所对应的数据。
正确写法
- <div id="root">
- <table>
- <tbody>
- <tr is="row"></tr>
- <tr is="row"></tr>
- <tr is="row"></tr>
- </tbody>
- </table>
- </div>
- <script>
- Vue.component('row',{
- data: function () {
- return {
- content: 'this is a row'
- }
- },
- template: '<tr><td>{{content}}</td></tr>'
- })
- var vm = new Vue({
- el: '#root'
- })
- </script>
刷新页面,显示正常。
结论
在子组件定义data的时候,data必须是函数不可以是对象,这样设计的原因是因为一个子组件和根组件不同,不可能只被调用一次,每个子组件的数据不希望和其他的子组件产生冲突,要有独立的数据。
通过一个函数返回对象的目的就是为了每个子组件都拥有独立的数据存储,这样的话不会出现多个子组件之间互相影响的情况。
ref的使用
当我们需要在vue中操作dom元素的时候,可以通过ref的引用形式来获取dom进行dom操作。
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的refs
对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例;
普通dom元素
- <div id="root">
- <div ref='hello' //引用名字为hello
- @click="handleClick"
- >
- hello world!</div>
- </div>
- <script>
- var vm = new Vue({
- el: '#root',
- methods: {
- handleClick: function () {
- console.log(this.$refs.hello.innerHTML)
- }
- }
- })
- </script>
打印结果为hello world!
用在子组件上
- <div id="root">
- <counter ref="one" @change="handleChange"></counter> //引用名字为one,监听change事件执行handleChange方法
- <counter ref="two" @change="handleChange"></counter> //引用名字为two,监听change事件执行handleChange方法
- <div>{{total}}</div>
- </div>
- <script>
- Vue.component('counter',{
- template: '<div @click="handleClick">{{number}}</div>',
- data: function () {
- return {
- number: 0
- }
- },
- methods: {
- handleClick: function () {
- this.number ++
- this.$emit('change') //$emit向上层触发事件
- }
- }
- })
- var vm = new Vue({
- el: '#root',
- data: {
- total: 0
- },
- methods: {
- handleChange: function () {
- console.log(this.$refs.one.number) //打印one改变后的值
- console.log(this.$refs.two.number) //打印two改变后的值
- this.total = this.$refs.one.number + this.$refs.two.number //计算改变后的(one+two)的和
- }
- }
- })
- </script>
打印结果为子组件实例内的结果