最直白的Vue3教程,5分钟就看完了

2023-02-15
17096 分钟
...

先来说说一些API的用法

setup script

这个语法糖真的是太爽了,想想之前vue2的那些写法,真的让人很恼火,自由度非常不够,setup script体验过的都说好别急,咱们继续

<script setup lang="ts">
// 在这里写vue的逻辑
</script>
<template>
  <div>哈哈</div>
</template>

ref & reactive & 事件

总结三点:

  • ref:需要响应式的常量,但是使用或者赋值时需要xxx.value

  • reactive:需要响应式的对象或者数组,可直接使用或赋值

  • 事件:在setup script中,直接定义事件,不需要像vue2那样在method中定义

<script setup lang="ts">
import { refreactive } from 'vue'
const enum Name {
  CN = '林三心',
  EN = 'Sunshine_Lin'
}
const enum Gender {
  MAN = '男',
  WOMAN = '女'
}
// 常量
const name = ref(Name.CN)
// 对象、数组
const user = reactive({
  nameName.CN,
  genderGender.MAN
})
const arr = reactive<{ namestring }[]>([{ name'嘻嘻嘻' }])
// 事件直接定义
const switchName = () => {
  // 赋值需要name.value
  name.value = name.value === Name.CN ? Name.EN : Name.CN
}
// 事件直接定义
const switchGender = () => {
  // 直接赋值
  user.gender = user.gender === Gender.MAN ? Gender.WOMAN : Gender.MAN
}
const handleAdd = () => {
  arr.push({ name'哈哈哈' })
}
</script>
<template>
  <button @click="switchName">
    我叫 {{name}}
  </button>
  <button @click="switchGender">
    我叫 {{user.name}},我是 {{user.gender}}
  </button>
  <button @click="handleAdd">新增</button>
  <ul>
    <li v-for="(item, index) in arr" :key="index">{{ item.name }}-{{ index }}</li>
  </ul>
</template>

computed & watch & watchEffect

  • computed:计算函数

  • watch:监听函数,可监听常量和引用变量,可传immediatedeep可监听对象也可只监听对象的某个属性

  • watchEffect:跟watch差不多,但是watchEffect不需要说明监听谁,用到谁就监听谁(详情见下)

<script setup lang="ts">
import { ref
         reactive
         computed
         watch
         watchEffect } from 'vue'
const enum Name {
  CN = '林三心',
  EN = 'Sunshine_Lin'
}
const enum Gender {
  MAN = '男',
  WOMAN = '女'
}
const name = ref(Name.CN)
const user = reactive({
  nameName.CN,
  genderGender.MAN
})
const switchName = () => {
  name.value = name.value === Name.CN ? Name.EN : Name.CN
}
const switchGender = () => {
  user.gender = user.gender === Gender.MAN ? Gender.WOMAN : Gender.MAN
}
// computed计算出userText
const userText = computed(() => {
  return `我叫${user.name},我是${user.gender}的`
})
// 监听常量name
watch(name, (next, pre) => {
  console.log('name被修改了'nextpre)
})
// 监听user对象
watch(user, (next) => {
  console.log('user被修改了'next)
}{
  immediatefalse// 首次执行
  deeptrue // 深度监听对象
})
// 可监听对象的某个属性,这里监听user.gender
watch(() => user.gender, (next, pre) => {
  console.log('user.gender被修改了'nextpre)
})
// 不需要说明监听谁
// 用到user.gender就监听user.gender
watchEffect(() => {
  const gender = user.gender
  console.log('user.gender被修改了'gender)
})
</script>
<template>
  <button @click="switchName">修改name</button>
  <button @click="switchGender">修改user.gender</button>
  <div>{{ userText }}</div>
</template>

生命周期

