Bootstrap

使用Mitt进行vue组件之间的通信例演示&&解说


前言

用 Mitt 进行组件传值几乎可以解决vue组件传值的所有问题

例如:兄弟传兄弟 /父传子 /子传父 /孙子传爷爷。


一、Mitt是什么?

mitt又称事务总线,是第三方插件。 Vue2.x 使用 EventBus 进行组件通信,而 Vue3.x 推荐使用 mitt.js。点击 Mitt 了解更多

二、使用步骤

1.npm 引入(实际项目中推荐)

cd到自己项目文件夹下在项目终端输入

$ npm install --save mitt

然后使用像rollupwebpack这样的模块捆绑器,像使用其他任何东西一样使用:

// using ES6 modules
import mitt from 'mitt'

// using CommonJS modules
var mitt = require('mitt')

2.UMD构建也可以在unpkg上使用(html单页面使用,常用来练习)

将以下代码复制到所要使用的 .html 文件 的 head标签中 例如

<head>
<!--放入-->
    <script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
</head>

你可以在window.mitt上找到这个mitt包

3.使用方法(参考官网)

//若是使用cdn引入这需要将以下两行代码更换成
// const emitter = window.mitt() //注意这里的 window 首字母小写
import mitt from 'mitt'

const emitter = mitt()

// listen to an event(监听一个event事件)
emitter.on('foo', e => console.log('foo', e) )

// listen to all events (监听所有的event事件)
emitter.on('*', (type, e) => console.log(type, e) )

// fire an event (发送事件)
emitter.emit('foo', { a: 'b' })

// clearing all events (清除事件)
emitter.all.clear()

// working with handler references (使用处理程序引用):
function onFoo() {}
emitter.on('foo', onFoo)   // listen
emitter.off('foo', onFoo)  // unlisten

三、案例分析

1.需求分析

(1)需要在root根组件下的my-form组件和my-table组件中进行数据的传递。并在my-table组件中回显----------是不是so easy ,让我们来试一试吧。 案例代码我会放在下面哦
在这里插入图片描述

2.实际操作

(1)初始代码

<!--
 * @Author: zshawk1982
 * @Date: 2023-02-28 18:59:03
 * @LastEditTime: 2023-04-16 21:19:50
 * @LastEditors: zshawk1982
 * @Description: 
 * @FilePath: \vue_course_2023\10_组件2\案例2原始userManager.html
-->
<!--
 * @Author: zshawk1982
 * @Date: 2023-02-28 18:59:03
 * @LastEditTime: 2023-02-28 19:25:51
 * @LastEditors: zshawk1982
 * @Description: 
 * @FilePath: \vue_course_2023\01_vue基础\helloVue.html
-->
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <style>
    li {
      list-style: none;
    }

    .center {
      margin: 0 auto;
      width: 400px;
      text-align: center;
    }

    .red {
      color: red;
    }

    .green {
      color: green;
    }
  </style>
</head>

<body>
  <div id="app"></div>
  <template id="root">
    <div class="center">
      <h3>用户注册</h3>
      <ul>
        <li>
          <label>用户名:</label><input type="text" @blur="checkUserName" v-model="username" placeholder="请输入用户名">
          <span :class="[this.errors.username=='校验通过'?'green':'red']">{{errors.username}}</span>
        </li>
        <li>
          <label>密码:</label><input type="password" @blur="checkPass" v-model=" password" placeholder="请输入密码">
          <span>{{errors.password}}</span>
        </li>
        <li>
          <label>性别:</label>
          <input type="radio" name="gender" value="male" v-model="gender" /><input type="radio" name="gender" value="female" v-model="gender" /></li>
        <li>
          <label>地址:</label>
          <select v-model="selectedCity">
            <option :value="c.name" v-for="c in cities">{{c.text}}</option>
          </select>
        </li>
      </ul>
      <button @click="handleRegister">注册</button>
      <div style="display: flex;flex-direction: row;justify-content: center;">
        <table border="1">
          <tr>
            <td>序号</td>
            <td>用户名</td>
            <td>性别</td>
            <td>地址</td>
          </tr>
          <tr v-for="(u,index) in users">
            <td>{{index+1}}</td>
            <td>{{u.username}}</td>
            <td>{{u.gender=='male'?'男':'女'}}</td>
            <td>{{handleCity(u.city)}}</td>
          </tr>
        </table>
      </div>

    </div>
  </template>
