一、创建一个新的应用
1.1应用实例
每个 Vue 应用都是通过 createApp 函数创建一个新的 应用实例:
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
1.2 跟组件。
我们需要一个跟组件来实现挂载。 我们传入的create App的对象实际是一个组件。每个应用都需要一个跟组件。 剩下的都是其子组件。我们可以在main.js 文件中 倒入一个文件做为跟组件。
import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
import App from './App.vue'
const app = createApp(App)
1.3。 如何实现挂载。
我们的应用实例必须调用app.mount() 方法后才会渲染出来。 这个方法需要接受一个类似容器(也就是App)作为参数,可以是一个实际的DOM元素 或是一个CSS选择器字符串(就是app)
<div id="app"></div>
-------------------------
app.mount('#app')
1.4 多个实例挂载
在vue3中。应用实例并不只限于一个。createApp
API 允许你在同一个页面中创建多个共存的 Vue 应用,而且每个应用都拥有自己的用于配置和全局资源的作用域。这里和vue2不一样。vue2是只能一个实例而已。
const app1 = createApp({
/* ... */
})
app1.mount('#container-1')
const app2 = createApp({
/* ... */
})
app2.mount('#container-2')
二、模版语法
2.1. 文本插值
最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):
<span>Message: {{ msg }}</span>
2.2 如果我们想要插入html的话 需要用到v-tml指令
<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
2.3 Attribute 绑定
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令: <div v-bind:id="dynamicId"></div>v-bind
指令指示 Vue 将元素的 id
attribute 与组件的 dynamicId
属性保持一致。如果绑定的值是 null
或者 undefined
,那么该 attribute 将会从渲染的元素上移除。简写是
<div :id="dynamicId"></div>
我们还可以动态的钱绑定多个值。如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:
data() {
return {
objectOfAttrs: {
id: 'container',
class: 'wrapper'
}
}
}
<div v-bind="objectOfAttrs"></div>
2.4 使用javascript表达式
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
在 Vue 模板内,JavaScript 表达式可以被使用在如下场景上:
- 在文本插值中 (双大括号)
- 在任何 Vue 指令 (以
v-
开头的特殊 attribute) attribute 的值中
2.5 指令
指令是带有 v-
前缀的特殊 attribute。Vue 提供了许多内置指令,包括上面我们所介绍的 v-bind
和 v-html。还有v-if v-bind(属性) v-on(事件)
<a v-bind:href="url"> ... </a>
<!-- 简写 -->
<a :href="url"> ... </a>
<a v-on:click="doSomething"> ... </a>
<!-- 简写 -->
<a @click="doSomething"> ... </a>
<!--
注意,参数表达式有一些约束,
参见下面“动态参数值的限制”与“动态参数语法的限制”章节的解释
-->
<a v-bind:[attributeName]="url"> ... </a>
<!-- 简写 -->
<a :[attributeName]="url"> ... </a>
2.6 修饰符
修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。例如 .prevent
修饰符会告知 v-on
指令对触发的事件调用 event.preventDefault()
:
<form @submit.prevent="onSubmit">...</form>
还有一些 比如 .stop .native .trim(去除前后空格)
三、响应式状态基础
3.1 我们可以使用 reactive() 函数创建一个响应式对象或数组:
import { reactive } from 'vue'
const state = reactive({ count: 0 })
要在组件模板中使用响应式状态,需要在 setup()
函数中定义并返回。
import { reactive} from 'vue'
export default{
setup(){
const state = reactive({ count:0 })
function increment () {
state.count++
}
return {
state,
increment
}
}
}
//使用的时候
<button @click="increment">
{{ state.count }}
</button>
这里可以看出 这种写法需要我们把状态和方法暴露出去 如果是大型项目的话 会非常繁琐 所以vue官网出来一个新的语法 就是 <script setup>
来大幅度地简化代码:
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
function increment() {
state.count++
}
</script>
<template>
<button @click="increment">
{{ state.count }}
</button>
</template>
<script setup>
中的顶层的导入和变量声明可在同一组件的模板中直接使用。你可以理解为模板中的表达式和 <script setup>
中的代码处在同一个作用域中。
3.2 DOM 更新时机
我们在修改响应式状态会 我们的系统会自动更新状态 但是 当我们的DOM更新并不是同步而是异步的时候 ,Vue 将缓冲它们直到更新周期的 “下个时机” 以确保无论你进行了多少次状态更改,每个组件都只更新一次。在vue2版本中一样适用。
若要等待一个状态改变后的 DOM 更新完成,你可以使用 nextTick() 这个全局 API:
import { nextTick } from 'vue'
function increment() {
state.count++
nextTick(() => {
// 访问更新后的 DOM
})
}
3.3 reactive()
的局限性
reactive()
API 有两条限制:
1.仅对对象类型有效(对象、数组和 Map
、Set
这样的集合类型),而对 string
、number
和 boolean
这样的 原始类型 无效。
2.因为 Vue 的响应式系统是通过属性访问进行追踪的,因此我们必须始终保持对该响应式对象的相同引用。这意味着我们不可以随意地“替换”一个响应式对象,因为这将导致对初始引用的响应性连接丢失:
let state = reactive({ count: 0 })
// 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
state = reactive({ count: 1 })
同时这也意味着当我们将响应式对象的属性赋值或解构至本地变量时,或是将该属性传入一个函数时,我们会失去响应性:
const state = reactive({ count: 0 })
// n 是一个局部变量,同 state.count
// 失去响应性连接
let n = state.count
// 不影响原始的 state
n++
// count 也和 state.count 失去了响应性连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收一个普通数字,并且
// 将无法跟踪 state.count 的变化
callSomeFunction(state.count)
3.4 用 ref()来定义响应式的状态
为了解决reactIve() 的局限性 vue官方出了一个 ref() 方法 来允许我们创建可以使用任何值类型的响应式状态:
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
const objectRef = ref({ count: 0 })
// 这是响应式的替换
objectRef.value = { count: 1 }
const obj = {
foo: ref(1),
bar: ref(2)
}
// 该函数接收一个 ref
// 需要通过 .value 取值
// 但它会保持响应性
callSomeFunction(obj.foo)
// 仍然是响应式的
const { foo, bar } = obj
还有当ref 在模板 <template> 中作为顶层属性被访问时,它们会自动“解包”,所以不需要 .value 请注意,仅当 ref 是模板渲染上下文的顶层属性时才适用自动“解包”。 例如, object
是顶层属性,但 object.foo
不是。
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }} <!-- 无需 .value -->
</button>
</template>
const object = { foo: ref(1) }
//错误用法
{{ object.foo + 1 }}
//正确用法
const { foo } = object
{{ foo + 1 }}
{{ object.foo }}
ref在响应式对象中解包 作为属性被访问或更改时,他会自动解包
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
如果将一个新的 ref 赋值给一个关联了已有 ref 的属性,那么它会替换掉旧的 ref:
只有当嵌套在一个深层响应式对象内时,才会发生 ref 解包。当其作为浅层响应式对象的属性被访问时不会解包。
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
// 原始 ref 现在已经和 state.count 失去联系
console.log(count.value) // 1
跟响应式对象不同,当 ref 作为响应式数组或像 Map
这种原生集合类型的元素被访问时,不会进行解包。
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)
四、计算属性
4.1 基础示例
我们知道 在模板中我们可以写一些简单的操作 但是如果是逻辑太多的话 会显得臃肿或者执行不成功 所以我们有一个计算属性的方法可以实现这个,首先这个计算属性相当于一个方法或者一个函数 里面写我们想要的逻辑 然后return 出去就行
computed() 方法期望接受一个getter函数 返回值为计算属性ref 和其他的一样 可以通过 XX.value去访问 同理 计算属性ref 也会在模板中自动解绑 所以在模板中无需添加 .value。
vue的计算属性会自动追踪响应式依赖 所以计算属性 依赖于 响应式数据 当响应式数据发生变化 相应的计算属性的值也会发生变化 示例:
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
4.2 计算属性缓存 VS 方法
了解完计算属性 我们会发现这个和方法似乎没啥不一样 一样可以实现逻辑,但是不同的是 计算属性值会基于响应式依赖被缓存 只有响应式数据发生变化才会重新计算 而方法不一样 是不会改变的。而为什么需要缓存 是因为计算属性是一个需要计算的东西 如果我们没有缓存的话 我们会重复执行很多次的getter 而实际并不必要。
还有 大部分我们写计算属性都是用来只读的 不会去修改。但是有些特殊场景中 不得不对计算属性进行写的操作,我们可以通过getter 和 setter 来创建入 看代码 现在当你再运行 fullName.value = 'John Doe'
时,setter 会被调用而 firstName
和 lastName
会随之更新。
<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>