Vue.js 组件(四)

1. 组件

1.1 全局组件

全局组件,全局任何 Vue 实例都可以使用,在 Vue 实例创建之前创建。

1. 方式一

<body>
    <div id="app">
        <!-- 使用 -->
        <mycom1></mycom1>
        <!-- <my-Com1></my-Com1> -->
    </div>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        // Vue.extend 创建全局组件
        var com1 = Vue.extend({
            template: '<h3>全局组件</h3>'
        })

        // Vue.component('组件名称', 创建出来的组件模块对象)
        // 若组件名称使用驼峰名,那么使用组件名时中间必须有横线 -
        // Vue.component('myCom1', com1)
        Vue.component('mycom1', com1)

        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {}
        })
    </script>
</body>

简写,省略为一步:

 Vue.component('mycom1', Vue.extend({
    template: '<h3>全局组件</h3>'
}))

2. 方式二

  • 可以省略 Vue.extend
  • 将组件模板直接定义在 template 元素中,然后定义 id 属性,最后在组件中取 id 属性即可
<body>
    <div id="app">
        <mycom2></mycom2>
        <mycom3></mycom3>
    </div>

    <!-- template 必须 Vue 实例之外 -->
    <template id="com3">
        <h2>在 template 中定义模板</h2>
    </template>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        Vue.component('mycom2', {
            template: '<h3>全局组件创建方式二</h3>'
        })

        Vue.component('mycom3', {
            template: '#com3'
        })

        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {}
        })
    </script>
</body>

Tips:template 必须 Vue 实例之外,模板中有且只能有一个根元素,不能有多个根元素

<!-- 不能存在多个根元素 -->
<template id="com3">
    <h2>在 template 中定义模板</h2>
    <h3>哈哈</h3>
</template>

<!-- 只能有一个根元素 -->
<template id="com3">
    <div>
        <h2>在 template 中定义模板</h2>
        <h3>哈哈</h3>
    </div>
</template>

1.2 局部组件

局部组件,Vue 实例中定义的组件,仅能 Vue 实例使用。

<body>
    <div id="app">
        <mycom4></mycom4>
    </div>

    <template id="com4">
        <h2>局部组件</h2>
    </template>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {},
            components: {
                // 组件名称
                mycom4: {
                    template: '#com4'
                }
            }
        })
    </script>
</body>

1.3 组件 data 和 method

  • 组件 data 必须是一个方法
  • data 内部必须返回一个对象
  • 使用方式与实例中 data 一致
  • method:与实例中一样
<body>
    <div id="app">
        <mycom4></mycom4>
    </div>

    <template id="com4">
        <div>
            <h2>局部组件:{{ msg }}</h2>
            <input type="button" value="按钮" @click="btn">
            <p v-if="flag">{{ msg }}</p>
        </div>
    </template>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        // 组件 data 必须是一个方法
        // data 内部必须返回一个对象才行
        // 使用方式与实例中 data 一致
        Vue.component('mycom4', {
            template: '#com4',
            data: function () {
                return {
                    msg: '组件 data',
                    flag: false
                }
            },
            methods: {
                btn() {
                    this.flag = true
                }
            }
        })

        var vm = new Vue({
            el: '#app',
            data: {},
            methods: {},
        })
    </script>
</body>

1.4 组件切换

在同一元素区域通过点击不同按钮,来显示不同元素,就可以使用组件切换来实现,比较常见的有:登陆注册

  • 点击登陆按钮,切换到登录框
  • 点击注册按钮,切换到注册框

1.4.1 使用 flag 标识符结合 v-if 和 v-else 切换组件

<body>
    <div id="app">
        <a href="" @click.prevent="flag=true">登陆</a>
        <a href="" @click.prevent="flag=false">注册</a>

        <login v-if="flag"></login> 
        <register v-else="flag"></register>
    </div>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        // 登陆组件
        Vue.component('login', {
            template: '<h3>登陆组件</h3>'
        })

        // 注册组件
        Vue.component('register', {
            template: '<h3>注册组件</h3>'
        })

        var vm = new Vue({
            el: '#app',
            data: {
                flag: false
            },
            methods: {},
        })
    </script>
