相关库版本要求
- 脚手架 Vue CLI
v4.5
或 Vite - 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 组织方式的重构(由从原型上共享改为以模块暴露)的好处:
- 结合 Treeshaking 和便于打包工具确定依赖关系,将无用的内容去除,按需输入,得到体积更小的生产包。
- 在需要不同的独立的 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')
})
}
}
watch
、computed
新的用法
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
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。