2023/1/10 Vue学习笔记 - 全局事件总线的理解与案例 并且改造 todoList案例

1 全局事件总线的理解与案例

功能:任意组件间通信【父子 子父 爷孙 。。。 兄弟。。。】
2023/1/10 Vue学习笔记 - 全局事件总线的理解与案例 并且改造 todoList案例_第1张图片

  • 1、这个X需要所有的vc的可见 - 我们在Vue的原型对象上放即可,这里给出一个概念的问题:关于VueComponent:
    2023/1/10 Vue学习笔记 - 全局事件总线的理解与案例 并且改造 todoList案例_第2张图片
1.School组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,Vue.extend生成的。

2、我们只需要写````,Vue解析时公帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)3、特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent !!!!

4、关于this指向:
(1) 组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数、它们的this均是【vuecomponent实例对象】
(2) new Vue(options)配置中:
data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是【Vue实例对象】。

5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象);Vue的实例对象,以后简称vm。
  • 2、$on $off $emit 都可以使用 备注:$on $off $emit 都在Vue的原型对象中
    Vue原型对象的属性都是给vm vc使用的 !!!!!!

实现方式一:
2023/1/10 Vue学习笔记 - 全局事件总线的理解与案例 并且改造 todoList案例_第3张图片
实现方式二:
2023/1/10 Vue学习笔记 - 全局事件总线的理解与案例 并且改造 todoList案例_第4张图片
2023/1/10 Vue学习笔记 - 全局事件总线的理解与案例 并且改造 todoList案例_第5张图片
main.js

import App from './App'
import Vue from 'vue'

Vue.config.productionTip = false
new Vue({
    el: '#single',
    render: h => h(App),
    beforeCreate() {
        // 所有组件可见 - 安装全局事件总线
        Vue.prototype.$bus = this
    }
})


App.vue

<template>
  <div class="app">
    <h1>{{ msg }}</h1>
    <School/>
    <Student/>
  </div>
</template>

<script>
import School from "./components/School"
import Student from './components/Student'

export default {
  name: "App",
  components: {Student, School},
  data() {
    return {
      msg: 'Hello-',
    }
  },
  methods: {
  },
  mounted() {
  },
}
</script>

<style scoped>
.app {
  background-color: gray;
}
</style>

School.vue

<template>
  <div class="sc">
    <h2>学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
  </div>
</template>

<script>

export default {
  name: `School`,
  data() {
    return {
      name: '北京大学',
      address: '北京'
    }
  },
  methods: {},
  mounted() {
    this.$bus.$on('hello', (data) => {
      console.log('i am school component, rev data', data)
    })
  },
  // 销毁
  beforeDestroy() {
    this.$bus.$off('hello')
  }
}
</script>

<style>
.sc {
  background-color: aquamarine;
  padding: 5px;
}
</style>

Student.vue

<template>
  <div class="student">
    <h2>学生名字:{{ name }}</h2>
    <h2>学生性别:{{ sex }}</h2>
    <button @click="sendStudentName">把学生名给school组件</button>
  </div>
</template>

<script>

export default {
  name: `Student`,
  data() {
    return {
      name: 'zhaoshuai-lc',
      sex: 'male'
    }
  },
  methods: {
    sendStudentName() {
      this.$bus.$emit('hello', this.name)
    }
  }
}
</script>

<style>
.student {
  background-color: pink;
  padding: 5px;
}
</style>

1)对以下代码的理解:

const Demo = Vue.extend({}) 
const d = new Demo()
Vue.prototype.x = d
  • 第一行 创建Demo全局组件。
  • 第二行 获取组件的实例对象。
因为需要写了标签才能使用组件,然而不能在main.js中引用标签,所以这一行亲自new一个。
  • 第三行获得了原型vc。

2)对以下代码的理解:

new Vue({
    ......
    beforeCreate() {
        Vue.prototype.$bus = this //安装全局事件总线
    }
})
  • 第三行 此时beforeCreate时无法访问到data中的数据的,只是初始化。
  • 第四行 已知这个时候vm原型还未生成,所以不能直接等于vm,但是Vue中有个this本身就是vue实例。$bus为总线。

3)为什么bus需要销毁,自定义事件不用销毁?