看一下vue2的生命周期在vue3中的表现

  • beforeCreate -> 没了

  • created -> 没了

  • beforeMount -> onBeforeMount

  • mounted -> onMounted

  • beforeUpdate -> onBeforeUpdate

  • updated -> onUpdated

  • beforeUnmount -> onBeforeUnmount

  • unmounted -> onUnmounted

  • activated -> onActivated

  • deactivated -> onDeactivated

  • errorCaptured -> onErrorCaptured

<script setup lang="ts">
import {
  onBeforeMount,
  onMounted,
  onUpdated,
  onBeforeUpdate,
  onBeforeUnmount,
  onUnmounted,
  onActivated,
  onDeactivated,
  onErrorCaptured
} from 'vue'
onBeforeMount(() => {
  console.log('挂载前')
})
onMounted(() => {
  console.log('挂载')
})
onBeforeUpdate(() => {
  console.log('更新前')
})
onUpdated(() => {
  console.log('更新')
})
onBeforeUnmount(() => {
  console.log('销毁前')
})
onUnmounted(() => {
  console.log('销毁')
})
onActivated(() => {
  console.log('kee-alive激活本组件')
})
onDeactivated(() => {
  console.log('kee-alive隐藏本组件')
})
onErrorCaptured(() => {
  console.log('错误捕获')
})
</script>

defineProps & defineEmits

还记得之前vue2的父传子,子改父,的做法吗?使用porps、this.$emit

到了vue3,这种写法改了

我们来看看父组件

<script setup lang="ts">
import { ref } from 'vue'
import Dialog from './Dialog.vue'
const msg = ref('我是msg')
const changeMsg = (val: string) => {
  msg.value = val
}
</script>
<template>
// 传进子组件
<Dialog :msg="msg" @changeMsg="changeMsg" />
</template>

再来看看子组件

<script setup lang="ts">
import { definePropsdefineEmits } from 'vue'
// 注册父传子的props
const { msg } = defineProps({
  msg: {
    typeString,
    requiredtrue
  }
})
// 注册父传子的事件
const emits = defineEmits(['changeMsg'])
const handleClick = () => {
  // 修改父组件的值
  emits('changeMsg''修改msg')
}
</script>
<template>
  <div @click="handleClick">{{ msg }}</div>
</template>

defineExpose

这个API主要主要作用是:将子组件的东西暴露给父组件,好让父组件可以使用

<!-- 子组件 -->
<script setup>
import { ref } from 'vue'
const msg = ref('hello vue3!')
function change(

{
  msg.value = 'hi vue3!'
  console.log(msg.value)
}
// 属性或方法必须暴露出去,父组件才能使用
defineExpose({ msgchange })
</script>
<!-- 父组件 -->
<script setup>
import ChildView from './ChildView.vue'
import { refonMounted } from 'vue'
const child = ref(null)
onMounted(() => {
  console.log(child.value.msg// hello vue3!
  child.value.change() // hi vue3!
})
</script>
<template>
  <ChildView ref="child"></ChildView>
</template>

全局API

需要注意几个点:

  • 1、vue3已经没有filter这个全局方法了

  • 2、vue.component -> app.component

  • 3、vue.directive -> app.directive

  • 4、之前Vue.xxx现在都改成app.xxx

// 全局自定义指令
app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})
// 全局自定义组件
import CustomComp from './components/CustomComp.vue'
app.component('CustomComp'CustomComp)

自定义指令

还记得vue2中的自定义指令吗?

Vue.directive('xxx', {
  // 指令绑定到指定节点,只执行一次
  bind() {},
  // 指定节点插入dom
  inserted() { },
  // 节点VNode更新时,可能刚更新,没完全更新
  update() {},
  // VNode完全更新
  componentUpdated() {},
  // 指令从指定节点解绑,只执行一次
  unbind() {}
})

再来看看vue3的,比较贴近vue本身的生命周期

