Vue组件高级

Vue组件高级

watch侦听器

watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。例如,监视用户名的变化并发

起请求,判断用户名是否可用。

语法

<script>
export default{
    data(){
        return {username:''}
    },
    watch:{
        //监听username值的变化
        username(newVal,oldVal){
            console.log(newVal,oldval)
        }
    },
}
</script>

检测用户名是否可用

import axios from 'axios'
export default{
    data(){
        return {username:''}
    },
    watch:{
        async username(newVal,oldVal){
            const {data:res} = await axios.get(url/${newVal})
        console.log(res)
        }
    }
}

immediate选项

默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使

用 immediate 选项。实例代码如下:

import axios from 'axios'
export default{
    data(){
        return {username:''}
    },
    watch:{
        async username(newVal,oldVal){
            const {data:res} = await axios.get(url/${newVal})
        console.log(res)
        }
    },
    //表示组件加载完毕后立即调用一次当前的watch侦听器
    immediate:true
}

deep选项

当 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项,

代码示例如下:

import axios from 'axios'
export default{
    data(){
        return {info:{
            username:''
        }}
    },
    watch:{
        async username(newVal,oldVal){
            const {data:res} = await axios.get(url/${newVal})
        console.log(res)
        }
    },
    //需要使用deep选项,否则username的值的变化无法被监听到
    deep:true
}

监听对象单个属性的变化

import axios from 'axios'
export default{
    data(){
        return {info:{
            username:''
        }}
    },
    watch:{
       'info.username':{
            async username(newVal,oldVal){
            const {data:res} = await axios.get(url/${newVal})
        console.log(res)
        }
    },
       }

}

计算属性 vs 侦听器

计算属性和侦听器侧重的应用场景不同:

计算属性侧重于监听多个值的变化,最终计算并返回一个新值

侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值

组件的生命周期

组件的生命周期指的是:组件从创建 -> 运行(渲染) -> 销毁的整个过程,强调的是一个时间段。

开始 ->import导入组件->components注册组件->以标签形式使用组件->在内存中创建组件的实例对象->把创建的组件实例渲染到页面上->组件切换时销毁需要被隐藏的组件->结束

vue 框架为组件内置了不同时刻的生命周期函数,生命周期函数会伴随着组件的运行而自动调用。例如:

  1. 当组件在内存中被创建完毕之后,会自动调用 created 函数
  2. 当组件被成功的渲染到页面上之后,会自动调用 mounted 函数
  3. 当组件被销毁完毕之后,会自动调用 unmounted 函数

如何监听组件的更新

当组件的 data 数据更新之后,vue 会自动重新渲染组件的 DOM 结构,从而保证 View 视图展示的数据和

Model 数据源保持一致。

当组件被重新渲染完毕之后,会自动调用 updated 生命周期函数。

组建中主要的生命周期函数

生命周期函数执行时机所属阶段执行次数应用场景
created组件在内存中创建完毕后创建阶段唯一1次发 ajax 请求初始数据
mounted组件初次在页面中渲染完毕后创建阶段唯一1次操作 DOM 元素
updated组件在页面中被重新渲染完毕后运行阶段0 或 多次-
unmounted组件被销毁后(页面和内存)销毁阶段唯一1次-

组件中全部的生命周期函数

生命周期函数执行时机所属阶段执行次数应用场景
beforeCreate在内存中开始创建组件之前创建阶段唯一1次
created组件在内存中创建完毕后创建阶段唯一1次发 ajax 请求初始数据
beforeMount在把组件初次渲染到页面之前创建阶段唯一1次
mounted组件初次在页面中渲染完毕后创建阶段唯一1次操作 DOM 元素
beforeUpdate在组件被重新渲染之前运行阶段0 或 多次
updated组件在页面中被重新渲染完毕后运行阶段0 或 多次-
beforeUnmount在组件被销毁之前销毁阶段唯一1次
unmounted组件被销毁后(页面和内存)销毁阶段唯一1次-

组件之间的数据共享

组件之间的关系

在项目开发中,组件之间的关系分为如下 3 种:

  1. 父子关系
  2. 兄弟关系
  3. 后代关系

父子组件之间的数据共享

父子组件之间的数据分享又分为:

  1. 父 -> 子共享数据
  2. 子 -> 父共享数据
  3. 父 -> 子双向数据同步

父组件向子组件共享数据

父组件通过 v-bind 属性绑定向子组件共享数据。同时,子组件需要使用 props 接收数据。示例代码如下:

父组件

<my-test-1 :msg='message' :user='userinfo'></my-test-1>
data(){
	return {
		message:'hello  vue',
		userinfo:{name:zs,age,20}
}
}

子组件

<template>
  <h3>
    测试父子传值  
    </h3>
  <p>
      {{msg}}
    </p>
  <p>
      {{user}}
    </p>
