Vue 3 知识点大全(二)

简介: 教程来源 https://xcfsr.cn/ 本文档系统讲解Vue 3核心功能:组件通信(Props/Emits、v-model、provide/inject、mitt事件总线)、内置组件(Teleport、Suspense、Fragment)及Vue Router 4路由配置与实战,涵盖动态导入、嵌套路由、路由守卫等要点。

四、组件通信

4.1 Props 与 Emits

vue
<!-- 父组件 Parent.vue -->
<template>
  <Child 
    :name="userName"
    :age="userAge"
    @update="handleUpdate"
    @change:name="handleNameChange"
  />
</template>

<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const userName = ref('张三')
const userAge = ref(25)

const handleUpdate = (value) => {
  console.log('收到更新:', value)
}

const handleNameChange = (value) => {
  userName.value = value
}
</script>
vue
<!-- 子组件 Child.vue -->
<template>
  <div>
    <p>姓名: {
  { name }}</p>
    <p>年龄: {
  { age }}</p>
    <button @click="emitUpdate">更新</button>
    <button @click="changeName">修改名字</button>
  </div>
</template>

<script setup>
// 定义 props
const props = defineProps({
  name: {
    type: String,
    required: true,
    default: '匿名'
  },
  age: {
    type: Number,
    default: 0,
    validator: (value) => value >= 0 && value <= 150
  }
})

// 定义 emits
const emit = defineEmits(['update', 'change:name'])

const emitUpdate = () => {
  emit('update', { name: props.name, age: props.age })
}

const changeName = () => {
  emit('change:name', '李四')
}
</script>

4.2 v-model 双向绑定

vue
<!-- 父组件 -->
<template>
  <!-- v-model 语法糖 -->
  <CustomInput v-model="message" />
  <!-- 等价于 -->
  <CustomInput :modelValue="message" @update:modelValue="message = $event" />

  <!-- 多个 v-model -->
  <CustomForm 
    v-model:name="name" 
    v-model:age="age" 
  />
</template>

<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
import CustomForm from './CustomForm.vue'

const message = ref('Hello')
const name = ref('张三')
const age = ref(25)
</script>
vue
<!-- CustomInput.vue -->
<template>
  <input 
    :value="modelValue" 
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
vue
<!-- CustomForm.vue -->
<template>
  <input 
    :value="name" 
    @input="$emit('update:name', $event.target.value)"
    placeholder="姓名"
  />
  <input 
    :value="age" 
    @input="$emit('update:age', $event.target.value)"
    placeholder="年龄"
  />
</template>

<script setup>
defineProps(['name', 'age'])
defineEmits(['update:name', 'update:age'])
</script>

4.3 provide / inject

vue
<!-- 祖先组件 -->
<template>
  <ChildComponent />
</template>

<script setup>
import { provide, ref, readonly } from 'vue'
import ChildComponent from './ChildComponent.vue'

// 提供响应式数据
const user = ref({ name: '张三', age: 25 })
const updateUser = (newName) => {
  user.value.name = newName
}

// 提供数据
provide('user', readonly(user))
provide('updateUser', updateUser)
provide('config', {
  theme: 'dark',
  language: 'zh-CN'
})
</script>
vue
<!-- 后代组件 -->
<template>
  <div>
    <p>姓名: {
  { user.name }}</p>
    <p>年龄: {
  { user.age }}</p>
    <p>主题: {
  { config.theme }}</p>
    <button @click="updateUser('李四')">修改名字</button>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// 注入数据
const user = inject('user')
const updateUser = inject('updateUser')
const config = inject('config', { theme: 'light', language: 'en' })  // 提供默认值

// 注入时使用工厂函数
const logger = inject('logger', () => ({
  log: (msg) => console.log(msg)
}))
</script>

4.4 事件总线(mitt)

bash
npm install mitt
javascript
// utils/eventBus.js
import mitt from 'mitt'

const emitter = mitt()

export default emitter
vue
<!-- ComponentA.vue -->
<template>
  <button @click="sendEvent">发送事件</button>
</template>

<script setup>
import emitter from '@/utils/eventBus'

const sendEvent = () => {
  emitter.emit('custom-event', { message: 'Hello from Component A' })
  emitter.emit('update', '数据更新')
}
</script>
vue
<!-- ComponentB.vue -->
<script setup>
import { onMounted, onUnmounted } from 'vue'
import emitter from '@/utils/eventBus'

const handleCustomEvent = (data) => {
  console.log('收到事件:', data)
}

const handleUpdate = (data) => {
  console.log('更新:', data)
}

onMounted(() => {
  emitter.on('custom-event', handleCustomEvent)
  emitter.on('update', handleUpdate)

  // 监听所有事件
  emitter.on('*', (type, event) => {
    console.log(`事件 ${type} 触发`, event)
  })
})

onUnmounted(() => {
  emitter.off('custom-event', handleCustomEvent)
  emitter.off('update', handleUpdate)
  emitter.all.clear()  // 清除所有事件
})
</script>

五、内置组件

5.1 Teleport

