目录
简介
(我们这里统一使用的是组合式)
Vue是什么
我们使用的是Vue3
Vue是一款用于构建用户界面的 (JS)JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型。
下面是一个基本的例子,这个写法非常的基础,createApp{}被绑定到mount('#app')这个div上
import { createApp, ref } from 'vue'
createApp({
setup() {
return {
count: ref(0)
}
}
}).mount('#app')
当button被点击(click)时,我们执行count++,两个button之间是我们的插值表达式(在页面上显示count的值。当count的值发生变化时,这个表达式会自动更新以显示最新的值)。
<div id="app">
<button @click="count++">
Count is: {{ count }}
</button>
</div>
结果展示:
点击数字会+1。
我们这里的count(html里的)是从上面的JS的count获取的,如果我们在JS里修改count的值,对应HTML里显示的count也会发生变化,他们两个数据是绑定好的。
上面的示例展示了 Vue 的两个核心功能:
-
声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法(如在html里的@click和{}都是模版),使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
-
响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。
渐进式框架
可以用不同的方式使用 Vue:
- 无需构建步骤,渐进式增强静态的 HTML
- 可以作为 Web Components 嵌入(在任何页面中)
- 单页应用 (SPA)
- 全栈 / 服务端渲染 (SSR)
- Jamstack / 静态站点生成 (SSG)
- 开发桌面端、移动端、WebGL,甚至是命令行终端中的界面
单文件组件
在大多数启用了构建工具的 Vue 项目中,我们可以使用一种类似 HTML 格式的文件来书写 Vue 组件,它被称为单文件组件 (也被称为.vue文件,英文 Single-File Components,缩写为 SFC)。顾名思义,Vue 的单文件组件会将一个组件的逻辑 (JavaScript),模板 (HTML) 和样式 (CSS) 封装在同一个文件里。下面我们将用单文件组件的格式重写上面的计数器示例:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">Count is: {{ count }}</button>
</template>
<style scoped>
button {
font-weight: bold;
}
</style>
这里就不用像上面一样进行绑定了(不用写createApp之类的了,只需要写里面的setup部分即可,这里我们换用了其他语句。< script setup >中的代码会在每次组件实例被创建的时候执行。顶层的绑定会被暴露给模板,在模版表达式中我们这里导入vue,ref值在模版中使用的时候会自动解包),直接写一个template模版里面就是我们要写的html的内容,下面的style是关于当前的样式。
如果你的用例需要进行构建,我们推荐用它来编写 Vue 组件。
API 风格
Vue 的组件可以按两种不同的风格书写:选项式 API 和组合式 API。
选项式 API (Options API)
使用选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑,例如data、method,这些都是不同的对象来描述逻辑,他们所定义的属性会暴露在函数内部的this上,指向当前第一个实例。
<script>
export default {
// data() 返回的属性将会成为响应式的状态
// 并且暴露在 `this` 上
data() {
return {
count: 0
}
},
// methods 是一些用来更改状态与触发更新的函数
// 它们可以在模板中作为事件处理器绑定
methods: {
increment() {
this.count++
}
},
// 生命周期钩子会在组件生命周期的各个不同阶段被调用
// 例如这个函数就会在组件挂载完成后被调用
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
展示结果:Count is:0
组合式 API (Composition API)
通过组合式 API,我们可以使用导入的 API 函数来描述组件逻辑。在单文件组件中,组合式 API 通常会与<script setup>搭配使用。这个setupattribute 是一个标识,在编译时进行一些处理,让我们可以更简洁地使用组合式 API。比如,<script setup>
中的导入和顶层变量/函数都能够在模板中直接使用。
下面是使用了组合式 API 与<script setup>改造后和上面的模板完全一样的组件:
<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>
两种 API 风格都能够覆盖大部分的应用场景。它们只是同一个底层系统所提供的两套不同的接口。意思就这两种API只是不同的表现形式,实际上底层都是Vue,选项式 API 是在组合式 API 的基础上实现的,关于 Vue 的基础概念和知识在它们之间都是通用的。
选项式 API 以“组件实例”的概念为中心,对于有面向对象语言背景的用户来说,这通常与基于类的心智模型更为一致,就是已经有类的想法了。同时,它将响应性相关的细节抽象出来,并强制按照选项来组织代码,从而对初学者而言更为友好。
组合式 API 的核心思想是直接在函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题。这种形式更加自由,也需要你对 Vue 的响应式系统有更深的理解才能高效使用。相应的,它的灵活性也使得组织和重用逻辑的模式变得更加强大。
组合式的API带来了潜在收益。
- 在学习的过程中,如果是Vue新手,我们建议学习时采用易于自己理解的风格即可,核心概念在两个风格之间是通用的。
-
在生产项目中:当你不需要使用构建工具,或者打算主要在低复杂度的场景中使用 Vue,例如渐进增强的应用场景,推荐采用选项式 API;当你打算用 Vue 构建完整的单页应用,推荐采用组合式 API + 单文件组件。
创建一个 Vue 应用
每个 Vue 应用都是通过createApp函数创建一个新的 应用实例:
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
我们传入createApp的对象实际上是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。
如果你使用的是单文件组件,我们可以直接从另一个文件中导入根组件。
import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
import App from './App.vue'
const app = createApp(App)
虽然许多示例只需要一个组件,但大多数真实的应用都是由一棵嵌套的、可重用的组件树组成的。例如,一个待办事项 (Todos) 应用的组件树可能是这样的:
App (root component)
├─ TodoList
│ └─ TodoItem
│ ├─ TodoDeleteButton
│ └─ TodoEditButton
└─ TodoFooter
├─ TodoClearButton
└─ TodoStatistics
挂载应用
应用实例必须在调用了.mount()方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串:
<div id="app"></div>
<div id="app"></div>
这里根应用的内容会被渲染在容器元素里面。容器元素自己将不会被视为应用的一部分。
.mount方法应该始终在整个应用配置和资源注册完成后被调用。同时请注意,不同于其他资源注册方法,它的返回值是根组件实例而非应用实例。
DOM 中的根组件模板
根组件的模板通常是组件本身的一部分,但也可以直接通过在挂载容器内编写模板来单独提供:
<div id="app">
<button @click="count++">{{ count }}</button>
</div>
import { createApp } from 'vue'
const app = createApp({
data() {
return {
count: 0
}
}
})
app.mount('#app')
当根组件没有设置template选项时,Vue 将自动使用容器的innerHTML作为模板。
DOM 内模板通常用于无构建步骤的 Vue 应用程序。它们也可以与服务器端框架一起使用,其中根模板可能是由服务器动态生成的。
应用配置
应用会暴露一个.confid的对象,允许我们做一些应用级的选项配置如定义一个错误处理器
app.config.errorHandler = (err) => {
/* 处理错误 */
}
还提供一些方法来注册应用范围内可用的资源,如注册组件。
app.component('TodoDeleteButton', TodoDeleteButton)
多个应用实例
应用实例并不只限于一个。允许你在同一个页面中创建多个共存的 Vue 应用,而且每个应用都拥有自己的用于配置和全局资源的作用域。
const app1 = createApp({
/* ... */
})
app1.mount('#container-1')
const app2 = createApp({
/* ... */
})
app2.mount('#container-2')
如果你正在使用 Vue 来增强服务端渲染 HTML,并且只想要 Vue 去控制一个大型页面中特殊的一小部分,应避免将一个单独的 Vue 应用实例挂载到整个页面上,而是应该创建多个小的应用实例,将它们分别挂载到所需的元素上去。(这种情况比较少,大部分情况下我们是将整个挂载都放进去)
模板语法
Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。
在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。
文本插值
最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):
<span>Message: {{ msg }}</span>
双大括号标签会被替换为相应组件实例中msg属性的值。同时每次msg属性更改时它也会同步更新。
原始 HTML
双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用v-html指令:
<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
下面是运行后显示的对应效果:
Using text interpolation: <span style="color: red">This should be red.</span>
Using v-html directive: This should be red.
这里我们可以看到v-html是一个指令,由v-作为前缀的特殊attribute,表明了他是vue的一些特殊属性,它将为渲染的DOM应用特殊的响应式行为。这里我们做的事情简单来说就是:在当前组件实例上,将此元素的 innerHTML 与ranHtml属性保持同步,如果我们将表达式的值改变,因为两者同步,故影响将会响应式的作用于DOM。
<div id="app">
<p v-html="rawHtml"></p>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{
rawHtml:'<span style="color:red">this is should be red</span>'
}
});
</script>
运行结果:This should be red.
注意:网站上动态渲染任意HTML是危险的,容易造成XSS漏洞,请仅在内容安全可信时再使用v-html,并且永远不要使用用户提供的HTML内容。
指令:带有v-前缀的特殊attribute,有下面几个指令:
v-once:变量的值只变化一次。
<span v-once>{{msg}}</span>
<!--msg的值只会在Vue对象渲染时赋值,即时后面对msg的值进行改变,插值处的内容也不会更新。-->
v-bind:Attribute 绑定
v-html:v-html="rawHTML"(rawHTML变量中可以写html原生代码)。
指令需要加到某一个标签上,为这个指令赋予一个变量值的方式进行指令参数的传递。
v-on:指令用于监听DOM事件,可以绑定HTML元素所有的事件
<a v-on:click="doSomething">...</a>
参数click是监听的事件名
v-if:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>指令</title>
<!-- 指令 (Directives) 是带有 v- 前缀的特殊 attribute。
指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。 -->
<script src="vue.js" type="text/javascript"></script>
</head>
<body>
<div id="app">
<!-- p标签是否被渲染取决于seen的值 -->
<p v-if="seen"> You can see me.</p>
<a v-bind:href="url">vue教程</a>
<!-- @click绑定一个点击事件 -->
<div @click="click1">
<div @click="click2">
click me
</div>
</div>
<div @click="click3">
<!-- stop是vue.js的修饰符,指当前click事件一旦执行完毕后就停止 -->
<div @click.stop="click4">
click me2
</div>
</div>
</div>
<span id="app2">
{{meg}}
</span>
</body>
<script type="text/javascript">
var vm=new Vue({
el:"#app",
data:{
seen:false,//设置seen值为false,p标签内的元素将不会被渲染
url:"https://cn.vuejs.org/guide/introduction.html",
},
methods:{
click1:function(){
console.log("click1事件");
},
click2:function(){
console.log("click2事件");
},
click3:function(){
console.log("click3事件");
},
click4:function(){
console.log("click4事件");
}
}
});
var vm2=new Vue({
el:"#app2",
data:{
meg:"data2"
}
})
</script>
</html>
这里我们因为v-if="seen"的seen的值为false,故无法被看到,后面的vue教程是使用的v-bind,在下面的url被赋值为一个网址,我们点击页面显示的vue教程字样会跳转进入url提供的网址,下面的click me点击之后会在控制台看到click2和click1,再点击click me2会在控制台显示click4,如下图所示:
Attribute 绑定
双大括号不能在 HTML attributes 中使用(就是不能在<div 这个里面使用>,只能在<>这里使用<>)。想要响应式地绑定一个 attribute,应该使用v-bind指令:
语法:v-bind:属性=""
用法:v-bind:属性名=值
:属性名=值
:[属性名]=值
<a v-bind:href="url">...</a>
v-bind指令将该元素的href属性与表达式url的值绑定。
响应式地更新HTML属性
使用JavaScript表达式:表达式会在所属vue实例的数据作用域下作为JavaScript被解析。
限制:每个绑定都只能包含单个表达式,流控制也不会生效。
例:v-bind的使用
<div id="app">
<!-- 为v-bind绑定一个动态样式 -->
<div v-bind:class="color">v-bind使用</div>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{
color:"blue",
}
});
</script>
v-bind指令指示 Vue 将元素的class attribute 与组件的color属性保持一致。如果绑定的值是 null
或者 undefined
,那么该 attribute 将会从渲染的元素上移除。
简写
因为v-bind非常常用,我们提供了特定的简写语法:
<div :class="color"></div>
开头为 :
的 attribute 可能和一般的 HTML attribute 看起来不太一样,但它的确是合法的 attribute 名称字符,并且所有支持 Vue 的浏览器都能正确解析它,不会出现在最终渲染的 DOM 中。简写语法是可选的,我们在实际开发中更经常使用简写的版本。
同名简写
如果 attribute 的名称与绑定的 JavaScript 值的名称相同,那么可以进一步简化语法,省略 attribute 值:(注:只能在Vue3.4及以上版本使用)
<!-- 与 :id="id" 相同 -->
<div :id></div>
<!-- 这也同样有效 -->
<div v-bind:id></div>
布尔型 Attribute
布尔型attribute依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled就是最常见的例子之一。
v-bind在这种场景下的行为略有不同:
<button :disabled="isButtonDisabled">Button</button>
当 isButtonDisabled
为真值或一个空字符串 (即 <button disabled="">
) 时,元素会包含这个 disabled
attribute。而当其为其他假值时 attribute 将被忽略。
动态绑定多个值
如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:
const objectOfAttrs = {
id: 'container',
class: 'wrapper',
style: 'background-color:green'
}
通过不带参数的v-bind,你可以将它们绑定到单个元素上(意思是id、class、style都会被objectOfAttrs绑定):
<div v-bind="objectOfAttrs"></div>
使用 JavaScript 表达式
我们仅在模板中绑定了一些简单的属性名, Vue 实际上在所有的数据绑定中都支持完整的JavaScript 表达式:
它可以做加法、三元表达式、分割、反转、连接等都是可以的
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
<!--双大括号不行这里,但是${}可以,我们可以取它的参数-->
这些表达式都会被作为 JavaScript ,以当前组件实例为作用域解析执行。
在 Vue 模板内,JavaScript 表达式可以被使用在如下场景上:
- 在文本插值中 (双大括号)
- 在任何 Vue 指令 (以v-开头的特殊 attribute) attribute 的值中
仅支持表达式
每个绑定仅支持单一表达式,也就是一段能够被求值的 JavaScript 代码。一个简单的判断方法是是否可以合法地写在 return
后面。
因此,下面的例子都是无效的:
<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}
<!-- 仅支持三元表达式,条件控制也不行 -->
{{ if (ok) { return message } }}
但是如果说我们在表达式中使用另一个组件的方法
<time :title="toTitleDate(date)" :datetime="date">
{{ formatDate(date) }}
</time>
比如这里的formateDate是另一个组件的方法,直接调用就可以了
绑定在表达式中的方法在组件每次更新时都会被重新调用,因此不应该产生任何副作用,比如改变数据或触发异步操作。
受限的全局访问
模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如 Math
和 Date
。
没有显式包含在列表中的全局对象是无法访问的,例如自己加到window上的一些属性,然而,我们可以在 app.config.globalProperties 里显式的添加它们,是通过在这个应用上加设置组件来绑定它的。
指令 Directives
指令是带有 v-
前缀的特殊 attribute。Vue 提供了许多内置指令,包括上面我们所介绍的v-bind和v-html。
指令 attribute 的期望值为一个 JavaScript 表达式 (除了少数几个例外,即之后要讨论到的v-for
、v-on
和 v-slot
)。一个指令的任务是在其表达式的值变化时响应式地更新 DOM。以 v-if 为例:
<p v-if="seen">Now you see me</p>
这里v-if指令会基于seen表达式的值,如果是真就能够看到,如果是假,p元素就看不到。
参数 Arguments
某些指令会需要一个“参数”,在指令名后通过一个冒号隔开做标识。例如用v-bind
指令后的href,我们通过这个属性来来更新、设置值,从而实现响应式地更新一个 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>
点击、鼠标触碰、空格的点击,或者某个字母的点击都是可以借助这个来实现。
简写用@代替v-on:。
动态参数
在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内,比如说:
<!--
注意,参数表达式有一些约束,
参见下面“动态参数值的限制”与“动态参数语法的限制”章节的解释
-->
<a v-bind:[attributeName]="url"> ... </a>
<!-- 简写 -->
<a :[attributeName]="url"> ... </a>
这里的attributeName是可以换的,而且在未赋值之前并不能确定。
这里的 attributeName
会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。如果说它的值为 "href"
,那么这个绑定就等价于 v-bind:href
。
还可以将一个函数绑定到动态的事件名称上:
<a v-on:[eventName]="doSomething"> ... </a>
<!-- 简写 -->
<a @[eventName]="doSomething"> ... </a>
当 eventName
的值是 "focus"
时,v-on:[eventName]
就等价于 v-on:focus
。
动态参数值的限制
动态参数中表达式的值应当是一个字符串,或者是 null
。(这是限制)特殊值 null
意为显式移除该绑定。其他非字符串的值会触发警告。
动态参数语法的限制
动态参数表达式因为某些字符的缘故有一些语法限制,比如空格和引号,在 HTML attribute 名称中都是不合法的。例如下面的示例(中括号里空格和引号是会引发编译器警告的):
<!-- 这会触发一个编译器警告 -->
<a :['foo' + bar]="value"> ... </a>
如果你需要传入一个复杂的动态参数,我们推荐使用计算属性替换复杂的表达式,也是 Vue 最基础的概念之一,我们很快就会讲到。
当使用 DOM 内嵌模板 (直接写在 HTML 文件里的模板) 时,我们需要避免在名称中使用大写字母,因为浏览器会强制将其转换为小写:
<a :[someAttr]="value"> ... </a>
上面的例子将会在 DOM 内嵌模板中被转换为 :[someattr]
。如果你的组件拥有 “someAttr” 属性而非 “someattr”,这段代码将不会工作。单文件组件内的模板不受此限制。
修饰符 Modifiers
修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。例如 .prevent
修饰符会告知 v-on
指令对触发的事件调用 event.preventDefault()
:
<form @submit.prevent="onSubmit">...</form>
在这里你可以直观地看到完整的指令语法:
参考内容:Vue.js