美文网首页Vue
vue 组件化的入门详解

vue 组件化的入门详解

作者: 大脸猫的前端之路 | 来源:发表于2019-02-01 16:41 被阅读11次

    组件

    组件(Component)是Vue.js最核心的功能,也是整个框架设计最精彩的地方,当然也是最难掌握的。组件可以扩展HTML元素,封装可重用的代码。为什么使用组件?当然是为了提高代码的重用性;

    组件的用法

    组件是需要注册才可以使用的,注册有全局注册和局部注册两种方式。全局注册后,任何Vue实例都可以使用,

    • 全局注册代码如下:
    Vue.component('my-component', {
    // 选项
    })
    
    

    // my-component就是注册的组件自定义标签名称,推荐使用小写加减号分割的形式命名。要在父实例中使用这个组件,必须要在实例创建前注册,之后就可以用<my-component></my-component>的形式来使用组件了。

    <div id="app">
        <my-component></my-component>
    </div>
    <script>
    Vue.component('my-component', {
    //选项
    });
    var app = new Vue({
        el: '#app'
    })
    </script>
    
    

    此时的页面还是空白的,因为我们注册的组件没有任何内容,在组件选项中添加template就可以显示内容了。此时注意template的DOM结构必须被一个元素包含。

    Vue.component('my-component', {
        template: '<div>这里是组件的内容</div>'
    });
    
    
    • 局部注册

    局部注册的组件只有在该实例作用域下有效。组件中也可以使用components选项来注册,使组件可以嵌套,示例如下:

    <div id="app">
        <my-component></my-component>
    </div>
    <script>
    var child = {
        template: '<div>局部注册组件的内容</div>'
    }
    var app = new Vue({
        el: '#app',
        components: {
            'my-component': child
        }
    })
    </script>
    
    

    Vue组件的模板在某些情况下会受到HTML的限制,比如table标签内只允许使用tr、th等表格元素,所以在table内直接使用组件是无效的。在这种情况下,可以使用特殊的is属性来挂载组件,示例如下:

    <table>
        <tbody is="my-component"></tbody>
    </table>
    
    

    tbody在渲染是会被替换为组件的内容,常用的限制元素如 ul、select等同理。

    父组件向子组件传值

    子组件通过props来声明需要从父组件接收的数据,props的值可以有两种,一种是字符串数组,一种是对象

    1. 字符串数组用法

    <div id="app">
        <my-component message="来自父组件的数据"></my-component>
    </div>
    <script>
    Vue.component('my-component', {
        props: ['message'],
        template: '<div>{{ message }}</div>'
    });
    var app = new Vue({
        el: '#app'
    })
    </script>
    
    

    渲染后的结果为

    <div id="app">
        <div>来自父组件的数据</div>
    </div>
    
    

    props中声明的数据与组件中data函数return的数据主要区别是:props的来自父级,而data中的是组件自己的数据,作用域是组件本身,这两种数据都可以在模板template及计算属性computed和方法methods中使用。

    在props中声明过的属性,不需要在data中重复声明,可直接使用

    由于HTML特性不区分大小写,当使用DOM模板时,驼峰命名(camelCase)的props名称要转为短横分隔命名(kebab-case);

    <div id="app">
        <my-component warning-text="提示信息"></my-component>
    </div>
    <script>
    Vue.component('my-component', {
        props: ['warningText'],
        template: '<div>{{ message }}</div>'
    });
    var app = new Vue({
        el: '#app'
    })
    </script>
    
    

    如果来自父级的数据并不是直接写死的,而是动态数据,这时可以使用指令v-bind来动态绑定props的值,当父组件的数据变化时,也会传递给子组件。

    <div id="app">
        <input type="text" v-model="parentMsg">
        <my-component :message="parentMsg"></my-component>
    </div>
    <script>
    Vue.component('my-component', {
        props: ['message'],
        template: '<div>{{ message }}</div>'
    });
    var app = new Vue({
        el: '#app',
        data() {
            return {
                parentMsg: ''
            }
        }
    })
    </script>
    
    
    • 单向数据流

    为了尽可能将父子组件解耦,避免子组件无意中修改了父组件的状态,Vue 2.x 之后通过props父组件向子组件传递数据是单向的。但有两种可改变prop的情况;

    1. 子组件中重新声明变量,在组件初始化时会获取来自父组件的数据,之后只用修改子组件的变量。

    <div id="app">
         <my-component :init-count="1"></my-component>
    </div>
    <script>
    Vue.component('my-component', {
        props: ['initCount'],
        template: '<div>{{ count }}</div>',
        data() {
            return {
                count: this.initCount
            }
        }
    });
    var app = new Vue({
        el: '#app'
    })
    </script>
    
    

    2. prop作为需要被转变的原始值传入时,可使用计算属性

    <div id="app">
        <my-component :width="100"></my-component>
    </div>
    <script>
    Vue.component('my-component', {
        props: ['width'],
        template: '<div :style="style">组件内容</div>',
        computed: {
            style() {
                return {
                    width: this.width + 'px'
                }
            }
        }
    });
    var app = new Vue({
        el: '#app'
    })
    </script>
    
    

    注意: 在JavaScript中,对象和数据是引用类型,指向同一个内存空间,所以props是对象和数组时,在子组件内改变是会影响父组件的。

    1. prop的对象用法 当prop需要验证时,就需要对象写法;
    Vue.component('my-component', {
        props: {
            propA: {
                type: [String, Number], // 必须是字符串或数字类型
                default: '',           // 默认值为空字符串
                required: true,        // 必传
                validator(value) {     // 自定义一个验证函数
                    return value > 10;
                }
            }
        }
    });
    
    

    验证的type类型可以是String、Number、Boolean、Object、Array、Function

    组件通信

    父子组件通信

    我们已经知道父组件向子组件通信,通过props传递数据子组件用$emit来触发事件,那父子组件、兄弟组件、跨级组件又是如何通讯的呢?请接着往下看:

    <div id="app">
        <p>总数:{{ total }}</p>
        <my-component @increse="handleIncrese"></my-component>
    </div>
    <script>
    Vue.component('my-component', {
        props: ['width'],
        template: '\
            <div>\
                <Button  @click="increse">+1</Button>\
            <div>',
        data() {
            return {
                counter: 0
            }
        },
        methods: {
            increse() {
                this.counter++;
                this.$emit('increse', this.counter);
            }
        }
    });
    var app = new Vue({
        el: '#app',
        data() {
            return {
                total: 0
            }
        },
        methods: {
            handleIncrese(count) {
                this.total = count;
            }
        }
    })
    </script>
    
    

    上面的示例中,在改变组件的data counter后,通过emit()再把它传递给父组件,父组件用v-on:recrese(示例使用的是语法糖),emit()方法的第一个参数是自定义事件名称,后面的参数是要传递的数据,可以不填或填多个。

    非父子组件通信

    Vue.js 2.x中,推荐使用空的vue实例作为中央事件总线(bus),

    先看下下面的示例,试着运行下,会有什么样的结果呢?

    <div id="app">
        <my-component :msg-a="otherMsg"></my-component>
        <other-component :msg-b="message"></other-component>
    </div>
    <script>
    var bus = new Vue();
    Vue.component('my-component', {
        props: ['msgA'],
        template: '\
            <div>\
            <p>A: {{msgA}}</p>\
                <Button @click="handleEvent">A兄弟组件</Button>\
            <div>',
        methods: {
            handleEvent() {
                bus.$emit('on-message', '来自A组件的内容');
            }
        }
    });
    Vue.component('other-component', {
        props: ['msgB'],
        template: '\
            <div>\
           <p> B:{{ msgB }}</p>\
                <Button @click="handleEvent1">B兄度组件</Button>\
            <div>',
        methods: {
            handleEvent1() {
                bus.$emit('other-message', '来自B组件内容');
            }
        }
    });
    var app = new Vue({
        el: '#app',
        data() {
            return {
                message: '',
                otherMsg: ''
            }
        },
        mounted() {
            // 在实例初始化时监听来自bus的事件
            bus.$on('on-message', (msg) => {
                this.message = msg;
            });
            bus.$on('other-message', (msg) => {
                this.otherMsg = msg;
            });
        }
    })
    </script>
    
    

    示例中首先创建了一个名为bus的空Vue实例,里面没有任何内容,然后全局定义了组件components,在app实例化时,在生命周期mounted钩子函数里监听来自bus的事件,可将组件A的内容传至组件B,组件B的内容传至组件A,从而巧妙的实现了任何组件间的通信。包括:父子、兄弟、跨级组件之间。

    子组件索引

    Vue提供了特殊的属性ref来为子组件指定一个索引名称,在父组件内通过this.$refs来访问指定名称的子组件

    <child-component ref="child"></child-component>
    
    

    refs只在组件渲染完后后才填充,并且是非响应式的,应当避免在模板或计算属性中使用refs。

    大家可以试着补全下面的代码,分别打印胡两个ref的内容看看会是什么?

    <div id="app">
        <p ref="p"></p>
        <child-component ref="child></child-component> 
    </div>
    

    相关文章

      网友评论

        本文标题:vue 组件化的入门详解

        本文链接:https://www.haomeiwen.com/subject/xrudsqtx.html