1.Vue3简介
Vue 3 是 Vue.js 框架的一次重大升级,于 2020 年 9 月 18 日正式发布,代号为"One Piece(海贼王)"。作为新一代的前端构建框架,它在性能、源码架构、开发体验以及生 态支持上进行了全面的革新,旨在为开发者提供更高效、更灵活的开发体验。
核心特性与优势
1. 性能的大幅提升
Vue 3 在底层进行了大量优化,使得应用运行更加流畅。其打包体积减少了 41%,初次渲染速度提升了 55%,更新渲染速度提升了 133%,同时内存占用减少了 54%。这得益于其重写的虚拟 DOM 实现和编译时优化(如静态标记和按需更新),使得 Diff 算法更加高效。
2. 响应式系统的升级
Vue 2 使用 Object.defineProperty 实现响应式,存在无法监听数组索引变化和对象新增属性等局限。Vue 3 则采用了 ES6 的 Proxy 来替代,能够直接拦截对象和数组的变化,不仅解决了上述问题,还实现了"懒拦截",使得初始化性能更高。
3. 引入组合式 API (Composition API)
这是 Vue 3 最重要的新特性之一。相比于 Vue 2 将逻辑分散在 data、methods、computed 等选项中的"选项式 API",组合式 API 允许开发者按照功能模块来组织代码。通过 setup 配置项以及 ref、reactive、watch 等函数,相关的逻辑可以被聚合在一起,极大地提升了代码的可读性和复用性,尤其适合大型复杂项目。
4. 更好的 TypeScript 支持
Vue 3 的源码完全使用 TypeScript 重写,使其能够原生、更好地支持 TypeScript。组合式 API 的设计也天然适配 TS,类型推导更加精准,为大型项目的类型安全和开发体验提供了有力保障。
5. 全新的内置组件
Vue 3 引入了几个强大的内置组件:
-
Fragment(片段): 允许组件模板拥有多个根节点,不再强制要求单根节点,减少了不必要的 DOM 嵌套。
-
Teleport(传送门): 可以将组件的 DOM 结构渲染到 DOM 树中的任意位置,非常适合实现模态框、全局提示等场景。
-
Suspense(异步组件): 能够优雅地处理异步组件的加载状态,在组件加载完成前展示占位内容,提升用户体验。
6. Tree-Shaking 支持
Vue 3 的 API 设计使得构建工具可以更好地进行 Tree-Shaking(摇树优化),将未使用的代码在打包时剔除,从而进一步减小最终产物的体积。
2.创建Vue3工程
2.1 基于vite创建
vite 是新一代前端构建工具,官网地址:https://vitejs.cn,vite的优势如下:
- 轻量快速的热重载(HMR),能实现极速的服务启动
- 对TypeScripts、JSX、CSS等支持开箱即用
- 真正的按需编译,不再等待整个应用编译完成
- webpack构建与vite构建对比图如下:


创建项目如图

