Vue3 #
相比vue2的提升 #
- 性能提升
- 打包大小减少41%
- 初次渲染快55%,更新渲染快133%
- 内存减少43%
- 源码升级
- 使用Proxy代替defineProperty实现响应式
- 重写虚拟DOM的实现和Tree-Shaking
- 支持TypeScript
- 可以更好的支持TypeScript
- 新特性
- Composition API
- 新的内置组件
Vite创建Vue3工程 #
Vue3 不推荐使用 vue-cli 来创建(使用vue/cli,要确保版本大于4.5.0),而是使用 Vite。
Vite是新一代的前端构建工具,在尤雨溪开发Vue3.0的时候诞生。类似于Webpack+ Webpack-dev-server。
其主要利用浏览器ESM特性导入组织代码,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。
生产中利用Rollup作为打包工具,号称下一代的前端构建工具。
npm create vite@latest
注意 Node 的版本,如果过低可能会报错。
安装依赖 #
默认创建的工程中,没有 vue-router 等依赖。
vue-router #
npm install vue-router
然后在 src 目录下创建 router/router.js,并声明自己定义的views
import { createRouter, createWebHistory } from "vue-router";
const routes = [
{
path: "/",
component: () => import("@/views/Index.vue"),
},
{
path: "/about",
component: () => import("@/views/About.vue"),
},
];
export const router = createRouter({
history: createWebHistory(),
routes,
});
在 main.js 中引入 router
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import { router } from "./router/index";
createApp(App)
.use(router)
.mount("#app");
sass #
npm i sass sass-loader --save-dev
Vue3的变化 #
main.js #
// 不再是引入Vue构造函数,而是引入createApp工厂函数
import { createApp } from 'vue'
import App from './App.vue'
//创建应用实例对象,类似于Vue实例对象,但是比Vue实例对象更轻
createApp(App).mount('#app')
App.vue #
<!-- vue3中的组件模板可以没有根标签 -->
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
</style>
Composition API #
setup #
vue3中的一个新的组件配置项,值是一个函数,setup是所有Composition API表演的舞台
组件中用到的所有数据、方法、计算属性,全部要配置在setup中
- setup函数的返回值
- 返回对象:对象的属性、方法在模板中可以直接使用
- 返回渲染函数:从vue中引入h,返回h渲染的结果
<template>
<h1>name:{{name}}</h1>
<h1>age:{{age}}</h1>
<button @click="show">show</button>
</template>
<script>
export default {
name: 'App',
setup(){
//数据
let name = "lucy";
let age = 18;
//方法
function show(){
alert("姓名:" + name + ",年龄:" + age);
}
return {name,age,show}
}
}
</script>
注意:尽量不要和vue2配置混用(vue2配置可以读取setup的属性和方法,但是setup不能访问vue2配置的);setup不能使用async修饰
setup函数的执行时机 #
在beforeCreate之前执行一次,this是undefined,也就是说,setup中不要写this
ref函数 #
对于在setup函数中返回的属性,虽然组件可以直接使用,但是不是响应式(页面随着数据进行改变)的,如果需要该属性是响应式的,也就是需要vue监测该属性,那么需要使用vue提供的ref函数
ref函数,接受的数据可以是基本类型也可以是对象类型
对于基本类型:底层依靠的是defineProperty()的get()和set()实现
对于对象类型:底层利用vue3的一个新函数reactive()实现
<template>
<h1>name:{{name}}</h1>
<h1>age:{{age}}</h1>
<button @click="ageAdd10">ageAdd10</button>
</template>
<script>
//引入ref函数
import {ref} from 'vue';
export default {
name: 'App',
setup(){
let name = "lucy";
//需要响应式的属性使用ref函数,生成引用实现对象
let age = ref(18);
//方法
function ageAdd10(){
//修改值的时候需要修改引用实现对象的value属性
age.value += 10;
}
return {name,age,ageAdd10}
}
}
</script>
reactive函数 #
和ref作用、用法都一样,定义响应式数据,通常用来定义对象类型数据,虽然ref也可以定义对象数据类型,但是底层调用的还是reactive
computed函数 #
用于在setup中定义计算属性
<template>
<input type="text" v-model="name">
<h1>{{sayHello}}</h1>
</template>
<script>
import {ref,computed} from 'vue';
export default {
name: 'App',
setup(){
let name = ref("lucy");
let sayHello = computed(() => {
return "Hello " + name.value;
});
return {name,sayHello}
}
}
</script>
watch函数 #
用于在setup中定义监视属性
注意:可以同时监视多个变量,只需要多个变量放到一个数组,传到第一个参数即可
<template>
<input type="text" v-model="name">
</template>
<script>
import {ref,watch} from 'vue';
export default {
name: 'App',
setup(){
let name = ref("lucy");
watch(name,(after,before)=>{
console.log(before,after);
})
watch(name,(after,before)=>{
console.log(before,after);
},{immediate: true}) //如果使用immediate:true,那么回调会在组件创建时调用一次
return {name}
}
}
</script>
watchEffect函数 #
这个函数的参数是一个回调函数(无参),可以智能的监视这个回调函数里面使用的数据,如果数据发生变化,就会调用该回调,并且在组件创建时调用一次
<template>
<input type="text" v-model="name">
</template>
<script>
import {ref,watchEffect} from 'vue';
export default {
name: 'App',
setup(){
let name = ref("lucy");
watchEffect(()=>{
console.log(before,after);
})
return {name}
}
}
</script>
toRef函数 #
创建一个ref对象,其value值指向另一个响应式对象中的某个属性,这个value也是响应式的
setup(){
let student = reactive({
name: 'lucy',
age: 18,
clazz: {
level: 9
}
})
let name = toRef(student,'name');
let level = toRef(student.clazz,'level');
return {name}
}
toRefs函数 #
可以将一个响应式对象中的所有属性(第一层属性)都变成响应式对象,也就是不会将下面level变成响应式
setup(){
let student = reactive({
name: 'lucy',
age: 18,
clazz: {
level: 9
}
})
let stu = toRefs(student);
return {name}
}
toRaw函数 #
可以将一个响应式对象转换为一个普通对象并返回
vue3没有this #
1、由于vue3中setup函数的执行时机要比beforeCreate早,所以在setup中无法拿到this
2、由于vue3不是使用Vue构造进行构建,而是使用App,所以如果要挂载一些属性到Vue原型对象上,方法和vue2不同
例如挂载axios
// 引入axios
import axios from 'axios';
const app = createApp(App);
//挂载全局属性
app.config.globalProperties.$axios = axios;
app.use(router).mount("#app");
在组件中使用
setup(){
const {proxy} = getCurrentInstance();
proxy.$axios.get();
}
vue3声明周期 #
- 和vue2相比,有两个钩子名称发生了变化
- vue2中的beforeDestroy,在vue3中是beforeUnmount
- vue2中的destroyed,在vue3中是unmouted
Hook #
Hook的本质就是一个函数,是对setup函数里面的组合式API进行封装,类似于Vue2中的mixin
- 在vue3的组合式API中,对于声明周期钩子,也提供了对应的函数
| 选项式 API | Hook inside setup |
|---|---|
beforeCreate |
setup |
created |
setup |
beforeMount |
onBeforeMount |
mounted |
onMounted |
beforeUpdate |
onBeforeUpdate |
updated |
onUpdated |
beforeUnmount |
onBeforeUnmount |
unmounted |
onUnmounted |
errorCaptured |
onErrorCaptured |
renderTracked |
onRenderTracked |
renderTriggered |
onRenderTriggered |
activated |
onActivated |
deactivated |
onDeactivated |
import {onMounted} from 'vue'
<script>
export default {
name: 'Demo',
setup() {
// 可以在setup中直接写
onMounted(() => {
})
}
}
</script>
自定义hook #
在src/hooks/中新建一个js文件,将多组件复用的组合式API抽离,并将组件需要的属性返回即可
import {ref} from 'vue';
export default () => {
//此处写组合式API的逻辑
let name = ref('lucy');
return name;
}
在需要该组合API的组件中引入并调用,一般函数名以use开头
import useName from '@/hooks/name.js';
<script>
export default {
name: 'Demo',
setup() {
// 在setup中调用,并接收参数
let name = useName();
return {name}
}
}
</script>
新增组件 #
Fragment #
在vue2中,所有组件必须有跟标签
在vue3中,组件可以没有跟标签,所有元素在Fragment虚拟元素中
Teleport #
可以将指定的html结构移动到指定位置
<template>
<!-- 传送到body标签中 -->
<teleport to="body">
<input/>
</teleport>
</template>
全局API变化 #
| vue2 | vue3 |
|---|---|
| Vue.config.productionTip | 移除 |
| Vue.config.ignoredElements | app.config.isCustomElement() |
| Vue.component | app.component |
| Vue.directive | app.directive |
| Vue.mixin | app.mixin |
| Vue.use | app.use() |
| Vue.filter | 移除 |
| Vue.prototype | app.config.globalProperties |
其他变化 #
1、移除v-on.native
2、由于vue3中setup没有this,所以获取ref的方法有所改变
<template>
<input type="text" ref="inputRef">
</template>
<script>
import { onMounted, ref } from 'vue'
/*
ref获取元素: 利用ref函数获取组件中的标签元素
功能需求: 让输入框自动获取焦点
*/
export default {
setup() {
const inputRef = ref(null)
onMounted(() => {
inputRef.value.focus()
})
return {
inputRef
}
},
}
</script>
Vite 配置 #
vite使用的是vite.config.js,下面是 vue.config.js 中常用的配置项在vite.config.js中的写法。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// 1. 路径别名配置(对应原Webpack的resolve.alias)
const resolve = (dir) => path.resolve(__dirname, dir)
// 2. 代理配置函数(更清晰的Vite风格)
const createProxy = (prefix, target) => ({
[prefix]: {
target,
changeOrigin: true,
configure: (proxy, _options) => {
proxy.on('proxyReq', (proxyReq) => {
if (proxyReq.getHeader('origin')) {
proxyReq.setHeader('origin', target)
}
})
},
rewrite: (path) => path.replace(new RegExp(`^${prefix}`), '')
}
})
export default defineConfig({
plugins: [vue()],
base: "./",
// 3. 路径别名配置
resolve: {
alias: {
'@': resolve('src'),
'@assets': resolve('src/assets'),
'@components': resolve('src/components'),
'@views': resolve('src/views'),
'@api': resolve('src/api'),
'@util': resolve('src/util'),
'@store': resolve('src/store')
}
},
// 4. 开发服务器配置
server: {
open: false, // 保持原配置
host: '0.0.0.0', // 保持原配置
port: 8081, // 保持原配置
proxy: {
...createProxy('/apis', 'http://localhost:8080/'),
...createProxy('/assets', 'http://localhost:19000/')
}
},
// 5. 生产环境配置
build: {
sourcemap: false, // 对应原 productionSourceMap: false
},
// 6. 其他优化配置(可选)
esbuild: {
// 如果需要关闭ESLint(原lintOnSave: false)
// 需要安装 vite-plugin-eslint 插件并配置
}
})
解决路径别名爆红 #
在tsconfig.json文件中,添加
{
"compilerOptions": {
"paths": {
"@/*": ["src/*"],
"@assets/*": ["src/assets/*"],
"@components/*": ["src/components/*"],
"@views/*": ["src/views/*"],
"@api/*": ["src/api/*"],
"@util/*": ["src/util/*"],
"@store/*": ["src/store/*"],
},
}