(七)VUE组件

   组件是可复用的 Vue 实例,且带有一个名字,可以把这个组件作为自定义元素来使用,组件化与模块化的功能类似,区别在于:

  • 模块化是从代码逻辑的角度划分,方便代码分层,保证每个功能模块的职能单一;
  • 组件化是从UI界面的角度划分,前端的组件化,是为了方便UI组件的重用。

一、组件注册

1、全局组件注册
1.1 Vue.extend() 配合 Vue.component()

   使用基础 Vue 构造器,创建一个组件模板对象,传入到 Vue.component() 中。

	var my-component-name = Vue.extend({
			// 通过 template 属性指定了要展示的HTML结构
			template: 'HTML元素'
		})
	
	Vue.component('my-component-name', my-component-name)
1.2 直接使用Vue.component()

   直接向 Vue.component() 中传入一个字面量对象。

    Vue.component("my-component", {
        template: '<p>This is a content.</p>'
    })
1.3 在 <template> 标签中定义模板字符串

   在被Vue实例控制的区域外,使用 <template> 元素定义组件的HTML模板结构,然后将元素id传入到 template 属性中。

    <div id="app">
    	<!-- 组件使用 -->
        <my-com></my-com>
    </div>

    <template id="my-com">
        <p>This is a content.</p>
    </template>

    <script>
        Vue.component("my-com", {
            template: '#my-com'
        })

        var vm = new Vue({
            el: "#app",
        })
    </script>
2、局部组件注册

   可以在Vue实例内,定义局部组件。

    var vm = new Vue({
        el: "#app",
        components: {
            'my-com': {
                template: '<p>This is a content.</p>'
            }
        }
    })

注意

  • 如果组件名称使用了大写字母,在使用时需要将大写字母替换为 ‘-小写字母’,例如组件名称 ‘myCom’ ,在使用时的名称为 ‘my-com’;
  • 组件的 template 属性指向的模板内容,必须有一个唯一的根元素;

二、组件属性

   组件与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等(el 选项是根实例特有的)。特别地,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

<!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>
    <script src="../vue.min.js"></script>
</head>
<body>
    <div id="app">
        <counter></counter>
        <counter></counter>
    </div>

    <template id="counter">
        <!-- 注意:这里外部需有个div容器,为了保证根元素唯一 -->
        <div>
            <input type="button" value="+1" @click="add">
            <p>{{ count }}</p>
        </div>
    </template>

    <script>
        Vue.component("counter", {
            template: '#counter',
            // data属性是个函数,且返回值为对象
            data: function(){
                return {count: 0}
            },
            methods: {
                add: function(){
                    this.count++
                }
            }
        })

        var vm = new Vue({
            el: "#app",
        })
    </script>
</body>
</html>

三、动态组件

   不同组件之间可以进行动态切换。

1、指令控制切换

   可以通过条件指令 v-if、v-else ,来控制组件的显示。

<!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>
    <script src="../vue.min.js"></script>
</head>
<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>

    <template id="login">
        <p>登录组件</p>
    </template>

    <template id="register">
        <p>注册组件</p>
    </template>

    <script>
        Vue.component("login", {
            template: '#login'
        })

        Vue.component("register", {
            template: '#register'
        })

        var vm = new Vue({
            el: "#app",
            data: {
                flag: true
            }
        })
    </script>
</body>
</html>
2、is 特性实现切换

   Vue 的 <component> 元素中一个特殊的 is 特性可以实现组件切换,currentTabComponent 可以包括:

  • 已注册组件的名字;
  • 一个组件的选项对象;
	<!-- 组件会在 `currentTabComponent` 改变时改变 -->
	<component v-bind:is="currentTabComponent"></component>
  • 示例代码:
    <div id="app">
        <a href="" @click.prevent="comName='login'">登录</a>
        <a href="" @click.prevent="comName='register'">注册</a>

        <!-- 由变量传入组件名称 -->
        <component :is="comName"></component>
    </div>
3、组件切换过渡

   多个组件的过渡不需要使用 key 特性,只需要使用动态组件:

    <!-- mode设置动画模式,这里设置为先出再进模式 -->
    <transition mode="out-in">
        <component :is="comName"></component>
    </transition>

四、组件传值

1、传属性值

   prop 是你可以在组件上注册的一些自定义特性,可以使用 v-bind 来动态传递 prop。子组件默认无法访问父组件的 data 数据,此时,我们可以通过 prop 向子组件传值:

<!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>
    <script src="../vue.min.js"></script>
