• Vue2:组件及组件通信


    Vue 组件

    组件是 可复用的 Vue 实例,封装标签,样式和 JS 代码。

    • 组件化 :封装的思想,把页面上 可重用的部分 封装为 组件,从而方便项目的 开发 和 维护。

    简单理解:一个页面, 可以拆分成一个个组件,一个组件就是一个整体,每个组件可以有自己独立的 结构 样式 和 行为 (html, css和js)

    组件的创建使用

    创建组件:创建.vue文件 – 标签 – 样式 – JS进去

    注册组件:(全局 / 局部)

    使用组件:(组件名用作标签)

    执行结果:把组件标签最终替换成封装组件内的标签。

    1. 创建组件 (components 下的 Pannel.vue)

      <template>
        <div>
          <div>
            <div class="title">
              <h4>芙蓉楼送辛渐h4>
              <span class="btn" @click="isShow = !isShow">
                {{ isShow ? '收起' : '展开' }}
              span>
            div>
            <div class="container" v-show="isShow">
              <p>寒雨连江夜入吴, p>
              <p>平明送客楚山孤。p>
              <p>洛阳亲友如相问,p>
              <p>一片冰心在玉壶。p>
            div>
          div>
        div>
      template>
      
      <script>
      export default {
        // 数据
        data () {
          return {
            isShow: false
          }
        }
      }
      script>
      
      <style lang="less" scoped>
        .title {
          display: flex;
          justify-content: space-between;
          align-items: center;
          border: 1px solid #ccc;
          padding: 0 1em;
          }
          .title h4 {
          line-height: 2;
          margin: 0;
          }
          .container {
          border: 1px solid #ccc;
          padding: 0 1em;
          }
          .btn {
          /* 鼠标改成手的形状 */
          cursor: pointer;
          }
      style>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
    2. 局部注册组件,并使用 (App.vue)

      <template>
        <div id="app">
          <h3>案例:折叠面板h3>
          
          <pannel>pannel>
          <pannel>pannel>
          <pannel>pannel>
        div>
      template>
      
      <script>
      // 2. 注册组件
      import Pannel from './components/Pannel'
      export default {
        components: {
          Pannel
        },
      }
      script>
      
      <style lang="less" scoped>
        body {
          background-color: #ccc;
          #app {
            width: 400px;
            margin: 20px auto;
            background-color: #fff;
            border: 4px solid blueviolet;
            border-radius: 1em;
            box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
            padding: 1em 2em 2em;
            h3 {
              text-align: center;
            }
          }
        }
      style>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37

    Vue 组件通信

    App.vue

    <template>
    	<div>
        
        <MyProduct title="俺是标题" price="100" info="俺是描述,这玩意儿真不错">MyProduct>
      div>
    template>
    
    <script>
    // 引入子组件
    import MyProduct from './components/MyProduct.vue'
    
    export default {
      // 注册组件
      components: {
        MyProduct,
      },
    };
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    MyProduct.vue

    <template>
    	<div class="my-product">
    		<h3>标题: {{ title }}h3>
    		<p>价格: {{ price }}元p>
    		<p>{{ info }}p>
    	div>
    template>
    
    <script>
    export default {
    	props: ["title", "price", "info"],
    };
    script>
    
    <style scoped>
    .my-product {
    	width: 400px;
    	padding: 20px;
    	border: 2px solid #000;
    	border-radius: 5px;
    	margin: 10px;
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    父向子 - props

    指定类型,是否必传

    props: {
    	title: {
        	typeof: String,
        	require: true
        },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    父向子 - 组件循环

    App.vue

    <template>
      <div>
        <MyProduct v-for="v in list"
                  :title="v.proname"
                  :price="v.proprice"
                  :info="v.info"
                  :key="v.id">
        MyProduct>
      div>
    template>
    
    <script>
    import MyProduct from './components/MyProduct.vue'
    
    export default {
      // 数据
      data () {
        return {
          list: [
              { id: 1, proname: "超级好吃的棒棒糖", proprice: 18.8, info: '开业大酬宾, 全场8折' },
              { id: 2, proname: "超级好吃的大鸡腿", proprice: 34.2, info: '好吃不腻, 快来买啊' },
              { id: 3, proname: "超级无敌的冰激凌", proprice: 14.2, info: '炎热的夏天, 来个冰激凌了' },
          ],
        }
      },
      // 注册组件
      components: {
        MyProduct,
      }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    MyProduct.vue

    <template>
    	<div class="my-product">
    		<h3>标题: {{ title }}h3>
    		<p>价格: {{ price }}元p>
    		<p>{{ info }}p>
    	div>
    template>
    
    <script>
    export default {
    	props: ["title", "price", "info"],
    };
    script>
    
    <style scoped>
    .my-product {
    	width: 400px;
    	padding: 20px;
    	border: 2px solid #000;
    	border-radius: 5px;
    	margin: 10px;
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    子向父

    单项数据流

    简单理解:父组件向子组件发送事件,子组件触发。

    触发父组件的自定义方法

    this.$emit("", 参数1, 参数2);
    
    • 1

    EventBus(兄弟通信)

    跨组件通信使用。

    在监听方(List)接收,发送方(MyProduct)发送。

    1. App.vue 父组件中写主体代码

      <template>
        <div>
          <div style="float: left;">
            <Product v-for="(v, i) in list" :key="v.id"
              :title="v.proname"
              :price="v.proprice"
              :info="v.info"
              :index="i">
            Product>
          div>
      
          <div style="float: left;">
            <List :arr="list">List>
          div>
      
        div>
      template>
      
      <script>
      // 1.导入组件
      import Product from './components/MyProduct.vue'
      import List from './components/List.vue'
      
      export default {
        // 2.注册组件
        components: {
          Product,
          List
        },
        // 数据
        data () {
          return {
            price: 1,
            list: [
              {id: 1, proname: "超级好吃的棒棒糖", proprice: 18.8, info: "开业大酬宾, 全场8折",},
              {id: 2, proname: "超级好吃的大鸡腿", proprice: 34.2, info: "好吃不腻, 快来买啊",},
              {id: 3, proname: "超级无敌的冰激凌", proprice: 14.2, info: "炎热的夏天, 来个冰激凌了",},
            ],
          }
        },
      }
      script>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
    2. MyProduct.vue 外部组件中使用

      <template>
      	<div class="my-product">
      		<h3>标题: {{ title }}h3>
      		<p>价格: {{ price }}元p>
      		<p>{{ info }}p>
          	<button @click="subFn">宝刀-砍1元button>
      	div>
      template>
      
      <script>
      import eventBus from '../EventBus/index.js'
      
      export default {
      	props: ["title", "price", "info", "index"],
      	methods: {
      		subFn () {
      			// 发送
      			eventBus.$emit('subPrice', this.index, 1);
      		}
      	}
      };
      script>
      
      <style scoped>
      .my-product {
      	width: 400px;
      	padding: 20px;
      	border: 2px solid #000;
      	border-radius: 5px;
      	margin: 10px;
      }
      style>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
    3. 在 src 下创建 EventBus 文件夹,下新建 index.js,并创建空白 Vue 对象并导出 (只负责监听和触发)

      import Vue from 'vue'
      export default new Vue();
      
      • 1
      • 2
    4. 在 components 文件夹下新建 List.vue

      箭头函数没有 this,可以直接指向

      <template>
      	<div>
      		<ul class="my-product">
      			<li v-for="(item, index) in arr" :key="index">
      				<span>{{ item.proname }}span>
      				<span>{{ item.proprice }}span>
      			li>
      		ul>
      	div>
      template>
      
      <script>
      import eventBus from "../EventBus/index.js";
      
      export default {
      	props: ["arr"],
      	// 生命周期 函数  vue创建完毕以后调用created
      	// 	created :处于loading结束后,还做一些初始化,实现函数自执行(data数据已经初始化,但是DOM结构渲染完成,组件没有加载)
      	// 	mounted :处于发起后端请求,获取数据,配合路由钩子执行操作(DOM渲染完成,组件挂载完成 )
      	created() {
      		// 接收(监听)
      		eventBus.$on("subPrice", (index, price) => {
      			if (this.arr[index].proprice > 1) {
      				this.arr[index].proprice = (this.arr[index].proprice - price).toFixed(2);
      			}
      		});
      	},
      };
      script>
      
      <style scoped>
      .my-product {
      	width: 400px;
      	padding: 20px;
      	border: 2px solid #000;
      	border-radius: 5px;
      	margin: 10px;
      }
      style>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39

    传数据注意事项

    数据在父组件中,修改父组件数据时

    • 如果数据类型是一般类型,要在父组件中修改
    • 如果数据类型的复杂类型,可以直接在自组件中修改

    因为指针问题,复杂类型访问的是指针。不管是什么类型的数据,都推荐在父组件中修改数据。


    案例:Todo 备忘录

    Todo案例完成参考.zip

    1. 创建工程和组件

      创建组件 todoHeader.vue、todoMain.vue、todoFooter.vue

      todoHeader.vue

      <template>
        <header class="header">
          <h1>todosh1>
          <input id="toggle-all" class="toggle-all" type="checkbox" >
          <label for="toggle-all">label>
          <input
            class="new-todo"
            placeholder="输入任务名称-回车确认"
            autofocus
          />
        header>
      template>
      
      <script>
      export default {
       
      }
      script>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      todoMain.vue

      <template>
        <ul class="todo-list">
          
          <li class="completed" >
            <div class="view">
              <input class="toggle" type="checkbox" />
              <label>任务名label>
              <button class="destroy">button>
            div>
          li>
        ul>
        
      template>
      
      <script>
      export default {
          
      }
      script>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

      todoFooter.vue

      <template>
        <footer class="footer">
          <span class="todo-count">剩余<strong>数量值strong>span>
          <ul class="filters">
            <li>
              <a class="selected" href="javascript:;" >全部a>
            li>
            <li>
              <a href="javascript:;">未完成a>
            li>
            <li>
              <a href="javascript:;" >已完成a>
            li>
          ul>
          <button class="clear-completed" >清除已完成button>
        footer>
      template>
      
      <script>
      export default {
      
      }
      script>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
    2. 导入组件和样式

      App.vue

      <template>
        <div class="todoapp">
          <TodoHeader>TodoHeader>
          <TodoMain>TodoMain>
          <TodoFooter>TodoFooter>
        div>
      template>
      
      <script>
      import './styles/base.css'
      import './styles/index.css'
      import TodoHeader from './components/todoHeader.vue'
      import TodoMain from './components/todoMain.vue'
      import TodoFooter from './components/todoFooter.vue'
      
      export default {
        components: {
          TodoHeader,
          TodoMain,
          TodoFooter
        },
      }
      script>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
  • 相关阅读:
    改变this指向
    redis 缓存击穿问题(互斥锁,逻辑过期)
    源码包部署
    LLVIP数据集下载
    力扣(700.701)补9.9
    Linux磁盘不足问题定位及解决
    Java架构师内功计算机硬件知识
    海外代理IP是什么?如何使用?
    泛微E-Cology CheckServer.jspSQL注入漏洞(QVD-2023-9849) 复现
    day02 快速上手
  • 原文地址:https://blog.csdn.net/qq_41952539/article/details/127763522