Vue2学习day06—浏览器本地存储&组件的自定义事件&全局事件总线(GlobalEventBus)&消息订阅与发布(pubsub)&nextTick
浏览器本地存储
- 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
- 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
- 相关API:
- xxxxxStorage.setItem(‘key’, ‘value’);
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。 - xxxxxStorage.getItem(‘person’);
该方法接受一个键名作为参数,返回键名对应的值。 - xxxxxStorage.removeItem(‘key’);
该方法接受一个键名作为参数,并把该键名从存储中删除。 - xxxxxStorage.clear()
该方法会清空存储中的所有数据。 - 示例:
- localStorage
<!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>localStorage</title>
</head>
<body>
<h2>localStorage</h2>
<button onclick="saveData()">点我保存一个数据</button>
<button onclick="readData()">点我读取一个数据</button>
<button onclick="deleteData()">点我删除一个数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
<script type="text/javascript">
let p = {name:'张三',age:18}
function saveData(){
localStorage.setItem('msg','hello!')
localStorage.setItem('person',JSON.stringify(p))
}
function readData(){
console.log(localStorage.getItem('msg'))
const person = localStorage.getItem('person')
console.log(JSON.parse(person))
}
function deleteData(){
localStorage.removeItem('msg')
}
function deleteAllData(){
localStorage.clear()
}
</script>
</body>
</html>
- sessionStorage
<!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>sessionStorage</title>
</head>
<body>
<h2>sessionStorage 浏览器一关就没了</h2>
<button onclick="saveData()">点我保存一个数据</button>
<button onclick="readData()">点我读取一个数据</button>
<button onclick="deleteData()">点我删除一个数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
<script type="text/javascript">
let p = {name:'张三',age:18}
function saveData(){
sessionStorage.setItem('msg','hello!')
sessionStorage.setItem('person',JSON.stringify(p))
}
function readData(){
console.log(sessionStorage.getItem('msg'))
const person = sessionStorage.getItem('person')
console.log(JSON.parse(person))
}
function deleteData(){
sessionStorage.removeItem('msg')
}
function deleteAllData(){
sessionStorage.clear()
}
</script>
</body>
</html>
TodoList案例实现本地存储
学习了浏览器本地存储后,我们来将上篇博客的TodoList案例实现本地存储,我们使用watch对todos进行监听,这样todos改变的时候就能调用我们编写的相关逻辑
watch:{
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
}
记得修改data中存放的todos数据,todos中的数据要从本地存储中读出
todos:JSON.parse(localStorage.getItem('todos')) || []
组件的自定义事件
之前我们在TodoList案例中子组件向父组件传递数据,是通过父组件事先定义的函数,父组件将此函数传递给子组件,子组件接收后进行调用的方式,下面我们学习子组件向父组件传递数据的另一种方法:组件的自定义事件
- 一种组件间通信的方式,适用于:
子组件 ===> 父组件 - 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(
事件的回调在A中 )。 - 绑定自定义事件:
- 解绑自定义事件
this.$off('atguigu') - 组件上也可以绑定原生DOM事件,需要使用native修饰符。
- 注意:通过
this.$refs.xxx.$on('atguigu',回调) 绑定自定义事件时,回调要么配置在methods中 ,要么用箭头函数 ,否则this指向会出问题! - 示例
- Student组件(数据传递使用案例TodoList中的方法)
<template>
<div class="school">
<h2 >学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="sendSchoolName(name)">传递学校名称</button>
</div>
</template>
<script>
export default {
name:'School',
props:['getSchoolName'],
data(){
return{
name:'曲阜师范大学',
address:'曲阜',
}
},
methods:{
sendSchoolName(schoolName){
this.getSchoolName(schoolName)
}
}
}
</script>
- Student组件(数据传递使用自定义事件,同时顺带验证destroy会销毁自定义事件)
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<h2>当前求和为:{{number}}</h2>
<button @click="add">点我number++</button>
<button @click="sendStudentName(name)">传递学生名给App</button>
<button @click="unbind">解绑atguigu事件</button>
<button @click="death">销毁当前Student组件的实例对象(vc)</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return{
name:'张三',
sex:'男',
number:0
}
},
methods:{
sendStudentName(){
this.$emit('atguigu',this.name,666,888,999)
},
unbind(){
this.$off('atguigu')
},
death(){
this.$destroy()
},
add(){
console.log('add回调被调用了')
this.number++
}
}
}
</script>
- App组件
<template>
<div class="app">
<h1>{{msg}},学生姓名是:{{studentName}}</h1>
<School :getSchoolName = "getSchoolName" />
<Student v-on:atguigu="getStudentName" @demo="m1"/>
<Student ref="student" @click="show"/>
<Student ref="student" @click.native="show"/>
</div>
</template>
<script>
import School from './components/School'
import Student from './components/Student'
export default {
name:'App',
components:{
Student,
School
},
data(){
return{
msg:'您好呀',
studentName:''
}
},
methods:{
getSchoolName(name){
console.log('收到了学校名:'+name)
},
getStudentName(name,...params){
console.log('收到了学生名:',name,params)
this.studentName = name
},
m1(){
console.log("demo事件被触发了")
},
show(){
alert('原生dom事件')
}
},
mounted(){
this.$refs.student.$on('atguigu',this.getStudentName)
}
}
</script>
TodoList案例优化,子组件==>父组件使用组价自定义事件实现
学习了组件自定义事件之后,我们继续给TodoList案例做下优化
全局事件总线(GlobalEventBus):任意组件间通信
分析: x需要具备的条件:
- 所有组件都能看到
- x可以调用到
o
n
、
on、
on、off、$emit
全局事件总线(GlobalEventBus)
TodoList案例优化,孙子组件==>爷爷组件使用全局事件总线实现
之前我们在TodoList案例中孙子TodoItem组件向其爷爷App组件中传递数据是通过其父亲TodoList做了个过渡,,当我们学习完全局事件总线后,我们来优化下该功能
- 首先我们去掉App组件和TodoList传递函数的部分,更改后App组件中该部分变为
<TodoList :todos="todos" />
- TodoList该部分变为:
<TodoItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" />
- 之后我们将TodoList和TodoItem中接收相关函数的props的相关数据项去掉,注意不要删多了,只删除相应的函数的参数即可
- main.js中安装全局事件总线
new Vue({
.....
beforeCreate() {
Vue.prototype.$bus = this
},
})
- 给App组件中的$bus绑定自定义事件,注意要在组件销毁前进行解绑,释放资源
mounted(){
this.$bus.$on('checkTodo',this.checkTodo)
this.$bus.$on('deleteTodo',this.deleteTodo)
},
beforeDestroy(){
this.$bus.$off('checkTodo')
this.$bus.$off('deleteTodo')
}
- TodoList组件给App组件提供数据
methods:{
handleCheck(id){
this.$bus.$emit('checkTodo',id)
},
handleDelete(id){
if(confirm('确定删除吗')){
this.$bus.$emit('deleteTodo',id)
}
}
}
消息订阅与发布(pubsub)
消息订阅与发布
-
一种组件间通信的方式,适用于任意组件间通信 。 -
使用步骤:
-
安装第三方消息订阅与发布库: npm install pubsub-js
-
在School组件引入第三方库 import pubsub from 'pubsub-js'
-
School组件要接收Student组件中的数据,要在School组件中订阅消息 methods:{
demo(msgName,data){
console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)
}
},
mounted(){
this.pubId = pubsub.subscribe('hello',this.demo)
},
beforeDestroy(){
pubsub.unsubscribe(this.pubId)
}
-
Student组件传递数据,同样需要也需要引入pubsub包 pubsub.publish('hello',this.name)
nextTick
- 语法:this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调。
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
给TodoList案例添加编辑功能,点击编辑按钮,列表中的数据变为输入框,并获得焦点,失去焦点触发事件,保存数据。这里点击编辑输入框会获取焦点,需要使用nextTick来实现,否则输入框还没渲染到界面,就执行获取焦点的方法,这是获取不到焦点的 修改后的TodoItem组件
<template>
<li>
<label>
......
<span v-show="!todo.isEdit">{{todo.title}}</span>
<input
v-show="todo.isEdit"
type="text"
:value="todo.title"
@blur="handleBlur(todo,$event)"
ref="inputTitle"
>
</label>
......
<button v-show="!todo.isEdit" class="btn btn-edit" @click="handleEdit(todo)">编辑</button>
</li>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
....
methods:{
......
handleEdit(todo){
if(todo.hasOwnProperty('isEdit')){
todo.isEdit = true
}else{
this.$set(todo,'isEdit',true)
}
this.$nextTick(function(){
this.$refs.inputTitle.focus()
})
},
handleBlur(todo,e){
if(!e.target.value.trim()) return alert('输入不能为空')
this.$bus.$emit('updateTodo',todo.id,e.target.value)
todo.isEdit = false
}
}
}
</script>
在App组件中添加修改逻辑
updateTodo(id,title){
this.todos.forEach((todo)=>{
if(todo.id === id) todo.title = title
})
},
|