vue.js重学之旅(3)——vue.js组件学习and组件间通信

       组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。(摘自官网,说的挺好直接抄了)

    下一章开始,会封装几个具体的常用组件(alert/model/search)。

    关于组件学习的知识点:

    1.利用template参数替换自定义标签内容

    2.利用props传递数据(父传子

    3.props验证(补充内容)

    4.data字段详解

    5.用this.$emit传递数据(子传父

    6.单项数据流(补充内容)

    7.利用slot分发数据,创建自定义dom

    8.作用域问题


1.利用template参数替换自定义标签内容

    在学习组件之前,首先需要用Vue.component(tagName, options)方法注册一个全局组件(当然你也可以在Vue实例里的Compnent对象里放组件,个人觉得这个功能有点鸡肋,因为组件本身的意义就是公用的,放到全局里方便使用)。

第一个参数TagName是组件的名称,可以理解为H5新增的自定义标签。(当然你也可以称为声明式标签,因为他的命名一般都是有意义的,如sel-tmp,指的是选项卡)。

第二个参数是选项,跟Vue实例里的参数差不多,具体会一个一个说明,下面的demo里用到了template,是最基本的参数之一,他会替换自定义标签里的内容。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue.js学习--组件and组件间通信</title>
    <link rel="stylesheet" type="text/css"  href="/static/css/common.css">
    <link rel="stylesheet" type="text/css" href="/static/css/tmp.css">
</head>
<body>
<div id="app">
    <div style="margin: 20px">
        <sel-tmp></sel-tmp>
    </div>
</div>
</body>
<script src="/static/js/vue.js"></script>
<script src="/static/js/jquery-1.11.1.js"></script>
<script>
    //全局注册
    //Vue.component(tagName, options)
    Vue.component('sel-tmp', {
        //模板
        template:
        '<div class="tmp-drag-wrap">'+
        '<div class="tmp-drag-search">' +
        '<input type="text" class="tmp-drag-input" />'+
        '</div>'+
        '<ul class="tmp-drag-ul">' +
            '<li class="tmp-drag-li" >北京</li>'+
            '<li class="tmp-drag-li" >上海</li>'+
            '<li class="tmp-drag-li" >广州</li>'+
        '</ul>'+
        '</div>',
    })

    var vm = new Vue({
        delimiters:['${','}'],
        el:"#app",
        data:{
        }
    })
</script>
</html>


可以看到自定义标签整个被替换成了template里的内容,该自定义标签不会存在于dom文档流。


2.利用props传递数据(父传子

上面的demo里,Vue实例包含了<sel-tmp></sel-tmp>,所以Vue实例相对于模板即为父级,如果父级需要向子级(也就是模板)传递一个数据应该在子级里使用props参数接受数据。下面我将,【北京,上海,广州】作为一个数组,从父级向子级传递。并在子级里用props接收。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue.js学习--组件and组件间通信</title>
    <link rel="stylesheet" type="text/css"  href="/static/css/common.css">
    <link rel="stylesheet" type="text/css" href="/static/css/tmp.css">
</head>
<body>
<div id="app">
    <div style="margin: 20px">
        <sel-tmp v-bind:receive-list="postList"></sel-tmp>
    </div>
</div>
</body>
<script src="/static/js/vue.js"></script>
<script src="/static/js/jquery-1.11.1.js"></script>
<script>
    //全局注册
    //Vue.component(tagName, options)
    Vue.component('sel-tmp', {
        //利用props接收父级数据
        props:["receiveList"],
        //模板
        template:
        '<div class="tmp-drag-wrap">'+
        '<div class="tmp-drag-search">' +
        '<input type="text" class="tmp-drag-input" />'+
        '</div>'+
        '<ul class="tmp-drag-ul">' +
            '<li class="tmp-drag-li" v-for="item in receiveList" v-text="item"></li>'+
        '</ul>'+
        '</div>',

    })
    var vm = new Vue({
        delimiters:['${','}'],
        el:"#app",
        data:{
            postList:["北京","上海","杭州"],
        }
    })
</script>
</html>

效果跟1中的相同。关于数据传递,有一个不知是bug还是我不懂的地方:

dom里绑定数据的时候请用烤串命名法 v-bind:receive-list

函数内请用驼峰命名法  props:["receiveList"]


3.props验证(补充内容)

关于props参数:props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义校验和设置默认值

为什么要有props验证?

可以将props理解为子级提供给父级的一个输入口,如果父级输入的内容有误,比如在输入金额的输入框里,输入了中文,这个时候,子级就应该报错。然而父级并不一定知道子级需要什么内容(很有可能是某程序员负责写某一个组件,另一个程序员在某个模块中加一个这个组件,双方没有说明沟通),所以vue.js提供了props验证来防止这种情况发生。

 props:{
            "receiveList":{
                //type:Number, //验证类型
                //default:"",//默认值,如年龄可默认为一岁,性别默认为男等
                //required:true or false,//传参是否必填
                validator:function(value){
                    console.log(value);//value = 父级传过来的值
                    //return true or false;
                    //比如来验证一下父级传过来的arr.lenght是否大于0,最好先判断传过来的是不是数组
                    return value.length>=4?true:false;
                }
            }
        },

事实上,如果传过来的数据有误,vue只会在控制台报错,并不会阻塞程序执行。所以验不验证对不看控制台的人来说是没啥意义的。(暗示某些测试测不出来的bug)


4.data字段详解

上一章对于vue实例里的data字段埋了一个坑,这里来填一下。

首先作为组件,可能会在一个页面多次用到,下面我通过实现一个功能来讲一下data里的坑。

功能:鼠标点击输入框,实现下拉框的显示和隐藏

实现:需要在data里添加一个ifShow字段来控制模板

 template:
        '<div class="tmp-drag-wrap">'+
        '<div class="tmp-drag-search">' +
        '<input type="text" class="tmp-drag-input" @click="ifShow=!ifShow" />'+
        '</div>'+
        '<ul class="tmp-drag-ul" v-show="ifShow">' +
            '<li class="tmp-drag-li" v-for="item in receiveList" v-text="item"></li>'+
        '</ul>'+
        '</div>',
        //data需要用函数返回值,避免多个组件公用一个data对象
        data:function(){
            return {
                ifShow:false,
            }
        },

在vue.component里,data不能是对象的形式,会直接报错。

可以用函数返回值的形式,避免组件间公用同一数据而相互影响。


5.用this.$emit传递数据(子传父

刚才的组件再划分一下,将输入框和下拉框分开,让下拉框单独出来作为一个组件(纯粹为了方便讲解)

现在,下拉框变成了子级,父级则是由下拉框组件+输入框,要把下拉框的内容传递给父级,使用了实例中的$emit触发自定义事件,并将参数传给父级。看代码注释:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue.js学习--组件学习</title>
    <link rel="stylesheet" type="text/css"  href="/static/css/common.css">
    <link rel="stylesheet" type="text/css" href="/static/css/tmp.css">
</head>
<body>
<div id="app">
    <div style="margin: 20px">
        <sel-tmp v-bind:name-arr="list"></sel-tmp>
    </div>
</div>
</body>
<script src="/static/js/vue.js"></script>
<script src="/static/js/jquery-1.11.1.js"></script>
<script>
    //全局注册
    Vue.component('sel-tmp', {
        // 声明 props
        props: ['nameArr'],
        template:
        '<div class="tmp-drag-wrap">'+
        '<div class="tmp-drag-search">' +
        '<input type="text" class="tmp-drag-input" @click="ifShow=!ifShow" v-model="content"/>'+
        '</div>'+
        '<ul-tmp :nameArr="nameArr" :ifShow="ifShow" @receive="receiveData"></ul-tmp>'+ //自定义事件触发父级函数
        '</div>',
        data:function(){
            return {
                ifShow:false,
                content:"",
            }
        },
        methods:{
            receiveData:function(args){
                this.content = args; //父级接收自定义函数参数
            }
        }
    })

    Vue.component("ul-tmp",{
        props:["nameArr","ifShow"],
        template:
        '<ul class="tmp-drag-ul" v-show="ifShow">' +
            '<li class="tmp-drag-li" v-for="item in nameArr" v-text="item" @click="postData(item)"></li>'+
        '</ul>',
        methods:{
            postData:function(item){
                this.$emit("receive",item); //子级触发自定义事件,并把想传的参数传出去
            }
        }
    })

    var vm = new Vue({
        delimiters:['${','}'],
        el:"#app",
        data:{
            list:["北京","上海","杭州"],
        }
    })
</script>
</html>

如此,完成了子级向父级传递数据。


 6.单项数据流(补充内容)

单项数据流是个神马东西?官网里并没有有关于这项东西的说明,我个人也还没完全摸透这块东西。

首先来解释一下这个名词(用自己语言组织一下)

在组件中,利用props父级向子级传递数据后,子级内部对数据进行了处理,如改变数据的某个值,这时候控制台就会报错(又是一个只报错不影响结果的破玩意儿)。

解决方案:在子级作用域中,将接收的值存入私有的data 或者 computed里,就可以操作(他只是不报错而已)。

来说一下我没搞明白的地方,就是已经标出来的“改变数据”四个字。如,我对上面父级传过来的地名数组进行处理,会发生以下两种情况,

methods:{
            postData:function(item){
                //this.nameArr=["北京","上海","杭州","广州"];//如果这样写,会报错,因为修改了整个数组
                //this.nameArr.push("广州");//这样写就不会报错,而且能push数据进去
                this.$emit("receive",item);
            }
        }

两种修改数据的方法都能执行,只是前者会报错,后者不报错,有兴趣的自己试试。

7和8的内容放到下一章讲吧,在研究研究...


猜你喜欢

转载自blog.csdn.net/dkr380205984/article/details/79368780