vue之component

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

组件基础:

全局组件:可以在任何(根)实例中使用的组件;

局部组件:只能在某一实例中使用的组件:

一、定义全局组件和局部组件的两种方法:

方法一:定义一个全局组件和局部组件

全局组件:

let like = Vue.extend({
    template: `
        <div>
            <h2>全局组件定义方法一</h2>
            <p>全局组件定义方法一内容</p>
        </div>
    `
});

Vue.component('like-com', like);

局部组件:

let like = Vue.extend({
    template: `
        <div>
            <h2>局部组件定义方法一</h2>
            <p>局部组件定义方法一内容</p>
        </div>
    `
});

new Vue({
    el: '#app1',
    components: {
        'like-com': like
    }
});

方法二:定义一个全局组件和局部组件

全局组件:

    Vue.component('like', {
        template: `
            <div>
                <h2>这是一个全局组件</h2>
                <p>全局组件内容省略!</p>
            </div>
        `
    });

局部组件:

    new Vue({
        el: '#app1',
        components: {
            'love': {
                template: `
                    <div>
                        <h2>这是一个局部组件</h2>
                        <p>布局组件内容同样省略!</p>
                    </div>
                `
            }
        }
    });

方法三、使用template定义

<div id="app1">
    <my-love></my-love>
</div>

<template id="temp-com">
    <div>
        <h3>使用template定义的组件</h3>
        <p>使用template定义的组件的内容!</p>
    </div>
</template>
Vue.component('my-love', {
    template: '#temp-com'
});

方法四、使用script定义

<div id="app1">
    <my-love></my-love>
</div>

<script type="text/template" id="temp-com">
    <div>
        <h3>使用template定义的组件</h3>
        <p>使用template定义的组件的内容!</p>
    </div>
</script>
Vue.component('my-love', {
    template: '#temp-com'
});

方法五:在模块系统中局部注册

你使用了诸如 Babel 和 webpack 的模块系统。在这些情况下,我们推荐创建一个 components 目录,并将每个组件放置在其各自的文件中。

然后你需要在局部注册之前导入每个你想使用的组件。例如,在一个假设的 ComponentB.js 或 ComponentB.vue 文件中:

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

现在 ComponentA 和 ComponentC 都可以在 ComponentB 的模板中使用了。

二、父子组件:

let child1 = Vue.extend({
    template: `
        <p>子组件child1</p>
    `
});

let child2 = Vue.extend({
    template: `
        <p>子组件child2</p>
    `
});

Vue.component('like', {
    components: {
        'child-com1': child1,
        'child-com2': child2
    },
    template: `
        <div>
            <child-com1></child-com1>
            <child-com2></child-com2>
        </div>
    `
});
let child1 = Vue.extend({
    template: `
        <p>子组件child1</p>
    `
});

let child2 = Vue.extend({
    template: `
        <p>子组件child2</p>
    `
});

new Vue({
    el: '#app1',
    components: {
        'child-app1': {
            components: {
                'child-com1': child1,
                'child-com2': child2
            },
            template: `
                <div>
                   <child-com1></child-com1>
                   <child-com2></child-com2>
                </div>
            `
        }
    }
});

三、父子组件通信:从父到子,用props属性绑定

<div id="app">
    <my-comp :username="nickname"></my-comp>
</div>
Vue.component('my-comp', {
    props: ['username'],
    template: '<h3>{{ username }}</h3>'
});

new Vue({
    el: '#app',
    data: {
        nickname: '小七'
    }
})

五、平行组件传递数据:用空实例搭建桥梁

<div id="app">
    <huahua></huahua>
    <shuangdan></shuangdan>
</div>
    var Event = new Vue();

    Vue.component('huahua', {
        template: `
        <div>我说: <input type="text" @keyup="on_change" v-model="i_said"/></div>
    `,
        methods: {
            on_change: function(){
                Event.$emit('huahua-said-something', this.i_said);
            }
        },
        data: function(){
            return {
                i_said: ''
            }
        }
    })

    Vue.component('shuangdan', {
        template: '<div>花花说:{{huahua_said}}</div>',
        data: function(){
            return {
                huahua_said: ''
            }
        },
        mounted: function(){
            var me = this;
            Event.$on('huahua-said-something', function(data){
                me.huahua_said = data;
            })
        }
    })

    new Vue({
        el: '#app'
    })

六、prop:

1、在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。

2、prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的。

七、is:

解析 DOM 模板时的注意事项

有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。

这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:

<table>
  <blog-post-row></blog-post-row>
</table>

