Vue3:组件高级(上)

简介: Vue3:组件高级(上)

Vue3:组件高级(上)

Date: May 20, 2023

Sum: watch倾听器、组件的生命周期、组件之间的数据共享、vue3.x中全局配置axios


目标:

能够掌握 watch 侦听器的基本使用


能够知道 vue 中常用的生命周期函数


能够知道如何实现组件之间的数据共享


能够知道如何在 vue3.x 的项目中全局配置 axios


前言:以下使用较老的axios,否则会报错

npm i axios@0.21.1 -S

watch 侦听器

1.什么是 watch 侦听器

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


变化并发起请求,判断用户名是否可用。


2.watch 侦听器的基本语法

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

export default {
  data() {
    return {
      username: ''
    }
  },
  watch: {
    // 监听 username 的值的变化
    // 形参列表中,第一个值是“变化后的新值”,第二个值是“变化之前的旧值”
   username(newVal, oldVal) {
      console.log(newVal, oldVal);
    }
  }
}

1.使用 watch 检测用户名是否可用

监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:

8d300640de4f7408164af53077adc88a.png

复习并理解:


返回promise对象:

const res = axios.get('http://www.escook.cn/api/finduser/' + newVal)

701202f12536c80768770a60ad8a0e88.png


若返回promise对象,我们可以通过await/async进行简化,返回的则是一个数据对象,

async username(newVal, oldVal) {
      console.log(newVal, oldVal);
      const res = await axios.get('http://www.escook.cn/api/finduser/' + newVal)
      console.log(res);
    }

f7b61aaaabe2558c02b0626211e8d8c2.png

1.immediate 选项

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

e36f819db7ea02b36336fc992873238a.png

1.deep 选项

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

data() {
    return {
      username: 'admin',
      info: {
        username: 'zs' //info中包含 username 属性
      }
    }
  },
  watch: {
    info: { //直接监听 info 对象的变化
      async handler(newVal) {
        const { data: res } = await axios.get('https://www.escook.cn/api/finduser' + newVal.username)
        console.log(res);
      },
      deep: true // 需要使用 deep 选项, 否则 username 值的变化无法被监听到
    }
  }

6.监听对象单个属性的变化

如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器:

8185dd6132bf4ae41b44e0f9de2dd09d.png

7.计算属性 vs 侦听器

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


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


计算属性侧重于得到最后的一个结果


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


侦听器侧重于去执行某个业务逻辑,业务逻辑执行完,侦听器的目的就达到了


组件的生命周期

1. 组件运行的过程

1280b380f2dfafb0d6a12bceba16b8b4.png

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


2. 如何监听组件的不同时刻


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


例如:


① 当组件在内存中被创建完毕之后,会自动调用 created 函数


② 当组件被成功的渲染到页面上之后,会自动调用 mounted 函数


③ 当组件被销毁完毕之后,会自动调用 unmounted 函数


案例:


Code:


App.vue

<template>
  <div>
    <h1>App 根组件</h1>
    <hr/>
    <life-cycle v-if="flag"></life-cycle>
    <button @click="flag = !flag">Toggle</button>
  </div>
</template>
<script>
import LifeCycle from './LifeCycle.vue'
export default {
  name: 'MyApp',
  components: {
    LifeCycle
  },
  data() {
    return {
      flag: false,
    }
  },
}
</script>
<style>
</style>

LifeCycle.vue

<template>
  <div>
    <h2>LifeCycle</h2>
  </div>
</template>
<script>
export default {
  name: 'LifeCycle',
  created() {
    console.log('组件在内存中被创建完毕了');
  },
  mounted() {
    console.log('组件被成功渲染到页面上了');
  },
  unmounted() {
    console.log('组件被销毁完毕了');
  }
}
</script>
<style>
</style>

理解:代码中的createde mounted() unmounted函数放到子组件LifeCycle中,当子组件创建完毕之后,会调用created函数,当组件被渲染到页面上后,会调用mounted函数,当组件被销毁完毕之后,会调用unmounted函数。


1.如何监听组件的更新

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


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


案例:


Code:

