VUE3—小白入门之路(图文详细版,一文全部搞定!)

目录
本文将会从0开始详细介绍VUE3有关内容,以及有关八股文内容融合在一起,真正入门VUE3。
1. 工程创建
现在,我们构建VUE的前端项目,一般会使用 VUE-CLI 或者 create-vue ,他们俩都是都是用于创建 Vue.js 项目脚手架工具。那么我们该如何选择使用哪种脚手架工具进行创建呢?这就不得不提到他们俩的差异了👇
| 特性 | Vue CLI | Create-Vue |
|---|---|---|
| 底层构建工具 | 基于 Webpack | 基于 Vite |
| vue版本支持 | vue2,vue3 | 只支持 vue3 |
| 开发服务器启动速度 | 较慢,随着项目依赖增多会变慢 | 极快,开发服务器启动速度是 Webpack 的 10-100 倍 |
| 热模块替换速度 | 较慢 | 极快,提供更好的开发体验 |
| 模块系统 | 支持 CommonJS 和 ES 模块 | 主要支持 ES 模块,对 CommonJS 支持有限 |
| 浏览器兼容性 | 可以兼容旧浏览器 | 仅支持现代浏览器(ES2015 及以上) |
| 插件生态系统 | 拥有丰富的插件系统,可通过安装各种插件扩展功能 | 可利用 Vite 的插件生态系统,该生态系统与 Rollup 兼容 |
| 是否包含构建工具 | 内置构建工具,可直接用于生产环境构建 | 本身仅是脚手架工具,构建工作交给 Vite 处理 |
从上面的表格,我们可以得出一条简单的结论:
- 使用VUE-CLI:适合复杂项目,需要高度定制化配置,对浏览器兼容性要求高,团队开发需要共享和管理项目配置。或者需要使用VUE2的项目
- 使用create-vue:适合快速搭建简单项目,对开发速度要求高,使用现代浏览器进行开发。
但无论选择哪一种脚手架,他们都需要Node.JS环境作为依赖,所以我们得先确保自己安装好Node.JS的环境。(其中create-vue的Node版本需要 ≥16.0,才可以)
跳转下载👉:Node.JS下载
可能安装Node的过程中,大家会碰到一些环境变量设置,权限问题,这里我就不多说了,大家可以参考这篇文章哈 -> Node.JS安装配置(超详细)
1.1 使用VUE-CLI创建前端项目
在自己的工程目录下,使用 vue create 项目名称,创建工程项目,这里要注意,项目名称不能包含大写字母,否则会报错(也可能是因为我的node版本原因,我的node版本为18.18)

接着就会跳到选项,第一个为选择创建一个默认的VUE3项目,第二个选项为创建一个默认的VUE2项目,最后一个为创建一个自定义的模板项目,这也是VUE-CLI脚手架一个突出的地方,允许我们高度自定义,创建需要的项目。后续的选项选择,就看大家实际项目的需求了~

1.2 使用create-vue创建前端项目
在项目目录中,输入命令 npm init vue@latest 接着依据提示,创建工程
这里就不过多介绍每个选项了
附上VUE3官方链接:Vue.js - The Progressive JavaScript Framework | Vue.js
PS.你也可以使用比较新版的Pycharm,WebStorm等编译器创建VUE项目,可以手动选择对应的脚手架选项,由编译器帮你自动构建VUE项目。
小结:

2.项目结构
进入到我们新创建的项目当中,先使用 npm install 命令,将项目环境依赖创建好。
这里,我们以使用create-vue创建的vue3项目为例子:
首先式package.json文件,这里主要就是我们存储着我们项目所需要的依赖包,比如页面路由vue-route,全局状态管理工具Pinia,还有我们的代码规范工具,eslint,prettier等,都在我们的这个文件当中,定义了我们项目所用到的工具包与对应版本。

我们点击preview旁边的按钮,便可执行 vite preview 命令,静态预览我们的项目👇:

在我们首次启动的页面右侧,可以看到官方提供的文档和社区资源,感兴趣的uu也可以点开看呐~如果想要我们修改代码的时候,页面能够同步更新,我们可以使用 npm run service 命令启动项目。
而在vite.config.ts文件中(因为我使用的式TypeScript语言,所以文件结尾为.ts,如果你使用JavaScript,文件结尾就是.js)是基于vite的配置文件

src文件下的APP.vue,便是我们项目的根组件,在这里你可以看见我上面启动项目之后的页面代码,而main.ts文件则是用于创建我们app实例,是我们项目的入口文件。还有一些其它的文件夹,具体作用如下:

那我们的VUE3项目是如何启动的呢?请看下图👇

