【vue】组件高级-watch侦听器等(实训1.5)

watch 侦听器

什么是 watch 侦听器
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。例如,监视用户名的变化并发起请求,判断用户名是否可用。

watch 侦听器的基本语法
开发者需要在 watch 节点下,定义自己的侦听器。实例代码如下:

<template>
	用户名:<input type="text" class="form-control"
		v-model.trim="username" />
</template>

<script>
	export default{
		name:'MyWatch',
		data(){
			return{
				username:'',
			}
		},
		watch:{
			//侦听username的变化
			//参数1是新值,参数2是老值
			username(newVal,oldVal){
				console.log(newVal,oldVal);
			}
		}
	}
</script>

<style>
</style>

使用 watch 检测用户名是否可用
监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用。
在这里插入图片描述

immediate 选项
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使用 immediate 选项。
在这里插入图片描述

deep 选项
当 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项
在这里插入图片描述
监听对象单个属性的变化
如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器:
在这里插入图片描述
计算属性 vs 侦听器
计算属性和侦听器侧重的应用场景不同:
计算属性侧重于监听多个值的变化,最终计算并返回一个新值
侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值

<template>
	用户名:<input type="text" class="form-control"
		v-model.trim="username" />{{msg}}
</template>

<script>
	export default{
		name:'MyWatch',
		data(){
			return {
				username:'',
				msg:'',
			}
		},
		watch:{
			// 侦听username的变化
			// 参数1是新值,参数2是老值
			username(newVal,oldVal){
				// console.log(newVal,oldVal);
				var api = 'https://www.escook.cn/api/finduser/'+newVal
				// axios向后台项目发出get请求,返回数据为response
				axios.get(api).then( function(response){
					// response.data是返回的数据
					//console.log(response.data);
					if(response.data.status==1){
						this.msg = response.data.messsage
					}
				} )
			}
		}
	}
	
</script>

<style>
</style>

watch的生命周期

组件运行的过程
在这里插入图片描述
如何监听组件的不同时刻
vue 框架为组件内置了不同时刻的生命周期函数,生命周期函数会伴随着组件的运行而自动调用。例如:
① 当组件在内存中被创建完毕之后,会自动调用 created 函数
② 当组件被成功的渲染到页面上之后,会自动调用 mounted 函数
③ 当组件被销毁完毕之后,会自动调用 unmounted 函数

如何监听组件的更新
当组件的 data 数据更新之后,vue 会自动重新渲染组件的 DOM 结构,从而保证 View 视图展示的数据和Model 数据源保持一致。
当组件被重新渲染完毕之后,会自动调用updated 生命周期函数。

组件中主要的生命周期函数
在这里插入图片描述
注意:在实际开发中,created 是最常用的生命周期函数!

App.vue

<template>
	<h1>App根组件</h1>
	<life-cycle></life-cycle>
</template>

<script>
	import LifeCycle from './LifeCycle.vue'
	// render 渲染
	export default{
		name:'App',
		components:{
			LifeCycle,
		},
		data(){
			return{
				
			}
		},
	}
</script>

<style>
</style>

LifeCycle.vue

<template>
	<h2>LifeCycle组件</h2>
	<input type="text" v-model="username" /><br />
	{{username}}
</template>

<script>
	export default{
		name:'LifeCycle',
		data(){
			return {
				username:'',
			}
		},
		// 组件在内存中创建完毕后执行
		created(){
			console.log("created:组件在内存中创建完毕");
		},
		// 组件渲染到页面完毕
		mounted(){
			console.log("mounted:组件渲染到页面完毕")
		},
		updated(){
			console.log("updated:data数据完成更新")
		},
		// 组件被销毁完毕
		unmounted(){
			console.log("unmounted:组件被销毁完毕")
		}
	}
</script>

<style>
</style>

效果如下:
在这里插入图片描述
2、

<template>
	<h2>LifeCycle组件</h2>
	<input type="text" v-model="username" /><br />
	{{username}}
</template>

