学Vue前的基础语法

插值表达式

插值表达式(Template Literal Interpolation)是 ES6 (ECMAScript 2015) 引入的特性,它允许你在字符串中直接嵌入变量、表达式或函数调用结果。

插值表达式有很多插值方法

传统的方法就是字符串拼接,使用 + 运算符将变量或表达式与字符串连接起来。

1
2
3
const name = "Alice";
const greeting = "Hello, " + name + "!";
console.log(greeting); // 输出: Hello, Alice!

它兼容性极佳,适用于所有 JavaScript 环境,而且性能相对较好

模板字符串是 JavaScript ES6 引入的一种新特性,使用反引号(`)包裹字符串,并在 ${}中放置要插入的内容。

1
2
3
4
5
6
7
8
9
10
11
// 基本变量插值
const name = "张三";
const age = 25;

// 传统拼接方式
const greeting1 = "你好,我是" + name + ",今年" + age + "岁。";

// 插值表达式方式
const greeting2 = `你好,我是${name},今年${age}岁。`;

console.log(greeting2); // 输出: 你好,我是张三,今年25岁。

它支持多行字符串(无需转义字符)。

1
2
3
4
5
6
// 传统方式
const poem1 = "床前明月光,\n疑是地上霜。\n举头望明月,\n低头思故乡。";

// 插值表达式方式(保留换行格式)
const poem2 = `床前明月光,
疑是地上霜。`;

而且支持复杂的表达式和函数调用。这种用法是 Vue 的核心

那么来看看如何进行表达式计算

1
2
3
4
5
const a = 10;
const b = 20;

const result = `${a} + ${b} = ${a + b}`;
console.log(result); // 输出: 10 + 20 = 30

那么表达式计算都支持,函数调用肯定也是支持的

1
2
3
4
5
6
7
function double(num) {
return num * 2;
}

const num = 5;
const message = `${num} 的两倍是 ${double(num)}`;
console.log(message); // 输出: 5 的两倍是 10

同样可以在插值表达式中访问对象属性

1
2
3
4
5
6
7
8
9
const user = {
name: "李四",
address: {
city: "北京"
}
};

const info = `${user.name} 住在 ${user.address.city}`;
console.log(info); // 输出: 李四 住在 北京

注意,在模板字符串中,反斜杠依然用于转义字符,而且插值表达式通常容易引起 XSS,直接插入用户输入的内容可能导致 XSS 攻击,需要进行转义处理。

在 Vue 中,使用双大括号{{ }}来表示文本插值:

1
<div>{{ message }}</div>

箭头函数

不再赘述,这个属于是学 Vue 前必会的了

Vue3 项目说明

Vue3项目结构

Vue 3 项目由多个文件和文件夹组成,每个部分都有其特定的作用。

  • Vue 3 项目由多个文件和文件夹组成,核心文件包括 index.htmlmain.jsApp.vue
  • Vue 组件是应用的基本构建块,使用单文件组件(.vue 文件)定义。
  • Vue Router 用于管理路由,Vuex 用于状态管理。

一个 Vue 3 项目通常包含以下文件和文件夹:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
my-vue-app/
├── node_modules/
├── public/
│ ├── favicon.ico
├── src/
│ ├── assets/
│ │ └── logo.png
│ ├── components/
│ │ └── HelloWorld.vue
│ ├── views/
│ │ └── Home.vue
│ ├── App.vue
│ ├── main.js
│ └── router/
│ └── index.js
├── .gitignore
├── babel.config.js
├── package.json
├── README.md
├── index.html
├── vue.config.js
└── yarn.lock or package-lock.json

其中,public/index.html 是 Vue 应用的 HTML 模板文件。Vue 会将应用挂载到 <div id="app"></div> 中。

这是什么意思?

简单来说,这是 Vue 应用的运行基础挂载机制

public/index.html,是整个应用的HTML 入口模板,是浏览器首先加载的文件,它定义了页面的基本结构,包括 <head> 中的元数据、样式引用等,其中的 <div id="app"></div> 是 Vue 应用的挂载点,Vue 会将整个应用渲染到这个元素内部

其中<div id="app"></div> 的具体作用

  1. 占位容器:它只是一个空的 DOM 元素,作为 Vue 应用的 “容器”
  2. 挂载目标:Vue 通过 mount('#app') 找到这个元素,并将整个应用渲染到里面
  3. 渲染结果:最终浏览器中,这个 <div> 会被替换为 App.vue 中的内容

例如,

  • index.html 中:<div id="app"></div>
  • App.vue 中:<template><h1>Hello Vue!</h1></template>
  • 最终浏览器显示:<div id="app"><h1>Hello Vue!</h1></div>

src/main.js,是 JavaScript 的入口文件,负责创建和挂载 Vue 实例,并将根组件(通常是 App.vue)挂载到 index.html 中的div#app中。

src/App.vue根组件,是整个应用的组件树的顶层组件,它包含了应用的整体布局和子组件的组织方式和路由视图

src/components/ 文件夹包含可复用的 Vue 组件。如按钮、表单、卡片等

src/views/ 文件夹包含页面级组件,通常与路由配置一起使用。

src/router.js 是 Vue Router 的配置文件,用于定义路由。

src/store.js 是 Vuex 状态管理的配置文件(如果使用 Vuex)。

package.json 是项目的配置文件,包含项目的元数据、依赖和脚本。

详细说明如下

目录/文件 说明
node_modules/ 存放项目的所有依赖包,由 npm 或 yarn 自动生成和管理。
public/ 静态文件目录,里面的文件不会被 Webpack 处理,最终会原样复制到打包目录下。
public/favicon.ico 网站的图标。
public/index.html 应用的主 HTML 文件,Vue CLI 会在构建时自动注入生成的静态资源链接。
src/ 源代码目录,存放应用的主要代码。
src/assets/ 存放静态资源,如图像、字体等。这些文件会由 Webpack 处理,可以通过相对路径引用。
src/assets/logo.png 示例图像文件。
src/components/ 存放 Vue 组件,每个组件都是一个独立的 .vue 文件。
src/components/HelloWorld.vue 默认生成的示例组件。
src/views/ 存放视图组件,通常对应路由,每个视图都是一个独立的 .vue 文件。
src/views/Home.vue 默认生成的主页组件。
src/router/ 存放路由配置文件。
src/router/index.js 路由的配置文件,定义了应用的路由规则。
src/App.vue 根组件,整个应用的入口组件。
src/main.js 应用的入口文件,负责创建 Vue 实例并挂载到 DOM 上。
根目录配置文件
.gitignore Git 忽略文件列表,指定哪些文件和目录不被包含在版本控制中。
babel.config.js Babel 配置文件,指定 Babel 的编译规则。
package.json 项目的依赖、脚本和其他元数据。
README.md 项目的说明文件,通常用于描述项目、如何安装和使用等信息。
vue.config.js Vue CLI 的配置文件,用于修改默认配置。
yarn.lockpackage-lock.json 锁定安装的依赖版本,确保项目依赖的一致性。

Vue3组件结构

Vue 组件本质上是一个可复用的 Vue 实例,它拥有预定义好的选项。

单文件组件

一个完整的 Vue 单文件组件(.vue 文件)通常包含三个部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<!-- 1. 模板部分:HTML结构 -->
</template>

<script>
// 2. 脚本部分:JavaScript逻辑
export default {
// 组件选项
}
</script>

<style>
/* 3. 样式部分:CSS样式 */
</style>
  1. 模板部分 (<template>)

    模板部分定义了组件的 HTML 结构,是组件的视图层。

    1
    2
    3
    4
    5
    6
    7
    <template>
    <div class="hello">
    <h1>{{ msg }}</h1>
    <button @click="count++">点击了 {{ count }} 次</button>
    <ChildComponent :message="msg" />
    </div>
    </template>
    • 必须有且只有一个根元素(Vue 3 中可以有多个根元素)
    • 使用 Vue 的模板语法(插值表达式、指令、事件绑定等)
    • 可以包含其他子组件
    • 支持条件渲染、列表渲染等
  2. 脚本部分 (<script>)

    脚本部分包含组件的逻辑,是组件的大脑。Vue 2 和 Vue 3 有不同的写法,这就涉及到下面说的组合式和选项式API

  3. 样式部分 (<style>)

    样式部分定义组件的 CSS 样式。

而在 Vue 项目中,组件通常分为:

  1. 页面组件(Page/View)
    • 位于 src/views/ 目录
    • 对应路由页面
    • 粒度较大,包含多个功能模块
  2. 业务组件(Business Component)
    • 位于 src/components/ 目录
    • 实现特定业务功能
    • 可在多个页面复用
  3. 基础组件(Base Component)
    • 位于 src/components/base/ 目录
    • 通用 UI 组件(按钮、输入框、卡片等)
    • 与业务无关,高度复用

Vue基础语法

实际上,一开始我们都不是使用使用 vue-cli 命令行工具来创建项目的,直接在页面引入 vue.global.js 文件更容易学习

创建 Vue 应用实例

每个 Vue 应用都是通过 createApp函数创建一个新的应用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 引入 createApp 用于创建应用
import { createApp } from 'vue'

// 1. 创建应用实例
const app = createApp({
// 根组件选项
data() {
return {
message: 'Hello Vue!'
}
},
template: `<div>{{ message }}</div>`
})

那么什么是应用实例

Vue 的应用实例通常用 app 变量表示,它是整个 Vue 应用的根容器核心控制中心

它就像是你 Vue 应用的 “大脑”,负责管理全局配置、组件注册、生命周期以及将应用挂载到 DOM 上。也可以理解为这个实例就是一个“容器”,它管理着这个应用里所有的数据、组件和配置。

((相当于 Spring Boot 里的 SpringApplication.run(App.class) 启动后返回的那个 ApplicationContext。))

应用实例的配置和注册的资源在整个应用中生效,是实现全局功能的关键入口。

image-20251122160210506

根组件 (Root Component)

在上面创建一个 Vue 应用实例的代码中,我们拆开写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 引入 createApp 用于创建应用
import { createApp } from 'vue'

// 1. 定义一个根组件(Root Component),这就是你的主类
const App = {
data() {
return {
message: 'Hello Vue!'
}
}
}

// 2. 创建应用实例
const app = createApp(App) // 此时 app 仅仅被创建,还没挂载到页面上

在上面的代码中,传递给 createApp 的那个对象(App),就是根组件

在之后你写的其他 .vue 文件,一个 .vue 就是一个组件,这些所有的组件都会被安装在 App.vue 上

然后你就可以

1
createApp(App).mount('#app')

摆到 ip 为 app 的容器里

image-20251122160726842

Vue 是组件化开发的,组件就像一棵树。根组件就是这棵树的根节点 (Root Node)。所有的页面、按钮、侧边栏,都是这个根组件的“子组件”或“孙子组件”。一般叶子组件在 components 中

image-20251122160846099

这就好比 Java 的入口类。虽然项目里有成百上千个 Class,但程序运行的入口只有一个。在实际开发(单文件组件)中,这个通常是一个 App.vue 文件。

那么仔细来看一下 App.vue 这个根组件,也来看看如何编写 .vue 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script setup lang="ts">
// 脚本,ts是vue推荐使用ts
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
<!-- html -->
<div>
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Vite + Vue" />
</template>

<style scoped>
/* 样式 */
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

挂载应用

光创建了 app 实例没用,它现在还只是内存里的一堆 JS 对象。你需要把它渲染(Render)出来,放到浏览器能看到的网页里。

应用实例必须在调用了.mount()方法后才会渲染出来,.mount()方法接收一个”容器”参数,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串

这个方法告诉 Vue:“请把上面创建好的应用,在这个 HTML 元素里渲染出来。”

1
2
// 2. 挂载到 DOM 元素
app.mount('#app')

createApp 的参数是根组件(HelloVueApp),在挂载应用时,该组件是渲染的起点。

把生成的 DOM 树“插入”到 HTML 的某个 <div>节点里。

1
2
<!-- 这是一个普通的 HTML 容器,用来放 Vue 应用 -->
<div id="app"></div>
1
2
// 把之前创建的 app 实例,挂载到 id="app" 的 div 里
app.mount('#app')

.mount 必须在应用配置完成后最后调用。

根组件模板

Vue 3 的根组件模板通常定义在 App.vue 文件的 <template> 标签中,它是整个应用的顶层视图结构,作为所有其他组件的父容器。根组件模板最终会被渲染到 public/index.html 中的 <div id="app"></div> 元素内。

也就是,Vue 3 根组件模板是应用的顶层视图容器,通常位于 App.vue 中,作为所有其他组件的父级

根组件模板的基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- src/App.vue -->
<template>
<!-- 根元素:Vue 3 支持多根元素,但通常使用单个根元素 -->
<div id="app">
<!-- 全局通用组件 -->
<Navbar />

<!-- 路由视图容器 -->
<router-view />

<!-- 全局通用组件 -->
<Footer />

<!-- 全局弹窗/提示等 -->
<GlobalToast />
<GlobalModal />
</div>
</template>

<script setup>
// 导入子组件
import Navbar from './components/Navbar.vue'
import Footer from './components/Footer.vue'
import GlobalToast from './components/GlobalToast.vue'
import GlobalModal from './components/GlobalModal.vue'
</script>

<style>
/* 全局样式或根组件样式 */
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

通常我们用 .vue 文件写代码(构建工具模式)。但在我们上面说的模式下(直接在 HTML 里引入 vue.js),Vue 允许你直接把 HTML 容器里的内容当作模板

什么意思,如果在挂载时,根组件没有定义 template 选项,Vue 就会自动提取容器 HTML 里的内容作为模板。

这就像以前在 JSP 里直接写 <% if(...) %>

在现代工程化开发(Vite + Vue)中,我们很少这样用,因为我们都写在 App.vue 里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<!-- 这部分 HTML 会被 Vue 解析,而不是被浏览器当作静态文本 -->
<button @click="count++">
当前点击次数:{{ count }}
</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue

// 这里没有定义 template,Vue 会去 #app 里找
createApp({
data() {
return { count: 0 }
}
}).mount('#app')
</script>

Vue 3 特性支持多根节点模板(Fragment),无需外层包裹元素,使模板结构更灵活。下面说这个东西

应用配置

app 实例提供了一个 .config 对象,允许你配置一些全局的东西

说实话很像application.properties 或 @Configuration,因为 Vue 3 的 .config 对象是应用级别的配置中心

这个对象的配置一旦配置,应用内的所有组件都能享受到,比如全局异常处理、全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const app = createApp(App)

// 1. 全局异常处理器
app.config.errorHandler = (err) => {
console.error("系统炸了,快去修!错误信息:", err)
// 这里可以发请求给后端记录日志
}

// 2. 注册全局组件(比如每个页面都有的 Loading 组件)
// 以后在任何地方直接写 <MyLoading /> 就能用,不用 import
app.component('MyLoading', MyLoadingComponent)

// 最后再挂载
app.mount('#app')

Vue 应用实例的 .config 对象包含了一系列配置选项,用于控制应用的全局行为。这些配置会影响整个应用中的所有组件。

说几个常用的配置选项

  1. 全局错误处理器 (errorHandler)

    捕获应用中所有组件生命周期钩子和事件处理器中的未捕获错误:

    1
    2
    3
    4
    5
    6
    7
    8
    app.config.errorHandler = (err, instance, info) => {
    console.error('全局错误捕获:', err)
    console.error('错误组件实例:', instance)
    console.error('错误位置信息:', info) // 如 "setup function" 或 "render"

    // 可以在这里实现错误上报、用户友好提示等
    alert(`发生错误: ${err.message}`)
    }
  2. 全局属性 (globalProperties)

    添加可在所有组件实例中访问的全局属性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 添加全局属性
    app.config.globalProperties.$filters = {
    formatDate(date) {
    return new Intl.DateTimeFormat('zh-CN').format(new Date(date))
    },
    toUpperCase(str) {
    return str.toUpperCase()
    }
    }

    // 添加全局 API 客户端
    app.config.globalProperties.$api = {
    fetchUsers() {
    return fetch('/api/users').then(res => res.json())
    },
    fetchPosts() {
    return fetch('/api/posts').then(res => res.json())
    }
    }

    // 在组件中使用
    // this.$filters.formatDate(new Date())
    // this.$api.fetchUsers()
  3. 性能相关配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 关闭生产环境提示
    app.config.productionTip = false

    // 启用/禁用性能追踪(开发环境)
    app.config.performance = process.env.NODE_ENV === 'development'

    // 自定义编译器选项
    app.config.compilerOptions = {
    delimiters: ['${', '}'], // 更改模板插值分隔符
    preserveWhitespace: false // 移除模板中的空格
    }
  4. isCustomElement

    告诉 Vue 编译器哪些元素是自定义元素(非 Vue 组件):

    1
    2
    // 忽略以 'ion-' 开头的元素(如 Ionic 组件)
    app.config.isCustomElement = tag => tag.startsWith('ion-')

多个应用实例

Vue3支持多个应用实例,所以在一个 HTML 页面中,你可以同时启动多个独立的 Vue 应用,而且它们之间互不干扰,各自维护自己的数据和配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- HTML -->
<div id="header-app"></div>
<div id="sidebar-app"></div>

<!-- JS -->
<script>
// 应用 1:管理头部
const headerApp = createApp({
data() { return { title: '我是头部' } }
})
headerApp.mount('#header-app')

// 应用 2:管理侧边栏(和上面完全隔离)
const sidebarApp = createApp({
data() { return { menu: ['首页', '设置'] } }
})
sidebarApp.mount('#sidebar-app')
</script>

选项式API和组合式API

选项式API

在 Vue2 之前,我们主要使用的是选项式API(Options API)。

这是 Vue 2 的经典写法。Vue 规定了一个对象的结构(Options),你需要把代码按“类型”填入指定的格子里。

这种 API 的设计方式是基于对象的,我们将一个 Vue 实例的各个部分拆分成不同的选项,如datamethodscomputedwatch等,并在创建 Vue 实例时将它们作为选项传入。

Vue 规定:

  • 所有数据必须放在 data() 里。
  • 所有方法必须放在 methods 里。
  • 所有计算属性必须放在 computed 里。
  • 你需要通过 this.xxx 来访问它们。

(data选项是一个函数,Vue 在创建新组件实例的过程中调用此函数,会返回一个对象,以 $data 的形式存储在组件实例中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
export default {
// 1. 数据区
data() {
return {
count: 0,
keyword: ''
}
},
// 2. 方法区
methods: {
increment() {
this.count++; // 必须用 this
},
search() {
console.log(this.keyword);
}
},
// 3. 生命周期区
mounted() {
this.increment();
}
}
</script>

选项式API的优点在于其结构清晰、易于理解和上手。每个选项都有其明确的职责,开发者只需关注自己需要实现的功能,而无需过多关心 Vue 内部的运行机制。这种开发方式对于小型到中型的应用来说是非常高效的。

然而,随着应用规模的扩大和复杂度的增加,选项式API也暴露出了一些问题。

当组件的逻辑变得复杂时,代码会变得难以维护和理解。由于数据和逻辑被分散在多个选项中,很难一眼看出它们之间的关系。此外,对于复用逻辑代码也存在一定的困难,因为逻辑代码往往与特定的 datamethods 紧密耦合。

  • 假设你要写一个“搜索功能”。你需要:
    1. 在 data 里写 keyword。
    2. 在 methods 里写 search()。
    3. 在 watch 里监听 keyword 变化。
  • 这三行代码在文件里相隔很远。如果文件有 500 行,你为了改一个功能,可能需要买一个罗技的鼠标(无极滚轮)。

组合式 API

这是 Vue 3 的核心。它不再强制你把代码分类,而是允许你把同一个功能的代码写在一起。

为了解决选项式API在复杂应用中的局限性,Vue3引入了组合式API。组合式API是一种基于函数的API,它允许开发者将组件的逻辑代码拆分成多个独立的函数,每个函数负责处理特定的功能或逻辑。

组合式API的核心思想是“组合”,即将复杂的组件逻辑拆分成小的、可复用的函数单元。这些函数单元可以独立测试、独立复用,并且可以灵活地组合在一起以构建复杂的组件逻辑。

  • 不再有一个大对象,而是一个 setup 上下文(或 <script setup>)。后面细说
  • 你需要什么,就 import 什么函数(例如你要创建响应式状态就引入ref, onMounted)。
  • 代码按照业务逻辑组织,而不是按照技术选项组织。
  • 不再使用 this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script setup>
import { ref, onMounted } from 'vue';

// --- 功能模块 1:计数器 ---
const count = ref(0); // 数据
function increment() { // 方法
count.value++;
}

// --- 功能模块 2:搜索 ---
const keyword = ref('');
function search() {
console.log(keyword.value);
}

// --- 生命周期 ---
onMounted(() => {
increment();
});
</script>

使用组合式API的好处是显而易见的。首先,它提高了代码的可读性和可维护性。通过将逻辑代码拆分成独立的函数,我们可以更容易地理解和跟踪代码的执行流程。其次,它提高了代码的复用性。我们可以将通用的逻辑代码封装成可复用的函数,并在多个组件中共享这些函数。最后,它使得代码的组织更加灵活。我们可以根据需要自由地组合和拆分逻辑代码,以适应不同的应用场景。

组合式 API 就是把 Vue 从“填空题”变成了“写作文”,这更符合大伙编写复杂业务逻辑的直觉。

两者对比理解

你可能会问:Vue 3 是不是为了组合式 API 搞了一套新引擎?

实际上,Vue 3 的底层核心全是 组合式 API 的机制(响应式系统 reactive/ref)。

选项式 API 只是一个包装层(Wrapper)

Vue 为了兼容 Vue 2 的习惯,在底层做了一个转换,例如

  • 当你写 data() 时,Vue 内部帮你调用了 reactive()。
  • 当你写 methods 时,Vue 内部帮你把它绑定到了实例上。

不要混用:虽然 Vue 允许你在一个文件里同时写 Options 和 Composition,但千万别这么做,那是维护的噩梦。