vue.js实战

第七章:vue.js组件详解Ⅱ(基础篇)

2018-08-14  本文已影响1人  六个周

7.5高级组件用法

        本节会介绍组件的一些高级用法,这些用法在实际业务中不是很常用,但在独立组件开发时可能会用到。如果你感觉以上内容已经足够完成你的业务开发,你可以跳过本节;如果你想继续探索Vue组件的奥秘,读完本节会对你有很多的启发。

7.5.1 递归组件

        组件在它的模板内可以递归的调用自己,只要给组件设置name的选项就可以了。示例代码如下:

<div id="app">

    <child-component :count="1"></child-component>

</div>

<script>

Vue.component('child-component',{

    name:'child-component',

    props:{

        count:{

            type:Number,

            default:1

    }

},

    template:'\

    <div class="child">\

        <child-component :count="count+1"  v-if="count<3"></child-component>\

    </div>',

});

    var app =new Vue({

    el:'#app'

})

</script>

        设置name后,在组件模板内就可以递归使用了,不过需要注意的是,必须给定一个条件来限制递归数量,否则会抛出错误:max stack size exceeded.

        组件递归使用可以开发一些具有未知层级关系的独立组件,比如级联选择器(省市区)和树形控件(mytudolist)等。实战篇中,会详细介绍级联选择器的实现。

7.5.2 内联模板

        组件的模板一般都是在template选项内定义的,Vue提供了一个内联模板的功能,在使用组件时,给组件标签使用inline-template特性,组件就会把它的内容当作模板,而不是把它当内容分发,这让模板更灵活。示例代码如下:

<div id="app">

    <child-component inline-template>

        <div>

            <h2>在父组件中定义子组件的模板</h2>

            <p>{{ message}}</p>

            <p>{{ msg }}</p>

        </div>

    </child-component>

</div>

<script>

    Vue.component('child-component',{

        data:function(){

        return {

            msg:'在子组件声明的数据'

            }

    }

});

    var app = new Vue({

        el:'#app',

        data:{

            message:'在父组件中声明的数据'

            }

    })

</script>

        渲染后的结果为:

<div id="app">

    <div>

        <h2>在自父组件中定义子组件的模板</h2>

        <p>在父组件声明的数据</p>

        <p>在子组件声明的数据</p>

    </div>

</div>

            在父组件中声明的数据message和子组件中声明的数据msg,两个都可以渲染(如果同名,优先使用子组件的数据)。这反而是内嵌模板的缺点,就是作用域比较难理解,如果不是非常特殊的场景,建议不要轻易使用内联模板。

7.5.3 动态模板

        Vue.js提供了一个特殊的元素<component>用来动态地挂载不同的组件,使用is特性来选择要挂载的组件。示例代码如下:

<div id="app">

    <component :is="currentView"></component>

    <button @click="handleChangeView('A')">切换到A</button>

    <button @click="handleChangeView('B‘)">切换到B</button>

     < button @click="handleChangeView('C')">切换到C</button>

</div>

<script>

    var app = new Vue({

        el:'#app',

        components:{

        comA:{ template:'<div>组件A</div>'},

        comB:{ template:'<div>组件B</div>'},

        comC:{ template:'<div>组件C</div>'}

    },

        data:{

        currentView:comA

    },

        methods:{

        handleChangeView:function(component){

            this.currentView = 'com'+component;

        }

    }

})

</script>

        动态地改变currenView的值就可以动态挂载组件了。也可以直接绑定在组件对象上:

<div id="app">

    <component :is="currentView"></component>

</div>

<script>

    var Home={

          template:' <p>Welcome Home</p>'

    };

    var app = new Vue({

        el:'#app',

        data:{

        currentView:Home

    }

})

</script>

7.5.4 异步组件

        当你的工程足够大,使用的组件足够多时,是时候考虑下性能问题了,因为一开始把所有的组件都加载是必要的一笔开销。还在Vue.js允许将组件定义为一个工厂函数,动态地解析组件。Vue.js只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。示例代码如下:

<div is="app">

    <child-component></child-component>

</div>

<script>

    Vue.component('child-component',function(resolve,reject){

        window.setTimeout(function(){

        resolve({

            template:'<div>我是异步渲染</div>'

        });

    },2000)

});

    var app = new Vue({

        el:'#app'

})

</script>

        工厂函数接受一个resolve回调,在收到从服务器下载的组件定义时调用。也可以调用reject(reason)指示加载失败。这里setTimeout只是为了演示异步,具体的下载逻辑可以自己决定,比如把组件配置写成一个对象配置,通过Ajax来请求,然后调用resolve传入配置选项。

        在进阶篇里,我们还会介绍主流的打包编译工具webpack和.vue单文件的用法,更优雅地实现异步组件(路由)。


7.6 其他

7.6.1 $nextTick

        我们先来看这样一个场景:有一个div,默认用v-if将它隐藏,点击一个按钮后,改变v-if的值,让它显示出来,同时拿到这个div的文本内容。如果v-if的值是false,直接去获取div的内容是获取不到的,因为此时div还没有创建出来,那么应该在点击按钮后,改变v-if的值为true,div才会被创建,此时再去获取,示例代码如下:

    <div id="app">

        <div id="div" v-if="showDiv">这是一段文本</div>

        <button @click="getText">获取div内容</button>

    </div>

<script>

    var app =new Vue({

        el:'#app',

        data:{

        showDiv:false

    },

        methods:{

        getText:function(){

            this.showDiv =true;

              var text =document.getElementById('div').innerHTML;

                console.log(text);

        }

    }

})

</script>

        这段代码并不难理解,但是运行后在控制台会抛出一个错误:Cannot read property 'innerHTML' of null,意思就是获取不到div元素。这里就涉及Vue一个重要概念:异步更新队列。

        Vue在观察到数据变化时并不是直接去除重复数据,从而避免不必要的计算和DOM操作。然后,在下一个事件循环tick中,Vue刷新队列并执行实际(已去重的)工作 。所以如果你用一个for循环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘100次,这固然是一个很大的开销。

        Vue会根据当前浏览器环境优先使用原生的Promise.then和MutationObsever,如果都不支持,就会采用setTimeout代替。

        知道了Vue异步更新DOM的原理,上面示例的报错也就不难理解了。事实上,在执行this.showDiv=true时,div仍然还是没有被创建出来,直到下一个Vue事件循环时,才开始创建。$nextTick就是用来知道什么时候DOM更新完成的,所以上面的示例代码修改为:

…………

    this.showDiv=true;

    this.$nextTick (function(){

                   var text =document.getElementById('div').innerHTML;

                console.log(text);

});

…………

        这时再点击按钮,控制台就打印出div的内容”这是一段文本“了。

        理论上,我们不应该去主动操作DOM,因为Vue的核心思想就是数据驱动DOM,但在很多业务里,我们避免不了会用一些第三方库,比如swiper等,这些基于原生JavaScript的库都有创建和更新及销毁的完整生命周期,与Vue配合使用时,就要利用好$nextTick..


上一章:第七章:vue.js组件详解Ⅰ(基础篇)

下一章:第八章 vue.js-自定义指令(基础篇)

上一篇 下一篇

猜你喜欢

热点阅读