<script>
	export default{
		name:'LifeCycle',
		data(){
			return {
				username:'kk',
			}
		},
		// 在内存中开始创建组件之前
		beforeCreate(){
			console.log("beforeCreate:在内存中开始创建组件之前")
			console.log(this.username)  // undefined
		},
		// 组件在内存中创建完毕后执行
		created(){
			console.log("created:组件在内存中创建完毕")
			console.log(this.username)  // kk
		},
		// 在把组件初次渲染到页面之前
		beforeMount(){
			console.log("beforeMount:在把组件初次渲染到页面之前")
			console.log(document.body.innerHTML)
		},
		// 组件渲染到页面完毕
		mounted(){
			console.log("mounted:组件渲染到页面完毕")
			console.log(document.body.innerHTML)
		},
		updated(){
			console.log("updated:data数据完成更新")
		},
		// 组件被销毁完毕
		unmounted(){
			console.log("unmounted:组件被销毁完毕")
		}
	}
</script>

<style>
</style>
<template>
	<h1>App根组件</h1>
	<life-cycle v-if="flag"></life-cycle>
	<button @click="flag=!flag">切换组件的创建和销毁</button>
</template>

<script>
	import LifeCycle from './LifeCycle.vue'
	// render 渲染
	export default{
		name:'App',
		components:{
			LifeCycle,
		},
		data(){
			return{
				flag:true,
			}
		},
	}
</script>

<style>
</style>

效果如下:
在这里插入图片描述

组件之间的数据共享

1、父组件向子组件共享数据
父组件通过 v-bind 属性绑定向子组件共享数据。同时,子组件需要使用 props 接收数据。示例代码如下:
在这里插入图片描述
在这里插入图片描述

father-to-son

//Father
<template>
	{{message}}--{{userinfo.name}}--{{userinfo.age}}
	<hr />
	<son :msg="message" :user="userinfo"></son>
</template>

<script>
import Son from './Son.vue'

export default{
	name:'Father',
	components:{
		Son,
	},
	data(){
		return {
			message:'hello vue',
			userinfo:{
				name:'tom',
				age:12
			}
		}
	}
}
</script>

<style>
</style>
//Son
<template>
	{{msg}}
	<br />
	{{user.name}}
</template>

<script>
export default{
	name:'Son',
	props:['msg','user'],
}


	
</script>

<style>
</style>

其运行效果为:
在这里插入图片描述

2、子组件向父组件共享数据
子组件通过自定义事件的方式向父组件共享数据。示例代码如下
在这里插入图片描述
在这里插入图片描述

son-to-father

//Father
<template>
	{{n1FromSon}}
	<hr />
	<son @n1change="getn1"></son>
</template>

<script>
import Son from './Son.vue'

export default{
	name:'Father',
	components:{
		Son,
	},
	data(){
		return{
			n1FromSon:0,
		}
	},
	methods:{
		getn1(n1){
			this.n1FromSon = n1
		}
	}
}
</script>

<style>
</style>
//Son
<template>
	n1:{{n1}}
	<button @click="addN1">n1++</button>
</template>

<script>
export default{
	name:'Son',
	emits:['n1change'],
	data(){
		return{
			n1:0
		}
	},
	methods:{
		addN1(){
			this.n1++
			this.$emit('n1change',this.n1)
		}
	}
}
</script>

<style>
</style>

其运行效果为:
在这里插入图片描述

3、父子组件之间数据的双向同步

//Father
<template>
	<button @click="count+=1"> +1 </button>
	<br />
	count: {{count}}
	<hr />
	<son v-model:num="count"></son>

</template>

<script>
import Son from './Son.vue'

export default{
	name:'App',
	data(){
		return{
			count:0
		}
	},
	components:{
		Son,
	}
}
	
</script>

<style>
</style>
//Son
<template>
	num:{{num}}
	<button @click="add"> +1 </button>
</template>

<script>
export default{
	name:'Son',
	props:['num'],   // props里面的数据是外部传入的,不能直接修改!!!
	// 声明自定义事件update:num,
	// 注意这个事件的名称格式为update:xxxx
	// xxxx表示修改了名字为xxxx的属性
	emits:['update:num'],
	methods:{
		add(){
			// 触发update:num事件,修改num属性值为this.num+1
			this.$emit('update:num',this.num+1)
		}
	}
}
</script>

<style>
</style>

效果如下:
在这里插入图片描述

4、兄弟组件之间的数据共享