</body>
<script>
  const app = Vue.createApp({
    template: "#root",
    data() {
      return {
        username: '',
        password: '',
        gender: 'male',
        cities: [
          { name: 'cq', text: '重庆' },
          { name: 'bj', text: '北京' },
        ],
        selectedCity: 'cq',
        errors: {},
        users: [],
        id: ''
      }
    },
    methods: {
      handleModify() {
        const user = this.users.find((u) => { return u.id == this.id })
        user.username = this.username
        user.password = this.password
        user.gender = this.gender
        user.city = this.selectedCity
      },
      handleRegister() {
        if (this.errors.username == '校验通过' && this.errors.password == '校验通过') {
          console.log({
            username: this.username,
            password: this.password,
            gender: this.gender,
            city: this.selectedCity
          })
          this.users.push({
            username: this.username,
            password: this.password,
            gender: this.gender,
            city: this.selectedCity
          })
        }
      },
      checkUserName() {
        if (this.username == "" || this.username == null) {
          this.errors.username = "用户名不能为空"
        } else {
          this.errors.username = "校验通过"
        }
      },
      checkPass() {
        if (this.password == "" || this.password == null) {
          this.errors.password = "密码不能为空"
        } else {
          if (this.password.length < 6) {
            this.errors.password = "密码长度不正确"

          } else {
            this.errors.password = "校验通过"

          }
        }
      },
      handleCity(name) {
        let text = ''
        this.cities.forEach(c => {
          if (c.name == name) {
            text = c.text
          }
        });
        return text
      }
    }
  })
  app.mount("#app")
</script>

</html>

(2)分离form 和 table 并引入 mitt.js

以上代码我们可以知道 form表单和table表格还并不是一个单独的组件,因此我们第一步就是将他们从根组件中分离出来并在 head 标签中引入 mitt 包

<!--
 * @Author: zshawk1982
 * @Date: 2023-02-28 18:59:03
 * @LastEditTime: 2023-04-16 21:19:50
 * @LastEditors: zshawk1982
 * @Description: 
 * @FilePath: \vue_course_2023\10_组件2\案例2原始userManager.html
-->
<!--
 * @Author: zshawk1982
 * @Date: 2023-02-28 18:59:03
 * @LastEditTime: 2023-02-28 19:25:51
 * @LastEditors: zshawk1982
 * @Description: 
 * @FilePath: \vue_course_2023\01_vue基础\helloVue.html
-->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!--引入vue.js-->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <!--引入mitt.js-->
    <script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
    <style>
        li {
            list-style: none;
        }
        
        .center {
            margin: 0 auto;
            width: 400px;
            text-align: center;
        }
        
        .red {
            color: red;
        }
        
        .green {
            color: green;
        }
    </style>
</head>

<body>
    <div id="app"></div>
    <!--这是根组件-->
    <template id="root">
    <div class="center">
     <div style="border:1px #123 solid">
      这是子组件 from
      <my-form></my-form>
     </div>
     <div style="border:1px #123 solid">
      这是子组件 table
      <my-table></my-table>
     </div>
    </div>
  </template>
 <!--这是my-form组件-->
    <template id="form">
    <div>
      <h3>用户注册</h3>
      <ul>
        <li>
          <label>用户名:</label><input type="text" @blur="checkUserName" v-model="username" placeholder="请输入用户名">
          <span :class="[this.errors.username=='校验通过'?'green':'red']">{{errors.username}}</span>
        </li>
        <li>
          <label>密码:</label><input type="password" @blur="checkPass" v-model=" password" placeholder="请输入密码">
          <span>{{errors.password}}</span>
        </li>
        <li>
          <label>性别:</label>
          <input type="radio" name="gender" value="male" v-model="gender" /><input type="radio" name="gender" value="female" v-model="gender" /></li>
        <li>
          <label>地址:</label>
          <select v-model="selectedCity">
            <option :value="c.name" v-for="c in cities">{{c.text}}</option>
          </select>
        </li>
      </ul>
      <button @click="handleRegister">注册</button>
    </div>
  </template>
 <!--这是my-table组件-->
    <template id="table">
    <div>
      <div style="display: flex;flex-direction: row;justify-content: center;">
        <table border="1">
          <tr>
            <td>序号</td>
            <td>用户名</td>
            <td>性别</td>
            <td>地址</td>
          </tr>
          <tr v-for="(u,index) in users">
            <td>{{index+1}}</td>
            <td>{{u.username}}</td>
            <td>{{u.gender=='male'?'男':'女'}}</td>
            <td>{{handleCity(u.city)}}</td>
          </tr>
        </table>
      </div>
    </div>
  </template>
</body>