- vite 项目中,index.html是项目的入口文件,在项目的最外层。
- 加载index.html后,Vite解析<script type="module" src="xxx"></script>指向的JavaScript。
- Vue3中是通过createApp函数创建一个应用实例。
2.2 一个简单的效果
html
<template>
<div class="app">
<h1>你好呀</h1>
</div>
</template>
<script lang="ts">
export default {
name:'APP'//组件名
}
</script>
<style scoped>
.app{
background-color: #ddd;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
</style>

sre目录创建一个Person.vue文件
html
<template>
<div class="person">
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="showTel">查看联系方式</button>
</div>
</template>
<script lang="ts">
export default {
name:'Person',
data(){
return {
name:'张三',
age:18,
tel:1388888888888
}
},
methods:{
showTel(){
alert(this.tel);
}
}
}
</script>
<style scoped>
.person{
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
</style>
App.vue中注册person.vue

效果如图

3.Vue3核心语法
3.1 OptionsApi与CompositionApi
- vue2的API设计是Options(配置) 风格的。
- Vue3的API设计是Composition(组合) 风格的。
Options API 的弊端
Options类型的API,数据、方法、计算属性等,是分散在:data、methods、computed中的,若想新增或者修改一个需求,就需要分别修改data、methods、computed,不便于维护和复用。
Composition Api的优势
可以使用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。
3.2 setup
setup概述
setup是Vue3中一个新的配置项,值是一个函数,它是CompositionApi表演的舞台,组件中所用到的:数组、方法、计算属性、监视.....等等,均配置在setup中。
特点如下:
- setup函数返回的对象中的内容,可直接在模板中使用。
- setup中访问this是undefined。
- setup函数会在beforeCreate之前调用,它是领先所有钩子函数执行的
setup 测试
html
<template>
<div class="person">
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="showTel">查看联系方式</button>
</div>
</template>
<script lang="ts">
import func from "vue-editor-bridge";
export default {
name: "Person",
setup() {
// console.log(this) //setup函数中的this是undefined,Vue3中已经弱化this了
// 数据,下面定义数据不是响应式的
let name = "张三";
let age = 18;
let tel = "13888888888";
// 方法
function changeName() {
// 这样修改name,页面是没有变化的
name = "zhang-san";//name确实改了,但name不是响应式的
}
function changeAge() {
age += 1;
}
function showTel() {
alert(tel);
}
//将数据、方法交出去,模板中才可以使用
return { name, age, changeName, changeAge, showTel };
},
};
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
setup语法糖
script 标签中加入setup,组件命名script标签中+ name="Person123" ,name="Person123"生效需要终端先运行npm i vite-plugin-vue-setup-extend -D,然后vite.config.ts中配置如下图。
组件名默认和文件名一致

html
<template>
<div class="person">
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="showTel">查看联系方式</button>
</div>
</template>
<script lang="ts" setup name="Person123">
// console.log(this) //setup函数中的this是undefined,Vue3中已经弱化this了
// 数据,下面定义数据不是响应式的
let name = "张三";
let age = 18;
let tel = "13888888888";
// 方法
function changeName() {
// 这样修改name,页面是没有变化的
name = "zhang-san";//name确实改了,但name不是响应式的
}
function changeAge() {
age += 1;
}
function showTel() {
alert(tel);
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
3.3 ref创建-基本类型的响应式数据
script标签中导入
html
import { ref } from "vue";
将变量用ref包裹,一个RefImpl的实例对象,简称ref对象或ref,ref对象的value属性是响应式的。
html
let name = ref("张三");
let age = ref(18);
script中要用xxx.value改变值,template中默认有.value,可以省略。
完整代码如下
html
<template>
<div class="person">
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="showTel">查看联系方式</button>
</div>
</template>
<script lang="ts" setup name="Person123">
// console.log(this) //setup函数中的this是undefined,Vue3中已经弱化this了
import { ref } from "vue";
let name = ref("张三");
let age = ref(18);
let tel = "13888888888";
console.log(name);
console.log(tel);
// 方法
function changeName() {
// 这样修改name,页面是没有变化的
name.value = "zhang-san"; //name确实改了,但name不是响应式的
}
function changeAge() {
age.value += 1;
}
function showTel() {
alert(tel);
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
3.4 reactive 创建响应式对象
scripts标签中应用
html
import { reactive } from "vue";
定义响应式对象;
html
let car = reactive({ brand: "奔驰", price: 8000 });
测试代码:
html
<template>
<div class="person">
<h2>汽车信息:一辆{{ car.brand }}车,价值{{ car.price }}万</h2>
<button @click="changePrice">修改汽车的价格</button>
</div>
<br>
<h2>游戏列表:</h2>
<ul>
<li v-for="item in games" :key="item.id">{{item.name}}</li>
</ul>
<button @click="changeFirstGame">修改第一个游戏的名字</button>
</template>
<script lang="ts" setup name="Person123">
import { ref } from "vue";
import { reactive } from "vue";
// 数据
let car = reactive({ brand: "奔驰", price: 8000 });
console.log(car);
function changePrice() {
car.price += 10;
}
let games =reactive([
{id:'aaaaa1',name:'王者荣耀'},
{id:'aaaaa2',name:'原神'},
{id:'aaaaa3',name:'三国志'}
])
function changeFirstGame(){
games[0].name='三国杀';
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
li{
font-size: 20px;
}
</style>
reactive 只能定义对象类型的响应式数据
3.5 ref 创建响应式对象数据类型
scripts标签中应用
html
import { ref } from "vue";
定义数据
html
/ 数据
let car = ref({ brand: "奔驰", price: 8000 });
let games =ref([
{id:'aaaaa1',name:'王者荣耀'},
{id:'aaaaa2',name:'原神'},
{id:'aaaaa3',name:'三国志'}
])
测试代码
html
<template>
<div class="person">
<h2>汽车信息:一辆{{ car.brand }}车,价值{{ car.price }}万</h2>
<button @click="changePrice">修改汽车的价格</button>
</div>
<br>
<h2>游戏列表:</h2>
<ul>
<li v-for="item in games" :key="item.id">{{item.name}}</li>
</ul>
<button @click="changeFirstGame">修改第一个游戏的名字</button>
</template>
<script lang="ts" setup name="Person123">
import { ref } from "vue";
// 数据
let car = ref({ brand: "奔驰", price: 8000 });
let games =ref([
{id:'aaaaa1',name:'王者荣耀'},
{id:'aaaaa2',name:'原神'},
{id:'aaaaa3',name:'三国志'}
])
function changePrice() {
car.value.price += 10;
}
function changeFirstGame(){
games.value[0].name='三国杀';
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
li{
font-size: 20px;
}
</style>
3.6 ref 对比reactive
宏观角度看
- 1.ref用来定义:基本类型数据、对象类型数据。
- 2.reactive用来定义:对象类型的数据。
区别:
- 1.ref 创建的变量必须用.value(可以使用volar插件自动添加.value)。
- 2.reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。
Object.assign使用:
html
function changeCar(){
Object.assign(car,{brand:"奥拓",price:1})
}
使用原则:
- 1.若需要一个基本的响应式数据类型,必须有ref。
- 2.如需要一个响应式对象,层级不深,ref、reactive都可以
- 3.若需要一个响应式对象,且层级较深,推荐使用reactive。
3.7 toRefs和toRef
html
import { reactive, toRefs, toRef } from "vue";
html
let { name, age } = toRefs(person);
let nl = toRef(person,'age');
toRefs 全部转换,toRef 单个属性转换
测试
html
<template>
<div class="person">
<h2>{{ person.name }}</h2>
<h2>{{ person.age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { reactive, toRefs, toRef } from "vue";
let person = reactive({
name: "张三",
age: 18,
});
let { name, age } = toRefs(person);
let nl = toRef(person,'age');
console.log("test"+nl.value);
function changeName() {
name.value += "~";
}
function changeAge() {
age.value += 1;
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
li {
font-size: 20px;
}
</style>
3.8 computed计算属性
- computed具有依赖缓存机制。只有当它依赖的数据发生变化时才会重新计算;否则直接返回缓存结果,性能优于普通方法。
- 读写特性 :默认只读,但可以通过配置
get和set实现双向读写。
使用例子;
html
import { ref, computed } from "vue";
html
<template>
<div class="person">
姓:<input type="text" v-model="firstName" /><br />
名:<input type="text" v-model="lastNmae" /><br />
全名:<span>{{ fullName }}</span
><br />
<button @click="changeFullName">将全名改为li-si</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { ref, computed } from "vue";
let firstName = ref("zhang");
let lastNmae = ref("san");
// 这么定义的fullName是一个计算属性,且是只读的
// let fullName = computed(() => {
// return (
// firstName.value.slice(0, 1).toUpperCase() +
// firstName.value.slice(1) +
// "-" +
// lastNmae.value
// );
// });
// 这么定义的fullName是一个可读可写的
let fullName = computed({
get() {
return (
firstName.value.slice(0, 1).toUpperCase() +
firstName.value.slice(1) +
"-" +
lastNmae.value
);
},
set(val) {
const [str1, str2] = val.split("-");
firstName.value = str1;
lastNmae.value = str2;
}
});
console.log(fullName);
function changeFullName() {
fullName.value = "li-si";
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
li {
font-size: 20px;
}
</style>