</body>

缺点

flag 标识符只有两种状态,truefalse,即最多只能切换两个组件

1.4.2 使用 :is 属性来切换不同的子组件

Vue 提供 :is 属性来解决组件之间的切换,它没有组件数量的限制。

<body>
    <div id="app">
        <a href="" @click.prevent="comName='login'">登陆</a>
        <a href="" @click.prevent="comName='register'">注册</a>

        <!-- Vue提供了 component ,来展示对应名称的组件 -->
        <!-- component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称 -->
        <component :is="comName">

        </component>

    </div>

    <script src="./lib/vue-2.4.0.js"></script>
    <script>
        // 登陆组件
        Vue.component('login', {
            template: '<h3>登陆组件</h3>'
        })

        // 注册组件
        Vue.component('register', {
            template: '<h3>注册组件</h3>'
        })

        var vm = new Vue({
            el: '#app',
            data: {
                comName: 'login'     // 当前 component 中的 :is 绑定的组件的名称
            },
            methods: {},
        })
    </script>
</body>

默认 comNamelogin,即为登陆组件,等点击注册按钮时,comName 赋值为 register,即可切换到注册组件。

1.4.3 组件切换动画

1、使用 transition 元素包裹 componentmode 属性设置组件切换时候的模式:

<div id="app">
    <a href="" @click.prevent="comName='login'">登陆</a>
    <a href="" @click.prevent="comName='register'">注册</a>

    <!-- 通过 mode 属性,设置组件切换时候的模式,先出后进,这样就不会有前一个组件还未消失,后一个组件就切换进入 -->
    <transition mode="out-in">
        <component :is="comName"></component>
    </transition>
</div>

2、设置切换样式:

 <style>
    .v-enter,
    .v-leave-to {
        opacity: 0;
        transform: translateX(150px);
    }

    .v-enter-active,
    .v-leave-active {
        transition: all 0.5s ease;
    }
</style>

参考文章:

1.5 父子组件间传值

1.5.1 父组件向子组件传值

在子组件中不能直接使用父组件的 data 上的数据 和 methods 中的方法,需要通过 props 传递,两个步骤:

1、使用 v-bind 或简化指令,将数据传递到子组件中:

<div id="app">
    <!-- 给子组件绑定一个属性 parentmsg,值为 msg,即父组件的 data -->
    <com1 v-bind:parentmsg="msg"></com1>
</div>

2、使用 props 属性来定义父组件传递过来的数据:

<script src="./lib/vue-2.4.0.js"></script>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            msg: "父组件"
        },
        methods: {

        },
        components: {
            // data 上的数据,都是可读可写的;
            data() {
                return {
                    title: 123
                }
            }
            com1: {
                template: '<h3>子组件 :{{ parentmsg }}</h3>',      // 在子组件中使用
                props: ["parentmsg"]        // 在 porps 中定义该属性, props 中的数据,都是只读的,无法重新赋值
            },
        }
    })
</script>

1.5.2 父组件方法传递给子组件

如何将父组件 method 传递给子组件:

1、父组件将方法的引用(字符串形式),传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时将要传递给父组件的数据,在调用方式时
当做参数传递进去。

<div id="app">
    <!-- 父组件向组组件传递 method,使用事件绑定机制 v-on,当自定义一个事件属性后,子组件中就能够通过某些方式 -->
    <!-- 来调用传递进来的这个方法 -->
    <com @func="show"></com>
</div>

<template id="tmp1">
    <div>
        <h1>子组件</h1>
        <input type="button" value="点击触发父组件传递过来的 func 方法" @click="myclick">
    </div>
</template>

2、子组件内部通过 this.$emit('方法名', 参数) 方法,来调用父组件中的方法,同时将数据传递给父组件:

<script src="./lib/vue-2.4.0.js"></script>
<script>
    // 定义一个字面量的全局子组件
    var com = {
        template: '#tmp1',      // 指定 ID,加载 template 元素中内容
        data() {
            return {
                somMsg: { 'name': 'rose'}
            }
        },
        methods: {
            myclick() {
                // 子组件中通过 this.emit('父组件事件属性', 参数一,参数二...),即可调用父组件中传递过来的方法
                this.$emit('func', this.somMsg)
            }
        }
    }

    var vm = new Vue({
        el: '#app',
        data: {
            datamsgFromdson: null
        },
        methods: {
            show(data) {
                console.log(data)
                this.datamsgFromdson = data
            }
        },
        components: {
            com         // 注册子组件
        }
    })
