Vue3基础看这一篇就够了(万字长篇,附实例代码及效果演示)

news2025/7/22 14:02:09

目录

前言

概述

Vue3组合式api  VS  Vue2选项式api

基础部分

setup

选项式api的风格

组合式api的风格

区别

 响应式数据

ref

reactive

shallowReactive 与 shallowRef 

 计算属性和监听

 computed 函数

 watch 函数

 watchEffect

生命周期

 响应式数据只读

toRaw 返回代理的源

markRaw 标记对象拒绝代理

provide 与 inject 跨组件传值

判断是否为响应式数据

toRef 和 toRefs 解构响应式数据

 新组件

Fragment

Teleport 

Suspense

组合式函数 

全局的api及指令的变动

结语


前言

vue3已经出了好长一段时间了,最近闲来无事简单学习了一下,新增的东西还是挺多的,写一篇文章来记录一下。

概述

Vue3组合式api  VS  Vue2选项式api

谈到 vue3,首先想到的就是组合式api,很大程度的解决了vue2选项式api的缺点,那有啥缺点?当文件中的业务代码非常多的时候,阅读修改代码的时候是非常痛苦的,data,method,watch还有计算属性之间来回跳转, 我已经准备拔刀了。

下面这些图被疯转,很形象的展现了vue2和vue3的区别,可以看到组合式api就是将单个功能的状态,方法,计算属性等等需要用到的东西都组合在一起抽离成一个hook,也就是对应图4的function,最终再统一引入组合到一起。这样做的好处就是单个功能的代码都在一起,方便调式修改。

 

 

基础部分

setup

setup是vue3的一个新的配置项,只在初始化的时候执行一次,所有的组合式函数都在此使用。setup可以在选项式api的风格中使用也可以通过组合式api的风格 。通过代码简单对比一下。vue3推荐使用组合式。

选项式api的风格

<script>
import { ref } from 'vue'
export default {
  setup() {
    const sum = ref(1)
    return {
      sum,
    }
  },
}
</script>

<template>
  <div>
    <h1>v3</h1>
    <h3>{{ sum }}</h3>
    <button @click="sum++">+1</button>
  </div>
</template>

<style scoped></style>

组合式api的风格

<script setup>
import { ref } from 'vue'
const sum = ref(1)
</script>

<template>
  <div>
    <h1>v3</h1>
    <h3>{{ sum }}</h3>
    <button @click="sum++">+1</button>
  </div>
</template>

<style scoped></style>

区别

  1. <script setup>中的导入和顶层变量/函数都能够在模板中直接使用, 选项式则需要导出
  2. <script setup>打包出来的体积更小
  3. <script setup>对ts更友好

官网介绍的比较详细,感兴趣可以查看组合式 API 常见问答 | Vue.js 

 响应式数据

vue2中 data 函数返回的对象就是响应式的数据,但是在增加删除对象属性时不是响应式的,当然vue2中也有对应的解决方法,this.$set(), this.$delete(), 其实这也能够理解,毕竟vue2的响应式式基于 Object.defineProperty 实现的,这个函数只提供了 get 和 set 以及一些描述符 descriptor,并没有提供 add 和 delete 方法。

vue3中的响应式包含了两种形态, ref(底层还是Object.defineProperty进行数据劫持, 处理简单数据类型)reactive(使用es6的Proxy进行数据劫持,处理复杂数据类型),完全修复了vue2响应式的痛点,vue3的响应式更加的友好。

ref

ref 接受一个值,返回一个响应式对象,一般用来处理简单数据类型的响应式,但如果传入的值是对象 ref 会求助 reactive,返回RefImpl的实例简称ref对象。 此时可能会有疑惑,既然ref是一个响应式的对象,为什么模板中能正常解析。这是因为在解析templete时遇到ref对象会自动取其value属性,但是如果要在方法中修改ref创建的响应式数据,你的写法应该是这样的 state.value = xxx

<script setup>
import { ref } from 'vue'
const sum = ref(1)
function add() {
  sum.value++
}
</script>

<template>
  <div>
    <h1>v3</h1>
    <h3>{{ sum }}</h3>
    <button @click="add">+1</button>
  </div>
</template>

<style scoped></style>

reactive

