Vue中注意点

创建Vue应用的注意点:

1..mount() 方法应该始终在整个应用配置和资源注册完成后被调用。同时请注意,不同于其他资源注册方法。-放到最后面去挂载!

2.当根组件没有设置 template 选项时,Vue 将自动使用容器的 innerHTML 作为模板。

多个应用实例

应用实例并不只限于一个。createApp API 允许你在同一个页面中创建多个共存的 Vue 应用,而且每个应用都拥有自己的用于配置和全局资源的作用域。

如果你正在使用 Vue 来增强服务端渲染 HTML,并且只想要 Vue控制一个大型页面中特殊的一小部分,应避免将一个单独的 Vue 应用实例挂载到整个页面上,而是应该创建多个小的应用实例,将它们分别挂载到所需的元素上去

const app1 = createApp({
  /* ... */
})
app1.mount('#container-1')

const app2 = createApp({
  /* ... */
})
app2.mount('#container-2')


模板语法注意点:

绑定在表达式中的方法在组件每次更新时都会被重新调用,因此应该产生任何副作用,比如改变数据或触发异步操作。


  {{ formatDate(date) }}


受限的全局访问

模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如 MathDate

没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在 window 上的属性。然而,你也可以自行在 app.config.globalProperties 上显式地添加它们,供所有的 Vue 表达式使用。

动态参数

同样在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内


 ... 


 ... 

这里的 attributeName 会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。举例来说,如果你的组件实例有一个数据属性 attributeName,其值为 "href",那么这个绑定就等价于 v-bind:href

相似地,你还可以将一个函数绑定到动态的事件名称上:

 ... 




动态参数值的限制

动态参数中表达式的值应当是一个字符串,或者是 null。特殊值 null 意为显式移除该绑定。其他非字符串的值会触发警告。

动态参数语法的限制

 ... 

还应该避免使用大写,因为浏览器会将它们强制转换成小写的


响应式基础

DOM更新时机nextTick

若要等待一个状态改变后的 DOM 更新完成,你可以使用 nextTick() 这个全局 API

import { nextTick } from 'vue'

export default {
  methods: {
    increment() {
      this.count++
      nextTick(() => {
        // 访问更新后的 DOM
      })
    }
  }
}

响应式对象VS原始对象

值得注意的是,reactive() 返回的是一个原始对象的 Proxy,它和原始对象是不相等的

const raw = {}
const proxy = reactive(raw)

// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false

只有代理对象是响应式的,更改原始对象不会触发更新。因此,使用 Vue响应式系统的最佳实践是 仅使用你声明对象的代理版本

这个规则对嵌套对象也适用。依靠深层响应性,响应式对象内的嵌套对象依然是代理


reactive()的对象的局限性

因为 Vue 的响应式系统是通过属性访问进行追踪的,因此我们必须始终保持对该响应式对象的相同引用。这意味着我们不可以随意地“替换”一个响应式对象,因为这将导致对初始引用的响应性连接丢失:

通过reactive设置的属性可以通过直接通过属性名来使用!

let state = reactive({ count: 0 })

// 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
state = reactive({ count: 1 })


用==ref()==定义响应式变量

ref()相对于reactive()可谓是自由了很多!

  • Vue 提供了一个 ref() 方法来允许我们创建可以使用任何值类型的响应式 ref
  • ref() 将传入参数的值包装为一个带 .value 属性的 ref 对象:所以在每次使用的时候,我们都要用.value方式!
  • 和响应式对象的属性类似,ref 的 .value 属性也是响应式的。同时,当值为对象类型时,会用 reactive() 自动转换它的 .value

一个包含对象类型值的 ref 可以响应式地替换整个对象

const objectRef = ref({ count: 0 })

// 这是响应式的替换
objectRef.value = { count: 1 }

ref 被传递给函数或是从一般对象上被解构时,不会丢失响应性

const obj = {
  foo: ref(1),
  bar: ref(2)
}

// 该函数接收一个 ref
// 需要通过 .value 取值
// 但它会保持响应性
callSomeFunction(obj.foo)

// 仍然是响应式的
const { foo, bar } = obj


ref在模板中的解包

当 ref 在模板中==作为顶层属性==被访问时,它们会被自动“解包”,所以不需要使用 .value。下面是之前的计数器例子,用 ref() 代替:

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

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">
    {{ count }} <!-- 无需 .value -->
  </button>