app.directive('xxx', {
    // 在绑定元素的 attribute 或事件监听器被应用之前调用, 在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用
    created() {},
    // 当指令第一次绑定到元素并且在挂载父组件之前调用
    beforeMount() {},
    // 在绑定元素的父组件被挂载后调用
    mounted() {},
    // 在更新包含组件的 VNode 之前调用
    beforeUpdate() {},
    // 在包含组件的 VNode 及其子组件的 VNode 更新后调用
    updated() {},
    // 在卸载绑定元素的父组件之前调用
    beforeUnmount() {},
    // 当指令与元素解除绑定且父组件已卸载时, 只调用一次
    unmounted() {},
});

defineAysncCompoment & Suspense

这个API用来加载异步组件,异步组件的好处我想大家也知道,就是用不到他就不加载,用到了才会加载,这大大降低了首屏加载时间,是优化的重要手段

defineAysncCompoment需配合vue3的内置全局组件Suspense使用,需要用Suspense包住异步组件

const AsyncPopup = defineAsyncComponent({ 
  loader: () => import("./LoginPopup.vue"),
   // 加载异步组件时要使用的组件
  loadingComponentLoadingComponent,
   // 加载失败时要使用的组件
  errorComponentErrorComponent
  // 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
  delay1000
  // 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
  // 默认值:Infinity(即永不超时,单位 ms)
  timeout3000 
})
// 使用时,可控制显隐
<Suspense v-if="show" >
  <AsyncPopup />
</Suspense>

Hooks

自定义hook——useUser

我们要知道Hooks的好处,其实它就是一个函数,好处有:

  • 复用性高

  • 减少代码量,提高可维护性

  • 相同逻辑可放一起

就比如刚刚我们修改user的那个例子,假如很多页面都需要这个逻辑,那岂不是每个地方都需要写一遍?

所以最好的办法就是把这部分逻辑封装成Hook

注意:规范点,hook都要以use开头

// useUser.ts
import { reactivecomputed } from 'vue'
const enum Name {
  CN = '林三心',
  EN = 'Sunshine_Lin',
}
const enum Gender {
  MAN = '男',
  WOMAN = '女',
}
const useUser = () => {
  const user = reactive({
    nameName.CN,
    genderGender.MAN,
  })
  const userText = computed(() => `我叫${user.name},我是${user.gender}的`)
  const switchGender = () => {
    user.gender = user.gender === Gender.MAN ? Gender.WOMAN : Gender.MAN
  }
  return {
    switchGender,
    userText,
  }
}
export default useUser

现在我们可以在页面中使用这个自定义hook

<script setup lang="ts">
import useUser from './useUser'
const {
  userText,
  switchGender
} = useUser()
</script>
<template>
  <div @click="switchGender">
    {userText }}
  </div>
</template>

useRouter & useRoute

这是vue3提供给我们的两个路由hook

  • useRouter:用来执行路由跳转

  • useRoute:用来获取路由参数

<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
function onClick(

{
  // 跳转
  router.push({
    path'/about',
    query: {
      msg'hello vue3!'
    }
  })
}
</script>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.msg) 
// hello vue3!
</script>

组件

介绍一些vue3内置的组件

Fragment

回想一下,在vue2中,只允许存在一个根节点,但是在vue3中,允许存在多个根节点,其实是因为vue3做了处理:

vue3检测到你有多个根节点时,会为你最外层补上一个Fragment,所以其实根节点还是一个

vue3支持多个根节点,大家要记得哦~


<template>
  <div>哈哈</div>
  <div>嘻嘻</div>
</template>

Teleport

Teleport是传送门组件,他可以将你的组件,挂载到任意一个节点之下,只要你指定一个选择器,可以是id、class

例如:将Dialog组件挂载到id为app的节点下

// Dialog.vue
<template>
  <div>我是林三心我是林三心我是林三心我是林
    三心我是林三心我是林三心我是林三心我是
    林三心我是林三心我是林三心我是林三心
    我是林三心</div>
</template>
<script setup lang="ts">
import Dialog from './Dialog.vue'
</script>
<template>
  // 将Dialog组件挂载到id为app的节点下
  <Teleport to="#app">
    <Dialog />
  </Teleport>
</template>