</template>
<script>
export default{
    props:['msg','user'],
}
</script>

子组件向父组件共享数据

子组件

<script>
	export default{
        emits:['n1change'],//声明自定义事件
        data(){return {n1:0}},
        methods:{
            addN1(){
                this.n1++
                this.$emit('n1change',this.n1)
            }
        }
    }
</script>

父组件

<my-test-1 @n1change='getn1'></my-test-1>

export default{
data(){return {n1FromSon:0}},
methods:{
	getn1(n1){//通过形参,接收子组件传递过来的数据
		this.n1FromSon = n1
}
}
}

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

父组件

<template>
<my-son v-model:num='count'></my-son>
</template>

<script>
import MySon from './Son.vue'
    
    export default{
        name:'MyApp',
        data(){
            return {
                count:0,
            }
        }
    }
</script>

子组件

<template>
<p>
    子组件{{num}}
    </p>
</template>

<script>
export default{
    name:'MySon',
    props:['num'],
    emits:['update:num'],
    methods:{
        add(){
            this.$emit('update:num',this.num + 1)
        }
    }
}
</script>

Vuex实现数据共享

vuex 是一个专门为vue.js应用程序开发的状态管理模式。

这个状态我们可以理解为在data中的属性,需要共享给其他组件使用的部分。

也就是说,是我们需要共享的data,使用vuex进行统一集中式的管理。

vuex中五种基本对象

  1. state:存储状态(变量)
  2. getters:对数据获取之前的再次编译,可以理解为state的计算属性。我们在组件中使用 $store.getters.fun()
  3. mutations:修改状态,并且是同步的。在组件中使用$store.commit(’’,params)。这个和我们组件中的自定义事件类似。
  4. actions:异步操作。在组件中使用是$store.dispath(’’)
  5. modules:store的子模块,为了开发大型项目,方便状态管理而使用的。这里我们就不解释了,用起来和上面的一样。

下载vuex

npm i vuex --save

导入vuex

创建一个index.js

import {createStore} from 'vuex';

export default createStore({
    state:{
        name:'admin',
        print(){
            console.log('this is a test')
        }
    }
})

main.js中注册

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import store from './index.js'

const app = createApp(App)
app.use(store).mount('#app')

调用数据

$store.state.name

使用mutations中的方法

index.js

import {createStore} from 'vuex';

export default createStore({
    state:{
        name:'admin',
        count:0,
        print(){
            console.log('this is a test')
        }
    },
    mutations:{
        addCount(){
            return {state.count++}
        }
    }
})

App.vue

<template>
  <div class="hello">
    <h3>{{$store.state.count}}</h3>
    <div>
      <button @click="AddClick()">增加</button>
    </div>
  </div>
</template>
methods: {
    AddClick(){
      this.$store.commit('addCount');
    },
  }

axios

在实际项目开发中,几乎每个组件中都会用到 axios 发起数据请求。此时会遇到如下两个问题:

  1. 每个组件中都需要导入 axios(代码臃肿)
  2. 每次发请求都需要填写完整的请求路径(不利于后期的维护)

全局配置axios

在 main.js 入口文件中,通过 app.config.globalProperties 全局挂载 axios,示例代码如下:

axios.defaults.baseURL= 'http://api.com'

app.config.globalProperties.$http = axios

使用方法

this.$http.get('/',{
params:{
name:'',
age:10,
}
})

ref引用

ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。

每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,

组件的 $refs 指向一个空对象。

使用ref引用DOM元素

如果想要使用 ref 引用页面上的 DOM 元素,则可以按照如下的方式进行操作:

<h3 ref='myh3'>MyRef组件</h3>
<button @click='getRef'>
    获取$refs引用
</button>
meethods:{
	getRef(){
		//类似于JQuery中的$("#myh3")
		console.log(this.$refs.myh3)
		//操作DOM元素,把文本颜色改为红色
		this.$refs.myh3.style.color = 'red'
}
}

$nextTick

ref获取页面上的DOM元素是异步进行的,所以是当页面元素发生改变重新渲染前获取的话是获取不到DOM元素的,这时就需要使用组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的

DOM 异步地重新渲染完成后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。

<input type='text' v-if='inputVisible' ref='ipt'>
<button v-else @click='showInput'>
    
</button>
methods:{
	showInput(){
		this.inputVisible = true
		this.$nextTick(() => {
			this.$refs.ipt.focus()
})
}
}

动态组件

动态组件指的是动态切换组件的显示与隐藏。vue 提供了一个内置的 <component> 组件,专门用来实现组件

的动态渲染。

  1. <component> 是组件的占位符
  2. 通过 is 属性动态指定要渲染的组件名称
  3. <component is=“要渲染的组件的名称”></component>
data(){
	return{
		//当前要渲染的组件的名称
		conName:'my-dynamic-1'
}
}
<template>
<button @click="conName='my-dynamic-1'">组件1</button>
<button @click="conName='my-dynamic-2'">组件2</button>
<component :is="comName"></component>
</template>

