# 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 构造函数创建的每个根实例共享相同的全局配置.