兄弟组件之间实现数据共享的方案是 EventBus。可以借助于第三方的包 mitt 来创建 eventBus 对象,从而实现兄弟组件之间的数据共享。示意图如下:
在这里插入图片描述
4.1 安装 mitt 依赖包
在项目中运行如下的命令,安装 mitt 依赖包:
在这里插入图片描述
4.2 创建公共的 EventBus 模块
在项目中创建公共的 eventBus 模块如下:
在这里插入图片描述
4.3 在数据接收方自定义事件
在数据接收方,调用 bus.on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件。示例代码如下:
在这里插入图片描述
4.4 在数据接发送方触发事件
在数据发送方,调用 bus.emit(‘事件名称’, 要发送的数据) 方法触发自定义事件。示例代码如下:
在这里插入图片描述
兄弟组件案例:

//App.vue
<template>
	<div>
	    <h1>App 根组件</h1>
	    <hr />
		<h1>
			注意:需要先写接收方组件,再写发送方组件
			两个组件中的事件名字必须相同!!!!!!
		</h1>
	    <div class="brother-box">
			<!-- 3. 使用组件 -->
			<my-left></my-left>
			<my-right></my-right>
	    </div>
	</div>
</template>

<script>
// 1. 导入需要的组件
import MyLeft from './MyLeft.vue'
import MyRight from './MyRight.vue'

export default{
	name:"App",
	 // 2. 注册组件
	components: {
		MyLeft,
		MyRight,
	},
}


</script>

<style lang="css" scoped="scoped">
	.brother-box {
	  display: flex;
	}
	.brother-box > div {
	  border: 1px solid #ccc;
	  flex: 1;
	  margin: 10px;
	  padding: 10px;
	  height: 300px;
	}
</style>

//eventBus.js
import mitt from 'mitt'
const bus = mitt()
export default bus
//MyLeft.vue
<template>
	<div>
	    <h3>数据发送方 --- count 的值为:{{ count }}</h3>
	    <button type="button" class="btn btn-primary" @click="add">+1</button>
	</div>
</template>

<script>
import bus from './eventBus.js'

export default {
  name: 'MyLeft',
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    add() {
      this.count++
      bus.emit('countChange', this.count)
    },
  },
}

</script>

<style>
</style>

//MyRight.vue
<template>
  <div>
    <h3>数据接收方 --- num 的值为:{{ num }}</h3>
  </div>
</template>

<script>
import bus from './eventBus.js'  // 导入js文件中的bus

export default {
  name: 'MyRight',
  data() {
    return {
      num: 0,
    }
  },
  created() {                  // 生命周期函数created
	// 接收方定义自定义事件countChange
	// bus.on('事件名称',事件处理函数)
	// count是事件传过来的参数
	let a = this;
    bus.on('countChange', function(count) {
      a.num = count
    })
  },
}
</script>

<style lang="less" scoped></style>
//readme.txt
如何使用mitt?

步骤1:在当前工程code4下执行

E:\XJA\vue\code4> npm install --save mitt

步骤2:编写代码,导入mitt,创建mitt对象

import mitt from 'mitt'
const bus = mitt()

可以把这些代码创建一个js文件,如eventBus.js,代码如下

import mitt from 'mitt'
const bus = mitt()
export default bus

步骤3:
vue文件中使用mitt

import bus from './eventBus.js'

。。。。

在数据接收方自定义事件
bus.on('事件名称',事件处理函数)


在数据发送方触发事件
bus.emit('事件名称', 要发送的数据)

运行效果如下:
在这里插入图片描述

5. 后代关系组件之间的数据共享
后代关系组件之间共享数据,指的是父节点的组件向其子孙组件共享数据。此时组件之间的嵌套关系比较复杂,可以使用 provideinject 实现后代关系组件之间的数据共享。

5.1 父节点通过 provide 共享数据
父节点的组件可以通过provide 方法,对其子孙组件共享数据:
在这里插入图片描述
5.2 子孙节点通过 inject 接收数据
子孙节点可以使用 inject 数组,接收父级节点向下共享的数据。示例代码如下:
在这里插入图片描述
5.3 父节点对外共享响应式的数据
父节点使用 provide 向下共享数据时,可以结合computed 函数向下共享响应式的数据。示例代码如下:
在这里插入图片描述
5.4 子孙节点使用响应式的数据
如果父级节点共享的是响应式的数据,则子孙节点必须以 .value 的形式进行使用。示例代码如下:
在这里插入图片描述
6. vuex
vuex 是终极的组件之间的数据共享方案。在企业级的 vue 项目开发中,vuex 可以让组件之间的数据共享变得高效、清晰、且易于维护。