为对象做深层!!!!响应式代理, 也就是如果对象有多层依旧是响应式的,返回一个Proxy实例, 如果传入一个字符串或者数字,它将不是响应式的。Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)Proxy - JavaScript | MDN。Vue使用 Proxy 进行数据劫持, Reflect 进行反射修改 Reflect - JavaScript | MDN

<script setup>
import { reactive } from 'vue'
const person = reactive({
  name: '张三',
  age: 12,
  job: {
    j1: {
      jname: '前端开发',
    },
  },
})

function add() {
  person.hobby = ['唱', '跳', 'rap']
}
function deleteHB() {
  delete person.hobby
}
</script>

<template>
  <div>
    <h1>v3</h1>
    <h1>{{ sum }}</h1>
    <h3>姓名:{{ person.name }}</h3>
    <h3>年龄:{{ person.age }}</h3>
    <h3>工作:{{ person.job.j1.jname }}</h3>
    <h3 v-if="person.hobby">爱好: {{ person.hobby }}</h3>
    <button @click="person.name += '-'">修改姓名</button>
    <button @click="person.age++">修改年龄</button>
    <button @click="person.job.j1.jname += '!'">修改工作</button>
    <button @click="add">增加爱好</button>
    <button @click="deleteHB">删除爱好</button>
  </div>
</template>

<style scoped></style>

shallowReactive 与 shallowRef 

shallowRef 直译过来意思是浅层的 ref,shallowRef 传入对象不会求助 reactive,仅仅对ref对象的 value 属性具有响应式。

shallowReactive 只处理对象第一层的响应式,  如果修改了深层的数据页面是不会响应的,但是会在下次页面更新中渲染出来。

<script setup>
import { shallowReactive, shallowRef, ref, reactive } from 'vue'
const shallowRef_jack = shallowRef({ name: 'jack', sex: '女' })
const shallowReactive_ben = shallowReactive({
  name: 'ben',
  sex: '女',
  child: {
    son: {
      name: '张三',
    },
  },
})

const ref_jack = ref({ name: 'jack', sex: '女' })
const reactive_ben = reactive({
  name: 'ben',
  sex: '女',
  child: {
    son: {
      name: '张三',
    },
  },
})
</script>

<template>
  <div>
    <h1>v3</h1>
    <h3>
      shallowRef_jack: {{ shallowRef_jack }}
      <button @click="shallowRef_jack = {}">修改整个对象</button>
      <button @click="shallowRef_jack.name += '!'">修改对象属性</button>
    </h3>
    <h3>
      ref_jack: {{ ref_jack }}
      <button @click="ref_jack = {}">修改整个对象</button>
      <button @click="ref_jack.name += '!'">修改对象属性</button>
    </h3>
    <h3>
      shallowReactive_ben: {{ shallowReactive_ben }}
      <button @click="shallowReactive_ben.child.son.name = '!'">
        修改对象的第三层属性
      </button>
      <button @click="shallowReactive_ben.name += '!'">
        修改对象第一层属性
      </button>
    </h3>
    <h3>
      reactive_ben: {{ reactive_ben }}
      <button @click="reactive_ben.child.son.name += '!'">
        修改对象的第三层属性
      </button>
      <button @click="reactive_ben.name += '!'">修改对象第一层属性</button>
    </h3>
  </div>
</template>

<style scoped>
h3 {
  font-size: 26px;
  border: 1px solid #ccc;
  padding: 20px;
  margin: 20px;
}
button {
  float: right;
  padding: 10px;
  font-size: 20px;
}
</style>

 计算属性和监听

 computed 函数

计算属性有两种写法,作用和vue2一样,通过监听某个值的变化计算出一个新值

  • 只读的写法 :computed(() => xxxxxx),
  • 可读可写的写法:  computed({ get: () => xxxx, set: (val) => { xxxx } })
<script setup>
import { ref, computed } from 'vue'
const count = ref(1)
const num1 = computed(() => count.value + 1)
const num2 = computed({
  get() {
    return count.value + 1
  },
  set(val) {
    count.value = val + 1
  },
})
</script>

<template>
  <div>
    <h1>v3</h1>
    <h2>
      ref 定义的 count: {{ count }} <button @click="count++">count++</button>
    </h2>
    <h2>计算属性 num1: {{ num1 }} <button @click="num1++">num1++</button></h2>
    <h2>计算属性 num2: {{ num2 }} <button @click="num2++">num2++</button></h2>
  </div>
</template>

<style scoped></style>

 watch 函数