<script>
  const app = Vue.createApp({
    template: "#root",
    data() {
      return {
        username: '',
        password: '',
        gender: 'male',
        cities: [
          { name: 'cq', text: '重庆' },
          { name: 'bj', text: '北京' },
        ],
        selectedCity: 'cq',
        errors: {},
        users: [],
        id: ''
      }
    },
    methods: {
      handleModify() {
        const user = this.users.find((u) => { return u.id == this.id })
        user.username = this.username
        user.password = this.password
        user.gender = this.gender
        user.city = this.selectedCity
      },
      handleRegister() {
        if (this.errors.username == '校验通过' && this.errors.password == '校验通过') {
          console.log({
            username: this.username,
            password: this.password,
            gender: this.gender,
            city: this.selectedCity
          })
          this.users.push({
            username: this.username,
            password: this.password,
            gender: this.gender,
            city: this.selectedCity
          })
        }
      },
      checkUserName() {
        if (this.username == "" || this.username == null) {
          this.errors.username = "用户名不能为空"
        } else {
          this.errors.username = "校验通过"
        }
      },
      checkPass() {
        if (this.password == "" || this.password == null) {
          this.errors.password = "密码不能为空"
        } else {
          if (this.password.length < 6) {
            this.errors.password = "密码长度不正确"

          } else {
            this.errors.password = "校验通过"

          }
        }
      },
      handleCity(name) {
        let text = ''
        this.cities.forEach(c => {
          if (c.name == name) {
            text = c.text
          }
        });
        return text
      }
    }
  })
  app.mount("#app")
</script>
</html>

(3)改造 js代码

1.在js中初始化mitt
//在<script>标签开头使用
const emitter = window.mitt()
2.全局定义 my-form \ my-table 子组件并初始化data数据
//my-form组件
	app.component('my-form', {
        template: '#form',
        // props: ['mag'],
        data() {
            return {
                username: '',
                password: '',
                gender: 'male',
                cities: [{
                    name: 'cq',
                    text: '重庆'
                }, {
                    name: 'bj',
                    text: '北京'
                }, ],
                selectedCity: 'cq',
                errors: {},
                users: [],
                id: ''
            }
        },
        methods: {
            handleModify() {
                const user = this.users.find((u) => {
                    return u.id == this.id
                })
                user.username = this.username
                user.password = this.password
                user.gender = this.gender
                user.city = this.selectedCity
            },
            //注册提交
            handleRegister() {
                if (this.errors.username == '校验通过' && this.errors.password == '校验通过') {
                    console.log({
                        username: this.username,
                        password: this.password,
                        gender: this.gender,
                        city: this.selectedCity
                    })
                    this.users.push({
                        username: this.username,
                        password: this.password,
                        gender: this.gender,
                        city: this.selectedCity
                    })
                }
            },
            //表单验证
            checkUserName() {
                if (this.username == "" || this.username == null) {
                    this.errors.username = "用户名不能为空"
                } else {
                    this.errors.username = "校验通过"
                }
            },
            checkPass() {
                if (this.password == "" || this.password == null) {
                    this.errors.password = "密码不能为空"
                } else {
                    if (this.password.length < 6) {
                        this.errors.password = "密码长度不正确"

                    } else {
                        this.errors.password = "校验通过"

                    }
                }
            },

        }

    })
    //my-table组件
    app.component('my-table', {
        template: '#table',
        // props: ['mmsg'],
        data() {
            return {
                users: [],
                cities: [{
                    name: 'cq',
                    text: '重庆'
                }, {
                    name: 'bj',
                    text: '北京'
                }, ],
            }
        },
        methods: {
            handleCity(name) {
                let text = ''
                this.cities.forEach(c => {
                    if (c.name == name) {
                        text = c.text
                    }
                });
                return text
            }
        },
    })
    
3.在 my-form \ my-table 子组件中发送和监听数据

只需在my-form注册事件中发送数据便可

 handleRegister() {
     if (this.errors.username == '校验通过' && this.errors.password == '校验通过') {
           this.users.push({
           username: this.username,
           password: this.password,
           gender: this.gender,
           city: this.selectedCity
     })
     //console.log(this.users)
     //发送事件 事件名称为 userlist 发送数据为this.users 数组
        	emitter.emit('userlist', this.users)
     }
 },

同样接受数据只需在my-table的生命周期生接收即可中发送数据便可

mounted() {
//注意这里使用的是 .on进行接收数据 接收数据名称必须和发送数据名称相同
  emitter.on('userlist', res => {
     this.users = res
  })
}

四、完整代码

<!--
 * @Author: zshawk1982
 * @Date: 2023-02-28 18:59:03
 * @LastEditTime: 2023-04-16 21:19:50
 * @LastEditors: zshawk1982
 * @Description: 
 * @FilePath: \vue_course_2023\10_组件2\案例2原始userManager.html