这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is 特性给了我们一个变通的办法:

<table>
  <tr is="blog-post-row"></tr>
</table>

再比如:

// ul下使用组件
<ul>
    <li is="component-name"></li>
</ul>
//声明一个组件
new Vue({
    components:{
        'component-name':{
           template:'<p>你好,这是一个示例</p>' 
        }
    }
})

八、动态组件:使用:is在不同组件之间进行动态切换:

让多个组件使用同一个挂载点,并动态切换,这就是动态组件。通过 Vue 的 <component> 元素加一个特殊的 is 特性来实现。

    <style>
        .tab-button {
            padding: 6px 10px;
            border-top-left-radius: 3px;
            border-top-right-radius: 3px;
            border: 1px solid #ccc;
            cursor: pointer;
            background: #f0f0f0;
            margin-bottom: -1px;
            margin-right: -1px;
        }
        .tab-button:hover {
            background: #e0e0e0;
        }
        .tab-button.active {
            background: #e0e0e0;
        }
        .tab {
            border: 1px solid #ccc;
            padding: 10px;
        }
    </style>
    <button v-for="tab in tabs" @click="current_tab = tab" :class="['tab-button', {'ctive': current_tab === tab}]">{{tab}}</button>
    <component :is="get_current_tab" class="tab"></component>
Vue.component('tab_home', {
    template: `<div>Home component</div>`
});

Vue.component('tab_posts', {
    template: `<div>Posts component</div>`
});

Vue.component('tab_archive', {
    template: `<div>Archive component</div>`
});

var vm = new Vue({
    el: '#app',
    data: {
        current_tab: 'Home',
        tabs: ['Home', 'Posts', 'Archive']
    },
    computed: {
        get_current_tab(){
            return 'tab_' + this.current_tab.toLowerCase();
        }
    }
});

示例:

<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <component :is="currentTab"></component>
    <button @click="change">切换组件</button>
</div>

<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.js"></script>
<script>
let home = {template: "<h1>我是主页</h1>"};
let post = {template: "<h2>我是提交页</h2>"};
let archive = {template: "<h3>我是存档页</h3>"};

let vm = new Vue({
    el: '#app',
    data: {
        index: 0,
        tabs: ['home', 'post', 'archive']
    },
    computed: {
        currentTab: function(){
            return this.tabs[this.index];
        }
    },
    methods: {
        change: function(){
            this.index = (++this.index)%3;
        }
    },
    components: {
        home,
        post,
        archive
    }
});
</script>
</body>
</html>

也可以直接绑定到组件对象上:

<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <component :is="currentTab"></component>
    <button @click="change">切换组件</button>
</div>

<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.js"></script>
<script>
let vm = new Vue({
    el: '#app',
    data: {
        index: 0,
        tabs: [
            {
                template: '<h1>我是主页</h1>'
            },
            {
                template: '<h2>我是提交页</h2>'
            },
            {
                template: '<h3>我是存档页</h3>'
            }
        ]
    },
    computed: {
        currentTab: function(){
            return this.tabs[this.index];
        }
    },
    methods: {
        change: function(){
            this.index = (++this.index)%3;
        }
    }
});
</script>
</body>
</html>
Vue.component('tab-home', { 
	template: '<div>Home component</div>' 
})
Vue.component('tab-posts', { 
	template: '<div>Posts component</div>' 
})
Vue.component('tab-archive', { 
	template: '<div>Archive component</div>' 
})

new Vue({
  el: '#dynamic-component-demo',
  data: {
    currentTab: 'Home',
    tabs: ['Home', 'Posts', 'Archive']
  },
  computed: {
    currentTabComponent: function () {
      return 'tab-' + this.currentTab.toLowerCase()
    }
  }
})
  <component
    v-bind:is="currentTabComponent"
    class="tab"
  ></component>

九、在动态组件上使用 keep-alive:

重新创建动态组件的行为通常是非常有用的,但是在有些应用中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。

<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <keep-alive>
        <component :is="currentTab"></component>
    </keep-alive>
    <button @click="change">切换组件</button>
</div>

<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.js"></script>
<script>
let vm = new Vue({
    el: '#app',
    data: {
        index: 0,
        tabs: [
            {
                template: '<h1>我是主页</h1>'
            },
            {
                template: '<h2>我是提交页</h2>'
            },
            {
                template: '<h3>我是存档页</h3>'
            }
        ]
    },
    computed: {
        currentTab: function(){
            return this.tabs[this.index];
        }
    },
    methods: {
        change: function(){
            let len = this.tabs.length;
            this.index = (++this.index)%len;
        }
    }
});
</script>
</body>
</html>

