Vue 爬坑

时间:2024-07-05 15:13:48

都是基于最新的Vue3版本 "vue": "^3.4.29"

1 vue组建样式设置

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div>
  <HelloWorld msg="Vite + Vue"  class="test-baz"/>
</template>

<style scoped>
.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
  filter: drop-shadow(0 0 2em #42b883aa);
}
.test-baz {
  color: red;
}
</style>

父组建里面的test-baz并不会被字组建HelloWorld用到,字组建只会用它自己里面的test-baz样式。

2  vue中getter函数理解

在JavaScript中箭头函数 () => x.value + y.value 是一个匿名函数,并不是getter函数。

  在Vue中,watch 函数用于观测一个表达式或函数(通常称为“getter”)的变化。当这个表达式的值发生变化时,watch 中定义的回调函数会被触发。因此,尽管上面提到的箭头函数 () => x.value + y.value 本身并不是一个严格意义上的JavaScript getter函数(如在对象属性定义中的那种),在Vue框架的上下文中,它可以被视作一个“getter”功能的实现,因为它用来获取并计算状态(即 x.value 和 y.value 的和)。

  这里的匿名箭头函数 ( ) => x.value + y.value 被Vue用作一个getter来监控依赖项(x.value 和 y.value)的变化。当这些依赖项的值发生变化导致计算结果(即两者的和)改变时,Vue就会调用第二个参数中的回调函数,打印出新的求和结果。因此,在Vue的语境下,将其称为一个“getter函数”是为了强调其观测数据变化并响应的功能,而非严格遵循JavaScript语言中getter的定义。

3  watch和 watchEffect 

 修改状态数据就是副作用

const testNumber = ref(0)

// 可以直接侦听一个 ref
watch(testNumber, async (newValue, oldValue) => {
  testNumber.value = testNumber.value + 1
},)

上面代码会造成循环依赖报错

Uncaught (in promise) Maximum recursive updates exceeded. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.

可以修改成只会执行一次,

const testNumber = ref(0)

// 可以直接侦听一个 ref
watch(
  testNumber,
  async (newValue, oldValue) => {
  testNumber.value = testNumber.value + 1
  },
  { once: true }
)

或者使用watchEffect

const testNumber = ref(0)

watchEffect(async () => {
  testNumber.value+=1
})

watchEffect修改自己内部ref不会造成循环依赖。

JPT:

在Vue中,watch 用于观察数据变化并在变化时执行一些操作。关于“避免在发生副作用时追踪依赖”的说法,这实际上与Vue的响应式系统如何处理数据变化和依赖收集有关,尤其是在Vue 3引入的Composition API中,通过refreactive创建的状态自动具有响应式特性。

当你在watch的回调函数内部修改正在被观察的数据时,如果没有正确处理,可能会导致无限循环或者不期望的行为。这是因为修改数据会再次触发依赖更新,进而重新触发watch,形成一个循环。为了避免这种情况,Vue在设计上要求开发者注意副作用的管理。

const testNumber = ref(0)

// 可以直接侦听一个 ref
watch(
  testNumber,
  async (newValue, oldValue) => {
  testNumber.value = testNumber.value + 1
  }
)

执行这段代码会导致以下问题:

  • 副作用:直接修改响应式数据是一种副作用,因为它改变了应用的状态。
  • 依赖追踪与无限循环:修改testNumber.value会触发表达式的重新计算,如果这个修改又满足watch的触发条件,那么watch回调将再次执行,从而可能形成无限循环。

为了避免这种情形,Vue提供了几种策略:

  1. 使用异步watch: 如果你的副作用需要修改触发它的数据,考虑使用异步watch(通过将回调设为返回Promise的函数,或使用immediateflush: 'post'选项)。这样Vue会在当前tick的微任务队列结束后再执行副作用,避免了即时的循环触发。
  2. 在副作用中谨慎修改数据: 确保修改数据的操作不会立即触发正在运行的watcher的重新计算。有时候可以通过条件判断来避免不必要的修改。
  3. 使用watchEffect: 如果你的副作用逻辑依赖于多个状态,并且修改这些状态是预期行为的一部分,考虑使用watchEffectwatchEffect更加灵活,会自动管理依赖,并确保副作用在依赖变更时执行,但同样需要注意避免循环调用的问题。

