vue2 to vue3 你可能用到的

368 阅读3分钟

前言

鉴于项目需要兼容IE11,项目组内讨论想使用vue3和typescript。vue2对TypeScript的支持不是很好,所以折中使用@vue/composition-api+vue2+TypeScript的方式进行开发。这样不仅能使用compostion api而且对TypeScript有更好的支持。 这样不仅可以学习vue3的compostion api的语法,对于以后新项目中直接使用vue3做准备,而且能兼容IE11。 下面来介绍vue2 和 vue3(或者说是compostion api) 我们需要知道:(tips: 如果直接使用vue3 示例代码中import { defineComponent, ref, reactive } from '@vue/composition-api’ 改成 import { defineComponent, ref, reactive } from 'vue' 即可)

TypeScript支持

Vue3以及Vue conf上所说的在Q3发布的Vue2.7都是使用的TypeScript来进行类型检查,所有composition-api也是支持TypeScript的

import { defineComponent } from '@vue/composition-api'
// 只需要把代码写在defineComponent里面,ts的静态检查就会工作
export default defineComponent({
  // type inference enabled
  setup(props,ctx){
   ...
   ....
  }
})

浏览器支持

与Vue3一样,composition-api支持所有现代浏览器以及IE11+,如果需要支持低版本的IE你应该安装babel-polyfill

  • npm install babel-polyfill or yarn add babel-polyfill
  • 入口文件中加入import "babel-polyfill"
  • webpack 中配置 entry:['babel-polyfill','./main.js']//假设这里的main.js是代码入口主文件
  • 配置babel-loader
module: {
  rules: [
      ...
      {
        test: /.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      }
      ...
 ]
}

在Vue2项目中使用@vue/composition-api

npm install @vue/composition-api
# or
yarn add @vue/composition-api
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'

Vue.use(VueCompositionAPI)

然后在.vue组件里面引入,就可以使用部分vue3的功能

import { ref, reactive } from '@vue/composition-api'

673d8a48ec9d4256b2c86b2918e60bbe_tplv-k3u1fbpfcp-zoom-1.png

reactive、ref 与 toRefs

在 vue2.x 中, 定义数据都是在data中, 但是 Vue3.x 可以使用reactive和ref来进行数据定义。 那么ref和reactive他们有什么区别呢?分别什么时候使用呢?说到这里,我又不得不提一下,看到很多网上不少文章说 (reactive用于处理对象的双向绑定,ref则处理 js 基础类型的双向绑定)。我其实不太赞同这样的说法,这样很容易初学者认为ref就能处理 js 基本类型, 比如ref也是可以定义对象的双向绑定的啊, 上段代码:

 setup() {
    const obj = ref({count:1, name:"张三"})
    setTimeout(() =>{
        obj.value.count = obj.value.count + 1
        obj.value.name = "李四"
    }, 1000)
    return{
        obj
    }
  }


我们将obj.count和obj.name绑定到页面上也是可以的;但是reactive函数确实可以代理一个对象, 但是不能代理基本类型,例如字符串、数字、boolean 等。 接下来使用代码展示一下ref、reactive的使用:

<template>
  <div class="home">
    {{ testRef }} {{userReactive.name}} {{userReactive.age}}
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>


<script lang="ts">
import { defineComponent, ref, reactive } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src

export default defineComponent({
  name: 'Home',
  components: {
    HelloWorld
  },
  setup (props) {
    console.log(props)
    const testRef = ref(0)
    const userReactive = reactive({
      name: 'lucy',
      age: 17
    })

    return {
      testRef,
      userReactive
    }
  }
})
</script>

我们绑定到页面是通过userReactive.name,userReactive.age;这样写感觉很繁琐,我们能不能直接将userReactive中的属性解构出来使用呢? 答案是不能直接对userReactive进行结构, 这样会消除它的响应式。那我们就想使用解构后的数据怎么办,解决办法就是使用toRefs。 toRefs 用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象。具体使用方式如下

<template>
  <div class="home">
    <p>{{ testRef }} </p>
    <p> {{userReactive.name}}</p>
    <p> {{userReactive.age}}</p>
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>


<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src

export default defineComponent({
  name: 'Home',
  components: {
    HelloWorld
  },
  setup (props) {
    console.log(props)
    const testRef = ref(0)
    const userReactive = reactive({
      name: 'lucy',
      age: 17
    })

    return {
      testRef,
      ...toRefs(userReactive)
    }
  }
})
</script>

通常建议对某一种类型的操作放在同一个reactive中,如:

<template>
  <div class="home">
    <button @click="changeUserName('lilei')"></button>
    <button @click="changeUserAge(33)"></button>
    <button @click="changePorjectId(2)"></button>
    <button @click="changePorjectName('porjectName2')"></button>
    <p>{{ testRef }} </p>
    <p> {{userInfo.name}}</p>
    <p> {{userInfo.age}}</p>
    <p> {{porjectInfo.id}}</p>
    <p> {{porjectInfo.name}}</p>
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>


<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src

export default defineComponent({
  name: 'Home',
  components: {
    HelloWorld
  },
  setup (props) {
    console.log(props)
    const testRef = ref(0)
    const userReactive = reactive({
      userInfo: {
        name: 'lucy',
        age: 17
      },
      changeUserName (name: string) {
        userReactive.userInfo.name = name
      },
      changeUserAge (age: number) {
        userReactive.userInfo.age = age
      }
    })

    const porjectReactive = reactive({
      porjectInfo: {
        id: 1,
        name: ' porjectName'
      },
      changePorjectId (id: number) {
        porjectReactive.porjectInfo.id = id
      },
      changePorjectName (name: string) {
        porjectReactive.porjectInfo.name = name
      }
    })

    return {
      testRef,
      ...toRefs(userReactive),
      ...toRefs(porjectReactive)
    }
  }
})
</script>

为什么要这么写呢? 这样能方便我们提取逻辑,如下:

<template>
  <div class="home">
    <button @click="changeUserName('lilei')"></button>
    <button @click="changeUserAge(33)"></button>
    <button @click="changePorjectId(2)"></button>
    <button @click="changePorjectName('porjectName2')"></button>
    <p>{{ testRef }} </p>
    <p> {{userInfo.name}}</p>
    <p> {{userInfo.age}}</p>
    <p> {{porjectInfo.id}}</p>
    <p> {{porjectInfo.name}}</p>
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>


<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src

function userOperation () {
  const userReactive = reactive({
    userInfo: {
      name: 'lucy',
      age: 17
    },
    changeUserName (name: string) {
      userReactive.userInfo.name = name
    },
    changeUserAge (age: number) {
      userReactive.userInfo.age = age
    }
  })
  return userReactive
}

function projectOperation () {
  const porjectReactive = reactive({
    porjectInfo: {
      id: 1,
      name: ' porjectName'
    },
    changePorjectId (id: number) {
      porjectReactive.porjectInfo.id = id
    },
    changePorjectName (name: string) {
      porjectReactive.porjectInfo.name = name
    }
  })
  return porjectReactive
}

export default defineComponent({
  name: 'Home',
  components: {
    HelloWorld
  },
  setup (props) {
    console.log(props)
    const testRef = ref(0)
    const userReactive = userOperation()

    const projectReactive = projectOperation()

    return {
      testRef,
      ...toRefs(userReactive),
      ...toRefs(projectReactive)
    }
  }
})
</script>

是不是setup函数中很干净呢?

我们甚至可以把userOperation projectOperation单独定义一个文件来处理逻辑: userOperation.ts中定义

import { reactive } from '@vue/composition-api'
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export default function userOperation () {
  const userReactive = reactive({
    userInfo: {
      name: 'lucy',
      age: 17
    },
    changeUserName (name: string) {
      userReactive.userInfo.name = name
    },
    changeUserAge (age: number) {
      userReactive.userInfo.age = age
    }
  })
  return userReactive
}


文件中引用

<template>
  <div class="home">
    <button @click="changeUserName('lilei')"></button>
    <button @click="changeUserAge(33)"></button>
    <button @click="changePorjectId(2)"></button>
    <button @click="changePorjectName('porjectName2')"></button>
    <p>{{ testRef }} </p>
    <p> {{userInfo.name}}</p>
    <p> {{userInfo.age}}</p>
    <p> {{porjectInfo.id}}</p>
    <p> {{porjectInfo.name}}</p>
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>


<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from '@vue/composition-api'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
import userOperation from './userOperation'

// function userOperation () {
//   const userReactive = reactive({
//     userInfo: {
//       name: 'lucy',
//       age: 17
//     },
//     changeUserName (name: string) {
//       userReactive.userInfo.name = name
//     },
//     changeUserAge (age: number) {
//       userReactive.userInfo.age = age
//     }
//   })
//   return userReactive
// }

function projectOperation () {
  const porjectReactive = reactive({
    porjectInfo: {
      id: 1,
      name: ' porjectName'
    },
    changePorjectId (id: number) {
      porjectReactive.porjectInfo.id = id
    },
    changePorjectName (name: string) {
      porjectReactive.porjectInfo.name = name
    }
  })
  return porjectReactive
}

export default defineComponent({
  name: 'Home',
  components: {
    HelloWorld
  },
  setup (props) {
    console.log(props)
    const testRef = ref(0)
    const userReactive = userOperation()

    const projectReactive = projectOperation()

    return {
      testRef,
      ...toRefs(userReactive),
      ...toRefs(projectReactive)
    }
  }
})
</script>

这可能就是函数式编程