vue
<template>
  <div>
    <!-- 将模态框渲染到 body 中 -->
    <Teleport to="body">
      <div v-if="showModal" class="modal">
        <div class="modal-content">
          <h3>模态框</h3>
          <p>这是通过 Teleport 渲染的</p>
          <button @click="closeModal">关闭</button>
        </div>
      </div>
    </Teleport>

    <!-- 渲染到指定选择器 -->
    <Teleport to="#app-footer">
      <p>渲染到页脚</p>
    </Teleport>

    <!-- 禁用 Teleport -->
    <Teleport to="body" :disabled="isMobile">
      <div>移动端不禁用</div>
    </Teleport>

    <!-- 多个 Teleport 到同一目标 -->
    <Teleport to="#modals">
      <div>模态框1</div>
    </Teleport>
    <Teleport to="#modals">
      <div>模态框2</div>
    </Teleport>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const showModal = ref(false)
const isMobile = ref(false)

const openModal = () => {
  showModal.value = true
}

const closeModal = () => {
  showModal.value = false
}
</script>

<style scoped>
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.modal-content {
  background: white;
  padding: 20px;
  border-radius: 8px;
  min-width: 300px;
}
</style>

5.2 Suspense

vue
<template>
  <Suspense>
    <!-- 异步组件 -->
    <template #default>
      <AsyncComponent />
    </template>

    <!-- 加载状态 -->
    <template #fallback>
      <div>加载中...</div>
    </template>
  </Suspense>
</template>

<script setup>
import { defineAsyncComponent } from 'vue'

// 异步组件
const AsyncComponent = defineAsyncComponent({
  loader: () => import('./HeavyComponent.vue'),
  loadingComponent: LoadingComponent,
  errorComponent: ErrorComponent,
  delay: 200,    // 延迟显示加载组件
  timeout: 3000  // 超时时间
})
</script>
vue
<!-- AsyncComponent.vue -->
<template>
  <div>
    <h1>{
  { data.title }}</h1>
    <p>{
  { data.content }}</p>
  </div>
</template>

<script setup>
// 异步组件可以使用 await
const data = await fetchData()

async function fetchData() {
  const response = await fetch('/api/data')
  return response.json()
}
</script>

5.3 Fragment

vue
<!-- Vue 3 支持多个根节点 -->
<template>
  <header>头部</header>
  <main>主要内容</main>
  <footer>页脚</footer>
</template>

<!-- 如果需要显式使用 Fragment -->
<template>
  <Fragment>
    <div>内容1</div>
    <div>内容2</div>
  </Fragment>
</template>

六、路由(Vue Router 4)

6.1 基本配置

javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

// 定义路由
const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue')
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('@/views/About.vue')
  },
  {
    path: '/user/:id',
    name: 'User',
    component: () => import('@/views/User.vue'),
    props: true  // 将路由参数作为 props 传递给组件
  },
  {
    path: '/posts',
    name: 'Posts',
    component: () => import('@/views/Posts.vue'),
    children: [
      {
        path: ':id',
        component: () => import('@/views/PostDetail.vue')
      }
    ]
  }
]

// 创建路由实例
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
  // 滚动行为
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  }
})

// 路由守卫
router.beforeEach((to, from, next) => {
  // 权限验证
  if (to.meta.requiresAuth && !isAuthenticated()) {
    next('/login')
  } else {
    next()
  }
})

router.afterEach((to, from) => {
  // 页面标题设置
  document.title = to.meta.title || 'Vue App'
})

export default router

6.2 路由使用

vue
<template>
  <div>
    <!-- 路由链接 -->
    <nav>
      <router-link to="/">首页</router-link>
      <router-link :to="{ name: 'About' }">关于</router-link>
      <router-link :to="{ path: '/user/123' }">用户123</router-link>
      <router-link :to="{ name: 'User', params: { id: 456 } }">用户456</router-link>

      <!-- 带查询参数 -->
      <router-link :to="{ path: '/search', query: { keyword: 'vue' } }">
        搜索
      </router-link>

      <!-- 自定义样式 -->
      <router-link 
        to="/profile" 
        active-class="active"
        exact-active-class="exact-active"
      >
        个人资料
      </router-link>
    </nav>

    <!-- 路由出口 -->
    <router-view />

    <!-- 命名视图 -->
    <router-view name="sidebar" />
    <router-view name="header" />
  </div>
</template>

<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

// 编程式导航
const goToHome = () => {
  router.push('/')
}

const goToUser = (id) => {
  router.push({ name: 'User', params: { id } })
}

const goBack = () => {
  router.back()
}

const goForward = () => {
  router.forward()
}

const go = () => {
  router.go(-2)
}

// 替换导航(不留下历史记录)
const replaceToHome = () => {
  router.replace('/')
}

// 获取路由信息
console.log(route.params)
console.log(route.query)
console.log(route.hash)
console.log(route.fullPath)
console.log(route.path)
console.log(route.name)

// 监听路由变化
watch(() => route.params, (newParams) => {
  console.log('路由参数变化:', newParams)
})

// 路由守卫(组件内)
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