条件判断:

如果有多个条件性的子元素,<keep-alive> 要求同时只有一个子元素被渲染。

<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <keep-alive>
        <home v-if="index===0"></home>
        <posts v-else-if="index===1"></posts>
        <archive v-else></archive>
    </keep-alive>
    <button @click="change">切换组件</button>
</div>

<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.js"></script>
<script>
let vm = new Vue({
    el: '#app',
    data: {
        index: 0,
    },
    components:{
        home:{template:`<div>我是主页</div>`},
        posts:{template:`<div>我是提交页</div>`},
        archive:{template:`<div>我是存档页</div>`},
    },
    computed: {
        currentTab: function(){
            return this.tabs[this.index];
        }
    },
    methods: {
        change: function(){
            let len = Object.keys(this.$options.components).length;//获取组件的个数
            this.index = (++this.index)%len;
        }
    }
});
</script>
</body>
</html>

十、异步组件:

vue开发过程中,我们会做出特别多特别多的组件,包括login,header,footer,main等等。

这样使整个网站看起来就十分的庞大,当我们在打开网页的时候,突然一下子把这些所有的组件加载上来,这么多的请求全部同时开始请求,势必会造成网页打开很慢,使客户得到的是非常差劲的体验。

因此,vue为我们专门设立了异步组件,通过异步组件,我们可以得到两点好处: 

1、用不到的组件不会加载,因此网页打开速度会很快,当你用到这个组件的时候,才会通过异步请求进行加载; 

2、缓存组件,通过异步加载的组件会缓存起来,当你下一次再用到这个组件时,丝毫不会有任何的疑迟,组件很快会从缓存中加载出来。

为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。例如:

<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <async-example></async-example>
</div>

<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.js"></script>
<script>
Vue.component('async-example', function (resolve, reject) {
    setTimeout(function () {
        // 向 `resolve` 回调传递组件定义
        resolve({
            template: '<div>I am async!</div>'
        })
    }, 2000)
});

let vm = new Vue({
    el: '#app',
    data: {
        index: 0,
    }
});
</script>
</body>
</html>

如你所见,这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 reject(reason) 来表示加载失败。这里的 setTimeout 是为了演示用的,如何获取组件取决于你自己。

一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:

Vue.component('async-webpack-example', function (resolve) {
  // 这个特殊的 `require` 语法将会告诉 webpack
  // 自动将你的构建代码切割成多个包,这些包
  // 会通过 Ajax 请求加载
  require(['./my-async-component'], resolve)
})

实例1:

假如你写一个test.vue文件,在<script>标签里,实际使用方法如下:

//test.vue的部分
<script>
    import Vue from 'vue'
 
    //关键是以下这部分代码
    //需要将引入的异步组件,赋值给变量searchSearch
    //然后在下方components对象里,将变量正常添加进去,就可以使用异步组件了
    //第一个参数是组件名,第二个是异步引入的方法
    const searchSearch = Vue.component('searchSearch', function (resolve) {
        require(['./service-search.vue'], resolve)
    })
 
    export default{
        data(){
            return {}
        },
        methods: {},
        components: {
            searchSearch: searchSearch
        }
    }
</script>

更简单的异步组件的使用方法:

<script>
    export default{
        data(){
            return {}
        },
        methods: {},
        components: {
            searchSearch: function (resolve) {
                //异步组件写法
                require(['./service-search.vue'], resolve)
            }
        }
    }
</script>

可以用到异步组件的地方: 

1、组件中,组件中需要子组件的位置,我们就可以使用异步组件形式;

2、路由中,一般我们打开一个页面时,其总是会进入其中一个默认的路由,那么其他路由在用不到的时候就没必要继续加载,当跳转到当前路由的时候,再来加载,这里也是使用异步组件非常恰当的地方。

十一、在组件上使用 v-model:

<custom-input v-model="searchText"></custom-input>
<span>{{searchText}}</span>
Vue.component('custom-input', {
    template: `<div><input :value="value" @input="$emit('input', $event.target.value)"/></div>`,
    props: ['value']
});

new Vue({
    el:"#app",
    data:{
        searchText: 'hello'
    }
});

这里的 searchText 的值将会传入这个名为 value的 prop。同时当 <custom-input> 触发一个 input 事件并附带一个新的值的时候,这个 searchText 的属性将会被更新。

posted @ 2018-08-05 10:47  Samve  阅读(43418)  评论(0编辑  收藏  举报