watch 函数用来监听数据的变化,和vue2大体上都是相同的。

参数列表:

  1. 参数1为需要监听的响应式对象(可以是单个对象,也可以是一个数组,也可以是一个getter函数),
  2. 参数2为监听对象发生变化时所执行的回调
  3. 参数3是一些配置项:immediate是否开启立即监听,deep是否开启深度监听,flush回调的触发时机,onTrack / onTrigger用于调试的两个函数

 

注意点: 

  1. 直接监听 reactive 定义的响应式对象默认开启了深度监听
  2. 通过 getter 形式监听响应式对象默认是浅层监听
<script setup>
import { reactive, ref, watch } from 'vue'
const count = ref(1)
const person = reactive({
  name: 'ben',
  child: {
    son: {
      name: 'zs',
    },
  },
})
// 监听 ref 对象
watch(count, (val, preVal) => {
  console.log('count变化了', val, preVal)
})
// 监听 reactive  定义的响应式对象
watch(person, (val, preVal) => {
  console.log('person变化了', val, preVal)
})
watch([count, person], (val, preVal) => {
  console.log('person变化了或count变化了', val, preVal)
})
</script>

<template>
  <div>
    <h1>v3</h1>
    <h2>
      ref 定义的 count: {{ count }} <button @click="count++">count++</button>
    </h2>
    <h2>
      reactive 定义的 person: {{ person }}
      <button @click="person.name += '!'">修改姓名</button>
      <button @click="person.child.son.name += '___'">修改儿子姓名</button>
    </h2>
  </div>
</template>

<style scoped></style>

 watchEffect

watchEffect 函数用于监听传入的函数内访问的所有响应式数据的变化。白话一点就是回调里我用了谁我就监听谁,监听ref定义的响应式数据时,不要忘记 .value ,哥们就是这么智能。

watch 和 watchEffect 都是监听数据变化的函数,和 react 中的 useState 放入依赖项有着异曲同工之妙。

例子:切换下拉框中的 name ,模拟请求后台接口 

<script setup>
import { onMounted, reactive, ref, watchEffect } from 'vue'
const name = ref('jack')
const info = [
  {
    id: 1,
    name: 'jack',
    child: {
      son: {
        name: 'zs',
      },
    },
  },
  {
    id: 2,
    name: 'ben',
    child: {
      son: {
        name: 'zs',
      },
    },
  },
]
let data = ref([])
async function getInfoByName(name) {
  const res = await new Promise((reslove) => {
    setTimeout(() => {
      reslove(info.filter((item) => item.name === name))
    }, 500)
  })
  data.value = res
}

watchEffect(async () => {
  getInfoByName(name.value)
})
</script>

<template>
  <div>
    <h1>v3</h1>
    <el-select v-model="name" placeholder="请选择">
      <el-option
        v-for="item in info"
        :key="item.name"
        :label="item.name"
        :value="item.name"
      >
      </el-option>
    </el-select>
    <div v-for="item in data" :key="item.id">
      {{ item.name }}的个人信息 {{ item }}
    </div>
  </div>
</template>

<style scoped></style>

生命周期

vue3的生命周期稍有变动,增加了 setup 钩子,且销毁前和销毁后的钩子命名更改为 beforeUnmount 和 unmounted,以下代码是验证的一些示例

App.vue 

<script setup>
import Demo from './Demo.vue'
import Demo2 from './Demo2.vue'
import { ref } from 'vue'
const isComDestory = ref(true)
const isOptionDestory = ref(true)
</script>

<template>
  <div>
    <h1>
      v3
      <button @click="isComDestory = false">引入组合式子组件</button>
      <button @click="isComDestory = true">销毁组合式子组件</button>
      <button @click="isOptionDestory = false">引入选项式子组件</button>
      <button @click="isOptionDestory = true">销毁选项式子组件</button>
    </h1>
    <Demo v-if="!isComDestory"></Demo>
    <Demo2 v-if="!isOptionDestory"></Demo2>
  </div>
</template>

<style scoped>
button {
  padding: 20px;
  font-size: 16px;
}
</style>

Demo.vue 

