一、Vue router4.x
①创建路由
Vue2使用的是router3.x的API,换成Vue 3 需要用router4.x的API
3.x
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export const constantRoutes = [...]
const createNewRouter = () => new Router({
base: process.env.VUE_APP_BASE_URL, // 根路径
routes: constantRoutes,
mode: 'history'
})
const router = createNewRouter()
export default router
4.x
import { createRouter, createWebHistory } from 'vue-router'
export const constantRoutes = [...]
const createNewRouter = () => createRouter({
// 控制滚动 滚动行为 https://next.router.vuejs.org/zh/guide/advanced/scroll-behavior.html
// https://next.router.vuejs.org/zh/api/#scrollbehavior
scrollBehavior: () => ({ y: 0 }),
history: createWebHistory(process.env.VUE_APP_BASE_URL), // history为必填项
routes: routes
})
const router = createNewRouter()
export default router
如官方文档:
① Vue Router 不再是一个类,而是一组函数。现在你不用再写 new Router(),而是要调用 createRouter,
② history 配置取代 mode
- “history”: createWebHistory()
- “hash”: createWebHashHistory()
- “abstract”: createMemoryHistory()
③ base根路径上下文从base配置改为作为 history (createWebHistory等)的第一个参数传递
②动态添加路由 router.addRoute/router.addRoutes(已废弃)
3.x
先看一下源码:
- 已废弃的
router.addRoutes
:
addRoutes(routes: RouteConfig[]): void
参数必须是一个符合 routes 选项要求的数组。
router.addRoute
:
addRoute(route: RouteConfig): () => void
添加一条新路由规则。如果已经存与之相同的名字,则覆盖。
addRoute(parentName: string, route: RouteConfig): () => void
添加一条新的路由规则作为现有路由的子路由。如果已经存与之相同的名字,则覆盖。
4.x
addRoute(route: RouteRecordRaw): () => void
参数 | 类型 | 描述 |
---|---|---|
route | RouteRecordRaw | 要添加的路由记录 |
添加一条新路由规则。如果已经存与之相同的名字,则覆盖。
————————————————————————————
addRoute(parentName: RouteRecordName, route: RouteRecordRaw): () => void;
参数 | 类型 | 描述 |
---|---|---|
parentName | string | symbol | 父路由记录,route 应该被添加到的位置 |
route | RouteRecordRaw | 要添加的路由记录 |
添加一条新的路由规则作为现有路由的子路由。如果已经存与之相同的名字,则覆盖。
以上可以简单看出,3.x有添加路由数组的方法,4.x只保留了添加单个
4.x添加路由参考↓
// generate accessible routes map based on roles
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
// dynamically add accessible routes
const maplist = new Map()
accessRoutes.forEach(item => {
if (item.parentId === 0) { // 把父级放到最上面 方便子路由往里添
maplist.set(item.id, { name: item.name, path: item.path })
router.addRoute({
path: item.path,
redirect: item.redirect,
component: //,
name: item.name,
meta: { title: item.title, icon: item.icon, id: item.id }
})
} else {
router.addRoute(maplist.get(item.parentId).name + '', {
path: `${item.path}`,
component: () => import(`@/${item.component.slice(2)}`),
name: item.name,
meta: {
title: item.title,
hidden: item.hidden != 0, id: item.id
}
})
}
})
// 一定要把404最后加上去 不然回刷新白屏
router.addRoute({
path: '*',
redirect: '/404',
meta: { hidden: true }
})
③路由的使用
3.x
略
4.x
import { useRouter } from 'vue-router'
setup() {
const router = useRouter()
router.push({path:...})
router.reslove({path:...,....})
}
二、Vuex4.x
vuex3.x到vue4.x破坏性变化不大,加了新特性
①安装store
3.x
简单写一下是下面这种
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
// ........
// 声明modules
// ........
Vue.use(Vuex)
const store = new Vuex.Store({
modules,
getters
})
export default store
4.x
4.0简单写是下面这样
import { createStore, createLogger } from 'vuex'
import getters from './getters'
export default createStore({
modules,
getters,
plugins: process.env.NODE_ENV !== 'production'
? [createLogger()]
: []
})
对比可以看出,store安装的方式变了,4.0需要使用新引入的 createStore 方法来创建 store 实例。
其他新特性比如:使用 useStore 组合式函数来检索 store。↓
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
threads: computed(() => store.getters.threads),
currentThread: computed(() => store.getters.currentThread),
unreadCount: computed(() => store.getters.unreadCount),
switchThread: (id) => store.dispatch('switchThread', id)
}
}
}
注意如果是store分了很多模块
store.dispatch('模块名/action')
store.commit('模块名/mutation')
三、Vue2和3区别
一个人性化的优化
在vue2的<template>
标签内放多个根标签经常会看到这种提示:
The template root requires exactly one element
但是Vue3允许多个根标签Multiple Root,不会再提示这个
①实例化 createApp
Vue2.x
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
/* element-ui*/
import Element from 'element-ui'
import './styles/element-variables.scss'
Vue.use(Element, {
size: Cookies.get('size') || 'medium' // 设置 element-ui 默认大小:medium
})
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
Vue3.x
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import store from './store/index'
/* element-plus*/
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import locale from 'element-plus/lib/locale/lang/zh-cn'
const app = createApp(App)
app.use(ElementPlus, { locale })
app.use(router)
app.use(store)
app.mount('#app')
②ref(Reactive Reference)响应式引用
Vue2.x
<template>
<div id="app">
<div>Name: {{ name }}</div>
</div>
</template>
<script>
export default {
name: "App",
data(){
return {
name: "Name"
}
}
};
</script>
Vue3.x
<template>
<div>Name: {{ name }}</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
const name = ref('Name');
return { name };
}
};
</script>
使用响应式引用,包装原始数据,我们可以跟踪数据变化
读到上面这里你可能注意到了setup()
组合式API,因为Vue3发现当使用data
、computed
、methods
、watch
来组织逻辑虽然很有效,但是逻辑关注点变长,所以新增了setup()
组合式API,想单独了解可以详细看👉什么是组合式API
组合式API优点
- 对TypeScript的支持
- 防止组件过大,优化代码组织逻辑
- 可复用代码
当然Vue3这里是可以兼容Vue2的,可以将新函数替换为组件、属性、绑定数据、计算属性、方法以及生命周期函数。所以Vue3还是可以使用以下这样的开发方式的
<template>
<!-- 模板代码 -->
</template>
<script>
export default {
name: "App",
props: {
/*...*/
}
components: {
/*...*/
},
data(){
return {
/*...*/
}
},
computed:{
/*...*/
},
watch:{
/*...*/
},
methods: {
/*...*/
},
created(){
/*...*/
},
mounted(){
/*...*/
}
};
</script>
③ 方法 Methods(Vue3setup()
内使用)
Vue2.x
<template>
<div>
<div>Amount: {{ amount }}</div>
<button @click="increaseAmount()">数字增加1</button>
</div>
</template>
<script>
export default {
data(){
return {
amount: 3,
}
},
methods: {
increaseAmount() {
this.amount+=1
}
}
};
</script>
Vue3.x
<template>
<div>
<div>Amount: {{ amount }}</div>
<button @click="increaseAmount()">数字增加1</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
const amount = ref(3);
function increaseAmount() {
amount.value+=1
}
return { amount, increaseAmount };
}
};
</script>
上面响应式引用说过Vue3新的组合式API,这里就演示Vue3在
setup()
函数内创建方法Methods.
④计算 Computed(Vue3setup()
内使用)
Vue2.x
<template>
<div id="app">
<p>Upper: {{ upperName }} out of {{ name }}</p>
</div>
</template>
<script>
export default {
data(){
return {
name: "author",
}
},
computed: {
upperName(){
return this.name.toUpperCase() + "VUE_2";
}
}
};
</script>
Vue3.x
<template>
<div id="app">
<p>Upper: {{ upperName }} out of {{ name }}</p>
</div>
</template>
<script>
import { ref, computed } from "vue";
export default {
setup() {
const name = ref("author");
const upperName = computed(() => {
return name.value.toUpperCase() + "VUE_3";
});
return { name, upperName };
}
};
</script>
这是演示如何在Vue3
setup()
内使用computed
⑤ Watch (Vue3setup()
内使用)
Vue2.x
<template>
<div>
<input type="text" v-model="name" />
</div>
</template>
<script>
export default {
data(){
return {
name: null,
}
},
watch: {
name(newVal, oldVal){
console.log(`${newVal} ${oldVal}`);
}
}
};
</script>
Vue3.x watchEffect
<template>
<div>
<input type="text" v-model="name" />
</div>
</template>
<script>
import { ref, watchEffect } from "vue";
export default {
setup() {
const name = ref('');
watchEffect(() => {
console.log(name.value);
})
return { name };
}
};
</script>
watch
用来监控绑定数据并对其变化做出反应。
在Vue3,新增了watchEffect
,每当其依赖改变时它就会执行。
👉点此详细了解watchEffect
但是你也可以不用watchEffect
,像Vue2一样使用watch
函数👇
Vue3.x watch
<template>
<div>
<input type="text" v-model="name" />
</div>
</template>
<script>
import { ref, watch } from "vue";
export default {
setup() {
const name = ref('');
watch(name, (newVal, oldVal) => {
console.log(`${newVal} ${oldVal}`);
})
return { name };
}
};
</script>
另外还可以在
setup
函数访问props
setup(props) {
watch(() => {
console.log(props.name);
});
}
⑥过滤器Vue filter 废弃
先看在Vue2中过滤器的使用
Vue2.x
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
{{ new Date() | formatUnix}}
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
import moment from "moment";
export default {
name: "App",
components: {
HelloWorld,
},
filters: {
formatUnix(value) {
if (value) {
return moment(value).format("DD/MM/YYYY");
}
},
},
};
</script>
官方大大可能是发现,像
{{ new Date() | formatUnix}}
这种过滤器的管道符|
太让人费解了,
而且对像以上代码中对日期处理的过滤器方法,主要目的还是重用,过滤器跟methods性能又没有差别,所以决定在Vue3弃用
Vue3中将不存在过滤器,但是开发者可以创建一个函数在每个组件重用,这样写👇
Vue3.x
import moment from "moment";
const format = function formatUnix(value) {
if (value) {
return moment(value).format("DD/MM/YYYY");
}
};
export default format;
<template>
<div id="app">
<div>{{formatUnix(new Date())}}</div>
</div>
</template>
<script>
import formatUnix from "./Utils/DateFormat.js";
export default {
name: "App",
components: {
},
setup() {
return {
formatUnix
};
},
};
</script>
⑦v-model
Vue2.x
简单写法👇
<input v-model="property" />
相同效果的写法👇
<input
:value="property"
@input="property = $event.target.value"
/>
Vue3.x
举例一个表单组件
<template>
<form>
<div>
<label for="name">Name</label>
<input type="text" :value="name" @input="updateName($event.target.value)" />
</div>
<div>
<label for="email">Email</label>
<input type="email" :value="email" @input="updateEmail($event.target.value)" />
</div>
</form>
</template>
<script>
export default {
props: {
name: String,
email: String,
},
setup(props, { emit }) {
const updateName = (value) => {
emit("update:name", value);
};
const updateEmail = (value) => {
emit("update:email", value);
};
return { updateName, updateEmail };
},
};
</script>
利用多个v-model绑定
<template>
<div id="app">
<InviteForm v-model:name="inviteName" v-model:email="inviteEmail" />
<div>
<div>{{ inviteName }}</div>
<div>{{ inviteEmail }}</div>
</div>
</div>
</template>
<script>
import InviteForm from "./components/InviteForm.vue";
import { ref } from "vue";
export default {
name: "App",
components: {
InviteForm,
},
setup() {
const inviteName = ref("name");
const inviteEmail = ref("invite");
return {
inviteName,
inviteEmail,
};
},
};
</script>
在Vue3,组件可以处理类似以上代码中
<InviteForm v-model:name=”inviteName” v-model:email=”inviteEmail” />
的多个绑定值。
所以在Vue3中处理父子组件传值更加简单。
⑧模块化 Modular
Vue3的
setup()
中,可以把函数分离出来,变成组合函数
import { ref } from "vue";
export default function download() {
const filePath = ref(null);
function downloadBlob(){
// any code here
}
function downloadJSON(){
// any code here
}
return { filePath, downloadBlob, downloadJSON}
}
在其他位置复用分离出来的组合函数
<template>
<!-- template code -->
</template>
<script>
import download from "@/use/download";
import upload from "@/use/upload";
export default {
setup(){
return { ...download(), ...upload() }
}
}
</script>
⑨传送 Teleport
https://v3.cn.vuejs.org/guide/teleport.html
顾名思义传送,使用teleport
组件需要定义带有dom查询选择器的to
属性,例如to
属性指向元素的id#anyId
,或者元素的类.anyClass
,可以将内容渲染到指定目的元素的子级。例如:
<teleport to=”#out-side-app”>
<teleport to=”.anyClass”>
⑩Suspense (Vue3新增试验型功能)
https://v3.cn.vuejs.org/guide/migration/suspense.html
Suspense主要用于使页面根据接口返回状态正确展示。
<template #default>
展示默认正常接口返回的内容,<template #fallback>
在加载中展示
<template>
<Suspense>
<template #default>
<Locale />
</template>
<template #fallback>Loading...</template>
</Suspense>
</template>
<script>
import Locale from "@/components/Locale.vue";
export default {
components: {
Locale,
},
};
</script>
⑪ 生命周期钩子
Vue2
- beforeCreate()
- created()
- beforeMount()
- mounted()
- beforeUpdate()
- updated()
- beforeDestroy()
Vue3
Vue3生命周期钩子有更名,且增加了新的声明周期钩子
beforeDestroy()
更名成beforeUnmount()
,setup()
内调用onBeforeUnmount()
destroyed()
更名成unmounted()
,setup()
内调用onUnmounted()
beforeMoun()t
,setup()
内调用onBeforeMount()
mounted()
,setup()
内调用onMounted()
beforeUpdate()
,setup()
内调用onBeforeUpdate()
updated()
,setup()
内调用onUpdated()
errorCaptured()
,setup()
内调用onErrorCaptured()
activated()
,setup()
内调用onActivated()
deactivated()
,setup()
内调用onDeactivated()
- 新增
onRenderTracked
,当渲染函数首次访问响应式依赖时调用,也就是说当组件更新就会调用,调试用。setup()
内调用onRenderTracked()
- 新增
onRenderTriggered()
,当新的渲染触发时,追踪组件重新渲染发生变化的值,调试用。setup()
内调用onRenderTriggered()
import { onBeforeMount, onMounted } from "vue";
export default {
setup(){
onBeforeMount(() => {
console.log("Before mount");
});
onMounted(() => {
console.log("Mounted");
});
}
}
另外,
beforeCreate()
和create()
在Vue3是没有必要使用的,因为应用会在调用setup()
之前调用beforeCreate()
,并且紧随其后回调用created()
,直接使用setup()
即可