onBeforeRouteLeave((to, from) => {
  // 离开当前组件前
  return confirm('确定要离开吗?')
})

onBeforeRouteUpdate((to, from) => {
  // 路由参数变化时
  console.log('路由更新:', to.params.id)
})
</script>

来源:
https://xcfsr.cn/

相关文章
|
24天前
|
SQL 安全 关系型数据库
Mysql指南大全(新手也能轻松掌握的Mysql教程)第四卷
教程来源:https://app-a6nw7st4g741.appmiaoda.com/ 本文系统讲解MySQL核心知识:第八章详解事务ACID特性与操作(START TRANSACTION/COMMIT/ROLLBACK);第九章涵盖用户管理、权限控制及备份恢复;第十章通过订单系统实战,演示建库建表、关联设计与复杂查询。附SQL速查表,助力快速上手。
|
6天前
|
安全 C语言
C语言函数知识点大全(二)
教程来源 https://www.xcfsr.cn/category/open-source.html 本文系统讲解C语言中递归函数、函数指针与内联函数三大核心概念:递归涵盖阶乘、斐波那契、汉诺塔等经典案例及递归/迭代效率对比;函数指针详解声明、传参、返回值及在qsort中的实际应用;内联函数则对比宏定义,突出其类型安全与副作用更少的优势。
|
17天前
|
JavaScript 前端开发 API
VUE前端初级新手知识大全(一)
教程来源 https://app-a6nw7st4g741.appmiaoda.com/ Vue.js是轻量、易上手的渐进式前端框架,专注视图层,支持声明式编程与MVVM模式。本文系统讲解入门知识:从CDN/CLI环境搭建、核心语法(插值、指令、ref/reactive)、响应式原理,到计算属性与侦听器,助你快速构建首个Vue应用。
|
4月前
|
安全 Java API
Java日期处理完全指南(新手也能轻松掌握的Java时间格式化与日期API教程)
教程来源https://www.vpshk.cn/本文介绍Java 8引入的java.time包,详解LocalDateTime、LocalDate等类的使用,涵盖获取当前时间、格式化、解析字符串及日期运算,助你轻松掌握现代Java日期处理方法,适合初学者快速上手。
|
3天前
|
Oracle 关系型数据库 PHP
PHP知识点大全(一)
教程来源 http://unbgv.cn/category/shengxiaoyunshi.html PHP是开源服务器端脚本语言,专为Web开发设计,可嵌入HTML。语法简洁、跨平台、数据库支持强,全球超70%网站使用(如WordPress、百度)。本文系统梳理PHP基础语法、变量类型、环境搭建等核心知识,兼顾新手入门与进阶参考。
|
3天前
|
PHP
PHP知识点大全(二)
教程来源 http://unbgv.cn/category/shengxiaoxingge.html 本文系统讲解PHP核心语法:涵盖算术、赋值、比较、逻辑等运算符;if/switch/match条件控制与while/for/foreach循环;函数定义、类型声明、可变参数、匿名函数(含箭头函数)、递归等完整函数特性。内容全面,示例丰富,适合作为PHP学习速查手册。
|
3天前
|
前端开发 JavaScript Go
HTML+CSS+JS知识点大全(三)
教程来源 https://xcfsr.cn/category/software-dev.html 本节系统讲解JavaScript基础:涵盖语言特性(动态/弱类型、原型继承、函数一等公民、异步编程)、引入方式(内联/内部/外部脚本及async/defer/module)、变量声明(var/let/const)、八大数据类型与检测、运算符、流程控制、函数(声明/表达式/箭头/闭包)及常用数组方法,夯实前端开发核心功底。
|
3天前
|
XML 前端开发 数据格式
HTML+CSS+JS知识点大全(二)
教程来源 https://htnus.cn/ CSS是控制网页外观的样式表语言,实现内容与样式的分离。涵盖层叠性、继承性、优先级等核心特性,支持内联、内部、外部三种引入方式;详解选择器(元素/类/ID/伪类/属性等)、盒模型、定位(static/fixed/sticky等)、Flex与Grid二维布局、响应式设计(媒体查询)及动画过渡效果,是现代前端开发基石。
|
3天前
|
移动开发 前端开发 JavaScript
HTML+CSS+JS知识点大全(一)
教程来源 https://htnus.cn/category/tech-trends.html 本文系统梳理HTML、CSS、JavaScript三大前端核心技术:HTML定义网页结构,CSS控制样式布局,JavaScript实现交互行为。涵盖HTML5语义标签、表单、多媒体、全局属性等核心知识点,兼顾基础语法与实战应用,助你构建完整前端知识体系。
|
4天前
|
设计模式 开发者 Python
Python架构知识点大全(三)
教程来源 http://unbgv.cn/category/shengxiaocaiyun.html 本文系统讲解Python架构核心:异常处理(体系、自定义、上下文管理)、23种设计模式的Python实现(单例、工厂、观察者等)、C扩展/Cython/ctypes集成、分层/微服务/事件驱动三大架构实践,助开发者深入理解Python底层机制与工程最佳实践。

热门文章

最新文章