-->
<!--
 * @Author: zshawk1982
 * @Date: 2023-02-28 18:59:03
 * @LastEditTime: 2023-02-28 19:25:51
 * @LastEditors: zshawk1982
 * @Description: 
 * @FilePath: \vue_course_2023\01_vue基础\helloVue.html
-->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
    <style>
        li {
            list-style: none;
        }
        
        .center {
            margin: 0 auto;
            width: 400px;
            text-align: center;
        }
        
        .red {
            color: red;
        }
        
        .green {
            color: green;
        }
    </style>
</head>

<body>
    <div id="app"></div>
    <template id="root">
    <div class="center">
     <div style="border:1px #123 solid">
      这是子组件 from
      <my-form></my-form>
     </div>
     <div style="border:1px #123 solid">
      这是子组件 table
      <my-table></my-table>
     </div>
     
    </div>
  </template>

    <template id="form">
    <div>
      <h3>用户注册</h3>
      <ul>
        <li>
          <label>用户名:</label><input type="text" @blur="checkUserName" v-model="username" placeholder="请输入用户名">
          <span :class="[this.errors.username=='校验通过'?'green':'red']">{{errors.username}}</span>
        </li>
        <li>
          <label>密码:</label><input type="password" @blur="checkPass" v-model=" password" placeholder="请输入密码">
          <span>{{errors.password}}</span>
        </li>
        <li>
          <label>性别:</label>
          <input type="radio" name="gender" value="male" v-model="gender" /><input type="radio" name="gender" value="female" v-model="gender" /></li>
        <li>
          <label>地址:</label>
          <select v-model="selectedCity">
            <option :value="c.name" v-for="c in cities">{{c.text}}</option>
          </select>
        </li>
      </ul>
      <button @click="handleRegister">注册</button>
    </div>
  </template>

    <template id="table">
    <div>
      <div style="display: flex;flex-direction: row;justify-content: center;">
        <table border="1">
          <tr>
            <td>序号</td>
            <td>用户名</td>
            <td>性别</td>
            <td>地址</td>
          </tr>
          <tr v-for="(u,index) in users">
            <td>{{index+1}}</td>
            <td>{{u.username}}</td>
            <td>{{u.gender=='male'?'男':'女'}}</td>
            <td>{{handleCity(u.city)}}</td>
          </tr>
        </table>
      </div>
    </div>
  </template>
</body>

<script>
    const emitter = window.mitt()
    const app = Vue.createApp({
        template: "#root",
    })

    app.component('my-form', {
        template: '#form',
        // props: ['mag'],
        data() {
            return {
                username: '',
                password: '',
                gender: 'male',
                cities: [{
                    name: 'cq',
                    text: '重庆'
                }, {
                    name: 'bj',
                    text: '北京'
                }, ],
                selectedCity: 'cq',
                errors: {},
                users: [],
                id: ''
            }
        },
        methods: {
            handleModify() {
                const user = this.users.find((u) => {
                    return u.id == this.id
                })
                user.username = this.username
                user.password = this.password
                user.gender = this.gender
                user.city = this.selectedCity
            },
            handleRegister() {
                if (this.errors.username == '校验通过' && this.errors.password == '校验通过') {

                    this.users.push({
                        username: this.username,
                        password: this.password,
                        gender: this.gender,
                        city: this.selectedCity
                    })
                    console.log(this.users)
                    emitter.emit('userlist', this.users)

                }
            },
            checkUserName() {
                if (this.username == "" || this.username == null) {
                    this.errors.username = "用户名不能为空"
                } else {
                    this.errors.username = "校验通过"
                }
            },
            checkPass() {
                if (this.password == "" || this.password == null) {
                    this.errors.password = "密码不能为空"
                } else {
                    if (this.password.length < 6) {
                        this.errors.password = "密码长度不正确"

                    } else {
                        this.errors.password = "校验通过"

                    }
                }
            },

        }

    })
    app.component('my-table', {
        template: '#table',
        // props: ['mmsg'],
        data() {
            return {
                users: [],
                cities: [{
                    name: 'cq',
                    text: '重庆'
                }, {
                    name: 'bj',
                    text: '北京'
                }, ],
            }
        },
        methods: {
            handleCity(name) {
                let text = ''
                this.cities.forEach(c => {
                    if (c.name == name) {
                        text = c.text
                    }
                });
                return text
            }
        },
        mounted() {
            emitter.on('userlist', res => {

                this.users = res
            })

        }
    })

    app.mount("#app")
</script>

</html>

欢迎小伙伴们讨论留言 欧耶!!!

;