<script setup>
import {
  onMounted,
  onBeforeMount,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  ref,
} from 'vue'
const sum = ref(1)
console.log('子组件1 setup')
onBeforeMount(() => {
  console.log('子组件1 onBeforeMount')
})
onMounted(() => {
  console.log('子组件1 onMounted')
})
onBeforeUpdate(() => {
  console.log('子组件1 onBeforeUpdate')
})
onUpdated(() => {
  console.log('子组件1 onUpdated')
})
onBeforeUnmount(() => {
  console.log('子组件1 onBeforeUnmount')
})
onUnmounted(() => {
  console.log('子组件1 onUnmounted')
})
</script>

<template>
  <div>
    <h2>我是子组件1</h2>
    <h2>{{ sum }} <button @click="sum++">+1</button></h2>
  </div>
</template>

<style scoped>
div {
  border: 1px solid #ccc;
}
</style>

Demo2.vue

<script>
import { ref } from 'vue'

export default {
  setup() {
    const sum = ref(1)
    console.log('子组件2 setup')
    return { sum }
  },
  beforeCreate() {
    console.log('子组件2 beforeCreate')
  },
  created() {
    console.log('子组件2 created')
  },
  beforeMount() {
    console.log('子组件2 beforeMount')
  },
  mounted() {
    console.log('子组件2 mounted')
  },
  beforeUpdate() {
    console.log('子组件2 beforeUpdate')
  },
  updated() {
    console.log('子组件2 updated')
  },
  beforeUnmount() {
    console.log('子组件2 beforeUnmount')
  },
  unmounted() {
    console.log('子组件2 unmounted')
  },
}
</script>

<template>
  <div>
    <h2>我是子组件2</h2>
    <h2>{{ sum }} <button @click="sum++">+1</button></h2>
  </div>
</template>

<style scoped>
div {
  border: 1px solid #ccc;
}
</style>

由于录频录不了控制台,打印结果看下图

 

 响应式数据只读

vue3提供了两个api,限制响应式数据为只读,不可修改。分别为 readonly(深层只读) 和shallowReadonly (浅层只读)

<script setup>
import { ref, reactive, readonly, shallowReadonly } from 'vue'
const sum = readonly(ref(1))
const p1 = readonly(
  reactive({
    name: 'ben',
    child: {
      son: {
        name: 'jack',
      },
    },
  })
)
const p2 = shallowReadonly(
  reactive({
    name: 'ben',
    child: {
      son: {
        name: 'jack',
      },
    },
  })
)
function edit() {
  sum.value = 2
  p1.name += '!'
  p1.child.son.name += '&'
}
function editShallow() {
  p2.name += '!'
  p2.child.son.name += '&'
}
</script>

<template>
  <div>
    <h1>v3</h1>
    <h2>readonly: {{ sum }}</h2>
    <h2>readonly: {{ p1 }}</h2>
    <h2>shallowReadonly: {{ p2 }}</h2>
    <button @click="edit">修改深层只读数据</button>
    <button @click="editShallow">修改浅层只读数据</button>
  </div>
</template>

<style scoped></style>

toRaw 返回代理的源

toRaw的功能官网的解释很清晰, 可以返回由 reactive()、readonly()、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象

<script setup>
import {
  ref,
  reactive,
  readonly,
  shallowReadonly,
  shallowReactive,
  toRaw,
} from 'vue'
const p1 = readonly(
  reactive({
    name: 'a',
    child: {
      son: {
        name: 'as',
      },
    },
  })
)
const p2 = shallowReadonly(
  reactive({
    name: 'b',
    child: {
      son: {
        name: 'bs',
      },
    },
  })
)
const p3 = reactive({
  name: 'c',
  child: {
    son: {
      name: 'cs',
    },
  },
})
const p4 = shallowReactive({
  name: 'd',
  child: {
    son: {
      name: 'ds',
    },
  },
})
console.log('toRaw p1 readonly', toRaw(p1))
console.log('toRaw p2 shallowReadonly', toRaw(p2))
console.log('toRaw p3 reactive', toRaw(p3))
console.log('toRaw p4 shallowReactive', toRaw(p4))
</script>

<template>
  <div></div>
</template>

<style scoped></style>

markRaw 标记对象拒绝代理

markRaw()将对象标记为不可代理,返回其本身。本身上多了一个 __v_skip 属性表示忽略代理。强行代理代理是无效的,返回的还是其本身而不是响应式对象。

<script setup>
import { markRaw, reactive } from 'vue'
const p1 = {
  name: 'a',
  child: {
    son: {
      name: 'as',
    },
  },
}
const noProxy_p1 = markRaw(p1)
console.log('不可代理对象', noProxy_p1)
console.log('reactive 代理不可代理对象', reactive(noProxy_p1))
</script>

