1. 安装
- npm init vue@latest
- npm install
- npm run dev
- npm run build
- cdn
- 启用es模板import { createApp } from ‘https://unpkg.com/vue@3/dist/vue.esm-browser.js’
- 启用import maps
<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>
- 选项式 API (Options API) 与 组合式 API (Composition API) 地址
2. 创建应用
- 创建应用实例
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
- 挂载应用 app.mount(‘#app’)
- 应用实例会暴露一个 .config 对象允许我们配置一些应用级的选项
#定义一个应用级的错误处理器,用来捕获所有子组件上的错误:
app.config.errorHandler = (err) => {
/* 处理错误 */
}
- 注册一个组件
app.component('TodoDeleteButton', TodoDeleteButton)
- 性能问题
如果你正在使用 Vue 来增强服务端渲染 HTML,并且只想要 Vue 去控制一个大型页面中特殊的一小部分,应避免将一个单独的 Vue 应用实例挂载到整个页面上,而是应该创建多个小的应用实例,将它们分别挂载到所需的元素上去。
3. 模板语法
- 双大括号绑定值
- v-html绑定原生html 插入
- 绑定一个 attribute,应该使用 v-bind, 可以绑定一个值, 也可以绑定一个对象
<div v-bind:id="dynamicId"></div>
#简洁写法
<div :id="dynamicId"></div>
#bool类型
<button :disabled="isButtonDisabled">Button</button>
- 绑定值使用 JavaScript 表达式, 或者调用一个方法
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
#调用方法
{{ formatDate(date) }}
- 指令 Directives
#v-for、v-on 和 v-slot
<p v-if="seen">Now you see me</p>
- 参数 Arguments
<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>
<!-- 你还可以将一个函数绑定到动态的事件名称上:-->
<a v-on:[eventName]="doSomething"> ... </a>
<!-- 简写 -->
<a @[eventName]="doSomething">
- 动态参数语法的限制
<!-- 这会触发一个编译器警告 比如空格和引号-->
<a :['foo' + bar]="value"> ... </a>
<!-- 避免在名称中使用大写字母,因为浏览器会强制将其转换为小写-->
<a :[someAttr]="value"> 这段代码将不会工作 </a>
- 修饰符 Modifiers
<!-- .prevent 修饰符会告知 v-on 指令对触发的事件调用 event.preventDefault():-->
<form @submit.prevent="onSubmit">...</form>
4. 响应式基础
- 响应式配置
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
function increment() {
state.count++
}
</script>
<template>
<button @click="increment">
{{ state.count }}
</button>
</template>
- 若要等待一个状态改变后的 DOM 更新完成,你可以使用 nextTick() 这个全局 API:
import { nextTick } from 'vue'
function increment() {
state.count++
nextTick(() => {
// 访问更新后的 DOM
})
}
- reactive() 的局限性
- 仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。
- Vue 的响应式系统是通过属性访问进行追踪的, 我们不可以随意地“替换”一个响应式对象,因为这将导致对初始引用的响应性连接丢失:
-
用 ref() 响应式变量 代替reactive
-
计算属性
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果
5. 绑定 HTML class
- 绑定对象
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
- 绑定数组
const activeClass = ref('active')
const errorClass = ref('text-danger')
<div :class="[activeClass, errorClass]"></div>
- 内嵌样式
const activeColor = ref('red')
const fontSize = ref(30)
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
6. 条件渲染
- v-if 、v-else、 v-else-if
- “template”标签 上的 v-if
- v-show 不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。
- v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。
7. 列表渲染
- v-for
<li v-for="item in items">
{{ item.message }}
</li>
#index
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
- v-for 与对象
const myObject = reactive({
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
})
<li v-for="value in myObject">
{{ value }}
</li>
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
- template 上的 v-for
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
- v-for 与 v-if
当它们同时存在于一个节点上时,v-if 比 v-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名:
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
- 通过 key 管理状态
推荐在任何可行的时候为 v-for 提供一个 key attribute,除非所迭代的 DOM 内容非常简单 (例如:不包含组件或有状态的 DOM 元素),或者你想有意采用默认行为来提高性能。
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
- 数组变化侦测
Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
- 计算属性中使用 reverse() 和 sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:
8. 事件处理
- 监听事件
v-on 指令 (简写为 @) 用法:v-on:click=“handler” 或 @click=“handler” - 事件修饰符, Vue 为 v-on 提供了事件修饰符。修饰符是用 . 表示的指令后缀,包含以下这些:
<!-- 单击事件将停止传递 -->
<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>
<!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
<!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
<div @click.capture="doThis">...</div>
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
<!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
<!-- 以防其中包含 `event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>
- 按键修饰符
<!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
<input @keyup.enter="submit" />
<input @keyup.page-down="onPageDown" />
- 按键别名
Vue 为一些常用的按键提供了别名:
- .enter
- .tab
- .delete (捕获“Delete”和“Backspace”两个按键)
- .esc
- .space
- .up
- .down
- .left
- .right
9. 表单输入绑定
- 基本用法
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
# checkedNames 数组将始终包含所有当前被选中的框的值。
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
# 选择器
<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
- v-for 动态渲染
const selected = ref('A')
const options = ref([
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
])
<select v-model="selected">
<option v-for="option in options" :value="option.value">
{{ option.text }}
</option>
</select>
<div>Selected: {{ selected }}</div>
- 修饰符
<!-- 在 "change" 事件后同步更新而不是 "input" -->
<input v-model.lazy="msg" />
# 你想让用户输入自动转换为数字
<input v-model.number="age" />
# 想要默认自动去除用户输入内容中两端的空格
<input v-model.trim="msg" />
10. 生命周期钩子
- onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后运行代码
11. 侦听器
- watch 函数直接侦听一个 ref
// 可以直接侦听一个 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 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组: -
深层侦听器,
直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:
const obj = reactive({ count: 0 })
watch(obj, (newValue, oldValue) => {
// 在嵌套的属性变更时触发
// 注意:`newValue` 此处和 `oldValue` 是相等的
// 因为它们是同一个对象!
})
obj.count++
- 即时回调的侦听器immediate: true
watch 默认是懒执行的:仅当数据源变化时,才会执行回调, 有时候需要在创建侦听器时,立即执行一遍回调
watch(source, (newValue, oldValue) => {
// 立即执行,且当 `source` 改变时再次执行
}, { immediate: true })
- watchEffect()
watchEffect() 允许我们自动跟踪回调的响应式依赖, 不用使用immediate: true,
watchEffect(async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
})
- 回调的触发时机watchPostEffect()
默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。
如果想在侦听器回调中能访问被 Vue 更新之后的 DOM,你需要指明 flush: ‘post’ 选项:
watch(source, callback, {
flush: 'post'
})
watchEffect(callback, {
flush: 'post'
})
import { watchPostEffect } from 'vue'
watchPostEffect(() => {
/* 在 Vue 更新后执行 */
})
- 停止侦查器
侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。
要手动停止一个侦听器,请调用 watch 或 watchEffect 返回的函数:
const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()
12. 模板引用
允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用, 然后对元素进行必要业务操作, 比如在组件挂载时将焦点设置到一个 input 元素上
- 访问独立元素模板引用
<script setup>
import { ref, onMounted } from 'vue'
// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
- v-for 中的模板引用
<script setup>
import { ref, onMounted } from 'vue'
const list = ref([
/* ... */
])
const itemRefs = ref([])
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="itemRefs">
{{ item }}
</li>
</ul>
</template>
- 函数模板引用
除了使用字符串值作名字,ref attribute 还可以绑定为一个函数,会在每次组件更新时都被调用。该函数会收到元素引用作为其第一个参数:
<input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">
- 组件上的 ref
模板引用也可以被用在一个子组件上。这种情况下引用中获得的值是组件实例:
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value 是 <Child /> 组件的实例
})
</script>
<template>
<Child ref="child" />
</template>
13. 组件基础
- 传递 数据props, defineProps([‘title’])
<script setup>
defineProps(['title'])
</script>
<template>
<h4>{{ title }}</h4>
</template>
<!-- BlogPost.vue -->
<script setup>
defineProps(['title'])
</script>
<template>
<h4>{{ title }}</h4>
</template>
<BlogPost title="My journey with Vue" />
<BlogPost title="Blogging with Vue" />
<BlogPost title="Why Vue is so fun" />
- 传递监听事件defineEmits([‘enlarge-text’])
<script setup>
const emit = defineEmits(['enlarge-text'])
emit('enlarge-text')
</script>
- 通过slot 插槽数据
<template>
<div class="alert-box">
<strong>This is an Error for Demo Purposes</strong>
<slot />
</div>
</template>
<style scoped>
.alert-box {
/* ... */
}
</style>
<AlertBox>
Something bad happened.
</AlertBox>
- 动态组件
<component :is="tabs[currentTab]" class="tab"></component>
当使用component :is=“…” 来在多个组件间作切换时,被切换掉的组件会被卸载。我们可以通过 KeepAlive 组件强制被切换掉的组件仍然保持“存活”的状态。