Vue第七天:组建通信:父传子、子传父、父传子复杂类型说明;组件化开发:非父子、开关灯案例;slot插槽:前置说明、基本语法、单个slot、具名插槽

组件通信

每个组件都有自己的数据, 提供在data中, data是一个函数, 保证每个组件的数据是独立的

封装成组件, 数据没法玩了~

组件的通讯:

​ 父传子

​ 子传父

​ 非父子

组件通信 - 父传子

需求: 就想要在子组件中使用根组件的msg

每个组件都有自己的数据

  1. 子组件通过prop属性接收
  2. 父组件通过给子组件加属性方式传值

todos 列表渲染练习 - 父传子

子组件中 希望能用 父组件的东西

  1. 子组件通过 props 接收
  2. 父组件通过给 子组件添加标签自定义属性的传值

区分 props 和 data

组件中的数据来源, data props

data自己提供的数据,可以随意更改

props 父组件传递过来的数据, 不允许修改

不要改父组件传递过来的值,是在遵循一个开发规范 => 单向数据流

单向数据流:父组件的数据如果修改了,会自动向下流动,影响到子组件,触发子组件的更新

代码演示:

    <style>
      #app {
        width: 500px;
        height: 500px;
        background-color: pink;
      }
      .son {
        width: 300px;
        height: 300px;
        background-color: skyblue;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <h1>我是父组件</h1>
      <son :msg="msg"></son>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 组件的数据来源:
      // 1. data 自己提供的数据, 可以随便改
      // 2. props 父组件传递过来的数据, 不要改  

      // 不要改父组件传递过来的值, 是在遵循一个开发规范 => 单向数据流
      // 单向数据流: 父组件的数据如果修改了, 会自动向下流动, 影响到子组件, 触发子组件的更新

      Vue.component('son', {
        template: `
          <div class="son">
            <h3>我是son组件</h3> 
            <p>父组件传递过来的数据: {{ msg }}</p>
            <p>自己的数据: {{ car }}</p>
            <button @click="fn">改值</button>
          </div>
        `,
        data () {
          return {
            car: '小车车'
          }
        },
        methods: {
          fn () {
            // 修改自己的数据
            // this.car = '大车车'

            // 修改父组件的数据
            this.msg = '老王'
          }
        },
        // props中的数据 和 data一样, 可以在组件中使用
        props: ['msg']
      })


      const vm = new Vue({
        el: '#app',
        data: {
          msg: 'hello vue'
        }
      })
    </script>
  </body>

组件通信 - 子传父

  1. 子组件可以通过出发事件的同事,传值。
this.$emit('事件名', 参数1, 参数2, ...) 触发事件的同时传参的
  1. 父组件给子组件注册一个对应的事件(事件名要跟对应的事件对应起来)
@事件名 = 'fatherFn'

代码演示:

    <style>
      #app {
        width: 500px;
        height: 500px;
        background-color: pink;
      }
      .son {
        width: 300px;
        height: 300px;
        background-color: skyblue;
      }
    </style>
  </head>
  <body>
    <!-- 模板在谁的管理范围内, 用的就是谁的变量 -->
    <div id="app">
      <h3>我是父组件</h3>
      <son @sb="fatherFn"></son>
    </div>

    <script src="./vue.js"></script>
    <script>

      // 子传父:
      // 1. 在子组件中, 通过触发事件的同时, 传值
      //    this.$emit(事件名, 参数1, 参数2, .....)

      // 2. 在父组件中, 给子组件注册对应的事件

      Vue.component('son', {
        template: `
          <div class="son">
            我是son组件 <button @click="fn">传值给父组件</button>
          </div>
        `,
        data () {
          return {
            money: '100块'
          }
        },
        methods: {
          fn () {
            this.$emit('sb', this.money, '小车车')
          }
        }
      })

      const vm = new Vue({
        el: '#app',
        data: {
          msg: 'hello vue'
        },
        methods: {
          fatherFn (money, str) {
            console.log(1111)
            console.log(money, str)
          }
        }
      })

      // 子传父:
      // 1. 在子组件中, 通过触发事件的同时传值
      //    this.$emit('事件名', 参数1, ...)

      // 2. 在父组件中, 给子组件去注册 对应的 事件
      //    @事件名 = 'fatherFn'
    </script>
  </body>

todos - 删除功能 - 子传父

注意: 事件名的规范, 所有的事件名都必须是小写

todos - 添加功能 - 子传父

todos - 数据持久化

todos - 修改 - 修改状态

父传子, 传递复杂类型的说明

  1. 如果传递的是简单数据类型, 修改了, vue会直接报错
  2. 如果传递的是复杂数据类型, 不要不修改地址, 就不会报错 (虽然不报错, 但是也避免),没有遵循单向数据流