</head>
<body>
    <div id="app">
        <!-- 父组件传值 -->
        <login :msg="msg"></login>
    </div>

    <template id="login">
        <p>{{ msg }}</p>
    </template>

    <script>
        Vue.component("login", {
            template: '#login',
            // 父组件传入的值的名称需在prop中声明
            props: ['msg']
        })

        var vm = new Vue({
            el: "#app",
            data: {
                msg: 'a message form vm'
            }
        })
    </script>
</body>
</html>

   当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性,我们能够在组件实例中访问这个值,就像访问 data 中的值一样。组件中的 data 数据和 prop 数据区别在于,prop 中的属性值是只可读的且是由父组件传入的。

2、传方法值

   可以通过自定义事件向子组件传递方法,我们通过事件绑定机制向自定义事件绑定方法,子组件就可以通过 “this.$emit(‘myevent’) ”调用传递过来的方法。

   注意,v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。

    <!-- 父组件传方法 -->
    <login @myShow="show"></login>

    Vue.component("login", {
		template: '#login',
		methods: {
			myShow: function(){
				// 注意,即使事件绑定时的名称含大写,这里也需要小写
				this.$emit('myshow')
			}
		}
	})

   在子组件调用父组件的方法时,我们可以通过函数传参的方式,从子组件向父组件传值:

<!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>
    <script src="../vue.min.js"></script>
</head>
<body>
    <div id="app">
        <!-- 父组件传方法 -->
        <login @myShow="show"></login>
    </div>

    <template id="login">
        <input type="button" value="show" @click="myShow">
    </template>

    <script>
        Vue.component("login", {
            template: '#login',
            data: function(){
                return {sonMsg: 'message from son component'}
            },
            methods: {
                myShow: function(){
                    // 注意,即使事件绑定时的名称含大写,这里也需要小写
                    // 通过函数传参向父组件传值
                    this.$emit('myshow', this.sonMsg)
                }
            }
        })

        var vm = new Vue({
            el: "#app",
            data: {
                parentMsg: null
            },
            methods: {
                show: function(msg){
                	// 接收子组件传递的值
                    this.parentMsg = msg
                    console.log(this.parentMsg)
                }
            }
        })
    </script>
</body>
</html>

五、获取组件引用

   JavaScript 可以通过 ref 特性为子组件赋予一个 ID 引用,以便直接访问这个子组件。Vue实例中的 vm.$refs 对象,持有注册过 ref 特性 的所有 DOM 元素和组件实例。

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。

<!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>
    <script src="../vue.min.js"></script>
</head>
<body>
    <div id="app">
        <login ref="myLogin"></login>
        <input type="button" value="获取组件" @click="show">
    </div>

    <template id="login">
        <div></div>
    </template>

    <script>
        Vue.component("login", {
            template: '#login',
            data: function(){
                return {msg: 'message from component'}
            }
        })

        var vm = new Vue({
            el: "#app",
            methods: {
                show: function(){
                    console.log(this.$refs.myLogin.msg)
                }
            }
        })
    </script>
</body>
</html>

六、综合案例

   一个具有发表评论功能的案例,我们将发表评论的UI区域做成组件,这里还用到了 localStorage 来对评论列表进行缓存。

   只读的 Window.localStorage 属性允许你访问一个Document 源(origin)的对象 Storage,且存储的数据将保存在浏览器会话中。

<!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>
    <script src="../vue.min.js"></script>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div id="app">
        <comment-box @func="loadComments"></comment-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="comment-box">
        <div>
            <div class="form-group">
                <label>评论人:</label>
                <input type="text" class="form-control" v-model="user">
            </div>

            <div class="form-group">
                <label>评论内容:</label>
                <textarea type="text" 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 vm = new Vue({
            el: "#app",
            data: {
                list: []
            },
            components: {
                'comment-box': {
                    template: "#comment-box",
                    data: function(){
                        return {user: '', content: ''}
                    },
                    methods: {
                        postComment: function(){
                            // 0、判断输入是否有效
                            if(this.user == '' && this.content == '') return
                            // 1、构造评论对象
                            var comment = {id: Date.now(), user: this.user, content: this.content}
                            // 2、从localStorage获取评论列表,考虑初始情况
                            var list = JSON.parse(localStorage.getItem('cmts') || '[]')
                            // 3、将新评论对象加入列表
                            list.unshift(comment)
                            // 4、更新localStorage
                            localStorage.setItem('cmts', JSON.stringify(list))
                            // 5、清空输入框
                            this.user = this.content = ''
                            // 6、刷新列表
                            this.$emit('func')
                        }
                    }
                }
            },
            methods: {
                // 自动加载本地的评论列表
                loadComments: function(){
                    this.list = JSON.parse(localStorage.getItem('cmts') || '[]')
                }
            },
            created(){
                this.loadComments()
            }
        })
    </script>
</body>
</html>

参考链接:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值