Vue3迁移笔记(持续更新2022/3/18)

8 篇文章 0 订阅
5 篇文章 0 订阅

一、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
参数类型描述
routeRouteRecordRaw要添加的路由记录

添加一条新路由规则。如果已经存与之相同的名字,则覆盖。

————————————————————————————

addRoute(parentName: RouteRecordName, route: RouteRecordRaw): () => void;
参数类型描述
parentNamestring | symbol父路由记录,route 应该被添加到的位置
routeRouteRecordRaw要添加的路由记录

添加一条新的路由规则作为现有路由的子路由。如果已经存与之相同的名字,则覆盖。

以上可以简单看出,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发现当使用datacomputedmethodswatch来组织逻辑虽然很有效,但是逻辑关注点变长,所以新增了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>

这是演示如何在Vue3setup()内使用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()tsetup()内调用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()即可
在这里插入图片描述


👉推荐!!!【腾讯云】爆款2核4G云服务器首年74元/年
👉推荐!!!【腾讯云】1核2G5M轻量应用服务器50元/年
【腾讯云】云数据库低至9.9/年!MySQL7.4元/月
【阿里云】ECS云服务器特惠
【阿里云】服务器首购优惠
如果文章对您有帮助,扫个红包码呗

红包码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值