代码演示:

    <style>
      #app {
        width: 500px;
        height: 500px;
        background-color: pink;
      }
      .son {
        width: 300px;
        height: 300px;
        background-color: skyblue;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <h3>我是父组件</h3>
      <son :msg="msg" :obj="obj"></son>
    </div>

    <script src="./vue.js"></script>
    <script>

      // 父传子:
      // 1. 如果传递的是简单数据类型, 修改了, vue会直接报错
      // 2. 如果传递的是复杂数据类型, 不要不修改地址, 就不会报错 (虽然不报错, 但是也避免)
      //    没有遵循单向数据流

      Vue.component('son', {
        template: `
          <div class="son">
            <h3>我是son组件 <button @click="fn">改值</button></h3>
            <p>{{ msg }}</p>
            <p>{{ obj.name }}</p>
            <p>{{ obj.age }}</p>
          </div>
        `,
        props: ['msg', 'obj'],
        methods: {
          fn () {
            // this.msg = '嘿嘿'
            this.obj.name = '老花'
          }
        }
      })


      const vm = new Vue({
        el: '#app',
        data: {
          msg: 'hello vue',
          obj: {
            name: '小花',
            age: 18
          }
        }
      })
    </script>
  </body>

todos - 修改 - 修改内容

todos - 底部计算属性完成 - 父传子

todos - 清空所有完成

todos案例

源代码已上传至资源,文件名叫:Vue第7天-todos

在这里插入图片描述

组件化开发

组件通讯-非父子

非父子组件之间通过一个空的Vue实例来传递数据。

(1) 先创建一个 都能访问到的 事件总线(event bus) 实际就是一个空的vue实例
(2) 在 A 组件中, 触发 bus 的事件, 触发事件的同时传值

 bus.$emit(事件名, 参数1, 参数2, ....)

(3) 在 B 组件中, 给 bus 注册对应的事件, 接收参数

bus.$on(事件名, 事件处理函数)
const bus = new Vue();   //bus:公交车  事件总线
  • 核心逻辑
组件A给组件B传值:
1. 创建一个bus对象
2. 组件a触发事件,给组件b发送数据
3. 组件b给bus对象注册事件
4. 组件b提供函数,用于接收数据
  • 组件A触发bus的事件
<button @click="send">表白</button>

methods: {
    send() {
        bus.$emit("get", this.msg);
    }
}
  • 组件B给bus注册对应的事件
 //rose在组件创建的时候,给bus注册了一个事件
created () {
    bus.$on("get", (msg)=>{
        console.log("这是rose注册的事件", msg);
        this.msg = msg;
    });
}
  • 组件B通过事件处理程序可以获取到传递的值
bus.$on("get", (msg)=>{
    console.log("这是rose注册的事件", msg);
    this.msg = msg;
});

注意点:1. 必须是同一辆公交车 2. 注册的事件和触发的事件必须保持一致

代码演示:

    <style>
      .jack, .rose {
        width: 250px;
        height: 250px;
        background-color: pink;
        margin: 20px;
        border: 3px solid #000;
      }

    </style>
  </head>
  <body>
    <div id="app">
      <jack></jack>
      <rose></rose>
    </div>

    <script src="./vue.js"></script>
    <script>
      // jack 和 rose 两个组件, jack想跟rose说一句话
      // 非父子通信
      // (1) 先创建一个 都能访问到的 事件总线(event bus)   实际就是一个空的vue实例
      // (2) 在 A 组件中, 触发 bus 的事件, 触发事件的同时传值
      //     bus.$emit(事件名, 参数1, 参数2, ....)
      // (3) 在 B 组件中, 给 bus 注册对应的事件, 接收参数
      //     bus.$on(事件名, 事件处理函数)

      const bus = new Vue()

      Vue.component('jack', {
        template: `
          <div class="jack">
            <h3>我是jack</h3>
            <button @click="fn">对rose说</button>
            <p>{{ info }}</p>
          </div>
        `,
        data () {
          return {
            msg: 'you jump, i look look',
            info: ''
          }
        },
        created () {
          bus.$on('rose-reply', (info) => {
            this.info = info
          })
        },
        methods: {
          fn () {
            // console.log('希望给rose传参')
            bus.$emit('jack-say', this.msg)
          }
        }
      })


      Vue.component('rose', {
        template: `
          <div class="rose">
            <h3>我是rose</h3>
            <button @click="fn">rose回答</button>
            <p>{{ info }}</p>
          </div>
        `,
        data () {
          return {
            info: ''
          }
        },
        // bus的事件注册的越早越好, 因为只有注册了事件, 将来才能触发到
        created () {
          bus.$on('jack-say', (msg) => {
            this.info = msg
          })
        },
        methods: {
          fn () {
            // 触发bus的事件的同时, 传值
            bus.$emit('rose-reply', 'get out!! 你个渣男~')
          }
        }
      })


      const vm = new Vue({
        el: '#app',
        data: {
          msg: 'hello vue'
        }
      })
    </script>

案例:开关灯案例

  <style>
    .light {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      text-align: center;
      line-height: 100px;
      margin: 0 auto;
      color: #fff;
      background-color: rgb(17, 4, 4);
    }

    /* 灯座 */
    .bottom {
      width: 150px;
      height: 50px;
      margin-top: 10px;
      line-height: 50px;
      text-align: center;
      color: #fff;
      background-color: #000;
    }

    .container {
      width: 150px;
    }

    .active {
      background-color: #ff0;
      color: #000;
    }
  </style>
</head>

<body>
  <div id="app" class="container">
    <light-pao></light-pao>
    <light-zuo></light-zuo>
  </div>

  <script src="vue.js"></script>
  <script>

    const bus = new Vue()

    // 灯泡组件
    Vue.component('light-pao', {
      template: `
        <div class="light" :class="{ active: isOn }">我是一盏灯</div>
      `,
      data () {
        return {
          isOn: false
        }
      },
      created () {
        bus.$on('turn', (flag) => {
          this.isOn = flag
        })
      }
    })

    // 灯座组件
    Vue.component('light-zuo', {
      template: `
        <div class="bottom">
          <button @click="turn(true)">开灯</button>
          <button @click="turn(false)">关灯</button>
        </div>
      `,
      methods: {
        turn (flag) {
          // console.log(flag)
          bus.$emit('turn', flag)
        }
      }
    })

    const vm = new Vue({
      el: '#app',
      data: {}
    })


  </script>
</body>

bus是一种通用的组件通讯方案

我们有三种组件通讯的方案

1. 父传子
2. 子传父
3. 非父子(bus)
其实bus方案也适用于父传子和子传父的方案

slot插槽

前置说明

    <style>
      .modal {
        width: 400px;
        padding: 30px;
        border: 3px solid #000;
        border-radius: 10px;
        margin: 10px;
      }

    </style>
  </head>
  <body>
    <div id="app">
      <modal title="警告"></modal>
      <modal title="危险"></modal>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 封装一个对话框组件,
      // 父传子, 只能配置一些简单的结构, 如果大段的内容需要用户定制, 父传子就不好实现了
      // 需求: 上面的对话框内容, 要求是 textarea, 下面对话框的内容, 要求是 p 段落
      // 复杂的组件定制, 需要学习插槽
      Vue.component('modal', {
        template: `
          <div class="modal">
            <div class="header">
              <h3>{{ title }}</h3>  
            </div>
            <div class="main">
              <p>亲! 你确认要退出本系统么?</p>  
            </div>
            <div class="footer">
              <button>确认</button>
              <button>取消</button>
            </div>
          </div>
        `,
        props: ['title']
      })


      const vm = new Vue({
        el: '#app',
        data: {
          msg: 'hello vue'
        }
      })
    </script>

内容分发,可以将组件使用时,内容写的内容,分发到slot标签

当组件中某一项需要单独定义,那么就应该使用solt

Vue 实现了一套内容分发的 API,将 <slot>元素作为承载分发内容的出口。

slot插槽:内容分发,可以将组件使用时,内容写得内容,分发到slot标签

  1. 匿名插槽(默认插槽):没有配置过名字的插槽,只要是没有具体分配的内容,都会给到匿名插槽
  2. 具名插槽:给插槽起个名字,可以实现定向分发,不是给他的他不要
    • 给插槽取名
    • 需要用template标签包裹内容
    • 通过 v-slot: 插槽名 制定分发给谁

slot插槽基本语法

    <style>
      .modal {
        width: 400px;
        padding: 30px;
        border: 3px solid #000;
        border-radius: 10px;
        margin: 10px;
      }

    </style>
  </head>
  <body>
    <div id="app">
      <modal title="警告">
        <textarea name="" id="" cols="30" rows="10"></textarea>
      </modal>

      <modal title="危险">
        <p>我是一个段落</p>
      </modal>

      <modal title="温馨">
        爱好:
        <input type="checkbox">篮球
        <input type="checkbox">足球
        <input type="checkbox">保龄球
      </modal>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 封装一个对话框组件,
      // 父传子, 只能配置一些简单的结构, 如果大段的内容需要用户定制, 父传子就不好实现了
      // 需求: 上面的对话框内容, 要求是 textarea, 下面对话框的内容, 要求是 p 段落
      // 复杂的组件定制, 需要学习插槽

      // slot插槽: 内容分发, 可以将组件使用时, 内部写的内容, 分发到 slot 标签

      Vue.component('modal', {
        template: `
          <div class="modal">
            <div class="header">
              <h3>{{ title }}</h3>  
            </div>
            <div class="main">
              <slot></slot> 
            </div>
            <div class="footer">
              <button>确认</button>
              <button>取消</button>
            </div>
          </div>
        `,
        props: ['title']
      })


      const vm = new Vue({
        el: '#app',
        data: {
          msg: 'hello vue'
        }
      })
    </script>
  </body>

单个slot

除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将会被丢弃 ,当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。

在组件的模版中定义slot插槽

Vue.component("modal", {
    template: `
    <div class="modal">
    	<p>温馨提示</p>
    	<div>
    		<slot></slot>
    	</div>
    	<button>关闭</button>
    </div>
`,
});

父组件传值

<modal>你确定要退出系统吗?</modal>
<modal>你确定要删除这个内容吗?</modal>

具名插槽

如果一个组件中想使用多个slot那么此时就应该使用具名slot。

Vue.component("modal", {
    template: `
    <div class="modal">
    <slot name="header"></slot>
    <slot name="content"></slot>
    <slot name="footer"></slot>
    </div>
	`,
});
<modal>
    <p slot="header">温馨提示</p>
    <div slot="content">你要删除内容吗</div>
    <button slot="footer">关闭</button>
</modal>
<modal>
    <p slot="header">提示</p>
    <div slot="content">你要删除这条记录吗</div>
    <button slot="footer">开启</button>
</modal>
  // 封装一个对话框组件,
  // 父传子, 只能配置一些简单的结构, 如果大段的内容需要用户定制, 父传子就不好实现了
  // 需求: 上面的对话框内容, 要求是 textarea, 下面对话框的内容, 要求是 p 段落
  // 复杂的组件定制, 需要学习插槽

  // slot插槽: 内容分发, 可以将组件使用时, 内部写的内容, 分发到 slot 标签
  // 1. 匿名插槽(默认插槽): 没有配置过名字的插槽, 没有具体分配的内容, 都会给到匿名插槽
  // 2. 具名插槽: 给插槽起个名字, 可以实现定向分发, 不是给他的他不要
  //    (1) 给插槽起个名字  <slot name="main"></slot> 
  //    (2) 在分发内容时, 使用 template标签, 包裹需要分发的内容
  //    (3) 在template标签上, 通过 v-slot:插槽名 指定分发给谁
    <style>
      .modal {
        width: 400px;
        padding: 30px;
        border: 3px solid #000;
        border-radius: 10px;
        margin: 10px;
      }

    </style>
  </head>
  <body>
    <div id="app">
      <modal title="警告">
        <template v-slot:main>
          <textarea name="" id="" cols="30" rows="10"></textarea>
        </template>
        
        <template v-slot:footer>
          <button></button>
          <button></button>
        </template>
      </modal>

      <modal title="温馨一波">
        <template v-slot:main>
          <p>段落</p>
          <p>段落</p>
        </template>

        <template v-slot:footer>
          <button>确认</button>
        </template>
      </modal>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 封装一个对话框组件,
      // 父传子, 只能配置一些简单的结构, 如果大段的内容需要用户定制, 父传子就不好实现了
      // 需求: 上面的对话框内容, 要求是 textarea, 下面对话框的内容, 要求是 p 段落
      // 复杂的组件定制, 需要学习插槽

      // slot插槽: 内容分发, 可以将组件使用时, 内部写的内容, 分发到 slot 标签
      // 1. 匿名插槽(默认插槽): 没有配置过名字的插槽, 没有具体分配的内容, 都会给到匿名插槽
      // 2. 具名插槽: 给插槽起个名字, 可以实现定向分发, 不是给他的他不要
      //    (1) 给插槽起个名字  <slot name="main"></slot> 
      //    (2) 在分发内容时, 使用 template标签, 包裹需要分发的内容
      //    (3) 在template标签上, 通过 v-slot:插槽名 指定分发给谁
      Vue.component('modal', {
        template: `
          <div class="modal">
            <div class="header">
              <h3>{{ title }}</h3>  
            </div>
            <div class="main">
              <slot name="main"></slot> 
            </div>
            <div class="footer">
              <slot name="footer"></slot>
            </div>
          </div>
        `,
        props: ['title']
      })


      const vm = new Vue({
        el: '#app',
        data: {
          msg: 'hello vue'
        }
      })
    </script>
  </body>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值