后代关系组件案例—06provide-inject

//App.vue
<template>
	<div>
		<h1>App根组件--{{color}}</h1>
		<hr />
		<TwoLevel></TwoLevel>
	</div>
</template>

<script>
import TwoLevel from './TwoLevel.vue'
	
export default{
	name:"App",
	components:{
		TwoLevel
	},
	data(){
		return {
			color:"blue"
		}
	},
	// 父节点的组件可以通过 provide 方法,对其子孙组件共享数据
	provide(){
		return{
			color:this.color,   // color共享
			age:12,             // age共享
		}
	},
	
}

</script>

<style lang="css" scoped="scoped">
</style>
//ThreeLevel.vue
<template>
	<div>
		<h3>ThreeLevel组件---{{color}}---{{age}}</h3>
		
	</div>
</template>

<script>

export default{
	name:"ThreeLevel",
	// 子孙节点通过 inject 接收数据
	inject:['color','age']
}	
	
</script>

<style lang="css" scoped="scoped">
</style>

//TwoLevel.vue
<template>
	<div>
		<h2>TwoLevel组件</h2>
		
		<hr />
		<ThreeLevel></ThreeLevel>
	</div>
</template>

<script>
import ThreeLevel from './ThreeLevel.vue'

export default{
	name:"TwoLevel",
	components:{
		ThreeLevel
	},
}	
	
</script>

<style lang="css" scoped="scoped">
</style>

其运行效果如下:
在这里插入图片描述

vue 3.x 中全局配置 axios

1. 为什么要全局配置 axios
在实际项目开发中,几乎每个组件中都会用到 axios 发起数据请求。此时会遇到如下两个问题:
① 每个组件中都需要导入axios(代码臃肿)
② 每次发请求都需要填写完整的请求路径(不利于后期的维护)
在这里插入图片描述
2. 如何全局配置 axios
在 main.js 入口文件中,通过 app.config.globalProperties 全局挂载 axios,示例代码如下:
在这里插入图片描述
axios案例

//readme.txt
全局配置axios

步骤1:index.html 
  
  <!-- 引入axios -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  
注意:不需要执行npm i axios -S
  
步骤2:main.js

const spa_app = createApp(App)

// 指定axios根路径
axios.defaults.baseURL = "https://www.escook.cn"

spa_app.mount('#app')


步骤3:代码
post请求
methods:{
    postInfo(){
      let api = "/api/post"
    
      // post请求以json对象形式传参
      axios.post(api,{name:'zs',age:12}).then(function(response)  {
        // response是所有响应数据,真正返回的数据是response.data
        console.log(response.data)
      })
    }
  }



get请求
methods:{
    getInfo(){
      // get请求以json对象形式传参,但是必须在外面加上params
      let api = '/api/get';
      
      // get请求以json对象形式传参,但是必须在外面加上params
      
      axios.get(api,{params:{name:'zs',age:12}}).then(function(response)  {
        // response是所有响应数据,真正返回的数据是response.data
        console.log(response.data)
      })
      
    }
  }

注意:// 导入 axios
import axios from 'axios'这句话可以不用写!!!!
//App.vue
<template>
	<button @click="getInfo">get请求</button>
	<hr />
	<button @click="postInfo">post请求</button>
</template>

<script>
	export default{
		name:'App',
		methods:{
			// get请求
			getInfo(){
				// 请求路径
				let api = "/api/get"
				
				// get提交,参数1是路径,参数2是json格式的上传参数
				axios.get(api,{params:{name:'zz',age:14}}).then(function(response){
					console.log(response.data)
				})
			},
			// post请求
			postInfo(){
				// 请求路径
				let api = "/api/post"
				// post提交,参数1是路径,参数2是json格式的上传参数
				axios.post(api,{name:'zs',age:12}).then(function(response){
					console.log(response.data)
				})
				
			}
		}
	}
	
</script>

<style>
</style>
//main.js
import { createApp } from 'vue'
//import App from './App.vue'
//import App from './components/01life-cycle/App.vue'
//import App from './components/05brother-eventbus/App.vue'
//import App from './components/06provide-inject/App.vue'
import App from './components/07axios/App.vue'
import './index.css'

const spa_app = createApp(App)

// 指定axios根路径
axios.defaults.baseURL = "https://www.escook.cn"

spa_app.mount('#app')

其运行效果为
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值