默认情况下,切换动态组件时无法保持组件的状态。此时可以使用 vue 内置的 <keep-alive> 组件保持动态组

件的状态。示例代码如下:

<keep-alive>
  <component :is="comName"></component>
</keep-alive>

插槽

插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的

部分定义为插槽。

image-20211220213357470

可以把插槽认为是组件封装期间,为用户预留的内容的占位符。

基础用法

<template>
<p>这是MyCom1 组件的第一个p标签</p>
<!--通过slot标签,为用户预留内容占位符(插槽) -->
<slot></slot>
<p>这是MyCom1 组件的最后一个p标签</p>
</template>
<my-com-1>
    <!--为插槽指定具体的内容 -->
<p>~~~用户自定义的内容~~~</p>
</my-com-1>

如果在封装组件时没有预留任何 <slot> 插槽,则用户提供的任何自定义内容都会被丢弃。示例代码如下:

后背内容

封装组件时,可以为预留的 <slot> 插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何

内容,则后备内容会生效。示例代码如下:

<template>
<p>这是MyCom1 组件的第一个p标签</p>
<!--通过slot标签,为用户预留内容占位符(插槽) -->
<slot>这是后备内容</slot>
<p>这是MyCom1 组件的最后一个p标签</p>
</template>

具名插槽

如果在封装组件时需要预留多个插槽节点,则需要为每个 <slot> 插槽指定具体的 name 名称。这种带有具体

名称的插槽叫做“具名插槽”。示例代码如下:

<div class="container">
    <header>
    <slot name="header"></slot>
    </header>
    <main>
    <slot></slot>
    </main>
    <footer>
    <slot name="footer"></slot>
    </footer>
</div>

在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的

形式提供其名称。示例代码如下:

<my-com-2>
  <template v-slot:header>
    <h1>
        TEST
      </h1>
    </template>
<template v-slot:default>
<p>
    this is a test
    </p>
</template>
    <template v-slot:footer>
      <p>
          test over.
        </p>
    </template>
</my-com-2>

v-slot简写

跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header

可以被重写为 #header

作用域插槽

在封装组件的过程中,可以为预留的 <slot> 插槽绑定 props 数据,这种带有 props 数据的 <slot> 叫做“作用

域插槽”。示例代码如下:

<div>
    <h3>
        这是TEST组件
    </h3>
    <slot :info="information"></slot>
</div>

<!--使用自定义组件 -->
<my-test>
<template v-slot:default="scope">
    {{ scope }}
    </template>
</my-test>

作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程。示例代码如下:

<div>
    <h3>
        这是TEST组件
    </h3>
    <slot :info="information"></slot>
</div>

<!--使用自定义组件 -->
<my-test>
<template v-slot:default="{user}">
    <td>{{user.id}}</td>
    <td>{{user.name}}</td>
    <td>{{user.s}}</td>
    </template>
</my-test>

自定义指令

vue 中的自定义指令分为两类,分别是:

  • 私有自定义指令

  • 全局自定义指令

声明私有自定义指令

directives:{
// 自定义一个私有指令
  focus:{
	//当被绑定的元素插入到DOM中时,自动触发mounted 函数
	mounted(el){
		el.focus()//让被绑定的元素自动获得焦点
}
}
}

使用自定义指令

<!--声明自定义指令时,指令的名字是focus-->
<!-- 使用自定义指令时,需要加上 v-指令前缀 -->
<input v-focus />

声明全局自定义指令

全局共享的自定义指令需要通过“单页面应用程序的实例对象”进行声明,示例代码如下:

const app = Vue.createApp({})

//注册一个全局自定义指令 v-focus
app.directive('focus',{
    mounted(el){
        el.focus()
    }
})

update函数

mounted 函数只在元素第一次插入 DOM 时被调用,当 DOM 更新时 mounted 函数不会被触发。 updated

函数会在每次 DOM 更新完成后被调用。示例代码如下:

const app = Vue.createApp({})

//注册一个全局自定义指令 v-focus
app.directive('focus',{
    mounted(el){//第一次插入DOM时触发这个函数
        el.focus()
    },
    updated(el){//每次DOM更新都会触发updated函数
        el.focus()
    }
})

注意:在 vue2 的项目中使用自定义指令时,【 mounted -> bind 】【 updated -> update 】

简写

如果 mounted 和updated 函数中的逻辑完全相同,则可以简写成如下格式:

app.directive('focus',(el) =>{
    el.focus()
})

指令的参数值

在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值,示例代码如下:

<input type="text" v-model.number="count" v-focus v-color="red">
    
app.directive('color',(el,binding) =>{
    //binding.value 就是通过等号为指令绑定的值
    el.style.color = binding.value
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bestkasscn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值