# Vue3迁移手册
Vue3 也发布了挺长时间了,Vue2.x也是时候放弃了。
# 打包工具
由于rolllup的出现webpack龙头老大的地位也开始被撼动。
- webpack拆分代码,实现按需加载。
- rollup所有资源放在同一个地方,一次性加载,利用 tree-shake 特性来剔除项目中未使用的代码,减少冗余,但是webpack2已经逐渐支持tree-shake
但rollup的社区暂时还没有像webpack那样完善。 通常来看,rollup更适合打包类库,webpack适合打包应用。 但是webpack的编译速度在项目体量达到一定程度后开始慢的离谱。还有可能会出现内存益出的问题。
尤雨溪坐不住了,vite出现了。
- webpack: 分析依赖=> 编译打包=> 交给本地服务器进行渲染。首先分析各个模块之间的依赖,然后进行打包,在启动webpack-dev-server,请求服务器时,直接显示打包结果。
- vite: 启动服务器=> 请求模块时按需动态编译显示。是先启动开发服务器,请求某个模块时再对该模块进行实时编译,因为现代游览器本身支持ES-Module,所以会自动向依赖的Module发出请求。所以vite就将开发环境下的模块文件作为浏览器的执行文件,而不是像webpack进行打包后交给本地服务器
更重要的是二者底层语言的不同,也决定了打包速度的快慢,vite使用go语言编写,比js编写的webpack快!
但vite也有他的缺点,由于生态的缺陷,在某些特定场景无法满足。还得使用webpack,但作为开发的辅助还是不错的。
# Vue2 - Vue3
想到哪写到哪,但都有用。
# Vue实例的创建
- vue2
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
- vue3
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
# filter
vue3 取消了。。这种管道方法好用程度就不用多说了。但改用还得用
- vue2
<p>时间{{ time | formatterTime('YYYY-MM-DD') }}</p>
<script>
export default {
filters: {
currency(val, str) {
return dayjs(val).format(str)
}
}
}
</script>
- vue3
计算属性写呗。
<p>时间{{ formatterTime(time, 'YYYY-MM-DD') }}</p>
<script>
export default {
computed: {
formatterTime(val, str) {
return dayjs(val).format(str)
}
}
}
</script>
但个人有写全局过滤器的习惯,比如日期,金额等格式化,全局过滤器也被废除。 所有,我们指令另辟蹊径。使用全局属性
const app = createApp(App)
app.config.globalProperties.$filters = {
currencyUSD(value) {
return '$' + value
}
}
<template>
<h1>Bank Account Balance</h1>
<p>{{ $filters.currencyUSD(accountBalance) }}</p>
</template>
# 全局属性
vue2将全局属性暴露出来提供了极大的方便,但什么都往prototype上放也不见得是什么好事。
vue3 将其封装起来,提供了方法供我们使用
app.config.globalProperties
比如
- $xhr
- $message
- ...
# 允许多根节点组件
<template>
<div></div>
<div></div>
<div></div>
</template>
# v-for
key不再是必须属性,vue会自动生成。
不兼容点
在vue2中,使用v-for的同时,使用ref 会用 ref 数组填充相应的 $refs property
- vue2
<template>
<div>
<div v-for="item in 10" :key="item" ref="liRef"></div>
</div>
</template>
<script>
export default {
name: 'demo',
data() {
return {}
},
mounted() {
console.log('refs', this.$refs.liRef)
// vue2时,输出: refs (10) [div, div, div, div, div, div, div, div, div, div]
// vue3时, 输出 refs <div>10<div> (不会创建数组)
}
}
</script>
- vue3
<template>
<div>
<div v-for="item in 10" :key="item" :ref="setItemRef">
{{item}}
</div>
</div>
</template>
<script>
export default {
data() {
return {
itemRefs: []
}
},
methods: {
setItemRef(el) {
if (el) {
this.itemRefs.push(el)
}
}
},
mounted() {
console.log('结合选项式 API:',this.itemRefs)
}
}
</script>
- composition api
<template>
<div v-for="item in 10" :ref="setItemRef">
{{item}}
</div>
</template>
<script>
import { defineComponent,onMounted} from 'vue'
export default defineComponent({
setup() {
let itemRefs = [];
const setItemRef = el => {
if (el) {
itemRefs.push(el)
}
}
onMounted(() => {
console.log('结合组合式 API:',itemRefs);
})
return {
setItemRef
}
}
})
</script>
itemRefs 不必是数组:它也可以是一个对象,其 ref 会通过迭代的 key 被设置。
可被监听
问题是解决了,但问题又来了,谁会这么写? 写的目的是为何?
# 异步组件
变化概览:
- 新的 defineAsyncComponent 助手方法,用于显式地定义异步组件
- component 选项重命名为 loader
- Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise
# Vue2
const AsyncComponent = () => import('@/components/AsyncComponent')
# Vue3
在 Vue 3 中,由于函数式组件被定义为纯函数,因此异步组件的定义需要通过将其包裹在新的 defineAsyncComponent 助手方法中来显式地定义:
import { defineAsyncComponent } from 'vue'
import ErrorComponent from '@/components/ErrorComponent';
import LoadingComponent from '@/components/LoadingComponent';
// 不带选项的异步组件
const AsyncComponent = defineAsyncComponent(() => import('@/components/AsyncComponent'))
个人理解:异步组件虽说提升性能 但是有限。。。除非你的自组件能阻塞页面渲染。那该异步还是得异步。
# $children 移除
<template>
<div>
<img alt="Vue logo" src="./assets/logo.png">
<my-button>Change logo</my-button>
</div>
</template>
<script>
import MyButton from './MyButton'
export default {
components: {
MyButton
},
mounted() {
console.log(this.$children) // [VueComponent]
}
}
</script>
还有$parents 可以使用
在3.x中,vue推荐使用$refs
# 全局Api
Vue 2.x 有许多全局 API 和配置,这些 API 和配置可以全局改变 Vue 的行为。例如,要注册全局组件,可以使用 Vue.component 这样的 API:
Vue.component('button-counter', {
data: () => ({
count: 0
}),
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
类似地,使用全局指令的声明方式如下:
Vue.directive('focus', {
inserted: el => el.focus()
})
虽然这种声明方式很方便,但它也会导致一些问题。从技术上讲,Vue 2 没有“app”的概念,我们定义的应用只是通过 new Vue() 创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例共享相同的全局配置.