全局事件总线
(没有新的api)利用自定义事件,实现兄弟组件之间的通信。
复习VueComponet
?复习重要的内置关系
?所以,在Vue的原型对象上添加属性。
?需要有$on(绑定事件,触发事件时的回调)、$emit(触发事件)。
$on、$emit、$off都在Vue的原型对象上。vm和vc都能看见。
利用beforeCreate钩子: 此时数据还没解析,数据代理、监测没开始。
main.js:
/*
const Demo = Vue.extend({});//Demo:一个Vuecomponent构造函数
const d = new Demo();//new调用,生成vc实例
Vue.prototype.x = d;//x是一个vc实例*/
//创建vue实例对象
new Vue({
//将App组件放入容器中
render: h => h(App),
beforeCreate() {
//安装全局事件总线
Vue.prototype.$bus = this;//$bus是一个vm实例,$bus是它的名字,$bus在Vue原型对象上,vc能够看见,没有必要new一个vc
},
}).$mount('#app')
?school.vue
<template>
<div class="school">
<h2>schoolname:{{ name }}</h2>
<h2>schoolage{{ schoolage }}</h2>
</div>
</template>
<script>
export default {
name: "SchoolInfo",
data() {
return {
name: "atguigu",
schoolage: 20,
stuname:'???'
};
},
methods:{
},
mounted(){
//console.log('school',this.$bus);
//给$bus绑定hello事件、事件回调在school上
this.$bus.$on('hello',(data)=>{
//写成箭头函数,使this是当前组件
this.stuname = data;
});
},
beforeDestroy() {
//销毁$bus上的hello事件
this.$bus.$off('hello')
},
};
</script>
student.vue
<template>
<div class="student">
<h1>{{ msg }}</h1>
<h2>stuname:{{ name }}</h2>
<h2>stuage:{{ stuage }}</h2>
<button @click='sendStudentName'>把学生名给school组件</button>
</div>
</template>
<script>
export default {
name: "StudentInfo",
data() {
return {
msg: "尚硅谷666",
name: "jack",
stuage: 18,
};
},
methods: {
sendStudentName(){
//触发$bus的hello事件
this.$bus.$emit('hello',this.stuname);
}
},
};
</script>
TodoList事件总线
让app能够直接和item通信。之前是把app上的函数一直传递下去,item接收,触发函数。
现在app收数据,item传id。给app绑定事件,事件回调。item传入id触发事件。
此时勾选、删除,触发事件的是$bus,vm
App.vue
mounted() {
this.$bus.$on('checkTodo',this.checkTodo)
this.$bus.$on('deleteTodo',this.deleteTodo)
},
beforeDestroy() {
this.$bus.$off('checkTodo');
this.$bus.$off('deleteTodo');
},
item.vue
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @change='handleCheck(todo.id)'/>
<!--eslint-disable-next-line vue/no-mutating-props-->
<!--<input type="checkbox" v-model="todo.done">-->
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
</li>
</template>
<script>
export default {
name: "OptionItem",
data() {
return {
};
},
//接收数据todo对象
props:["todo"],
methods: {
handleCheck(id){
//通知app组件将id对应的todo对象done取反
//this.checkTodo(id);
this.$bus.$emit("checkTodo",id);
},
handleDelete(id){
if(confirm('确定要删除吗?')){
//通知app组件将id对应的todo对象删除
//this.deleteTodo(id);
this.$bus.$emit("deleteTodo",id);
}
},
},
};
</script>
消息订阅与发布
pubsub.js库 :
回调函数为普通函数时,this为undefined,所以需要写成箭头函数,使this为当前vc。
或者把回调定义在外部
?
student.vue
methods: {
sendStudentName(){
pubsub.publish('hello',this.name)
}
},
?school.vue
mounted(){
/*//console.log('school',this.$bus);
//给$bus绑定hello事件、事件回调在school上
this.$bus.$on('hello',(data)=>{
//写成箭头函数,使this是当前组件
this.stuname = data;
});*/
//订阅一个消息
this.pubId = pubsub.subscribe('hello',(msgName,data)=>{
console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)
})
},
beforeDestroy() {
//取消订阅
pubsub.unsubsribe(this.pubId)
},
?TodoList pubsub
app和item之间的通信用pubsub:
item.vue
methods: {
handleCheck(id){
//通知app组件将id对应的todo对象done取反
//this.checkTodo(id);
this.$bus.$emit("checkTodo",id);
},
handleDelete(id){
if(confirm('确定要删除吗?')){
//通知app组件将id对应的todo对象删除
//this.deleteTodo(id);
/*this.$bus.$emit("deleteTodo",id);*/
pubsub.publish('deleteTodo',id);
}
},
},
app.vue
//删除对象
deleteTodo(_,id){
this.todos = this.todos.filter( todo => todo.id !== id);
},
mounted() {
this.$bus.$on('checkTodo',this.checkTodo)
/*this.$bus.$on('deleteTodo',this.deleteTodo)*/
this.pubId = pubsub.subscribe('deleteTodo',this.deleteTodo);
},
beforeDestroy() {
this.$bus.$off('checkTodo');
/*this.$bus.$off('deleteTodo');*/
pubsub.unsubscribe(this.pubId);
},
TodoList 编辑
添加响应式的属性:
?给item添加按钮,绑定事件回调handleEdit。handleEdit(todo)中判断有没有isEdit这个属性,有就修改(出现input框,span消失),没有就添加todo.isEdit = true属性。
然后给input框绑定失去焦点事件,修改todo.isEdit = false;,然后触发update事件并传递数据到app,this.$bus.$emit('updateTodo',todo.id,e.target.value),app触发事件回调updateTodo,修改todo的title属性。
需要点击编辑,input框自动获取焦点:
此时执行完46行,并没有立刻重新解析模板,而是执行51行,然后再解析模板。
执行51行的时候,因为通过v-show控制(display:none 隐藏了),input框并没有出现在页面上,隐藏了调focus不能获取焦点。
?
?app.vue
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<header-item
@addTodo='addTodo'>
</header-item>
<option-list
:todos='todos'
>
</option-list>
<footer-item
:todos='todos'
@checkAllTodo='checkAllTodo'
@clearAllTodo='clearAllTodo'>
</footer-item>
</div>
</div>
</div>
</template>
<script>
import pubsub from 'pubsub-js';
import HeaderItem from "./components/HeaderItem.vue";
import FooterItem from "./components/FooterItem.vue";
import OptionList from "./components/OptionList.vue";
export default {
name: "App",
data() {
return {
//从本地中获取数据
//初始为null 把null传给了footer,foot计算length,null没有length
todos:JSON.parse(localStorage.getItem('todos')) || [],
};
},
components: {
// eslint-disable-next-line vue/no-unused-components
HeaderItem,
// eslint-disable-next-line vue/no-unused-components
FooterItem,
// eslint-disable-next-line vue/no-unused-components
OptionList,
},
methods: {
//添加对象
//把addTodo事件传给了header
addTodo(obj){
this.todos.unshift(obj);
},
//修改勾选状态todo
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id === id){
todo.done = !todo.done;
}
});
},
//删除对象
deleteTodo(_,id){
this.todos = this.todos.filter( todo => todo.id !== id);
},
//全选or取消全选
//接收参数
checkAllTodo(done){
this.todos.forEach((todo)=>{
//遍历,使每一个todo的done都为传入的done值
todo.done = done;
});
},
//清除已完成的todo
clearAllTodo(){
this.todos = this.todos.filter((todo)=>{
return !todo.done
})
//console.log('app',this.todos.length)
},
//修改title
updateTodo(id,title){
this.todos.forEach((todo)=>{
if(todo.id === id){
todo.title = title;
}
});
}
},
watch:{
//监视todos的变化,一变化就存入本地
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
},
mounted() {
this.$bus.$on('checkTodo',this.checkTodo)
/*this.$bus.$on('deleteTodo',this.deleteTodo)*/
this.pubId = pubsub.subscribe('deleteTodo',this.deleteTodo);
//绑定修改事件
this.$bus.$on('updateTodo',this.updateTodo);
//APP的mounted下写个初始化就解决了
this.todos.forEach((todo)=>{todo.isEdit=false})
},
beforeDestroy() {
this.$bus.$off('checkTodo');
this.$bus.$off('updateTodo');
/*this.$bus.$off('deleteTodo');*/
pubsub.unsubscribe(this.pubId);
},
};
</script>
item.vue
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @change='handleCheck(todo.id)'/>
<!--eslint-disable-next-line vue/no-mutating-props-->
<!--<input type="checkbox" v-model="todo.done">-->
<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 class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
<button v-show='!todo.isEdit' class="btn btn-edit" @click='handleEdit(todo)'>编辑</button>
</li>
</template>
<script>
import pubsub from 'pubsub-js';
export default {
name: "OptionItem",
data() {
return {
};
},
//接收数据todo对象
props:["todo"],
methods: {
handleCheck(id){
//通知app组件将id对应的todo对象done取反
//this.checkTodo(id);
this.$bus.$emit("checkTodo",id);
},
handleDelete(id){
if(confirm('确定要删除吗?')){
//通知app组件将id对应的todo对象删除
//this.deleteTodo(id);
/*this.$bus.$emit("deleteTodo",id);*/
pubsub.publish('deleteTodo',id);
}
},
//编辑
handleEdit(todo){
//判断有没有isEdit这个属性
//用'isEdit' in todo也可以,只不过要保证原型对象里没有isEdit属性
if(Object.prototype.hasOwnProperty.call(todo, 'isEdit')){
todo.isEdit = true;
}else{
//当点击编辑的时候添加属性
this.$set(todo,'isEdit',true)
}
/*
setTimeout(()=>{
this.$refs.inputTitle.focus()
},0)*/
//$nextTick的回调会在dom节点更新之后再执行
this.$nextTick(function(){
console.log(this)//vc
this.$refs.inputTitle.focus()
})
/*
this.$nextTick(()=>{
console.log(this)//vc
})*/
},
//失去焦点完成编辑,完成修改数据
handleBlur(todo,e){
todo.isEdit = false;
//事件总线,触发update事件。
if(!e.target.value.trim()) return alert('输入不能为空')
{
this.$bus.$emit('updateTodo',todo.id,e.target.value)
}
}
},
};
</script>
css3复习
?
?
?
实现反弹
动画效果
transition标签:
<template>
<div>
<button @click="change">显示/隐藏</button>
<transition name='hello' :appear="true">
<h1 v-show="isShow" class="go">Hello!</h1>
</transition>
</div>
</template>
<script>
export default {
name:'AnTest',
data(){
return{
isShow:true,
}
},
methods:{
change(){
this.isShow = !this.isShow
}
}
}
</script>
<style>
h1{
background-color: yellow;
}
.hello-enter-active{
animation: atguigu 1s;
}
.hello-leave-active{
animation: atguigu 1s reverse;
}
@keyframes atguigu{
from{
transform: translateX(-100%);
}
to{
transform: translateX(0px);
}
}
</style>
过度效果
<template>
<div>
<button @click="change">显示/隐藏</button>
<transition name='hello' :appear="true">
<h1 v-show="isShow" class="go">Hello!</h1>
</transition>
</div>
</template>
<script>
export default {
name:'AnTest',
data(){
return{
isShow:true,
}
},
methods:{
change(){
this.isShow = !this.isShow
}
}
}
</script>
<style>
h1{
background-color: yellow;
}
.hello-enter-active,.hello-leave-active{
transition: 1s linear;
}
/* 进入的起点*/
.hello-enter,.hello-leave{
transform: translateX(-100%);
}
/* 进入的终点*/
.hello-enter-to,.hello-leave-to{
transform: translateX(0);
}
/* 离开的起点*/
/*.hello-leave{
transform: translateX(-100%);
}*/
/* 离开的终点*/
/*.hello-leave-to{
transform: translateX(0);
}*/
</style>
配置代理_方式一
原生ajax:
?JQuery(主要用来操作dom)(对xhr封装):
?axios(对xhr封装):
fetch:(兼容性)
此时跨域了。同源策略:1.协议名字,2.主机名字,3.端口号。
请求发送了,服务器接收了,并交给了浏览器,但浏览器没有呈现出来。
利用8080代理服务器解决跨域问题。
?把请求发给8080代理服务器,然后转发给5000.
当请求的资源8080代理服务器本身就有,就不会转发给5000.
public文件夹下的文件,8080都有。
配置代理_方式二
第一种只能配置一个代理,并且不能灵活的控制走不走代理。
?
github案例静态
<template>
<div id="app">
<div class="container">
<SearchOption><SearchOption/>
<SearchList><SearchList/>
</div>
</div>
</template>
<script>
//import axios from "axios";
import SearchOption from "./components/SearchOption.vue";
import SearchList from "./components/SearchList.vue";
export default {
name: "App",
methods: {},
comments: {
SearchOption,
SearchList,
},
};
</script>
github案例列表
SearchOption获取数据,需要传递给List。(全局事件总线)
List获取数据后需要处理数据,v-for遍历? key为login(唯一标识)。
SearchOption发请求:
methods: {
search(){
//ES6模板字符串
axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
response =>{
//请求成功并获取了数据
console.log('请求成功了');
//触发事件
this.$bus.$emit('getUsers',response.data.items)
},
error =>{
console.log('请求失败了',error.message);
}
)
}
},
List处理数据:
<template>
<div class="row">
<div class="card" v-for='user in users' :key='user.login'>
<a href="user.html_url" target="_blank">
<img src="user.avatar_url" style="width: 100px" />
</a>
<p class="card-text">{{user.login}}</p>
</div>
</div>
</template>
<script>
export default {
name: "SearchList",
data(){
return{
users:[]
}
},
mounted() {
//绑定事件和事件回调函数
this.$bus.$on('getUsers',(users)=>{
console.log("List获取到了数据",users)
this.users = users;
})
},
};
</script>
|