响应式变量以及对象
用ref()定义响应式变量, 用作值类型引用的响应式, ref()将传入参数的值包装成一个带有.value属性的ref对象, 当类型为对象时, 会用reactive()自动转换它的.value。 ref 在模版中会自动解包 也就是说在temlate语法中可以直接调用ref定义的变量
用reactive()函数创建一个响应式对象或数组需要注意的是vue的响应式是通过属性进行追踪, 所以必须保持对该响应式对象的相同引用. 当响应式对象的属性赋值或者解构成本地变量后, 将失去响应式
// Some code
import { reactive, ref } from 'vue'
const ref_num = ref(0)
const state = reactive({ count:0 })
const mutation = () => {
state.count ++;
ref_num.value ++;
}
// 调用mutation方法后 ref_num 以及 state 会进行同步更新
深层响应式以及浅层响应式
toRef toRefs 的使用
toRefs 是一种用于破坏响应式对象并将其所有属性转换为ref的使用方法, 也就是说将一个reactive封装的对象转换成么个属性所对应的ref. 当一个封装后的reactive对象拥有比较复杂的结构时, toRefs可以派上用场. 可以让你少写点代码
const state = reactive({ a: 1, b: 2, c: 3 })
return toRefs(state)
// 在template中可以直接写 {{ a }} {{ b }} {{ c }}
// 达到简化代码的目的
toRef 也是一样, 可以自己选择将reactive包裹的对象的某个属性分离出来并自定义属性名同时保留响应式
// Some code
const state = reactive({ a:1, b:2, c:3 })
const a = toRef(state, 'a' )
return { a }
组件的生命周期
vue2 与 vue3 生命周期变化不大, 在composition api中的用法会略有不同。但是并不影响逻辑上的实现
setup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
onBeforeMount() : 组件挂载到节点上之前执行的函数。
onMounted() : 组件挂载完成后执行的函数。
onBeforeUpdate(): 组件更新之前执行的函数。
onUpdated(): 组件更新完成之后执行的函数。
onBeforeUnmount(): 组件卸载之前执行的函数。
onUnmounted(): 组件卸载完成后执行的函数
onActivated(): 被包含在中的组件,会多出两个生命周期钩子函数。被激活时执行。
onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行。
Vue3的模板语法
文本插值
插入原始HTML
指令
<p v-if="seen">Now you see me</p>
v-if 与 v-else-if v-else 联动, 是vue中使用条件渲染的方式。v-else 元素必须跟在一个v-if或者一个v-else-if 后,否则不会被识别
v-if 亦可以依附在一个包装器元素上, 这样可以达到同时条件渲染多个元素
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-for-列表渲染
我们可以使用 v-for
指令基于一个数组来渲染一个列表。v-for
指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名
Vue 的列表渲染与react的逻辑不同, 需要使用特殊的指令, 但是列表元素同样需要key标识符来提升渲染的性能
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<li v-for="item in items">
{{ item.message }}
</li>
v-for不仅适用于数组的渲染同样可以 遍历对象的所有属性, 遍历的顺序会基于对象调用的Object.keys()的返回值来决定
const myObject = reactive({
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
})
<ul>
<li v-for="value in myObject">
{{ value }}
</li>
</ul>
当数组需要排列或者是过滤可以使用监听属性
这样的好处在于不会变更原始数据解构, 而将处理过后的数组作为一个缓存只进行遍历并具备响应式
const numbers = ref([1, 2, 3, 4, 5])
const evenNumbers = computed(() => {
return numbers.value.filter((n) => n % 2 === 0)
})
<li v-for="n in evenNumbers">{{ n }}</li>
v-bind v-on
某些指令会需要一个“参数”,在指令名后通过一个冒号隔开做标识。例如用 v-bind
指令来响应式地更新一个 HTML attribute:
<a v-bind:href="url"> ... </a>
<!-- 简写 -->
<a :href="url"> ... </a>
这里 href
就是一个参数,它告诉 v-bind
指令将表达式 url
的值绑定到元素的 href
attribute 上。在简写中,参数前的一切 (例如 v-bind:
) 都会被缩略为一个 :
字符。
另一个例子是 v-on
指令,它将监听 DOM 事件:
<a v-on:click="doSomething"> ... </a>
<!-- 简写 -->
<a @click="doSomething"> ... </a>
这里的参数是要监听的事件名称:click
。v-on
有一个相应的缩写,即 @
字符。我们之后也会讨论关于事件处理的更多细节。
动态参数
使用修饰符简写指令
修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。例如 .prevent
修饰符会告知 v-on
指令对触发的事件调用 event.preventDefault()
:
我们可以使用 v-on
指令 (简写为 @
) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="methodName"
或 @click="handler"
。
事件处理器的值可以是:
内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与 onclick
类似)。
方法事件处理器:一个指向组件上定义的方法的属性名或是路径。
// 内联事件处理器, 在模板语言中直接定义的事件处理方式.适用于一些简单的事件处理
<button @click="count++">Add 1</button>
<p>Count is: {{ count }}</p>
// 方法事件处理器
const name = ref('Vue.js')
function greet(event) {
alert(`Hello ${name.value}!`)
// `event` 是 DOM 原生事件
if (event) {
alert(event.target.tagName)
}
}
<!-- `greet` 是上面定义过的方法名 -->
<button @click="greet">Greet</button>
// 也可以传入自定义参数来替代原生事件 写法如下
<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>
function say(message) {
alert(message)
}
// 当你需要在内联事件处理器中访问原生DOM事件时, 需要传入一个特殊的$event变量.或者使用箭头函数传入event
<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
Submit
</button>
function warn(message, event) {
// 这里可以访问原生事件
if (event) {
event.preventDefault()
}
alert(message)
}
计算属性以及监听属性
计算属性 computed
我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑.
computed()
方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 publishedBooksMessage.value
访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value
。
Vue 的计算属性会自动追踪响应式依赖。它会检测到 publishedBooksMessage
依赖于 author.books
,所以当 author.books
改变时,任何依赖于 publishedBooksMessage
的绑定都会同时更新。
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
//现在当你再运行 fullName.value = 'John Doe' 时,setter 会被调用而 firstName 和 lastName 会随之更新
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// 注意:我们这里使用的是解构赋值语法
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
计算属性的计算函数应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举例来说,不要在计算函数中做异步请求或者更改 DOM!一个计算属性的声明中描述的是如何根据其他值派生一个值。因此计算函数的职责应该仅为计算和返回该值。
从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。
监听属性 watch watchEffect
watch 和 watchEffect都能响应式地执行有副作用的回调. 它们之间的主要区别是追踪响应式依赖的方式:
watch 只追踪明确监听的数据源. 它不会追踪任何回调中访问到的东西, 另外仅在数据源确实改变时才会触发回调.
watchEffect 则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。
vue 与 react 不同的地方在于, react 通过useEffect钩子追踪state的状态变化并处理后续的逻辑, 而vue通过监听响应式变量的变化只想相应的回调. 两者的实现方式不同但目的基本一致.
<script setup>
import { ref, watch } from 'vue'
const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')
const x = ref(0)
const y = ref(0)
const obj = reactive({ count: 0 })
// 可以直接侦听一个 ref
watch(question, async (newQuestion, oldQuestion) => {
if (newQuestion.indexOf('?') > -1) {
answer.value = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
answer.value = (await res.json()).answer
} catch (error) {
answer.value = 'Error! Could not reach the API. ' + error
}
}
})
watch(x, (newX) => {
console.log(`x is ${newX}`)
})
// getter 函数
watch(
() => x.value + y.value,
(sum) => {
console.log(`sum of x + y is: ${sum}`)
}
)
// *** 注意,你不能直接侦听响应式对象的属性值,这里需要用一个返回该属性的 getter 函数:
watch(
() => obj.count,
(count) => {
console.log(`count is: ${count}`)
}
)
// 深层监听
watch(
() => state.someObject,
(newValue, oldValue) => {
// 注意:`newValue` 此处和 `oldValue` 是相等的
// *除非* state.someObject 被整个替换了
},
{ deep: true }
)
</script>
异步组件
通过异步组件 我们可以实现按需加载, 当一个组件仅在需要它的时候才进行加载
<script setup>
import { defineAsyncComponent } from 'vue'
const AdminPage = defineAsyncComponent(() =>
import('./components/AdminPageComponent.vue')
)
----------------------------------------------
app.component('MyComponent', defineAsyncComponent(() =>
import('./components/MyComponent.vue')
))
</script>
<template>
<AdminPage />
</template>
插槽
Provide Inject