总之,“避免在发生副作用时追踪依赖”意味着在执行可能改变响应式数据的操作时,要小心不要无意中触发了自己正在依赖的数据变化,从而进入无限循环或不可控的状态变更。正确的做法是合理安排数据修改的位置和时机,以及利用Vue提供的机制来控制副作用的执行。

4  DOM内模板和<template>区别

在Vue中,<template> 标签有特定的用途,根据上下文的不同,它扮演着不同的角色。以下是两种主要的使用场景:

1. Vue单文件组件内的 <template>

在Vue的单文件组件(SFC,Single File Component)中,<template> 标签用于包裹组件的HTML模板部分。这部分定义了组件的结构和布局,Vue会解析这个模板并将其编译为虚拟DOM。例如:

vue

<template>
  <div class="example">
    <h1>{{ message }}</h1>
    <button @click="reverseMessage">Reverse Message</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello Vue!'
    };
  },
  methods: {
    reverseMessage() {
      this.message = this.message.split('').reverse().join('');
    }
  }
};
</script>

在这个例子中,<template> 包含了组件的HTML结构,其中可以绑定数据和事件处理器。

2. DOM内模板插值

在Vue 2中,有一种较为少见的用法是在DOM中直接使用模板字符串插值,这种方式不推荐在现代Vue应用中使用,但在某些老版本的应用或特定场景下可能存在。这通常指的是直接在HTML元素内使用双大括号 {{ }} 来插入数据。例如:

html

<div id="app">
  {{ message }}
</div>

<script>
new Vue({
  el: '#app',
  data: {
    message: 'Hello World!'
  }
});
</script>

这里虽然没有直接使用<template>标签,但双大括号 {{ }} 实现了数据绑定,相当于简单模板的使用。然而,对于复杂的结构和逻辑,应该使用Vue组件和<template>标签。

总结

  • Vue SFC中的 <template> 定义了组件的HTML结构,是Vue组件的核心部分,支持指令、插槽等高级功能。
  • DOM内模板插值 使用双大括号 {{ }} 直接在HTML中插入数据,适用于非常简单的数据展示,但不适用于复杂组件结构和逻辑。

在现代Vue开发实践中,推荐使用Vue组件和单文件组件内的<template>来构建应用界面,以充分利用Vue的响应式系统和组件化优势。

v-model 自定义修饰符

子组件

const [myDemo, myDemoModifiers] = defineModel('myDemo',{
  // get我们这里不需要
  set(value:string) {
    if (myDemoModifiers.uppercase) {
      return value?.toUpperCase();
    }
  },
});



<input type="text" v-model="myDemo" />

父组件

const name = ref(0)


<HelloWorld 
    msg=""  
    v-model:my-demo.uppercase="name"
/>

6 什么是构建步骤

构建步骤就是将源码转化为可执行的代码的过程。例如使用vite、webpack进行转换!

7  组件Class 与 Style 绑定

//父组件
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'

</script>

<template>
  <HelloWorld 
      class="test-baz"     
      :style="{ backgroundColor:'pink'}"
  />
 
</template>

<style scoped>

</style>

//子组件

<script setup lang="ts">

</script>

<template>
   <p class="input-color">Hi!</p>
</template>

<style scoped >

.test-baz {
  color: green ;
}

.input-color {
  color: red;
}

</style>

渲染结果

外部样式放在默认样式后面class="input-color test-baz"

按道理应该是test-baz的优先级比input-color高(错误认为)

当一个元素指定多个 class 时,class 的优先级与指定顺序无关,而是和 class 的定义顺序有关。后声明的优先级高。

.test-baz {
  color: green ;
}

.input-color {
  color: red;
}

input-color定义在下面所以优先级高。