相干库版本要求

  • 脚手架 Vue CLI v4.5Vite
  • Vue Router v4.0
  • Vuex v4.0
  • Element Plus 地址:https://element-plus.gitee.io...
  • Ant Design v2.0 地址:https://2x.antdv.com/
  • 可能须要手动装置的插件

    • @vue/compiler-sfc ^3.0.7
    • ...

构建

npm i -g @vue/clivue create <Project Name> // 抉择装置配置后实现// 或者采纳可视化构建形式vue ui

迁徙

将所有库更新到反对 Vue3 的版本后,执行 npm i 装置依赖,接下须要手动更改各项 API 调用以及插件的装置形式。

全局 API 的更换

入口文件 main.js :

// Vue2.x,vue实例形式为应用 Vue 构造函数实例化import Vue from 'vue'import App from './App.vue'import router from '@/router'new Vue({  router,  render: h => h(App)}).$mount('#app')
// Vue3.0,调用 createApp 办法创立实例import { createApp } from "vue"; // 从vue模块引入的不再是一个构造函数,而是一个对象import App from './App.vue'import router from "@/router";const app = createApp(App);app  .use(router)  .mount("#app");

路由 @/router/index

// 联合 Vue2.x 版本的 router 创立形式是基于构造函数 Router 的实例化,并import Vue from 'vue'import Router from 'vue-router'Vue.use(Router) // 须要调用 构造函数Vue的静态方法use装置一次,将router提供的办法和属性挂载到 Vue 原型上const router = new Router({ ...})
// Vue3.0,同样相似的形式,vue-router 模块裸露进去的为一个对象而不再是一个构造函数,须要调用外面的 createRouter 办法创立 routerimport { createRouter } from "vue-router";// 无需再调用Vue.use 装置插件const router = createRouter({routes:[...]})

Vuex @/store/index.js

// Vue2.x 装置形式import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const store =  new Vuex.Store({...})
// Vue3.0 装置形式import { createStore } from "vuex";const store = createStore({...});

Vue.prototype => app.config.globalProperties

Vue2.x 最罕用的全局变量设置是通过在构造函数 Vue 的原型上挂上罕用的属性和办法

let someProps = 'some things'Vue.prototype.$someProps = someProps// 组件中拜访console.log(this.$someProps); // 'some things'// 通过原型拜访import Vue from 'vue'console.log(Vue.prototype.$someProps); // 'some things'

比照 Vue3.0 应用 app.config.globalProperties

main.js

import { createApp } from 'vue'const app = createApp()let someProps = 'some things'app.config.globalPropertis.$someProps =someProps...export defaul app; // 须要将 app 实例裸露进来
// 通过调用 API 获取以后实例拜访全局 propertyimport { getCurrentInstance } from "vue";const currentInstance = getCurrentInstance(); //获取以后实例console.log(currentInstance.appContext.config.globalProperties)// 组件中通过 this 拜访 全局 property// SomeComp.vueconsole.log(this.$someProps) // 'some things'// 通过引入实例拜访import app from '@/main.js'console.log(app.config.globalProperties.$someProps); // 'some things'

留神: 通过引入实例拜访是须要与我的项目的 app 根实例建立联系,而并非从新创立 app 实例,因而在 main.js 入口文件处要将 app 对外裸露。

Provide / Inject

跨层级组件传值个别不用用全局属性时,能够思考到 provide / inject

// Provide.vueimport { provide } from "vue";setup() {  let str = "provide str";  provide("str", str);}
// Inject.vueimport { inject } from "vue";setup() {   let injectStr = inject("str");   console.log(injectStr); // 'provide str' }

组件注册

// Vue2.x 注册形式import Vue from 'vue'import someComp from './someComp.vue'Vue.component('SomeComp',someComp)
//比照 Vue3.x 注册import { createApp } from 'vue'import someComp from './someComp.vue'const app = createApp()app.component('SomeComp',someComp)

自定义插件装置

// somePlugin.jsexport default { install(Vue,config){  ... Vue.prototype.$pluginHandler = ()=>{...} }}// main.jsimport Vue from 'vue'import somePlugin from './somePlugin.js'Vue.use(somePlugin)

比照 Vue3.0

// somePlugin.jsexport default { install(app){  //通过参数 app 传递实例的援用  ...  app.config.globalProperties.$pluginHandler = ()=>{...} }}// main.jsimport { createApp } from 'vueimport somePlugin from './somePlugin.js'const app = createApp()app.use(somePlugin)

小结