(1)解析项目入口
Vite 会找到项目入口文件(通常是 src/main.js 或 src/main.ts),并开始解析依赖。
(2)模块热替换(HMR)初始化
Vite 初始化模块热替换(HMR)功能,这样在开发过程中修改代码时,页面会自动更新而无需刷新。
(3)启动 HTTP 服务器
Vite 启动一个 HTTP 服务器,默认端口是 5173,你可以在浏览器中访问 http://localhost:5173。
打开浏览器,访问 http://localhost:5173,Vite 会将请求的静态资源(如 index.html 和 JavaScript 文件)发送给浏览器。
(5)创建VUE运用实例
在浏览器中,Vite 加载 index.html 文件,该文件引用了 src/main.ts 文件。main.ts 中的代码会执行以下操作:通过 createApp(App) 创建 Vue 应用实例,其中 App 是根组件(App.vue)。使用 mount 方法将 Vue 应用挂载到 index.html 中的 DOM 元素上(通常是 #app)。

(6)渲染组件
Vue 将 App.vue 的模板渲染到页面上,显示初始页面内容。如果项目中引入了 Vue Router 或 Pinia 等插件,它们也会在此时初始化并开始工作。
3.基本指令
我们使用 npm run service 命令启动项目,这样我们在修改的代码时候,页面能够同步修改。
v-model 与 插值显示
v-model用于绑定组件的输入值到某个变量中,使用{{ 变量名 }},便可以在我们的页面中显示变量的值。
<script setup>
import {ref} from "vue";
let name = ref('') # 定义一个字符串变量
</script>
<template>
<input v-model=name type="text"> # 将输入框的输入内容绑定到变量 name
<div>{{ name }}</div> # 使用插值方法,显示变量的值
</template>
页面显示效果如下:

v-show
用于控制组件的显示与隐藏,一个组件能否被用户看到,接收的数据类型为布尔值。

我们给组件绑定v-show指令,当其接收的值为true时,组件则会显示,与平常没有差别,接收值为flase,组件则不显示。

v-for
类似Python语言当中的enumerate循环,常用来依次读取列表内容,返回元素的值和下标。
<script setup>
import { ref } from 'vue'
let list = ref(['苹果', '香蕉', '橙子'])
</script>
<template>
<ul>
<li v-for="(item, index) in list" :key="index">
<strong> {{ item }}</strong>
<div> {{index}} </div>
</li>
</ul>
</template>
这里的 :key 相当于给我们每一对 li 添加索引,索引的值就是列表中的 index索引下标。
v-bind 和 v-on(@click)
- v-bind用于为组件的某个属性绑定到变量上,通常简写为 :属性="变量"
- v-on用于为组件绑定动态函数,当鼠标点击组件的时候,则会触发对应的函数功能,一般简写为 @click="函数名"
<script setup>
import { ref } from 'vue'
let imageUrl = ref('https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png')
let count = ref(0)
const handleClick = () => {
count.value++
}
</script>
<template>
<!-- <img v-bind:src="imageUrl" alt="p">-->
<img :src="imageUrl" alt="p">
<!-- <button v-on="handleClick">点击 {{ count }}</button>-->
<button @click="handleClick">点击 {{ count }}</button>
</template>
在上面的例子中,我们使用v-bind,将img的src属性绑定到变量imageUrl上,这样我们每次只需要更改imageUrl变量的值,img的图片就会对应更改;而button按钮则是绑定上的handleClick的函数,每次点击按钮时,便会触发该函数执行。

v-if
常用于通过某个变量判读,显示那部分内容 ,当v-if的判断值为true时,则显示对应部分的内容,否则显示v-else的内容。可以进行多个v-if-else的嵌套
<script setup>
import { ref } from 'vue'
let isVisible = ref(true)
</script>
<template>
<button @click="isVisible = !isVisible">切换显示</button>
<div v-if="isVisible">显示内容 AA</div>
<h1 v-else>显示内容 BB</h1>
</template>

但使用v-if的时候,需要注意,v-if的组件和其它绑定v-if-else以及v-else的组件,顺序一定要相连,中间不能出现不带 v-if 类型的组件,否则会出错。 以下写法就是错误的
<div v-if="isVisible">显示内容 AA</div>
<button @click="isVisible = !isVisible">切换显示</button>
<h1 v-else>显示内容 BB</h1>
# v-if和v-else之间,出现了不带 v-if类型 的组件 button,会出错!
为什么不建议 v-if 和 v-for 一起使用 ?
1.在Vue2中,当v-if和v-for同时用在一个元素上时,v-for的优先级更高。这意味着v-if会在循环内部的每次迭代中都被执行,可能导致性能问题。
2.而在Vue3中,优先级关系发生了变化,v-if的优先级高于v-for。这会导致v-if无法访问v-for作用域内定义的变量,从而引发错误。
3.这种优先级的不一致性使得代码在不同版本间的行为差异较大,容易引起混淆。
v-html
<script setup>
import { ref } from 'vue'
let htmlContent = ref('<h1>这是一个HTML内容</h1>')
</script>
<template>
<div v-html="htmlContent"></div>
</template>
使用v-html,可以将html代码内容,自动渲染为网页,并进行显示,实际上有些博客系统,就是通过存储用户写博客时的html代码,后续用户想要查看时,就使用v-html,将用户所写的内容渲染到网页,从而保持博客当中的文字位置和字体效果。
v-slot
用于父子组件之间插槽功能的实现

我们在子组件(ChildComponent)中使用slot标签,设置插槽。
父组件在使用子组件的时候,使用 v-slot:name 就可以定位到子组件中对应的插槽位置,设置对应的内容显示。
由于插槽的使用比较复杂,这里就是介绍比较简单的使用。
VUE组件的命令规范
使用 PascalCase(大驼峰)或kebab-case(短横线)命名
·在单文件组件、字符串模板和JSX中推荐使用PascalCase
在DOM模板中必须使用kebab-case
多单词命名,避免与HTML标签冲突
文件命名与组件名保持一致,同样使用PascalCase或kebab-case格式
小结:

4.脚本语法
4.1组合式API
在VUE2中,我们的脚本语言,主要是选项式API的形式,而在VUE3中,主要为组合式API形式。
在组合式API中,我们通过 setup 选项启动,语法如下:
<script>
import {ref} from "vue";
export default {
setup(){
// 数据
const vm = ref('');
// 函数
const fun = () => {};
// 返回,使得外部可以直接调用
return{
vm,
fun
}
}
}
</script>
PS. setup的执行时间,是在 beforeCreate 之前的
或许你会觉得上面的写法太过于复杂,我们可以使用 <script setup>语法糖,进行简化
<script setup lang="ts">
import {ref} from "vue";
// 数据
const x = ref()
// 函数
const fun = () => {}
</script>
<template>
<div>{{x}}</div>
<button @click="fun"></button>/
</template>
这个时候,我们就不再需要return了,可以直接获取变量和函数。
值得注意的是,使用我们的组合式API(开启setup),this 便不在指向组件实例,而是udefined,没有意义。
选项式API VS 组合式API
| 对比维度 | 选项式 API | 组合式 API |
|---|---|---|
| 定义 | Vue 2 中的主要 API 形式,以选项的形式组织代码,如 data、methods、computed 等选项来定义组件的不同部分 | Vue 3 中引入的新 API 形式,通过组合函数来组合不同的功能模块,将相关的逻辑代码集中在一起 |
| 代码组织方式 | 以选项对象的形式组织代码,每个选项都有其特定的用途和生命周期 | 通过组合函数(setup)来组织代码,将相关的逻辑组合在一起,不受组件生命周期的限制 |
| 数据管理 | data 选项返回一个对象,对象中的属性即为组件的数据,数据和方法在选项中是分离的 | 在 setup 函数中定义响应式数据,通常使用 ref 或 reactive 函数来创建响应式数据,数据和方法可以在同一个函数中定义 |
| 方法定义 | 在 methods 选项中定义方法,方法中可以通过 this 访问组件的数据和方法 | 在 setup 函数中定义方法,方法中可以直接使用 setup 函数中定义的响应式数据和其他方法,无需通过 this |
| 计算属性 | 在 computed 选项中定义计算属性,计算属性的 getter 和 setter 都在 computed 选项中定义 | 在 setup 函数中可以使用 computed 函数来定义计算属性,与选项式 API 的计算属性类似,但更加灵活 |
| 生命周期钩子 | 在组件的选项中定义生命周期钩子,如 created、mounted 等 | 在 setup 函数中可以通过 onBeforeMount、onMounted 等函数来注册生命周期钩子 |
| 代码复用性 | 代码复用性较差,如果多个组件中有相同的逻辑,需要将这些逻辑提取到 mixins 中 | 代码复用性较好,可以将相关的逻辑组合在一起,形成一个可复用的组合函数,然后在多个组件中导入和使用 |
| 可读性 | 对于简单的组件,可读性较好,因为代码结构比较固定;但对于复杂的组件,代码可能会变得比较冗长和难以维护 | 可读性较好,因为将相关的逻辑组合在一起,使得代码更加模块化和易于理解 |
| 适用场景 | 适用于简单的组件或项目,尤其是在 Vue 2 项目中 | 适用于复杂的组件或项目,尤其是在 Vue 3 项目中,能够更好地组织和管理代码 |
4.2 响应式语法
- reactive和ref函数

reactive和ref都是用于script中声明常量和变量的函数,但是reactive函数只能构造对象类型的数据,像字符串,数字类型的数据,它无法构造,ref函数则可以构造任何类型的数据,更加方便,所以平常开发中,我们一般只是用ref函数来声明变量和常量。(记得使用的时候,要先从vue中导入这两个函数哟~)
<script setup lang="ts">
import {reactive, ref} from "vue";
let x = ref(0);
let students = ref([{'id':1,'name':'张三'},{'id':2,'name':'李四'}])
const filteredStudents = students;
// reactive仅构造对象类型数据
const base = reactive(['1','2','3','4','5','6'])
</script>
而我们要想在脚本中获取由ref构造的变量的值,得再变量名后面加上.value 才能够真正访问到变量所蕴含的值,否则是访问不到,与我们直接在前端 {{变量名}} 的插值显示方法是不一样的。
举个例子体会一下:
<script setup lang="ts">
import {reactive, ref} from "vue";
let x = ref(0);
const students = ref([{'id':1,'name':'张三'},{'id':2,'name':'李四'}])
let filteredStudents = students; // 复制
// 只要涉及到值的使用,一定记得加上.value
const fun = () =>{
filteredStudents.value = students.value.filter(item => item.id > 1); // 过滤,取出id大于1的内容
x.value ++ // 让x的值增加
}
</script>
<template>
<div>{{filteredStudents}}</div>
<button @click="fun">测试 {{x}}</button>
</template>
运行结果:

可以看到当我们点击按钮的时候,就触发了fun函数,修改了变量x和filterstudent的值,在前端显示出来。
- computed函数
computed函数,用于对某个变量进行运算,返回处理之后的结果,语法格式如下:
computed(()=>{运算,return ...})
举例:
<script setup lang="ts">
import {computed, ref} from "vue";
const list = ref([1,2,3,4,5,6,7,8,9,10]);
const ComputeList = computed(()=>{
let temp = ref([]);
for(let i=0;i<list.value.length;i++){
temp.value.push(list.value[i]+100);
}
return temp;
})
setTimeout(()=>{
list.value.push(20,30,50)
},5000)
</script>
<template>
<div>
原数组:{{list}}
</div>
<div>
computed数组:{{ComputeList}}
</div>
</template>
在上述代码中,我们使用ComputeList来记录computed函数计算list之后返回的结果,并设置了setTimeout函数,在5s之后,为list列表末尾中增加20,30,50三个值。运行结果如下:

可以看到我们的ComputeList是会随着List的变化而变化,会更具其内容的改变,对新改变的内容重新计算,存储到我们的ComputeList当中。
- watch函数
watch函数用于监听变量的变化,只要监听的变量的value发生变化,就自动触发watch当中的函数
--------------------------------------------------- 基 本 使 用-------------------------------------------------------
举例:
<script setup lang="ts">
import {ref, watch} from "vue";
let count = ref(0);
const plus = ()=>{
count.value++;
}
watch(count,()=>{
console.log(count.value)
})
</script>
<template>
<button @click="plus">{{count}}</button>
</template>
当我们点击按钮的时候则就触发plus函数,修改count变量的值,而count变量的值一修改,就会被watch监听到,触发watch当中的操作,在操作台中输出count的值。

而watch函数中的操作部分可以接收两个变量,获取监听变量的新值和旧值
watch(count,(NewValue,OlderValue)=>{
console.log(NewValue,OlderValue)
})
// NewValue 为 count 更改之后的新值
// OlderValue 为 count 更改之前的旧值

当然我们也可以使用watch监听多个变量的值,只要当中的一个变量状态发生变化,就会触发watch,以下为一个示例:
<script setup lang="ts">
import {ref, watch} from "vue";
let count = ref(0)
let name = ref("SC")
const plus = ()=>{ // 修改count
count.value++;
}
const changeName = ()=>{ // 修改name
if(name.value == "SC"){
name.value = "FJNU";
}else{
name.value = "SC";
}
}
// 注意这里watch的用法,使用[]接收监听变量
watch([count,name], ([newCount,NewName],[oldCount,OldName])=>{
console.log(newCount,oldCount)
console.log(NewName,OldName);
})
</script>
<template>
<button @click="plus">{{count}}</button>
<button @click="changeName" style="margin-left: 15px">{{name}}</button>
</template>

如果你想在页面加载的时候,就自动触发一次watch,可以开启watch的immediate选项
watch([count,name], ([newCount,NewName],[oldCount,OldName])=>{
console.log(newCount,oldCount)
console.log(NewName,OldName);
},{
immediate:true
});
这样再打开页面,哪怕没点过按钮,也会自动触发一次watch函数的内容。
--------------------------------------------------- 深 度 监 听-------------------------------------------------------
如果你所想监听的变量是一个object变量,此时你会发现直接使用watch变量是无法监听得到的,此时得开启watch的深度监听选项——deep,这样才能够监听到object对象。

如果你只想监听object对象当中的某个属性,而不是全部属性,你可以使用下面的语法:
watch(
()=>state.value.age, // 表明监听的字段
()=>{console.log(0)}, // 操作
{deep:true}
)
举例:
<script setup>
import {ref, watch} from "vue";
const state = ref({age:18,name:'张三'})
watch(()=>state.value.age,()=>{
console.log(state.value)
},{
deep: true,
})
const changeAge = ()=>{
state.value.age ++;
}
const changeName=()=>{
state.value.name = 'fjnu';
}
</script>
<template>
<button @click="changeAge">{{state.age}}</button>
<button @click="changeName" style="margin-left: 15px">{{state.name}}</button>
</template>
运行结果如下:

4.3 生命周期
如果你对vue2的生命周期有所了解,那么可以直接参考下面的比较,如果不熟悉也没事,且听我慢慢和你说~
| Vue 2 生命周期钩子 | Vue 3 生命周期钩子 | 说明 |
|---|---|---|
beforeCreate | setup() | 在实例初始化之后,数据观测和事件配置之前执行。对应 Vue 3 的 setup 函数。 |
created | setup() | 实例创建完成后执行。对应 Vue 3 的 setup 函数。 |
beforeMount | onBeforeMount() | 挂载开始之前执行。 |
mounted | onMounted() | 挂载完成后执行。 |
beforeUpdate | onBeforeUpdate() | 数据更新时执行,但在虚拟 DOM 重新渲染和打补丁之前。 |
updated | onUpdated() | 数据更新导致的虚拟 DOM 重新渲染和打补丁完成后执行。 |
beforeDestroy | onBeforeUnmount() | 卸载组件实例之前执行。 |
destroyed | onUnmounted() | 卸载组件实例完成后执行。 |
activated | onActivated() | 组件激活时执行(用于保持活跃状态的组件)。 |
deactivated | onDeactivated() | 组件停用时执行(用于保持活跃状态的组件)。 |
errorCaptured | onErrorCaptured() | 捕获子组件错误时执行。 |
Vue 3 生命周期就像是一个虚拟角色从“诞生”到“成长”再到“老去”的过程,每个阶段都有特定的钩子函数,你可以在这个阶段给这个角色设置一些行为或进行一些操作。这些钩子函数让开发者能够在组件的每个关键阶段插入自己的逻辑。
组件的“诞生”
1. 初始化阶段(setup())
- 在 Vue 3 中,`setup()` 函数就像是角色的“出生”阶段。在这里,你可以初始化角色的基本属性,比如角色的名字、年龄等(对应组件的数据和方法)。
- 这个阶段主要处理:创建响应式数据(用 `ref` 和 `reactive`)、定义方法(函数)、注册生命周期钩子等。
组件的“成长”
2. 挂载前阶段(onBeforeMount())
- 当角色准备好进入游戏世界,但还没正式出现的时候,就是挂载前阶段。这时你可以进行一些准备工作,比如检查角色的装备是否齐全(检查数据和 DOM 的准备情况)。
3. 挂载完成阶段(onMounted())
- 角色正式进入游戏世界后,这个阶段就代表角色已经完全出现在场景中。你可以在这个阶段进行一些需要访问 DOM 的操作,比如让角色向玩家问好或者播放一段动画。
组件的“变化”
4. 更新前阶段(onBeforeUpdate())
- 当角色的属性发生变化(比如升级了、装备更新了),在角色发生变化但还没正式展示给玩家时,就是更新前阶段。你可以在这里做一些更新前的准备,比如记录旧状态或者检查更新的内容。
5. 更新完成阶段(onUpdated())
- 角色的新状态已经展示给玩家后,这个阶段允许你进行一些更新后的操作。比如,你可以检查更新后的角色状态是否符合预期,或者根据更新后的状态触发一些新的行为。
组件的“老去”
6. 卸载前阶段(onBeforeUnmount())
- 当角色即将离开游戏世界时,这个阶段允许你做一些清理工作。比如,你可以保存角色的当前状态(数据),或者移除一些在角色存在期间设置的监听器。
7. 卸载完成阶段(onUnmounted())
- 角色已经完全离开游戏世界后,这个阶段表示角色的所有资源都被释放。你可以在这里进行一些最后的记录或者通知,比如告诉系统角色已经离开了。
其他阶段
- 激活阶段(onActivated())
- 当角色从一个不活跃状态(比如休眠)被激活时会触发这个阶段。比如,当你切换到一个之前访问过的页面(组件),角色重新变得活跃。
- 停用阶段(onDeactivated())
- 当角色从活跃状态变成不活跃状态时会触发这个阶段。比如,当你切换到另一个页面(组件),原来的页面中的角色进入休眠状态。
- 错误捕获阶段(onErrorCaptured())
- 如果角色在游戏中遇到错误(比如技能施放失败),这个阶段允许你捕获并处理这些错误,比如显示错误提示或者记录错误日志。
通过这些生命周期钩子,你可以精确地控制组件在不同阶段的行为,就像控制游戏中的角色一样。
PS.生命周期函数是可以执行多次的,多次执行时,传入的回调会在时机成熟时依次运行。所以,以后在我们的团队合作项目当中,如果害怕把别人写的omMounted代码搞乱,我们可以自己单独再写一个,把自己的逻辑功能写在里面,就不怕搞乱别人的代码了。
小结:

4.4 组件通信
- 父传子 defineProps
我们可以在子组件中使用defineProps,定义父组件可以传给子组件的变量,在当中声明变量的名字和变量的数据类型
<script setup lang="ts">
const props = defineProps({
message:String, // 格式 变量名:数据类型
count:Number
})
</script>
在父组件中,我们就可以通过导入子组件,并根据子组件中定义好的props,进行信息的传递。
(如果发现子组件无法导出,检查一下你的子组件中的script中是不是没加setup语法糖)
在下面的代码中,父组件(右边)将子组件的count变量,使用v-bind绑定到父组件的变量num中,所以只要父组件中的num变化,子组件中的count也会随之变化,而message则是在父组件中直接赋值的方式,传递给我们的子组件。

什么是单向数据流,什么是双向数据流?
Vue中的单向数据流和双向数据绑定是两个重要但不同的概念。
单向数据流是一种设计理念,指父组件向子组件传递数据时,数据只能从父组件流向子组件,子组件不能直接修改父组件传来的数据。这通过props实现,所有props都遵循单向绑定原则。
双向数据绑定则是一种使数据和视图保持同步的机制,当数据变化时视图自动更新,用户在视图上的操作也能自动更新数据。在Vue中主要通过v-model指令实现,它本质上是v-bind和@input事件的语法糖。
两者并不矛盾,而是在不同层面上解决不同问题的机制。单向数据流保证了数据流动的可预测性,而双向绑定提供了便捷的表单处理方式。
- 子传父 defineEmits
在子组件中,通过 emit = defineEmits(['事件名']) 来自定义事件
接着在子组件中通过传递信息的组件,触发事件emit,向父组件发送消息。
父组件中的子组件,通过 @事件名="函数名" ,进一步处理如何使用子组件传来的信息

- 模板引用ref
通过ref属性,我们就可以获取dom对象或者组件实例对象
<script setup lang="ts">
import ChildComponent from "@/components/ChildComponent.vue";
import {onMounted, ref} from "vue";
const href = ref(null)
const cref = ref(null)
onMounted(()=>{
console.log(href.value)
console.log(cref.value)
})
</script>
<template>
<h1 ref="href">我是父组件</h1>
<child-component ref="cref"></child-component>
</template>

但是这样,你会发现我们拿不到子组件当中定义好的变量和函数方法。那是因为子组件当中的变量和函数方法是封闭的,出于安全考虑,一般外部无法直接调用。此时我们需要使用defineExpose函数,将变量和函数方法暴露给外部。

暴露出去之后,你就会发现,此时我们可以访问到子组件内部的变量和函数方法了,那要使用他们,我只需要使用 .名字 的方法就可以了

<script setup lang="ts">
import ChildComponent from "@/components/ChildComponent.vue";
import {onMounted, ref} from "vue";
const href = ref(null)
const cref = ref(null)
onMounted(()=>{
console.log(href.value)
console.log(cref.value.num)
})
</script>
<template>
<h1 ref="href">我是父组件</h1>
<child-component ref="cref"></child-component>
<button @click="cref.plus">{{cref.num}}</button> // 使用子组件的plus函数
</template>
- provide 和 inject
你可能会遇到这样一种情况,一个组件中引用了一个子组件,而这个子组件又引用了另一个它的子组件,那么从父组件传递消息到最底层的子组件,一层一层传太过于麻烦。欸,这个时候我们就可以使用provide和inject方法。

- provide:可以在顶层定义提供的数据或者方法,通过键值对的形式,进行传递
- infect:接收provide提供的数据和方法,通过获取键的方式,进行匹配

5.路由
首先我们先了解一些基本概念:
- 路由 :路由是一种 URL 与页面或组件之间的映射关系。当用户访问不同的 URL 时,路由会根据映射关系加载相应的页面或组件。
- 单页面应用程序(SPA) :只有一个主页面,其他页面都是通过 JavaScript 动态加载和渲染的。Vue 路由可以帮助我们实现这种无刷新的页面切换。
一般路由的记录都在route文件下的index.ts文件中,我自己个人习惯,我喜欢新建一个routers.ts文件,专门记录路由信息。 里面的结构如下,可以看到routes变量是一个路由列表,列表当中的每个元素都包括三个属性。
- path:浏览器访问的路径,简单理解就是项目网址中除去ip的部分
- name:这个路由的名字
- component:页面(组件),一般对应某个vue文件
通过上面的路由定义,我们就可以清楚地知道,在浏览器中,我们访问哪个路径,可以看到我们所写的哪个页面,形成1对1的关系。

而在index.ts文件的最下面,你或许会看到这样的代码:

这里其实就是在定义路由的模式,vue router一共支持以下三种路由模式:
| 模式 | 实现原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| hash(默认) | 使用 URL 的 hash 值(# 后面的部分),如 http://example.com/#/home | 兼容性强,支持所有浏览器(包括 IE);无需服务器配置;hash 变化不会触发页面重新加载。 | URL 中包含 #,不够美观;不利于 SEO(搜索引擎优化),因为搜索引擎通常会忽略 hash 部分。 | 中小型单页应用(SPA);对 URL 美观性要求不高的场景。 |
| history | 使用 HTML5 History API(pushState、replaceState),如 http://example.com/home | URL 更美观,没有 #;对 SEO 友好;支持高级路由特性(如 history.state)。 | 需要服务器配置支持,否则刷新页面会返回 404;低版本浏览器(如 IE9 及以下)不支持。 | 企业官网、电商网站等对 SEO 要求较高的应用;需要美观 URL 的场景。 |
| abstract | 不依赖浏览器环境,路由状态保存在内存中。 | 适用于非浏览器环境(如 Node.js、Weex、小程序等);测试环境中使用方便。 | 只能在非浏览器环境中使用,无法直接用于 Web 应用。 | 服务端渲染(SSR)应用;原生应用(如 Weex);单元测试。 |
当使用 history 模式时,由于刷新页面会直接请求服务器,需确保服务器能够正确处理所有路由请求,通常需要配置所有路径都返回同一个 HTML 文件(如 index.html)。
三种模式的配置方式如下:
// hash 模式(默认)
const router = createRouter({
history: createWebHashHistory(),
routes: [...]
});
// history 模式
const router = createRouter({
history: createWebHistory(), // 等价于 createWebHistory(process.env.BASE_URL)
routes: [...]
});
// abstract 模式(用于非浏览器环境)
const router = createRouter({
history: createMemoryHistory(),
routes: [...]
});
5.1 动态路由
动态路由,实际上就是一种带参数的路由,比如CSDN当中的博客,你会发现大部分的博客,它的页面样式基本都一样,只有内容不一样,那它是写了好几个不同页面进行实现的吗?
显然不是,这样子太费效率和存储了,而且每次新增的博客,你就要多写一个页面文件。那这里我们就可以使用动态路由来实现,通过访问不同的博客id,显示不同的内容,但页面都是同一个页面。示例图如下:

在路由定义中使用冒号 : 标记动态参数:
// router/index.js
const routes = [
{
path: '/user/:id',
name: 'User',
component: UserComponent
}
];
- 匹配路径:/user/123 → params: { id: '123' }
- 多参数支持:path: '/user/:id/post/:postId' → params: { id: '123', postId: '456' }
而在我们的UserComponent页面当中,我们要获取路由中的id参数,从而发送给我们的后端,可以在UserComponent页面中使用以下方法实现:
在选项式API中(多为VUE2),通过 $route.params 获取参数:
export default {
mounted() {
console.log(this.$route.params.id); // 获取路由中的动态参数id
}
}
而在组合式API中,使用useRouter钩子:
import { useRoute } from 'vue-router';
export default {
setup() {
const route = useRoute();
console.log(route.params.id); // 获取动态参数中的id
}
}
此外动态路由中的参数,也是支持正则约束的,举几个例子:
//限制参数格式
path: '/user/:id(\\d+)' // 仅匹配数字 ID
// ?表示可选参数
path: '/user/:id?' // 匹配 /user 或 /user/123
// *为通配符,匹配任意字符
path: '/user/*' // 匹配 /user/a/b/c,参数为 { pathMatch: ['a', 'b', 'c'] }
扩展:跳转404页面的页面设置
{
path: '/:pathMatch(.*)*',
component: NotFoundComponent
}
使用pathMatch,配置通配符路由捕获未匹配路径
5.2 嵌套路由
嵌套路由指的是在一个路由组件内部再渲染其他路由组件。比如,一个应用的布局是侧边栏搭配主内容区,主内容区会依据用户的操作显示不同的子页面,这时就可以采用嵌套路由。就比如下面的网页,点击头部菜单栏,头部菜单栏不变,但是下面显示的网页界面变了。

路由编写,通过children属性,来确定子路由。
const routes = [
{
path: '/user',
name: 'User',
component: UserLayout,
children: [
{
path: 'profile',
name: 'UserProfile',
component: UserProfile
},
{
path: 'settings',
name: 'UserSettings',
component: UserSettings
}
]
}
];
在上面的示例中,子路由UserProfile和UserSettings的path采用的是相对路径,实际上浏览器访问它们两个的路径是 /user/profile 和 /user/settings ,也就是在它们前面加上父路由的path。
而在父路由对应的vue组件文件中,页面切换的部分,使用 <router-view /> 标签进行子路由界面的显示
<!-- UserLayout.vue -->
<template>
<div class="user-layout">
<Sidebar />
<div class="main-content">
<router-view />
</div>
</div>
</template>
在需要进行权限控制的场景中,嵌套路由也能发挥作用。可以把需要权限验证的路由作为一个父路由的子路由 。
5.3 懒加载
路由懒加载(也称为延迟加载)是一种优化技术,允许你在需要时才加载路由组件,而不是在应用初始化时就加载所有组件。这显著提高了应用的初始加载速度,尤其是对于大型应用而言。
为什么需要路由懒加载?
传统的路由配置会在应用启动时加载所有组件,即使这些组件可能永远不会被用户访问。这会导致以下问题:
- 初始加载时间长:应用包体积过大,导致首屏加载缓慢。
- 资源浪费:用户可能永远不会访问某些页面,但这些页面的代码仍然被加载。
路由懒加载通过按需加载组件解决了这些问题,只有当用户访问特定路由时,对应的组件才会被加载。
现在一般使用 ES6 的动态导入语法 (import()) 来实现路由懒加载 ,如下:
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/home',
name: 'Home',
// 懒加载Home组件
component: () => import('../views/Home.vue')
},
{
path: '/about',
name: 'About',
// 懒加载About组件
component: () => import('../views/About.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
在上述例子中,Home.vue和About.vue组件不会在应用初始化时加载,而是在用户首次访问/home或/about路径时才会被加载。
路由懒加载的使用场景
- 大型应用:当应用包含多个页面或功能模块时,使用懒加载可以显著减少初始加载时间。
- 访问频率较低的页面:如帮助中心、隐私政策等页面,用户可能很少访问,适合懒加载。
- 权限控制页面:某些需要特定权限才能访问的页面,如管理后台,只有授权用户才能访问,可以懒加载。
- 第三方库集成:如果某个路由组件依赖大型第三方库(如 Chart.js、地图库等),可以将该组件懒加载,避免增加主包体积。
5.4 导航守卫
导航守卫主要用于在路由切换过程中执行特定逻辑,比如身份验证、权限检查、数据预加载等。Vue Router 提供了多种类型的导航守卫,它们会在不同的路由生命周期阶段被触发。

全局前置守卫(beforeEach):
在路由导航开始前触发,可以用于身份验证、权限检查等。
router.beforeEach((to, from, next) => {
// to: 即将进入的目标路由
// from: 当前导航正要离开的路由
// next: 必须调用的函数,用于 resolve 这个钩子
if (to.meta.requiresAuth && !isAuthenticated()) {
next('/login') // 未登录时重定向到登录页
} else {
next() // 继续导航
}
})
全局解析守卫(beforeResolve):
在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后触发。
router.beforeResolve((to, from, next) => {
// 常用于处理路由组件的异步加载
next()
})
全局后置钩子(afterEach):
在导航完成后触发,不接受next函数,不能改变导航本身。
router.afterEach((to, from) => {
// 常用于记录页面访问日志、滚动位置恢复等
logPageView(to.fullPath)
})
路由独享守卫:定义在单个路由配置中的守卫,只会在特定路由被访问时触发。
const routes = [
{
path: '/admin',
component: AdminPanel,
beforeEnter: (to, from, next) => {
// 只有管理员用户才能访问
if (!isAdmin()) {
next('/forbidden')
} else {
next()
}
}
}
]
组件内守卫:直接在路由组件内部定义的守卫,用于控制组件自身的导航行为。
<script>
export default {
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被验证前调用
// 不能访问组件实例 `this`,因为守卫在导航确认前被调用
// 可以通过传一个回调给 next 来访问组件实例
next(vm => {
// 通过 `vm` 访问组件实例
})
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /users/:id,在 /users/1 和 /users/2 之间跳转的时候
// 由于会渲染同样的 User 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 在导航离开渲染该组件的对应路由时调用
// 可以访问组件实例 `this`
// 常用于阻止用户在未保存修改时离开
if (this.hasUnsavedChanges) {
next(false) // 取消导航
} else {
next()
}
}
}
</script>
下面就是导航守卫的大体触发流程

5.5 编程式导航和滚动行为
编程式导航是通过调用 Vue Router 提供的 API 实现路由跳转,而非依赖 <router-link> 标签的声明式导航。适用于需要动态决定跳转逻辑的场景(如按钮点击、表单提交、条件判断等)。
---------------------------------------------总结一下,其实就是下面一幅图--------------------------------------

举个跳转页面的例子:
// 组合式 API(setup 或 Composition API)
import { useRouter } from 'vue-router';
const router = useRouter();
router.push('/home'); // 字符串路径
router.push({ path: '/user', query: { name: 'John' } }); // 带查询参数
router.push({ name: 'UserDetail', params: { userId: 1 } }); // 命名路由+参数
滚动行为(Scroll Behavior):控制路由切换时的页面滚动位置,提升用户体验(如返回列表页时恢复滚动位置,进入详情页时滚动到顶部)
在实际开发中,你或许会遇到一个问题:
我们在一个很长的列表页往下拉,然后点击列表中的某一个数据进入到详情页查看。此时我们决定返回列表也继续查看列表。
很多情况下,由于列表页的组件已经被销毁,所以我们返回到列表页后页面会置顶,不得不又重新下拉查看列表,这样就做了很多没有必要的操作,也是不符合用户的预期。用户希望当我查看玩详情页以后返回,返回列表页的位置是刚刚浏览的位置。
这个问题就可以使用我们的滚动行为解决。
1)在路由配置文件(index.ts)中添加 scrollBehavior
const router = createRouter({
history: createWebHistory(),
routes: [...],
scrollBehavior(to, from, savedPosition) {
// 从详情页返回列表页时,恢复滚动位置
if (from.name && to.name === 'ListPage' && savedPosition) {
return savedPosition; // 直接使用浏览器保存的位置(需配合组件内保存)
}
// 或手动记录位置(适用于组件被销毁的情况)
if (to.meta.keepScroll) { // 给列表页路由添加 meta 标记
return { top: window.scrollY }; // 返回指定位置
}
// 其他情况默认置顶
return { top: 0 };
}
});
2)在列表页组件中,离开时保存滚动位置(组件销毁前):
// Vue 组件
export default {
data() {
return { scrollPosition: 0 };
},
beforeRouteLeave(to, from, next) {
this.scrollPosition = window.scrollY; // 保存当前滚动高度
next();
},
mounted() {
// 从详情页返回时,恢复位置(若 scrollBehavior 未生效)
if (this.scrollPosition) {
window.scrollTo(0, this.scrollPosition);
}
}
};
| 功能 | 编程式导航 | 滚动行为 |
|---|---|---|
| 作用 | 动态控制路由跳转(替代 <router-link>) | 控制路由切换时的滚动位置,优化用户体验 |
| 核心 API | push/replace/go(组合式:useRouter()) | 全局配置 scrollBehavior,返回滚动位置对象 |
| 应用场景 | 条件跳转、携带参数、历史记录控制 | 恢复滚动位置、滚动到顶部 / 锚点、平滑滚动 |
| 关键参数 | 路由对象(path/name/params/query) | to、from、savedPosition(浏览器回退时) |
--------------------------------------完结撒花,快上手试试吧--------------------------------------------
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐




所有评论(0)