<template>
  <div>
    <h2>LifeCycle</h2>
    <p>{{ count }}</p>
    <button @click="count += 1">+1</button>
  </div>
</template>
export default {
  name: 'LifeCycle',
  data() {
    return {
      count: 0,
    }
  },
  updated() {
    console.log('组件被重新渲染完毕了');
  },
}

4. 组件中主要的生命周期函数

39d444a37f328145598d89012e2d4440.png

注意:在实际开发中,created 是最常用的生命周期函数!


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

59360df246a8d4f8cb7f309d1e5ed9e0.png

疑问:为什么不在 beforeCreate 中发 ajax 请求初始数据


发起Ajax请求最好都在creat中


1.完整的生命周期图示

可以参考 vue 官方文档给出的“生命周期图示”,进一步理解组件生命周期执行的过程:

https://www.vue3js.cn/docs/zh/guide/instance.html#生命周期图示


组件之间的数据共享

组件之间的关系

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


① 父子关系 ② 兄弟关系 ③ 后代关系


父子组件之间的数据共享

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


① 父 -> 子共享数据 ② 子 -> 父共享数据 ③ 父 <-> 子双向数据同步

8b45929ec97f0e7a9a67bb1b961f8c99.png

AB是父子关系,BC有一个共同的父级节点,故二者为兄弟关系。B和EFI都为特殊的兄弟关系。


A和DGH属于后代关系


2.1 父组件向子组件共享数据


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


案例:

<template>
  <div>
    <h1>MyAPP -- {{ count }}</h1>
    <button @click="count += 1">父+1</button>
    <my-son :num="count"></my-son>
  </div>
</template>
<script>
import MySon from './Son.vue'
export default {
  name: 'MyApp',
  components: {
    MySon,
  },
  data() {
    return {
      count: 0,
    }
  }
}
</script>
<template>
  <div>
    <h2>MySon -- {{ num }}</h2>
  </div>
</template>
<script>
export default {
  name: 'MySon',
  props: ['num']
}
</script>

效果:

ff2958a69824ac31d9cde1f5433c8cdc.png

2.2 子组件向父组件共享数据


子组件通过自定义事件的方式向父组件共享数据。


具体步骤:


子组件:


1.声明自定义事件 2. 数据变化时,触发自定义事件


父组件:


1.监听子组件的自定义事件 numchang 2.通过形参,接收子组件传递过来的数据


案例:


Code:


App.vue

<template>
  <div>
    <h1>MyAPP -- {{ count }}</h1>
    <button @click="count += 1">父+1</button>
    <!-- 1. 监听子组件的自定义事件 numchange -->
    <my-son :num="count" @numchange="getNum"></my-son>
  </div>
</template>
<script>
import MySon from './Son.vue'
export default {
  name: 'MyApp',
  components: {
    MySon,
  },
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    getNum(num) { // 2. 通过形参,接收子组件传递过来的数据
      this.count = num
    }
  }
}
</script>

Son.vue

<template>
  <div>
    <h2>MySon -- {{ num }}</h2>
    <button @click="add">+1</button>
  </div>
</template>
<script>
export default {
  name: 'MySon',
  props: ['num'],
  emits: ['numchange'], //1. 声明自定义事件
  methods: {
    add() {
      this.$emit('numchange', this.num + 1) //2,数据变化时,触发自定义事件
    }
  }
}
</script>

效果:

680af104e3719159b907c2bbbf0456ff.png

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


父组件在使用子组件期间,可以使用 v-model 指令维护组件内外数据的双向同步:


具体步骤:

1.父组件向子组件的props中传递数据

  1.这里通过 v-model 方式进行双向数据绑定,维护组件两方数据同步

2.子组件声明emits属性,组件内的元素需要以 update: 的方式开头,这里需要更新哪个数据,就把相应数据的值丢过来,比如number

  1.通过 $emits 的方式将数据发送出去

7eb900ddf7866c04a442ee10b2de8a0a.png

好处:父组件中不用再监听自定义事件,也不用再额外定义事件处理函数


案例:


Code:


App.vue

<template>
  <div>
    <h1>MyAPP -- {{ count }}</h1>
    <button @click="count += 1">父+1</button>
    <my-son v-model:num="count" ></my-son>
  </div>