</template>

Tips:

请注意,仅当 ref 是模板渲染上下文的顶层属性时才适用自动“解包”。 例如, foo 是顶层属性,但 object.foo 不是。

所以我们给出以下 object:

js

const object = { foo: ref(1) }

下面的表达式将不会像预期的那样工作:

template

{{ object.foo + 1 }}

渲染的结果会是一个 [object Object],因为 object.foo 是一个 ref 对象。我们可以通过将 foo 改成顶层属性来解决这个问题:

js

const { foo } = object

template

{{ foo + 1 }}

现在渲染结果将是 2

需要注意的是,如果一个 ref 是文本插值—就是不是JS表达式(即一个 {{ }} 符号)计算的最终值,它也将被解包。因此下面的渲染结果将为 1

template

{{ object.foo }}

这只是文本插值的一个方便功能,相当于 {{ object.foo.value }}

ref在响应式对象中的解包

当一个 ref嵌套在一个响应式对象中,作为属性被访问或更改时,它会自动解包,因此会表现得和一般的属性一样:通过对象来调用这个属性的时候,可以不使用value!

js

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1

如果将一个新的 ref 赋值给一个关联了已有 ref 的属性,那么它会替换掉旧的 ref:

js

const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2
// 原始 ref 现在已经和 state.count 失去联系
console.log(count.value) // 1

只有当嵌套在一个深层响应式对象内时,才会发生 ref 解包。当其作为浅层响应式对象的属性被访问时不会解包。


数组和集合类型的解包

响应式对象不同,当 ref 作为响应式数组或像 Map 这种原生集合类型的元素被访问时,不会进行解包。

js

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

响应性语法糖(新特性–目前在实验中)

相对于普通的 JavaScript 变量,我们不得不用相对繁琐的 .value 来获取 ref 的值。这是一个受限于 JavaScript 语言限制的缺点。然而,通过编译时转换,我们可以让编译器帮我们省去使用 .value 的麻烦。Vue 提供了一种编译时转换,使用$

vue

<script setup>
let count = $ref(0)

function increment() {
  // 无需 .value
  count++
}
</script>

<template>
  <button @click="increment">{{ count }}</button>
</template>

计算属性

computed返回值为一个计算属性ref。和其它的ref相似,你可以通过.value访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value

响应式依赖:

Vue 的计算属性会自动追踪响应式依赖,如果它依赖的那个属性为响应式的数据,那么它本身也变成了一个响应式的数据!


计算属性缓存vs方法:

有时候,我们定义一个方法,然后在模板中使用,获取到的效果与计算属性是一致的。但计算属性它是根据依赖而进行缓存的,一个计算属性仅会在其响应式依赖更新时才重新计算

这也解释了为什么下面的计算属性永远不会更新,因Date.now() 并不是一个响应式依赖:

const now = computed(() => Date.now())

而方法,在每次重新渲染的情况下都会执行,这就造成了性能的损耗!


可写计算属性

计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 gettersetter 来创建:

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

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // 注意:我们这里使用的是解构赋值语法
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

现在当你再运行 fullName.value = 'John Doe' 时,setter 会被调用而 firstNamelastName 会随之更新。


类与样式的绑定

Vue 专门为 classstylev-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组

绑定对象

<div :class="{ active: isActive }"></div>

你可以在对象中写多个字段来操作多个 class。此外,:class 指令也可以和一般的 class attribute 共存。举例来说,下面这样的状态:

<div
  class="static"
  :class="{ active: isActive, 'text-danger': hasError }"
></div>

这也会渲染出相同的结果。我们也可以绑定一个返回对象的计算属性。这是一个常见且很有用的技巧:

const isActive = ref(true)
const error = ref(null)

const classObject = computed(() => ({
  active: isActive.value && !error.value,
  'text-danger': error.value && error.value.type === 'fatal'
}))


在组件上使用

<!-- 子组件模板 -->
<p class="foo bar">Hi!</p>

<!-- 在使用组件时 -->
<MyComponent class="baz boo" />

<p class="foo bar baz boo">Hi</p>

如果你的组件有多个根元素,你将需要指定哪个根元素来接收这个 class。你可以通过组件的 $attrs 属性来实现指定:

$attrs可以访问到父节点上的类名


条件渲染

上的v-if

如果我们想要切换多个元素,可以使用template将他们包裹起来。这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个