</script>

1.5.3 评论列表示例

练习:父子之间 datamethod 传递。

1、html

<div id="app">
    <com @func="add"></com>

    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <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>
    </div>
</div>

<!-- 子组件模板 -->
<template id="tmp1">
    <div class="row" style="margin-top: 5%;">
        <div class="col-md-8 col-md-offset-2">
            <div class="form-group">
                <label for="">评论人:</label>
                <input type="text" class="form-control" v-model="user">
            </div>

            <div class="form-group">
                <label for="">评论内容:</label>
                <input type="text" class="form-control" v-model="content">
            </div>

            <div class="form-group">
                <input type="button" class="btn btn-primary" value="发表评论" @click="postComment">
            </div>
        </div>
    </div>
</template>

2、js

<script src="./lib/vue-2.4.0.js"></script>
<script>
    var com = {
        template: '#tmp1',
        data() {
            return {
                user: '',
                content: '',
            }
        },
        methods: {
            // 发表评论
            postComment() {
                // this.$emit('func', this.user, this.content)
                // this.user = this.content = ''

                // 存入 localStorage
                // 1、组织一个评论数据对象
                // 2、localStorage 仅支持字符串,JSON.stringify() 转换为字符串
                // 3、保存最新的评论之前,从 localStorage 中获取之前的评论,转换为数组,再将最新的评论 unshift 到这个数组
                // 4、若获取的评论为空,不存在,则返回一个 [],JSON.parse 转换不会报错
                // 5、再将评论数组转换为字符串,setItem 存入到 localStorage 中
                var comment = { id: new Date(), 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')
            }
        }
    }

    var vm = new Vue({
        el: '#app',
        data: {
            list: [
                { id: Date.now(), user: '李白', content: '天生我材必有用' },
                { id: Date.now(), user: '江小白', content: '劝君更尽一杯酒' },
                { id: Date.now(), user: '小马', content: '我姓马, 风吹草低见牛羊的马' }
            ]
        },
        created() {
            this.add()
        },
        methods: {
            // add(name, comment) {
            //     this.list.unshift({ id: Date.now(), user: name, content: comment})
            // }

            add() {
                // 从 localStorage 中取出评论数据
                var list = JSON.parse(localStorage.getItem('cmts') || '[]')
                this.list = list

            }
        },
        components: {
            com
        }
    })
</script>

将新评论存储到 localStorage 中,父组件 add 方法再从 localStorage 中获取所有评论列表,并将其渲染到页面中,
created 生命周期函数中调用 add 方法,以保证刷新时最新评论不会消失。

2.6 ref 获取 DOM 元素和组件

使用 this.$refs 来获取元素对象和组件(数据、方法),this.$refs 获取的是 DOM 元素对象,因此可以通过其获取元素的文本、属性等。

<div id="app">
    <input type="button" value="获取元素" @click="getElement">
    <h3 ref="myh3">标题三</h3>

    <com ref="son"></com>
</div>

<template id="tmp1">
    <h1>子组件,标题一</h1>
</template>

<script src="./lib/vue-2.4.0.js"></script>
<script>
    var com = {
        template: '#tmp1',
        data() {
            return {
                msg: "som msg"
            }
        },
        methods: {
            show() {
                console.log('子组件方法')
            }
        }
    }

    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            getElement() {
                // 获取 h3 的文本值
                // console.log(this.$refs.myh3.innerText)
                
                // 获取子组件的 data 数据
                // console.log(this.$refs.son.msg)

                // 执行子组件的 show 方法
                this.$refs.son.show()
            }
        },
        components: {
            com
        }
    })
</script>

Tips:在浏览器中 F12 -- console 中,vm.$refs 可查看页面中的 refs 对象,refs 也可以用来替代父子组件直接传值

原文地址:https://www.cnblogs.com/midworld/p/13611047.html