<template>
  <div></div>
</template>

<style scoped></style>

 provide 与 inject 跨组件传值

使用 provide 与 inject 进行跨组件传值十分方便。以父子孙为例,父组件 provide ('name',value) 子组件 inject ('name') 即可

 父组件

<script setup>
import { reactive, provide } from 'vue'
import Demo from './Demo.vue'
const obj = {
  name: 'a',
  child: {
    son: {
      name: 'as',
    },
  },
}
const person = reactive(obj)
provide('person', person)
</script>

<template>
  <div class="father">
    <h1>父组件</h1>
    <h3>{{ person }}</h3>
    <Demo></Demo>
  </div>
</template>

<style scoped>
.father {
  padding: 10px;
  background: orange;
}
</style>

 子组件

<script setup>
import Demo2 from './Demo2.vue'
</script>

<template>
  <div>
    <h2>子组件</h2>
    <Demo2></Demo2>
  </div>
</template>

<style scoped>
div {
  padding: 10px;
  background: salmon;
  border: 1px solid #ccc;
}
</style>

孙组件

<script>
import { ref, inject } from 'vue'
export default {
  setup() {
    const person = inject('person')
    return { person }
  },
}
</script>

<template>
  <div class="sonson">
    <h2>孙组件</h2>
    <h3>{{ person }}</h3>
  </div>
</template>

<style scoped>
.sonson {
  background: sandybrown;
  border: 1px solid #ccc;
}
</style>

判断是否为响应式数据

  • isRef(data)判断data是否是通过ref创建的响应式数据
  • isReactive(data)判断data是否是通过reactive创建的响应式数据
  • isReadonly(data)判断data是否是通过readOnly创建的只读数据
  • isProxy(data)判断data是否为Proxy代理对象

<script setup>
import {
  reactive,
  readonly,
  ref,
  isProxy,
  isReactive,
  isRef,
  isReadonly,
} from 'vue'
const person = reactive({
  name: 'a',
  child: {
    son: {
      name: 'as',
    },
  },
})
const num = ref(1)
const str = readonly(ref('str'))
console.log(isRef(num))
console.log(isReactive(person))
console.log(isReadonly(str))
console.log(isProxy(person), isProxy(str))
</script>

<template></template>

<style scoped></style>

toRef 和 toRefs 解构响应式数据

当响应式对象的属性过多且页面用到很多次的时候, toRef 和 toRefs 可以进行响应式解构,解构出来的数据依旧具备响应式的能力。下面的例子是在 <script setup> 中进行演示的,setup()中的需要显示的返回

 toRef

<script setup>
import { reactive, toRef } from 'vue'
const person = reactive({
  name: 'a',
  age: 18,
  child: {
    son: {
      name: 'as',
    },
  },
})
const personName = toRef(person, 'name')
const personAge = toRef(person, 'age')
const personSonName = toRef(person.child.son, 'name')
</script>

<template>
  <div>
    <h3>toRef 解构出 person的name ----- {{ personName }}</h3>
    <h3>toRef 解构出 person的age ----- {{ personAge }}</h3>
    <h3>toRef 解构出 person的child的son的name ----- {{ personSonName }}</h3>
    <h3>toRef 解构出 person的name ----- {{ personName }}</h3>
    <h3>toRef 解构出 person的age ----- {{ personAge }}</h3>
    <h3>toRef 解构出 person的child的son的name ----- {{ personSonName }}</h3>
    <h3>toRef 解构出 person的name ----- {{ personName }}</h3>
    <h3>toRef 解构出 person的age ----- {{ personAge }}</h3>
    <h3>toRef 解构出 person的child的son的name ----- {{ personSonName }}</h3>
    <button @click="personName += '!'">修改person的name</button>
    <button @click="personAge += 1">修改person的age</button>
    <button @click="personSonName += '*'">person的child的son的name</button>
  </div>
</template>

<style scoped></style>

 

 toRefs

<script setup>
import { reactive, toRefs } from 'vue'
const person = reactive({
  name: 'a',
  age: 18,
  child: {
    son: {
      name: 'as',
    },
  },
})
const { name, age, child } = toRefs(person)
</script>