</template>
<script>
import MySon from './Son.vue'
export default {
  name: 'MyApp',
  components: {
    MySon,
  },
  data() {
    return {
      count: 0,
    }
  },
}
</script>

Son.vue

<template>
  <div>
    <h2>MySon -- {{ num }}</h2>
    <button @click="add">+1</button>
  </div>
</template>
<script>
export default {
  name: 'MySon',
  props: ['num'],
  emits: ['update:num'],
  methods: {
    add() {
      // this.$emit('numchange', this.num + 1)
      this.$emit('update:num', this.num + 1)
    }
  }
}
</script>

效果:

32cb0bc1e3642f7681b5c7f7c07e9e33.png


兄弟组件之间的数据共享

兄弟组件之间实现数据共享的方案是 EventBus。可以借助于第三方的包 mitt 来创建 eventBus 对象,从而实现兄弟组件之间的数据共享。示意图如下:

8fb6295a8ee17fceb0fde7f1834f0859.png

理解:在数据接收方调用on方法来声明自定义事件,在数据发送方通过emit方法来触发emit事件


3.1 安装 mitt 依赖包


在项目中运行如下的命令,安装 mitt 依赖包:

npm install mitt@2.1.0

3.2 创建公共的 EventBus 模块


在项目中创建公共的 eventBus 模块如下:

// eventBus.js
// 导入 mitt 包
import mitt from 'mitt'
// 创建 EventBus 的实例对象
const bus = mitt()
// 将 EventBus 的实例对象共享出去
export default bus

3.3 在数据接收方自定义事件


在数据接收方,调用 bus.on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件。


示例代码如下:

// 导入 eventBus.js 模块, 得到共享的bus对象
export default {
  data() {return { count: 0}},
  created() {
    // 在created生命周期函数中声明自定义事件
    // 调用 bus.on 方法注册一个自定义事件,通过事件处理函数的形参数接收数据
    bus.on('countChange', (count) => {
      this.count = count
    })
  }
}

3.4 在数据接发送方触发事件


在数据发送方,调用 bus.emit(‘事件名称’, 要发送的数据) 方法触发自定义事件。示例代码如下:

// 导入 eventBus.js 模块,得到共享的 bus 对象
import bus from './eventBus.js'
export default {
  data() {return { count: 0}},
  methods: {
    addCount() {
      this.count++
      bus.emit('countChange', this.count) // 调用 bus.emit() 方法触发自定义事件,并发送数据
    } 
  }
}

案例:


Code:


Left.vue

<template>
  <div>
    <h2>Left--数据发送方--num的值为: {{ count }}</h2>
    <button @click="addCount">+1</button>
  </div>
</template>
<script>
import bus from './eventBus.js'
export default {
  name: 'MyLeft',
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    addCount() {
      this.count++
      bus.emit('countChange', this.count)
    }
  }
}
</script>

Right.vue

<template>
  <div>
    <h2>Right--数据接收方--num的值为:{{ num }}</h2>
  </div>
</template>
<script>
import bus from './eventBus.js'
export default {
  name: 'MyRight',
  data() {
    return {
      num: 0,
    }
  },
  created() {
    bus.on('countChange', count => {
      this.num = count
    })
  }
}
</script> 

效果:

c10b98eff194f0fd7033bc562de025c8.png


后代关系组件之间的数据共享

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


4.1 父节点通过 provide 共享数据


父节点的组件可以通过 provide 方法,对其子孙组件共享数据:

d039fe47c65d687c5dfcb9016869c905.png

4.2 子孙节点通过 inject 接收数据

子孙节点可以使用 inject 数组,接收父级节点向下共享的数据。示例代码如下:

b19341578701f234a289419c8e7e0df5.png

4.3 父节点对外共享响应式的数据


值得注意的是,provide中return回去的数据,并非是响应式的数据,即若我在父组件中用button修改p标签的颜色,子组件的中的p标签颜色不会跟着一块变。


父节点使用 provide 向下共享数据时,可以结合 computed 函数向下共享响应式的数据。示例代码如下:

98192cb12e4c7b2aceca721086a138f5.png

4.4 子孙节点使用响应式的数据


如果父级节点共享的是响应式的数据,则子孙节点必须以 .value 的形式进行使用。示例代码如下:

6794c31f18bf675e12f4300c492cdc42.png


vuex

vuex 是终极的组件之间的数据共享方案。在企业级的 vue 项目开发中,vuex 可以让组件之间的数据共享变得高效、清晰、且易于维护。


总结


父子关系


① 父 -> 子 属性绑定

② 子 -> 父 事件绑定

③ 父 <-> 子 组件上的 v-model


兄弟关系


④ EventBus


后代关系


⑤ provide & inject


全局数据共享


⑥ vuex


vue 3.x 中全局配置 axios

1.为什么要全局配置 axios

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


① 每个组件中都需要导入 axios(代码臃肿)


② 每次发请求都需要填写完整的请求路径(不利于后期的维护)

95e781d3ba6e3b1ccf85c542d5b642fa[0].png

2. 如何全局配置 axios


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

cf324a4df6761d78a566775be7a37fac.png

Code:


main.js

const app = createApp(MyApp)
axios.defaults.baseURL = 'https://www.escook.cn' //为axios配置请求的根路径
//将axios挂载为app的全局自定义属性之后,
//每个组件可以通过this直接访问到全局挂载的自定义属性
app.config.globalProperties.$http = axios

GetInfo:

export default {
  name: 'GetInfo',
  methods: {
    async getInfo() {
      const { data: res } = await this.$http.get('/api/get', {
        params: {
          name: 'ls',
          age: 33,
        },
      })
      console.log(res)
    },
  },
}

PostInfo:

export default {
  name: 'PostInfo',
  methods: {
    async postInfo() {
      const { data: res } = await this.$http.post('/api/post', { name: 'zs', age: 20 })
      console.log(res)
    },
  },
}

总结:

① 能够掌握 watch 侦听器的基本使用


定义最基本的 watch 侦听器


immediate、 deep、监听对象中单个属性的变化


② 能够知道 vue 中常用的生命周期函数


创建阶段、运行阶段、销毁阶段


created、mounted


③ 能够知道如何实现组件之间的数据共享


父子组件、兄弟组件、后代组件


④ 能够知道如何在 vue3 的项目中全局配置 axios


main.js 入口文件中进行配置


app.config.globalProperties.$http = axios


相关文章
|
1天前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable为了适配文件上传改造VForm3的代码记录
ruoyi-nbcio-plus基于vue3的flowable为了适配文件上传改造VForm3的代码记录
|
1天前
|
JavaScript 前端开发
vue2升级到vue3的一些使用注意事项记录(四)
vue2升级到vue3的一些使用注意事项记录(四)
|
1天前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable收回任务后重新进行提交表单的处理
ruoyi-nbcio-plus基于vue3的flowable收回任务后重新进行提交表单的处理
|
1天前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable多租户机制
ruoyi-nbcio-plus基于vue3的flowable多租户机制
|
1天前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable的消息中心我的消息的升级修改
ruoyi-nbcio-plus基于vue3的flowable的消息中心我的消息的升级修改
|
1天前
|
JavaScript 前端开发
vue3中使用动态组件
vue3中使用动态组件
|
1天前
|
JavaScript 前端开发 容器
Vue 3 中 <transition-group> 组件报错的非 props 属性传递问题
Vue 3 中 <transition-group> 组件报错的非 props 属性传递问题
12 1
|
1天前
|
移动开发 JavaScript 前端开发
ruoyi-nbcio-plus基于vue3的flowable的自定义业务显示历史信息组件的升级修改
ruoyi-nbcio-plus基于vue3的flowable的自定义业务显示历史信息组件的升级修改
|
1天前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable的支持自定义业务流程处理页面detail.vue的升级修改
ruoyi-nbcio-plus基于vue3的flowable的支持自定义业务流程处理页面detail.vue的升级修改
|
1天前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable的自定义业务撤回申请组件的升级修改
ruoyi-nbcio-plus基于vue3的flowable的自定义业务撤回申请组件的升级修改