vue3 鲜为人知的知识点

vue3 鲜为人知的知识点

该篇文章是个人觉得在平常开发过程中没怎么注意到(新增加)的知识点,每个章节的内容在官网中不只文章提到的这些。

💕 模板语法

✔ 动态参数

vue 复制代码
<script setup>
import { ref } from 'vue'

const attributeName = ref('msg')
const eventName = ref('click')

const handle = () => {
  console.log('动态事件触发')
}
</script>
<template>
    <!-- 动态事件绑定 -->
    <button @[eventName] = "handle">动态事件绑定</button>
    <!-- 动态变量绑定 -->
    <HelloWorld :[attributeName] = "HelloWorld"/>
</template>

💕 列表渲染

v-for 与对象

vue 复制代码
<script setup>
import { reactive } from 'vue'
const myObject = reactive({
  title: 'How to do lists in Vue',
  author: 'Jane Doe',
  publishedAt: '2016-04-10'
})    
</script>
<template>
   <ul>
        <li v-for="(value, key, index) in myObject">
          {{ index }}. {{ key }}: {{ value }}
        </li> 
   </ul>
</template>

v-for 使用范围值

注意此处 n 的初值是从 1 开始而非 0

vue 复制代码
<template>
    <ul>
      <li v-for="n in 10">{{ n }}</li>
    </ul>
</template>

v-forv-if

当它们同时存在于一个节点上时,v-ifv-for 的优先级更高。

vue 复制代码
<script setup>
import { reactive } from 'vue'
const todos = reactive([
    { isComplete: true, name: 'work' },
    { isComplete: false, name: 'play' }
])    
</script>
<template>
    <ul>
        <li v-for="todo in todos" v-if="!todo.isComplete">{{ todo.name }}</li>
    </ul>
</template>

如果使用上述代码,这会抛出错误和警告

因为:v-if 的优先级高于 v-for,从而导致 v-for 作用域内定义的变量别名

如何解决:

vue 复制代码
<script setup>
import { reactive } from 'vue'
const todos = reactive([
    { isComplete: true, name: 'work' },
    { isComplete: false, name: 'play' }
])    
</script>
<template>
    <ul>
        <template v-for="todo in todos">
            <li v-if="!todo.isComplete">{{ todo.name }}</li>
        </template>
    </ul>
</template>

💕 侦听器

✔ 侦听多个数据源

vue 复制代码
<script setup>
import { watch, ref } from 'vue'
const x = ref(0)
const y = ref(1)

watch([x.value, y.value], ([newx, newY]) => {
     console.log(`x is ${newX} and y is ${newY}`)
})
</script>

✔ getter 函数

例子一:

vue 复制代码
<script setup>
import { watch, reactive } from 'vue'
const obj = reactive({ count: 0 })

watch(obj.count, (count) => {
  console.log(`count is: ${count}`)
})
</script>

上诉代码会报错:因为 watch() 得到的参数是一个 number

例子二:

vue 复制代码
<script setup>
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import { watch, reactive } from 'vue'

const obj = reactive({
  count: 0 
})

watch(obj, (newValue, oldValue) => {
  console.log('新值', newValue)
  console.log('旧值', oldValue)
})
    
const handleAcc = () => {
  obj.count++
}
</script>
<template>
   <button @click="handleAcc">count++</button>
</template>

在嵌套的属性变更时触发,因为它们是同一个对象 !故newValue 此处和 oldValue 是相等的

除非 obj 整个被替换掉,才能使 newValueoldValue 不一样

上诉俩个例子都需要将监听函数的第一个参数修改成 getters 函数

javascript 复制代码
watch(() => obj.count, (count) => {
  console.log(`count is: ${count}`)
})

✔ watch 第三个参数

javascript 复制代码
watch(() => obj.count, (newCount, oldCount) => {
    console.log('立即执行,并深度监听')
}, { deep: true, immediate: true })

注:深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能

上述俩个都比较经常使用的,下面这个配置是控制监听器的触发时机

javascript 复制代码
watch(() => obj.count, (newCount, oldCount) => {
    console.log('在这里可以访问 被 vue 更新后的 DOM')
}, { flush: 'post' })

✔ watchEffect

自动跟踪回调的响应式依赖,不需要显性设置监听源。可以自动监听依赖项,并自动触发相关操作

javascript 复制代码
watchEffect(async() => {
    const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
    data.value = await response.json()
});

这个例子中,回调会立即执行,不需要指定 immediate: true。在执行期间,它会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。有了 watchEffect(),我们不再需要明确传递 todoId 作为源值。


✔ watchEffect vs watch

watchwatchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:

  • watch 只追踪明确侦听的数据源 。它不会追踪任何在回调中访问到的东西 。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机
  • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确