全局 API 组织形式的重构(由从原型上共享改为以模块裸露)的益处:

  1. 联合 Treeshaking 和便于打包工具确定依赖关系,将无用的内容去除,按需输出,失去体积更小的生产包。
  2. 在须要不同的独立的 Vue 实例场景下,以在原型上操作的形式,仍然会使不同实例之间内容产生分割,因而取代的新形式将实现这种隔离。

新个性

组合式 API setup

Vue2.x 组件构造可能会相似如下:

// src/components/SomeComp.vueexport default {  data:()=>({   res1:[],   res2:[],   res3:[]  }) ,  methods: {   handler1(){    // ...    // 解决失去数据并赋值给 res1   },   handler2(){    // ...    // 解决失去数据并赋值给 res2   },   handler3(){    // ...    // 解决失去数据并赋值给 res3   }  },}

官网给图:

Vue3.0 应用 setup 将相干逻辑组织在一块,便于解读、保护。

export default { setup(props,context){  // ----------业务逻辑1------------  const res1 = ref([])  const handler1 = ()=>{   // ...   // 解决失去数据并赋值给 res1  }  // ----------业务逻辑2------------  const res2 = ref([])  const handler2 = ()=>{   // ...   // 解决失去数据并赋值给 res2  }  // ----------业务逻辑3------------  const res3 = ref([])  const handler3 = ()=>{   // ...   // 解决失去数据并赋值给 res3  }  // -------将属性裸露给组件---------  return {   res1,   handler1,   res2,   handler2,   res3,   handler3  } }}

setup 中注册 生命周期钩子

可将须要在某个生命周期执行的办法,在对应的钩子注册办法通过回调传入,届时会在对应的生命周期所触发。

import { onBeforeMount, onMounted } from 'vue'export default {  setup(){    onBeforeMount(()=>{     conosle.log('onBeforeMount')    })    onMounted(()=>{     conosle.log('onMounted1')    })    onMounted(()=>{     conosle.log('onMounted2')    })  }}

watchcomputed 新的用法

import { watch , computed } from 'vue'export default {  props: {    someProp: {      default: () => 0,      type: Number,    },  },  setup(props) {    const { someProp } = toRefs(props);    watch(someProp, (newValue) => {      console.log(newValue);    });    let computedVal = computed(() => someProp.value * 10);    return {      computedVal,    };  }};

响应性 API

  • reactive 以援用类型的数据创立一个响应式对象,即以组件 data() 返回的对象创立响应式的原理。
  • ref 以根底类型的数据创立响应式对象,并会将原始值包裹在一个对象的 value 属性下。(测试发现其实也能够依据援用类型创立,但响应性不高)
  • toRefs 将响应式对象转换为一般对象(不便解构获取值),并且后果中的每个 property 放弃对响应式对象的指向。若不必该办法转换,取到的值将不具备响应性,也不为 ref
import { reactive, ref } from "vue";export default {  setup() {    // let num = 0; // 视图不更新    let num = ref(0); // 视图更新    setInterval(() =>  {num.value++ }, 500);    // let obj = { num: 0 }; // 视图不更新    let obj = reactive({ num: 0 }); // 视图更新    setInterval(() => {      obj.num++;      console.log(obj); // 数据更新    }, 500);    // let { time } = foo; // 视图不更新    let { time } = toRefs(foo);    setInterval(() => {      time.value = Date.now();      console.log(time.value === foo.time); // true    }, 1000);    return {      num,      obj    };  }};

异步更改响应式对象进行视图更新时,须要留神:将 已裸露的属性 指向 新的响应式对象 是有效的操作,要响应则须要间接对 在 setup 阶段裸露的响应式对象 进行操作。

import { reactive, ref } from "vue";export default{  setup(){    let arr = reactive([])    setInterval(()=>{      // arr = [1,2,3] // 不更新      // arr = reactive([1,2,3]) // 不更新      arr.push(1,2,3) // 更新    },1000)    // 或者外面包一层,并在该对象在属性节点上进行从新指向的形式    let obj = reactive({      arr:[]    })    setInterval(()=>{      obj.arr = [1,2,3] // 更新    },1000)    // 但在第二种形式下,以构造的形式拜访属性尽管没有问题,但勿在解构赋予的变量上做雷同的错误操作。例如:    let { arr } = obj    setInterval(()=>{      arr = reactive([1,2,3]) // 模板上的{{ arr }}是obj.arr,此时指向了新的响应式对象,因而不会更新    },1000)    export {      arr,      obj    }  }}