Vue 3 一 初识
2023-05-15 本文已影响0人
吴摩西
单文件组件
选项 API 是基于组合 API 实现的。
- 选项 api,使用 this 为中心,对有面向对象语言背景的用户更容易理解。
- 组合 api,使用状态组合处理复杂问题。形式自由,灵活性和复用性更好。
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
ES 语法
<div id="app">{{ message }}</div>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>
或使用 import map
<script type="importmap">
{
"imports": {
"vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
}
}
</script>
<div id="app">{{ message }}</div>
<script type="module">
import { createApp } from 'vue'
createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>
挂载
<div id="app"></div>
app.mount('#app')
- 应用根组件内容会被渲染在容器组件内,容器元素自身不会被视为应用的一部分。可以直接挂载有内容的根组件。可以用于服务器端渲染。
<div id="app">
<button @click="count++">{{ count }}</button>
</div>
import { createApp } from 'vue'
const app = createApp({
data() {
return {
count: 0
}
}
})
app.mount('#app')
属性
原始 HTML
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
动态参数
<!--
注意,参数表达式有一些约束,
参见下面“动态参数值的限制”与“动态参数语法的限制”章节的解释
-->
<a v-bind:[attributeName]="url"> ... </a>
<!-- 简写 -->
<a :[attributeName]="url"> ... </a>
<a v-on:[eventName]="doSomething"> ... </a>
<!-- 简写 -->
<a @[eventName]="doSomething">
<!-- 属性中有空格,会触发警告 -->
<a :['foo' + bar]="value"> ... </a>
<!-- 避免使用大小写,因为浏览器会强制小写 -->
<a :[someAttr]="value"> ... </a>
指令构成
image.png声明响应式状态
import { reactive } from 'vue'
export default {
// `setup` 是一个专门用于组合式 API 的特殊钩子函数
setup() {
const state = reactive({ count: 0 })
// 暴露 state 到模板
return {
state
}
}
}
<div>{{ state.count }}</div>
简写如下:
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
function increment() {
state.count++
}
</script>
<template>
<button @click="increment">
{{ state.count }}
</button>
</template>
reactive() 的局限性
- 仅对 Object, Array, Map, Set 集合类型生效,对 string, number, boolean 原始类型无效
- 必须保持对响应对象的引用。不能随意的更改
let state = reactive({ count: 0 })
// 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
state = reactive({ count: 1 })
ref 定义响应式变量
<script setup>
import { ref } from 'vue'
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
</script>
<script setup>
// 更具体的例子
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }} <!-- 无需 .value -->
</button>
</template>
ref 解包行为
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
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)
计算属性 vs 方法
<script>
// 一个计算属性 ref
import { reactive, computed } from 'vue'
// 使用计算属性
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
// 使用方法
function calculateBooksMessage() {
return author.books.length > 0 ? 'Yes' : 'No'
}
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
<p>{{ calculateBooksMessage() }}</p>
</template>
- 计算属性会在其依赖的属性变化时重新计算
- 方法会在渲染时每次都计算
// 此计算永远不会更新
const now = computed(() => Date.now())
写计算属性
<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>
计算属性实践
- 不要在 getter里面修改 DOM 或者修改对象。
- 不要直接修改计算属性值。
列表渲染
Vue 可以监听以下方法:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
也可以直接替换引用
// `items` 是一个数组的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))
vue 会较为明智的找到重复的对象来做替换。
事件修饰符
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>
<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>
<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>
生命周期
image.png监听
<script setup>
import { ref, watch } from 'vue'
const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')
// 可以直接侦听一个 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(
() => state.someObject,
() => {
// 仅当 state.someObject 被替换时触发
}
)
</script>
<template>
<p>
Ask a yes/no question:
<input v-model="question" />
</p>
<p>{{ answer }}</p>
</template>
watchEffect
<script setup>
// 无需指定 todoId.value,会自动测得所依赖状态
// 无需指定 { immediate: true },第一次进入时会自动执行
watchEffect(async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
})
import { watchEffect } from 'vue'
// 它会自动停止
watchEffect(() => {})
// ...这个则不会!
setTimeout(() => {
watchEffect(() => {})
}, 100)
const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()
</script>
模版引用
<script setup>
import { ref, onMounted } from 'vue'
const list = ref([
/* ... */
])
// v3.2.25 以后支持通过数组传递引用
const itemRefs = ref([])
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="itemRefs">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
a,
b
})
</script>
定义 props
<!-- BlogPost.vue -->
<script setup>
// defineProps 是setup 中才可以使用的一个宏,不需要显示导入,返回一个对象,包含可以传递给组件的所有 props:
defineProps(['title'])
</script>
<template>
<h4>{{ title }}</h4>
</template>