<template>
  <div>
    <h3>toRefs 解构出 person的name ----- {{ name }}</h3>
    <h3>toRefs 解构出 person的age ----- {{ age }}</h3>
    <h3>toRefs 解构出 person的child的son的name ----- {{ child.son.name }}</h3>
    <h3>toRefs 解构出 person的name ----- {{ name }}</h3>
    <h3>toRefs 解构出 person的age ----- {{ age }}</h3>
    <h3>toRefs 解构出 person的child的son的name ----- {{ child.son.name }}</h3>
    <h3>toRefs 解构出 person的name ----- {{ name }}</h3>
    <h3>toRefs 解构出 person的age ----- {{ age }}</h3>
    <h3>toRefs 解构出 person的child的son的name ----- {{ child.son.name }}</h3>
    <button @click="name += '!'">修改person的name</button>
    <button @click="age += 1">修改person的age</button>
    <button @click="child.son.name += '*'">person的child的son的name</button>
  </div>
</template>

<style scoped></style>

 新组件

Fragment

在vue2中模板标签内必须包裹一层根标签,vue3中则不需要。vue3会为多个跟标签包裹一层Fragment。这是写法上的优化。前面很多例子的代码中我都包裹了一层根标签,这是由于我的编辑器的eslint的问题,去掉根标签也可以正常运行。

有根标签的编译结果

 没有根标签的编译结果

Teleport 

Teleport 组件的功能是将元素渲染到任意的页面位置中,直接扣过来官网的例子。

下列代码主要表达的是:点击按钮将弹框插入到 body 标签下 

 ModalButton.vue

<template>
  <button @click="modalOpen = true">
    Open full screen modal! (With teleport!)
  </button>

  <teleport to="body">
    <div v-if="modalOpen" class="modal">
      <div>
        I'm a teleported modal! (My parent is "body")
        <button @click="modalOpen = false">Close</button>
      </div>
    </div>
  </teleport>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'modal-button',
  setup() {
    const modalOpen = ref(false)
    return {
      modalOpen,
    }
  },
}
</script>

