Vue学习-组件中的数据通信

组件可以访问Vue实例数据吗?

  • 组件是一个单独功能模块的封装,有属于自己的HTML模板,也有自己的数据data

  • 组件中不能直接访问Vue实例里的数据,以下代码在组件是不能访问到message的

<div id="app">
     <hello></hello>
</div>

<template id="myId">
      <div>消息内容:{{message}}</div>
</template>

<script src="vue.js"></script>
<script>
let app=new Vue({
      el:"#app",
      data:{
           message:"hello world"
      },
      components:{
            'hello':{
                  template:"#myId"
            }
      }
})
</script>

组件的data参数

  • 组件定义时也有一个data参数,但这个data参数不像Vue实例的data是一个对象类型,它这里是一个函数类型,如果你定义成了一个对象类型,会报以下错误
  • 该data函数需要返回一个对象,对象内部保存着数据
  • 示例代码:https://jsrun.net/EevKp/edit
const cpnC = Vue.extend({
    template: "#cpn",
    data(){
        return {
            title:"这里是标题吧",
            content:"这里是内容哦!~~"
        }
    }
})

组件中的methods函数

const cpnC = Vue.extend({
    template: "#cpn",
    data(){
        return {
            counter:0
        }
    },
    methods:{
        increment:function(){
            this.counter++
        },
        decrement:function(){
            this.counter--
        }
    }
})

父子组件

使用场景

  • 比如一个商城的首页,上面有一个轮播图,下面有一个商品的列表
  • 像这种页面,我们在开发时,会在最外层组件(父组件)里,向服务端一次请求到轮播图,商品列表数据,然后将数据分别传给轮播图组件,商品列表组件;然后商品列表组件在将数据传传商品组件来进行整个页面的渲染

如何通信(传递数据)

  • Vue实例也可以看作一个父组件

