Vue是一个框架,也是一个生态。其功能覆盖了大部分前端开发需求
Vue的组件可以按照两种不同的风格书写:选项式API(Vue2写法)和组合式API(Vue3写法)
大部分的核心概念在这两种风格之间都是相通的。
import { createApp } from 'vue'
createApp({
data() {
return {
count: 0
}
}
}).mount('#app')
import { createApp, ref } from 'vue'
createApp({
setup() {
return {
count: ref(0)
}
}
}).mount('#app')
构建工具让我们能使用Vue单文件组件(SFC)。Vue官方的构建流程是基于Vite的,一个现代轻量的构建工具
安装15.0或更高版本的Node.js
npm init vue@latest
# 在cmd下运行
这一指令会安装和执行create-vue
,它是Vue官方的项目脚本架工具。你会开看到一些可选功能提示
Need to install the following packages:
create-vue@3.7.3
Ok to proceed? (y) y
Vue.js - The Progressive JavaScript Framework
√ Project name: ... vue-1 #项目名称不要大写
√ Add TypeScript? ... No / Yes
√ Add JSX Support? ... No / Yes
√ Add Vue Router for Single Page Application development? ... No / Yes
√ Add Pinia for state management? ... No / Yes
√ Add Vitest for Unit Testing? ... No / Yes
√ Add an End-to-End Testing Solution? » No
√ Add ESLint for code quality? ... No / Yes
创建后按照提示安装依赖并打开服务器
cd
npm install
npm run dev
npm create-vue project
npm create vue project
npm create-react project
npm init vue@latest
Vue使用一种基于HTML的模板语法,使我们能够声明式的将其组件实例的数据绑定到出现的DOM上。所有的Vue模板都是语法层面合法的HTML,可以被符合规范的浏览器和HTML解析器解析。
最基本的数据绑定形式使文本插值,它使用的是“Mustache”语法(双大括号)
<span>Message: {{ msg }}span>
每个绑定仅支持单一表达式,也是一段能被求值的JS代码。一个简单的判断方法是是否可以合法的写在return后面
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`">div>
因此,下面的例子都是无效的:
<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}
<!-- 条件控制也不支持,请使用三元表达式 -->
{{ if (ok) { return message } }}
双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html
指令:
<p>Using text interpolation: {{ rawHtml }}p>
<p>Using v-html directive: <span v-html="rawHtml">span>p>
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind
指令:
<div v-bind:id="dynamicId">div>
v-bind
指令指示 Vue 将元素的 id
attribute 与组件的 dynamicId
属性保持一致。如果绑定的值是 null
或者 undefined
,那么该 attribute 将会从渲染的元素上移除。
因为 v-bind
非常常用,我们提供了特定的简写语法:
<div :id="dynamicId">div>
<script>
export default{
data(){
return{
msg:"active",
style:"app"
}
}
}
</script>
<template>
<div :class="msg" :id="style">测试</div>
</template>
<style>
#app{
color: red;
}
</style>
指令是带有 v-
前缀的特殊 attribute。Vue 提供了许多内置指令,包括上面我们所介绍的 v-bind
和 v-html
。
指令 attribute 的期望值为一个 JavaScript 表达式 (除了少数几个例外,即之后要讨论到的 v-for
、v-on
和 v-slot
)。一个指令的任务是在其表达式的值变化时响应式地更新 DOM。以 v-if
为例:
<script>
export default{
data(){
return{
msg:false
}
}
}
</script>
<template>
<div v-if="msg">你能看见我吗</div>
<div v-else>那你看看我</div>
</template>
v-if
和v-show
的区别v-if
也是惰性的:如果在初次渲染条件值为false,则不会做任何事。条件区只有当条件首次变为true时才被渲染。
相比之下,v-show
简单许多,元素无论初始条件如何,始终会被渲染,只有CSSdisplay
属性会被切换。
总的来说,v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用v-show
较好;如果在运行时绑定条件很少改变,则v-if
会更合适
我们可以使用v-for
指令基于一个数组来渲染一个列表。v-for
指令的值需要使用item in items
形式来遍历。
<script>
export default{
data(){
return{
names:["小丁","小海","小段"]
}
}
}
</script>
<template>
<p v-for="i in names">{{ i }}</p>
</template>
v-for
支持使用可选的第二个参数标识当前项的位置索引
<p v-for="(i,index) in names">{{ i }}-{{ index }} p>
可以使用of
作为分隔符来代替in
,这样更接近JS的迭代器语法
<p v-for="(i,index) of names">{{ i }}-{{ index }} p>
v-for
支持使用可选的第二个参数标识当前项的名称
<p v-for="(i,key,index) in names">{{ i }}-{{ key }}-{{ index }} p>
遍历对象的时候名称要不一样,如果names:{name:"小丁",name:"小海",name:"小段"}
只会输出一个小段
export default{
data(){
return{
names:{name:"小丁",age:"小海",id:"小段"}
}
}
}
Vue默认按照“就地更新”的策略来更新通过v-for
渲染的元素列表。当数据项的顺序改变时,Vue不会随之移动DOM元素的顺序,而是就地更新每一个元素,确保它们在原本指定的索引位置上渲染。
为了给Vue一个提示,以便他可以跟踪每一个节点的标识,从而重用和重新排序现有的元素,你需要为每一个元素对应的块提供一个唯一的key
:
<p v-for="(i,index) of names" :key="index">{{ i }}p>
key
在这里是一个提供v-bind
绑定的特殊attribute推荐在任何可行的时候为
v-for
提供一个key
attribute
key
绑定的值期望是一个基础类型的值,例如字符串或number类型
Vue能够侦测响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
变更方法,就是会对调用它们的原数组进行变更。相对的,也有一些不可变方法,如filter()
,concat()
和slice()
,这些都不会更改原数组,而总是返回一个新数组。
{{ i }}
我们可以使用v-on
指令(简写为@)来监听DOM事件,并在事件触发时执行对应的JS。用法:v-on:click="methodName"
或@click="handler"
事件处理器的值可以是
onclick
类似)<template>
<button v-on:click="count++">add</button>
<p>{{ count }}</p>
</template>
<script>
export default{
data(){
return{
count:0
}
}
}
</script>
<template>
<button @click="add">add</button>
<p>{{ count }}</p>
</template>
<script>
export default{
data(){
return{
count:0
}
},
methods:{
add(){
this.count+=1
}
}
}
</script>
在处理事件时调用event.preventDefault()
或event.stopPropagation()
是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注与数据逻辑而不用处理DOM事件的细节会更好
为解决这一问题,Vue为v-on
提供了事件修饰符,常用有以下几个:
.stop
.prevent
.once
.enter
<template>
<a href="" @click="one">阻止默认事件</a>
</template>
<script>
export default{
data(){
return{
}
},
methods:{
one(e){
e.preventDefault();
console.log("1");
}
}
}
<a href="" @click.prevent="one">阻止默认事件a>
<template>
<div @click="one">
<p @click="two">阻止冒泡事件</p>
</div>
</template>
<script>
export default{
data(){
return{
}
},
methods:{
one(e){
console.log("1");
},
two(e){
e.stopPropagation();
console.log("2");
}
}
}
</script>
<div @click="one">
<p @click.stop="two">阻止冒泡事件p>
div>
<template>
<p>{{ person.content.length > 0 ? "yes" : "no"}}</p>
</template>
<script>
export default{
data(){
return{
person:{
name:"小丁",
content:["1","2","3"]
}
}
}
}
</script>
模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。因此我们推荐使用计算属性来描述响应式状态的复杂逻辑
<template>
<p>{{ getcontent }}</p>
</template>
<script>
export default{
data(){
return{
person:{
name:"小丁",
content:["1","2","3"]
}
}
},
computed:{
getcontent() {
return this.person.content.length > 0 ? "yes" : "no"
}
},
methods:{
getcontent2() {
return this.person.content.length > 0 ? "yes" : "no"
}
}
}
</script>
重点区别:
计算属性:计算属性值会基于其相应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算
方法:方法调用总是会在重渲染发生时再次执行函数
数据绑定的一个常见需求场景是操作的CSS class 列表,因为class
是attribute,我们可以和其它attribute一样使用v-bind
将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue专门为class
的v-bind
用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。
<div :class="{ active: isActive }">div>
上面的语法表示 active
是否存在取决于数据属性 isActive
的真假值。
你可以在对象中写多个字段来操作多个 class。此外,:class
指令也可以和一般的 class
attribute 共存。举例来说,下面这样的状态:
const isActive = ref(true)
const hasError = ref(false)
class会绑定对象、数组
:style
支持绑定 JavaScript 对象值,对应的是 HTML 元素的 style
属性:
const activeColor = ref('red')
const fontSize = ref(30)
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }">div>
尽管推荐使用 camelCase,但 :style
也支持 kebab-cased 形式的 CSS 属性 key (对应其 CSS 中的实际名称),例如:
<div :style="{ 'font-size': fontSize + 'px' }">div>
直接绑定一个样式对象通常是一个好主意,这样可以使模板更加简洁:
const styleObject = reactive({
color: 'red',
fontSize: '13px'
})
<div :style="styleObject">div>
同样的,如果样式对象需要更复杂的逻辑,也可以使用返回样式对象的计算属性。
我们还可以给 :style
绑定一个包含多个样式对象的数组。这些对象会被合并后渲染到同一元素上:
<div :style="[baseStyles, overridingStyles]">div>
计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。
在组合式 API 中,我们可以使用 watch
函数在每次响应式状态发生变化时触发回调函数:
<template>
<p>{{ msg }}</p>
<button @click="change"></button>
</template>
<script>
export default{
data(){
return{
msg:"hello"
}
},
methods:{
change(){
this.msg="world"
}
},
watch:{
msg(newValue,oldValue){
//数据发生变化,自动执行的函数
console.log(newValue,oldValue);
}
}
}
</script>
在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦:
<input
:value="text"
@input="event => text = event.target.value">
v-model
指令帮我们简化了这一步骤:
<input v-model="text">
另外,v-model
还可以用于各种不同类型的输入,