<style>
.modal {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.modal div {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: white;
  width: 300px;
  height: 300px;
  padding: 5px;
}
</style>

 App.vue

<template>
  <h2>App</h2>
  <modal-button></modal-button>
</template>

<script lang="ts">
import ModalButton from './ModalButton.vue'

export default {
  setup() {
    return {}
  },

  components: {
    ModalButton,
  },
}
</script>

 

Suspense

Suspense 组件用于将异步组件包裹,提供一个过渡UI在异步完成之前。

Suspense 组件提供两个插槽:

  1. #default   默认插槽 存放异步组件
  2. #fallback  备用插槽 存放过渡UI

异步组件:

  1. 带有异步 setup() 钩子的组件。这也包含了使用 <script setup> 时有顶层 await 表达式的组件。

  2. defineAsyncComponent

 App.vue

<template>
  <div>
    <h2>App</h2>
    <Suspense>
      <Demo></Demo>
      <template #fallback> 加载中.... </template>
    </Suspense>
  </div>
</template>

<script setup>
import Demo from './Demo.vue'
</script>

 Demo.vue

<script setup>
const res = await new Promise((resolve) => {
  setTimeout(() => {
    resolve({ name: 'zs', age: 12, sex: '男' })
  }, 1000)
})
</script>

<template>
  <div>
    <h2>异步组件</h2>
    <h3>{{ res }}</h3>
  </div>
</template>

<style scoped>
div {
  padding: 10px;
  background: salmon;
  border: 1px solid #ccc;
}
</style>

组合式函数 

组合式api的优点之一式将单个功能代码组合在一起,如果是可以复用的逻辑,那么可以抽离为一个组合式函数或者称为自定义hook,在需要该逻辑的地方导入即可

例子:提供一个组合函数,此函数在当前组件中监听鼠标移动事件,并将坐标显示出来,组件卸载前清掉事件。

App.vue

<template>
  <div>
    <h2>App</h2>
    <h2>
      <button @click="Demo1Visible = false">销毁子组件1</button>
      <button @click="Demo2Visible = false">销毁子组件2</button>
    </h2>
    <Demo v-if="Demo1Visible"></Demo>
    <Demo2 v-if="Demo2Visible"></Demo2>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import Demo from './Demo.vue'
import Demo2 from './Demo2.vue'
const Demo1Visible = ref(true)
const Demo2Visible = ref(true)
</script>

Demo1.vue

<template>
  <div class="demo_1">
    <h2>子组件1</h2>
    <p v-if="x && y">x坐标为 {{ x }}, y坐标为{{ y }}</p>
  </div>
</template>
<script setup>
import useMouse from './mouse'
const { x, y } = useMouse('.demo_1')
</script>

<style scoped>
.demo_1 {
  height: 100px;
  background: salmon;
}
</style>

Demo2.vue 

<template>
  <div class="demo_2">
    <h2>子组件2</h2>
    <p v-if="x && y">x坐标为 {{ x }}, y坐标为{{ y }}</p>
  </div>
</template>
<script setup>
import useMouse from './mouse'
const { x, y } = useMouse('.demo_2')
</script>

<style scoped>
.demo_2 {
  height: 100px;
  background: salmon;
}
</style>

 

全局的api及指令的变动

API 参考 | Vue.js,大家先自行参考,后续深入学习时再进行更新。

结语

  • 如果想在 vue3 中使用 element,请下载 element-plus
  • vue3 的文档是最全的。Vue.js - 渐进式 JavaScript 框架 | Vue.js

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/16999.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

呼叫中心中间件(mod_cti基于FreeSWITCH)-背景音(彩话)接口

背景音&#xff0c;就是给通话添加一个背景音&#xff0c;比如办公室的噪音&#xff0c;键盘敲击声&#xff0c;等。彩话&#xff0c;就是通话过程播放一个声音&#xff0c;代替人工说话&#xff0c;这个声音双方可以同时听到&#xff0c;而且播放过程不影响双方通话。 用处 …

「JVS低代码开发平台」关于逻辑引擎的触发讲解

JVS逻辑引擎是代码开发套件中的业务瓶装的核心&#xff0c;用于去实现各种场景下的逻辑功能&#xff0c;可以把他理解为一个程序配置器与程序的执行器。 逻辑引擎是可以被多种配置器调用的触发的&#xff0c;从而实现了各种业务场景中对应功能的实现&#xff0c;那么接下来我们…

RabbitMQ初步到精通-第四章-RabbitMQ工作模式-PUB/SUB

第四章-RabbitMQ工作模式-PUB/SUB 1.模式介绍 1.1 模式 此模式称为发布订阅模式&#xff0c;从此模式开始&#xff0c;我们就不再使用默认的交换机了&#xff0c;开始定义我们自己的交换机。 此发布订阅模式&#xff0c;使用的交换机类型为Fanout。定义好交换机&#xff0c;消…

【MATLAB教程案例42】语音信号的MFCC特征提取matlab仿真

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 本课程学习成果预览: 目录 1.软件版本 2.MFCC理论概述

JavaScript之BOM复习(54th)

1、BOM概述 1、BOM Browser Object Model 浏览器对象模型 2、它提供了独立于内容而与浏览器窗口进行交互的对象&#xff0c;其核心对象是 window 3、BOM 由一系列相关的对象构成&#xff0c;并且每个对象都提供了很多方法与属性 4、BOM 缺乏标准&#xff0c;JavaScript 语法的…

用QT实现一个简单的桌面宠物

有时候桌面空空的&#xff0c;或者屏幕空旷了&#xff0c;我们就可以找一点东西来点缀一下&#xff0c;那么桌面宠物是一个不错的选择。 作为一个程序猿&#xff0c;如何实现一个桌面宠物呢&#xff1f; 本文就给大家带来的是如何用qt提供一种思路并写一个简单的桌面宠物。 思…

深入理解Linux网络技术内 幕(八)——设备注册和初始化

文章目录前言设备注册之时设备除名之时分配net_device结构NIC注册和除名的架构设备初始化设备驱动程序初始化设备类型初始化&#xff1a;xxx_setup函数可选的初始化和特殊情况net_device结构的组织查询设备状态队列规则状态注册状态设备的注册和除名切割操作&#xff1a;netdev…

C#编程的构成要素(结合unity做实例分析)

目录 定义变量 变量的名称很重要 将变量作为占位符 疯狂的方法 方法驱动行为 方法也是占位符 类的引入 一直在使用类 日常蓝图 注释是关键 将脚本附加到游戏对象上 脚本成为组件 类与组件通信 本文主要来自<<C#实践入门>>哈里森.费隆 著&#xff0c;仅用…

甘露糖-聚乙二醇-氨基|mannose-PEG-NH2|氨基-PEG-甘露糖

甘露糖-聚乙二醇-氨基|mannose-PEG-NH2|氨基-PEG-甘露糖 氨基&#xff08;Amino&#xff09;由一个氮原子和两个氢原子构成&#xff0c;化学式为-NH2。在有机化学中&#xff0c;氨基是基本碱基&#xff0c;大多数含有氨基的有机物都有一定碱的特性&#xff0c; 中文名称&…

基于数学形态学的路面裂缝图像处理技术-含Matlab代码

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、图像预处理✳️ 三、路面裂缝图像的边缘检测✳️ 3.1 裂缝识别✳️ 3.2 裂缝区域信息获取✳️ 3.3 裂缝特征提取✳️ 四、参考文献✳️ 五、Matlab代码获取✳️ 一、引言 对于路面裂缝而言&#xff0c; 采用图像处理技术对其进行识别与计…

③计算机病毒实验实验报告

班级 计科2101 姓名 彭彭头 学号 时间 2022年5月6日 成绩 实验项目名称 计算机病毒实验二 实验目的 1、了解脚本病毒的感染方式。 2、了解脚本病毒的手工清除方法。 实验内容 通过批处理文件进行计算机病毒和编写&#xff0c;了解脚本病毒的感染方式。 实验环…

Java笔记(十三)

文献种类&#xff1a;专题技术总结文献 开发工具与关键技术&#xff1a; IntelliJ IDEA、Java 语言 作者&#xff1a; 方建恒 年级&#xff1a; 2020 撰写时间&#xff1a; 2022 年 11 月 18 日 Java笔记(十三) 今天我给大家继续分享一下我的Java笔记&#xff0c; 我们继续来…

【Linux】环境基础开发工具使用

Vim Vim 是一个编辑器 只能编辑&#xff0c;只能写代码 直接输入vim &#xff1a; q就是退出 touch新文件&#xff0c;vim 进入 vim是一款多模式的编辑器 命令模式&#xff08;默认打开的模式&#xff09; 按 i 进入编辑模式/插入模式 esc回到命令模式 冒号进入底行…

【前沿技术RPA】 一文了解UiPath的代码审查工具Workflow Analyzer

&#x1f40b;作者简介&#xff1a;博主是一位.Net开发者&#xff0c;同时也是RPA和低代码平台的践行者。 &#x1f42c;个人主页&#xff1a;会敲键盘的肘子 &#x1f430;系列专栏&#xff1a;UiPath &#x1f980;专栏简介&#xff1a;UiPath在传统的RPA&#xff08;Robotic…

[附源码]java毕业设计企业员工管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Azdio-PEG-Maleimide,N3-PEG-MAL,叠氮-PEG-马来酰亚胺化学试剂供应

1、名称 英文&#xff1a;Azdio-PEG-Maleimide&#xff0c;N3-PEG-MAL 中文&#xff1a;叠氮-聚乙二醇-马来酰亚胺 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Azide PEG Maleimide PEG 4、分子量&#xff1a;可定制&#xff0c;N3-PEG 20k -MAL、N3-PEG 10k -MAL…

EFK部署centos7.9(四)Filebeat 部署

下载安装包 wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.5.4-linux-x86_64.tar.gz tar xzvf filebeat-6.5.4-linux-x86_64.tar.gz -C /usr/local/ 解压安装包 cd /usr/local/ mv filebeat-6.5.4-linux-x86_64 filebeat cd filebeat/ mv filebe…

Springboot导出Excel,支持大数据量

1、添加maven依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17</version> </dependency> 2、ExcelUtil工具类 import org.apache.poi.ss.usermodel.Cell; import org.…

【附源码】Python计算机毕业设计天气预报APP

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;我…

甘露糖-聚乙二醇-炔基|mannose-PEG-Alkyne|炔基-PEG-甘露糖

甘露糖-聚乙二醇-炔基|mannose-PEG-Alkyne|炔基-PEG-甘露糖 中文名称&#xff1a;甘露糖-炔基 英文名称&#xff1a;mannose-Alkyne 别称&#xff1a;炔基修饰甘露糖&#xff0c;炔基-甘露糖 mannose-PEG-Alkyne 甘露糖-聚乙二醇-炔基 炔基-PEG-甘露糖 纯度&#xff1a;…