父传子

  • 通过prots向子组件传数据(protsproterties的简写,意为属性
  • props的二种方式
    1.一种是可以传一个字符串数组,里面第一项对应变量名
    将godos和message传给子组件进行渲染,示例代码:http://jsrun.net/5PvKp/edit
<div id="app">
	<my-cpn v-bind:cgoods="goods" :cmessage="message"></my-cpn>
</div>

<script type="text/x-template" id="cpn1">
	<div>
		<h2>{{cmessage}}</h2>
		<ul>
            <li v-for="item in cgoods">{{item}}</li>
        </ul>
	</div>
</script>


//1.Vue.extend创建组件
const cpnC = Vue.extend({
    template: "#cpn1",
    props:['cgoods','cmessage']
})

//2.注册组件
Vue.component('my-cpn', cpnC)

const app = new Vue({
    el: "#app",
    data: {
        message: "我是message",
        goods:["衣服","鞋子","帽子","短裤"]
    }
})

2.第二种是传一个对象类型,可以进行类型限制type和默认值default,是否必需reauired
type-指定参数的类型,有:String,Array,Boolean,Number,Date,Function,Symbol
default-指定默认值
reauired-指定是否必须要传该参数,是boolean类型,传(true或false)

const cpnC = Vue.extend({
    template: "#cpn1",
    props: {
        //1.类型限制
        // cgoods: Array,
        // cmessage: String

        //2.提供默认值,没传时显示
        cgoods:{
            type:Array,
            default:[]
        },
        cmessage:{
            type:String,
            default:"这里是默认值",
            required:true
        }
    }
})

  • props的一些使用示例
Vue.component('cnp',{
      props:{
            //1.基础的类型检查(null匹配任何类型)
            propA:Number,
            //2.多个可能的类型
            propB:[String,Number],
            //3.必填的字符串
            propC:{
                  type:String,
                  required:true
            },
            //4.带有默认值的数字
            propD:{
                  type:Number,
                  default:100
            },
            //5.默认值的对象,数组或对象的需要返回一个函数
            propE:{
                  type:Object,
                  default:function(){
                        return {name:"hello"}
                  }
            },
            //6.带自定义验证函数
            propF:{
                  validator:function(val){
                        //这个val必须匹配下列数组中的一个
                        return ['张三','李四','王五'].indexOf(val) !== -1
                  }
            }
      }
})
  • 组件中驼峰标识,props中定义了cTitle,但在组件使用是需要变为:c-title,子组件中使用时又为cTitle
<div id="app">
	<my-cpn :c-title="title"></my-cpn>
</div>

<script type="text/x-template" id="cpn1">
	<div>
        <h1>{{cTitle}}</h1>
	</div>
</script>

//1.Vue.extend创建组件
const cpnC = Vue.extend({
    template: "#cpn1",
    props: {
        //驼峰标识
        cTitle:{
            type:String
        }
    }
})

//2.注册组件
Vue.component('my-cpn', cpnC)

const app = new Vue({
    el: "#app",
    data: {
        title:"我是驼峰标识"
    }
})

子传父

<div id="app">
	<my-cpn @itemclick="btnClick"></my-cpn>
	<h3>{{message}}</h3>
	<image width="200px" :src="imgUrl"></image>
</div>

<script type="text/x-template" id="cpn1">
	<div>
	   <button @click="btnclick(stu)" v-for="stu in students">
              {{stu.name}}
          </button>
	</div>
</script>
//1.Vue.extend创建组件
const cpnC = Vue.extend({
    template: "#cpn1",
    data: function () {
        return {
            students: [
                { id: "001", name: "关晓彤",imgUrl:"https://5b0988e595225.cdn.sohucs.com/images/20181224/ecf125cb199d4808a067f682c6c5e6c6.jpeg" },
                { id: "002", name: "迪丽热巴",imgUrl:"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1593861542943&di=9fe9365124a4aae6cc6cf483aa2a9e18&imgtype=0&src=http%3A%2F%2Fb.hiphotos.baidu.com%2Fzhidao%2Fwh%253D680%252C800%2Fsign%3Dbb541c4377f40ad115b1cfe56f1c3de7%2F50da81cb39dbb6fde349ce5d0524ab18972b37be.jpg" },
                { id: "003", name: "郑爽",imgUrl:"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1593861570612&di=60b6d39dcbac9e1ea77b747d42f9f19d&imgtype=0&src=http%3A%2F%2Ftc.sinaimg.cn%2Fmaxwidth.800%2Ftc.service.weibo.com%2Fmmbiz_qpic_cn%2F1ab918195e7c8a94cc6c95182fbde786.jpg" },
                { id: "004", name: "杨幂",imgUrl:"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1593861759697&di=6771e7279c145513968ce221a29a6b0e&imgtype=0&src=http%3A%2F%2Fphotocdn.sohu.com%2F20130816%2FImg384334041.jpg" }
            ]
        }
    },
    methods: {
        btnclick(item) {
            //发射事件,自定义事件
            this.$emit("itemclick", item)
        }
    }
})

const app = new Vue({
    el: "#app",
    components: {
        "my-cpn": cpnC
    },
    data: {
        message: "",
        imgUrl:""
    },
    methods: {
        btnClick: function (item) {
            this.message = "你选择了:" + item.name + ",学号:" + item.id
            this.imgUrl=item.imgUrl
        }
    }
})

组件中的双向绑定

<div id="app">
	<my-cpn :number1="num"></my-cpn>
</div>

<script type="text/x-template" id="cpn">
	<div>
		<h2>{{dnumber}}</h2>
	    <input type="text" :value="dnumber" @input="numInput"></input>
	</div>
</script>

//1.Vue.extend创建组件
const cpnC = Vue.extend({
    template: "#cpn",
    data(){
        return {
            dnumber:this.number1
        }
    },
    props:{
        number1:{
            type:Number,
            default:0
        }
    },
    methods:{
        numInput:function(event){
            this.dnumber=event.target.value
        }
    }
})

const app = new Vue({
    el: "#app",
    components:{
        'my-cpn':cpnC
    },
    data: {
        num: 1
    }
})

父组件访问子组件($children或$refs)

  • $children-是一个数组,通过下标访问,这种方式不建议使用,因为组件变化维护后,相应的下标也要变化,后期难维护
    示例代码:http://jsrun.net/VcvKp/edit
<div id="app">
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <button @click="btnClick">点击</button>
</div>

<script type="text/x-template" id="cpn">
	<div>
		<h2>{{title}}</h2>
	</div>
</script>

const app = new Vue({
    el: "#app",
    methods:{
        btnClick:function(){
            //1.$children是一个数组
            alert(this.$children[0].title)
        },
    },
    components:{
        'my-cpn':{
            template:"#cpn",
            data(){
                return {
                    title:"我是子组件的标题"
                }
            }
        }
    }
})
<div id="app">
    <my-cpn ref="aaa"></my-cpn>
    <my-cpn ref="bbb"></my-cpn>
    <button @click="btnClick">点击</button>
</div>

<script type="text/x-template" id="cpn">
	<div>
		<h2>{{title}}</h2>
	</div>
</script>

const app = new Vue({
    el: "#app",
    methods:{
        btnClick:function(){
            //1.$children是一个数组
            //alert(this.$children[0].title)

            alert(this.$refs.aaa.title)
        },
    },
    components:{
        'my-cpn':{
            template:"#cpn",
            data(){
                return {
                    title:"我是子组件的标题"
                }
            }
        }
    }
})

子组件访问父组件($parent)

  • 子组件通过$parent访问父组件
  • $root可以用来访问根组件,一般是Vue实例
    示例代码:http://jsrun.net/dcvKp/edit
<div id="app" class="main">
    <h2>{{message}}</h2>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
</div>

<script type="text/x-template" id="cpn">
	<div class="cpn">
	我是子组件
	<button @click="btnClick">点击</button>
	</div>
</script>
const app = new Vue({
    el: "#app",
    data: {
        message: "我是Vue标题"
    },
    components: {
        'my-cpn': {
            template: "#cpn",
            methods: {
                btnClick: function () {
                    alert(this.$parent.message)
                    //访问根组件还可以使用$root,根组件一般是Vue实例
                    //我们这里的parent就等于根组件
                    //alert(this.$root.message)
                }
            }
        }
    }
})

组件的扩展-slot

基本使用

<div id="app" class="main">
	<h2>{{message}}</h2>
	<my-cpn>
        <span>我是slot</span>
    </my-cpn>
    <my-cpn>
        <button>我是slot按钮</button>
        <sapn>我是slot span</sapn>
    </my-cpn>
    <my-cpn></my-cpn>
</div>

<script type="text/x-template" id="cpn">
	<div class="cpn">
	    我是组件
        <div class="div-slot">
            <slot><i>我是slot的默认值</i></slot>
        </div>
	</div>
</script>
const app = new Vue({
    el: "#app",
    data: {
        message: "我是Vue标题"
    },
    components: {
        'my-cpn': {
            template: "#cpn"
        }
    }
})

具名插槽

<div id="app" class="main">
	<h2>{{message}}</h2>
	<my-cpn></my-cpn>
	<my-cpn>
		<span slot="center" style="color:red">center</span>
    </my-cpn>
    <my-cpn>
       <span slot="right" style="color:red">right</span>
       <span slot="left" style="color:red">right</span>
    </my-cpn>
</div>

<script type="text/x-template" id="cpn">
	<div class="cpn">
	    我是组件-具名插槽
        <div class="div-slot">
            <slot name="left"><i>左边</i></slot>
            <slot name="center"><i>中间</i></slot>
            <slot name="right"><i>右边</i></slot>
        </div>
	</div>
</script>
const app = new Vue({
    el: "#app",
    data: {
        message: "我是Vue标题"
    },
    components: {
        'my-cpn': {
            template: "#cpn"
        }
    }
})
原文地址:https://www.cnblogs.com/bqh10086/p/13212271.html