乐趣区

关于javascript:Vue30升级以及部分新特性

相干库版本要求

  • 脚手架 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/cli

vue 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 办法创立 router

import {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 获取以后实例拜访全局 property

import {getCurrentInstance} from "vue";

const currentInstance = getCurrentInstance(); // 获取以后实例

console.log(currentInstance.appContext.config.globalProperties)

// 组件中通过 this 拜访 全局 property

// SomeComp.vue

console.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.vue

import {provide} from "vue";

setup() {
  let str = "provide str";
  provide("str", str);
}
// Inject.vue

import {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.js

export default {install(Vue,config){

  ...

 Vue.prototype.$pluginHandler = ()=>{...}
 }
}


// main.js

import Vue from 'vue'
import somePlugin from './somePlugin.js'

Vue.use(somePlugin)

比照 Vue3.0

// somePlugin.js

export default {install(app){
  // 通过参数 app 传递实例的援用

  ...

  app.config.globalProperties.$pluginHandler = ()=>{...}
 }
}


// main.js

import {createApp} from 'vue
import somePlugin from './somePlugin.js'

const app = createApp()

app.use(somePlugin)

小结

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

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

新个性

组合式 API setup

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

// src/components/SomeComp.vue

export 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
    }

  }
}
退出移动版