让多个组件使用同一个挂载点,并动态切换,这就是动态组件。通过使用保留的 <component> 元素,动态地绑定到它的 is 特性,可以实现动态组件。它的应用场景往往应用在路由控制或者 tab 切换中。
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8"/>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <button @click="change">切换页面</button>
    <component :is="currentView"></component>
</div>
<script>
    new Vue({
        el: '#app',
        data:{
            index:0,
            arr:[
                {template:'<div>我是主页</div>'},
                {template:'<div>我是提交页</div>'},
                {template:'<div>我是存档页</div>'}
            ],
    },
        computed:{
            currentView(){
                return this.arr[this.index];
            }
        },
        methods:{
            change(){
                this.index = (++this.index)%3;
            }
        }
    })
</script>
</body>
</html>
component 标签中 is 属性决定了当前采用的子组件,:is 是 v-bind 的简写,绑定了父组 件中 data 的 currentView 属性。点击按钮时,会更改数组 arr 的索引值,同时也修改了子组 件的内容。
keep-alive

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,以便提高提取效率。和 <transition> 相似,<keep-alive> 是一个抽象组件,它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8"/>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <button @click="change">切换页面</button>
    <keep-alive>
        <component :is="currentView"></component>
    </keep-alive>
</div>
<script>
    new Vue({
        el: '#app',
        data:{
            index:0,
            arr:[
                {template:'<div>我是主页</div>'},
                {template:'<div>我是提交页</div>'},
                {template:'<div>我是存档页</div>'}
            ],
        },
        computed:{
            currentView(){
                return this.arr[this.index];
            }
        },
        methods:{
            change(){
                /*es6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。*/
                let len = this.arr.length;
                this.index = (++this.index)% len;
            }
        }
        })
</script>
</body>
</html>
activated 钩子函数

Vue给组件提供了activated钩子函数,作用于动态组件切换或者静态组件初始化的过程中。activated是和template、data等属性平级的一个属性,形式是一个函数,函数里默认有一个参数,而这个参数是一个函数,执行这个函数时,才会切换组件,即可以延迟执行当前的组件。
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8"/>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <button @click='toShow'>点击显示子组件</button>
    <!----或者<component v-bind:is="which_to_show" keep-alive></component>也行----->
    <keep-alive>
        <component v-bind:is="which_to_show" ></component>
    </keep-alive>
</div>
<script>
    // 创建根实例
    var vm = new Vue({
        el: '#app',
        data: {
            which_to_show: "first"
        },
        methods: {
            toShow: function () {   //切换组件显示
                var arr = ["first", "second", "third", ""];
                var index = arr.indexOf(this.which_to_show);
                if (index < 2) {
                    this.which_to_show = arr[index + 1];
                } else {
                    this.which_to_show = arr[0];
                }
                console.log(this.$children);
            }
        },
        components: {
            first: { //第一个子组件
                template: "<div>这里是子组件1</div>"
            },
            second: { //第二个子组件
                template: "<div>这里是子组件2,这里是延迟后的内容:{{hello}}</div>",
                data: function () {
                    return {
                        hello: ""
                    }
                },
                activated: function (done) { //执行这个参数时,才会切换组件
                    console.log('beixi')
                    var self = this;
                    var startTime = new Date().getTime(); // get the current time
                    //两秒后执行
                    while (new Date().getTime() < startTime + 2000){
                        self.hello='我是延迟后的内容';
                    }
                }
            },
            third: { //第三个子组件
                template: "<div>这里是子组件3</div>"
            }
        }
    });
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8"/>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <button @click="change">切换页面</button>
    <keep-alive>
        <home v-if="index===0"></home>
        <posts v-else-if="index===1"></posts>
        <archive v-else></archive>
    </keep-alive>
</div>
<script>
    new Vue({
        el: '#app',
        components:{
            home:{template:'<div>我是主页</div>'},
                posts:{template:'<div>我是提交页</div>'},
                    archive:{template:'<div>我是存档页</div>'},
                    },
        data:{
            index:0,
        },
        methods:{
            change(){
                //  在data外面定义的属性和方法通过$options可以获取和调用
                let len = Object.keys(this.$options.components).length;
                this.index = (++this.index)%len;
            }
        }
                })
</script>
</body>
</html>
 异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从 服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个 工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染,代码如下:

<div id="app">
        <async-example></async-example>
    </div>
    <script>
            Vue.component ( 'async-example ', function (resolve, reject){
                setTimeout (function (){
                    //向resolve 回调传递组件定义
                resolve ( {
                    template:  '<div>这是异步渲染的内容!</div>'
                })
            },1000)
        })
        new vue ( {
            el : '#app'
        })
    </script>
这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到 组件定义的时候被调用。你也可以调用 reject(reason) 来表示加载失败。这里的 setTimeout 是 为了演示异步,如何获取组件取决于你自己。比如把组件配置成一个对象配置,通过 Ajax 来请求,然后调用 reslove 传入配置选项。
在 Vue 中一般很少会用到直接操作 DOM,但不可避免有时候需要用到,这时我们可以 通过 ref 和$refs 来实现:
      ref: ref 被用来给元素或子组件注册引用信息, 引用信息将会注册在父组件 的 $refs 对象上,如果是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素,如果是 在子组件上,引用就指向组件的实例。 
 $refs:$refs 是一个对象,持有已注册过 ref 的所有的子组件。
普通获取 DOM 的方式 
先通过 getElementById 方法来获取,代码如下:

<div id="app">
        <input type="button" value="获取h3的值"@click="getElement"><h3 id="myh3">我是h3</ h3>
    </div>
    <script>
        var vm=new vue ( {
            el : "#app" ,
            data : { } ,
            methods : {
                getElement ( ) {
                    //通过getElementById方式获取DOM对象
                     console.log (document. getElementById ( "myh3" ) .innerHTML) ;
                }
            }
        })
    </script>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <!--引入vue-->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="button" value="获取h3的值" @click="getElement">
    <h3 id="myh3" ref="myh3">我是h3</h3>
    <hr>
    <login ref="mylogin"></login>
</div>
<script>
    var login = {
        template: "<h3>我是login子组件</h3>",
        data(){
        return {
            msg: "ok"
        }
    },
        methods:{
            show(){
                console.log("show方法执行了...")
            }
        }
    }
    var vm=new Vue({
        el:"#app",
        data:{},
        methods:{
            getElement(){
                //通过getElementById方式获取DOM对象
                // console.log(this.$refs.myh3.innerHTML);
                console.log(this.$refs.mylogin.msg);
                this.$refs.mylogin.show();
            }
        },
     components:{
         login
     }
    })
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!--引入vue-->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!--引入bootstrap-->
    <link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
</head>
<body>
<div id="app">
    <cmt-box @func="loadComments"></cmt-box>
    <ul class="list-group">
        <li class="list-group-item" v-for="item in list" :key="item.id">
            <span class="badge">评论人: {{ item.user }}</span>
            {{ item.content }}
        </li>
    </ul>
</div>
<template id="tmpl">
    <div>
        <div class="form-group">
            <label>评论人:</label>
            <input type="text" class="form-control" v-model="user">
        </div>
        <div class="form-group">
            <label>评论内容:</label>
            <textarea class="form-control" v-model="content"></textarea>
        </div>
        <div class="form-group">
            <input type="button" value="发表评论" class="btn btn-primary" @click="postComment">
        </div>
    </div>
</template>
<script>
    var commentBox = {
        data() {
            return {
                user: '',
                content: ''
        }
        },
        template: '#tmpl',
        methods: {
            postComment() { // 发表评论的方法
                var comment = { id: Date.now(), user: this.user, content: this.content }
                // 从 localStorage 中获取所有的评论
                var list = JSON.parse(localStorage.getItem('cmts') || '[]')
                list.unshift(comment)
                // 重新保存最新的 评论数据
                localStorage.setItem('cmts', JSON.stringify(list))
                this.user = this.content = ''
                this.$emit('func')
            }
        }
    }
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el: '#app',
        data: {
            list: [
                { id: Date.now(), user: 'beixi', content: '这是我的网名' },
                { id: Date.now(), user: 'jzj', content: '这是我的真名' },
                { id: Date.now(), user: '贝西奇谈', content: '有任何问题可以关注公众号' }
        ]
        },
        beforeCreate(){ /* 注意:这里不能调用 loadComments 方法,因为在执行这个钩子函数的时候,data 和 methods 都还没有被初始化好*/
    },
        created(){
        this.loadComments()
    },
        methods: {
            loadComments() { // 从本地的 localStorage 中,加载评论列表
                var list = JSON.parse(localStorage.getItem('cmts') || '[]')
                this.list = list
            }
        },
        components: {
            'cmt-box': commentBox
        }
    });
</script>
</body>
</html>