我们可以看到,Dialog组件被挂到了id为app的节点下

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
vue2025-10-22
shallowRef 与 ref 的区别、场景与坑位
结论先行: 会 深度 地把你放进去的对象转成响应式(递归代理),因此"对象内部的改动"也会触发视图更新。 只在 顶层 做依赖追踪: 更换 才触发更新 ;若你只是"在原对象上改属性",不会触发,除非手动 。 1\. 快速对比 维度 追踪粒度 深度(递归) 仅顶层 对象内部属性变更 会触发更新 不会 触发(除非 或重新赋值) 性能 有递归 & 依赖跟踪开销 更轻量,适合大对象/第三方实例 适用对象 普通标量、普通对象、数组 第三方实例(图表...
学习面试
vue2025-10-20
Vue 3 中的 setup执行时机与异步逻辑详解
Vue 3 中的 执行时机与异步逻辑详解 本文详细介绍 Vue 3 组合式 API 的核心函数 :它在组件生命周期中的执行时机、异步行为( 的影响)、常见用法和最佳实践。 一、 的执行时机 是 Vue 3 组件实例创建后、渲染前执行的函数。 它相当于 Vue 2 中的 和 的合体。 执行流程: 也就是说: 所有响应式数据( 、 、 )都在这里定义; 所有生命周期钩子( 、 等)都在这里注册; 模板渲染会等待 执行完成(或异步返回的 Pr...
学习面试
vue2025-10-08
vue中ref与reactive
Vue 3 的 与 :区别、实现,以及为什么更推荐 1\. 一句话结论 :适合 原始类型 或 独立、细粒度 的状态单元;类型推断明确、解构安全、追踪粒度更小, 默认首选 。 :适合管理 对象/集合 这类结构化状态;但 解构会丢失响应性 , 默认 非深度 ,需要正确的使用姿势配合。 2\. 用法与语义差异 2.1 基本示例 2.2 模板中的解包(unwrapping) 在 模板 里, 会 自动解包 ,可直接写 ,不必 。 在响应式对象中...
学习面试
vue2025-09-23
Vue3 中computed属性依赖总结
Vue3 属性依赖总结 1. 依赖收集机制 的 getter 内部 读取 到哪些响应式变量 ( / ),就会把这些变量收集为依赖。 当这些依赖发生变化时, 会重新计算。 2. 如果只返回常量 始终是 。 修改 或 不会触发 重新计算,因为 getter 内没有访问它们。 3. 如果访问了响应式变量但仍返回常量 初始化时会打印一次。 当 或 改变时,getter 会重新执行(再次打印),因为它们被收集为依赖。 但 依旧始终是 。 4. 总...
学习面试
vue2025-09-18
Vue 中的scoped样式是如何实现样式隔离的
Vue 中的 样式是如何实现样式隔离 结论先行: Vue 的 不是 Shadow DOM 。它通过 编译期选择器改写 + 运行时为 DOM 打上唯一属性 (如 )来约束样式“只作用于本组件的 DOM 子树”。 1\. 一句话总结 每个带 的单文件组件(SFC)会被分配一个 作用域 ID (scopeId)。 编译器把你写的选择器 改写 ,在每个简单选择器上 追加 。 运行时渲染该组件时,会把同一个 添加到组件生成的每一个真实 DOM 元...
面试
vue2025-09-17
template标签为什么不可以使用v-show
前言 写 Vue 时,最容易踩的小坑之一,就是给 套一个 。编辑器不报错,页面却“消失”了。很多人以为这是 bug,其实它只是 Vue 的底层设计在默默提醒: 不是真节点,它只是个“草稿纸”。 一、为什么 拒绝 v show 的本质是切换元素的 CSS: 。浏览器要找到真实 DOM 节点,才能给它加样式。而 在运行时会被完全剥离,不会留下任何标签,Vue 找不到节点,也就无处下刀。 举个反面教材: 打开控制台,你会看到 还是生成了,但...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录