举个例子,比如说Student组件被销毁了,那么整个vc都没有了,所以自定义事件就直接没有了。
但是bus不是这样的,就算Student组件被销毁了,bus也会一直在,所以bus用完了最好要销毁。

2 全局事件总线改造 todoList案例

父亲给子传值【props的方式】
子给父亲传值【回调函数的方式】

全局事件总线【爷孙传值,兄弟传值等】
APP — UserItem 爷孙之间的传值
2023/1/10 Vue学习笔记 - 全局事件总线的理解与案例 并且改造 todoList案例_第6张图片

2.1 main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false
new Vue({
    // 将App组件放入容器中
    render: h => h(App),
    beforeCreate() {
        Vue.prototype.$bus = this
    }
}).$mount('#app')

2.2 App.vue
<template>
  <div class="todo-container">
    <div class="todo-wrap">
      <UserHeader @addTodo="addTodo"/>
      <!--      <UserList :todoList="todoList" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>-->
      <UserList :todoList="todoList"/>
      <UserFooter :todoList="todoList" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
    </div>
  </div>
</template>
<script>

import UserHeader from "@/components/UserHeader"
import UserFooter from "@/components/UserFooter"
import UserList from "@/components/UserList"

export default {
  name: 'App',
  data() {
    return {
      todoList: JSON.parse(localStorage.getItem('todoList')) || []
    }
  },
  methods: {
    addTodo(data) {
      this.todoList.unshift(data)
    },
    checkTodo(id) {
      this.todoList.forEach(todo => {
        if (todo.id === id) {
          todo.done = !todo.done
        }
      })
    },
    deleteTodo(id) {
      this.todoList = this.todoList.filter(todo => {
        return todo.id !== id
      })
    },
    checkAllTodo(done) {
      this.todoList.forEach(todo => {
        todo.done = done
      })
    },
    clearAllTodo() {
      this.todoList = this.todoList.filter(todo => !todo.done)
    }
  },

  watch: {
    todoList: {
      // 深度监视
      deep: true,
      handler(newValue, oldValue) {
        localStorage.setItem('todoList', JSON.stringify(newValue))
      }
    },

  },
  components: {UserHeader, UserList, UserFooter},
  mounted() {
    this.$bus.$on('checkTodo', this.checkTodo)
    this.$bus.$on('deleteTodo', this.deleteTodo)
  },
  beforeDestroy() {
    this.$bus.$off('checkTodo')
    this.$bus.$off('deleteTodo')
  }
}
</script>

<style>
/* base */
body {
  background-color: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}

.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

2.3 UserList.vue
<template>
  <ul class="todo-main">
    <!--    <UserItem
            v-for="todo in todoList"
            :key="todo.id" :todo="todo"
            :checkTodo="checkTodo"
            :deleteTodo="deleteTodo"></UserItem>-->

    <UserItem
        v-for="todo in todoList"
        :key="todo.id" :todo="todo"></UserItem>
  </ul>
</template>

<script>
import UserItem from "@/components/UserItem";

export default {
  name: `UserList`,
  components: {UserItem},
  data() {
    return {}
  },
  // props: ['todoList', 'checkTodo', 'deleteTodo']
  props: ['todoList']
}
</script>


<style scoped>
/* main */
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>

2.4 UserItem.vue
<template>
  <li>
    <label>
      <!--      <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>-->
      <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
      <span>{{ todo.title }}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  </li>
</template>

<script>

export default {
  name: `UserItem`,
  // 声明接受todo
  // props: ['todo', 'checkTodo', 'deleteTodo'],
  props: ['todo'],
  methods: {
    handleCheck(id) {
      // 通知App组件将对应的todo 的 done 取反
      // this.checkTodo(id)
      this.$bus.$emit('checkTodo', id)
    },
    handleDelete(id) {
      if (confirm('确定删除吗?')) {
        // this.deleteTodo(id)
        this.$bus.$emit('deleteTodo', id)
      }
    }
  }
}
</script>

<style scoped>
/* item */
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}

li label {
  float: left;
  cursor: pointer;
}

li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button {
  float: right;
  display: none;
  margin-top: 3px;
}

li:before {
  content: initial;
}

li:last-child {
  border-bottom: none;
}

li:hover {
  background-color: #ddd;
}

li:hover button {
  display: block;
}
</style>

你可能感兴趣的:(vue.js,javascript,学习)