总结:

  • 对于这种只有一个依赖项的例子来说,watchEffect() 的好处相对较小。
  • 对于有多个依赖项的侦听器来说,使用 watchEffect() 可以消除手动维护依赖列表的负担。
  • 如果你需要侦听一个嵌套数据结构中的几个属性,watchEffect() 可能会比深度侦听器更有效,因为它将只跟踪回调中被使用到的属性,而不是递归地跟踪所有的属性

✔ 停止监听

setup()<script setup> 中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。

如下面这个例子:

vue 复制代码
<script setup>
import { watchEffect } from 'vue'

// 它会自动停止
watchEffect(() => {})

// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>

要手动停止一个侦听器,请调用 watchwatchEffect 返回的函数:

javascript 复制代码
const unwatch = watchEffect(() => {})

// ...当该侦听器不再需要时
unwatch()

💕 模板引用

✔ 函数模板引用

下诉代码是模板引用的写法:

vue 复制代码
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null)

onMounted(() => {
    inputRef.value.focus()
    console.log(inputRef.value)
})
</script>
<template>
   <input ref="inputRef" />
</template>

ref 除了可以绑定一个对象外,还能绑定一个函数(函数模板应用):

vue 复制代码
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null)
</script>
<template>
   <input :ref="(el) => { console.log('input 值', el) /* 将 el 赋值给一个数据属性或 ref 变量 */ }" />
</template>

注:当绑定的元素被卸载时,函数也会被调用一次 ,此时的 el 参数会是 null


✔ 组件实例引用

也可以通过模板引用来获取到子组件的实例。但这里需要分情况:

  • 如果一个子组件使用的是选项式 API 或没有使用 <script setup>。则父组件对子组件的每一个属性和方法都有完全的访问权。
  • 如果使用了 <script setup> 的组件是默认私有 的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西。

针对第二种情况:子组件在其中通过 defineExpose 宏显式暴露属性或方法

vue 复制代码
<script setup>
import { ref, defineExpose } from 'vue'

const a = 1
const b = ref(2)

// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
  a,
  b
})
</script>

💕 Props

✔ 如何单向修改 Props

总所周知:Props 是单向数据流,且在子组件不能进行修改。要想修改,只能通知父组件修改,或者使用双向数据绑定。

javascript 复制代码
const props = defineProps(['foo'])

// 修改值
props.foo = 'bar' // 报错

可以通过下述几个方法,在不通知父组件的前提下进行修改:

javascript 复制代码
const props = defineProps(['initialCounter'])
// 将 props 赋值给 counter,则prop 和后续更新无关了
const counter = ref(props.initialCounter)

但有一个缺点:如果上层数据发生改变时,下层是不能实时更新的。可以在做修改:

javascript 复制代码
const props = defineProps(['initialCounter'])
// 将 props 赋值给 counter,则prop 和后续更新无关了
const counter = computed(() => props.initialCounter)

这样就可以保证:

  • 如果上层数据发生改变时,下层能够实时更新的
  • 下层数据修改时,不会影响到上层数据

💕 事件

✔ 事件校验

vue 复制代码
<script setup>
const emit = defineEmits({
  // 没有校验
  click: null,

  // 校验 submit 事件
  submit: ({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('Invalid submit event payload!')
      return false
    }
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>
相关推荐
JosieBook11 分钟前
【ASP.NET学习】Web Pages 最简单的网页编程开发模型
前端·asp.net·菜鸟教程
雨 子1 小时前
Spring Web MVC
前端·spring boot·spring·mvc·postman
计算机毕设指导61 小时前
基于Springboot美食推荐商城系统【附源码】
java·前端·spring boot·后端·spring·tomcat·美食
!win !1 小时前
外部H5唤起常用小程序链接规则整理
前端·小程序
染指悲剧1 小时前
vue实现虚拟列表滚动
前端·javascript·vue.js
林涧泣1 小时前
【Uniapp-Vue3】navigator路由与页面跳转
前端·vue.js·uni-app
xiangxiongfly9152 小时前
Vue3 内置组件之KeepAlive
vue.js·keepalive
浩浩测试一下2 小时前
Web渗透测试之XSS跨站脚本之JS输出 以及 什么是闭合标签 一篇文章给你说明白
前端·javascript·安全·web安全·网络安全·html·系统安全
一棵开花的树,枝芽无限靠近你3 小时前
【PPTist】插入形状、插入图片、插入图表
前端·笔记·学习·编辑器·ppt·pptist
不会玩技术的技术girl3 小时前
获取淘宝商品详情高级版 API 接口 Java 示例代码
java·开发语言·前端