[重构倒数第29天] - Vue2 Option API 转换 Vue3 Composition API

前言

该系列是为了让看过Vue官方文件或学过Vue但是却不知道怎麽下手去重构现在有的网站而去规画的系列文章,在这边整理了许多我自己使用Vue重构很多网站的经验分享给读者们。

Vue3 的 Composition Api 可以说是改变了原本的 Option Api 撰写上面的方式,还有撰写 Vue 上面的一些思维的变动。

所以今天如果我们接手了一个 Vue2 的专案要升级成 Veu3 Composition Api 的话,我们可以先从甚麽地方下手,会需要注意那些地方,然後要怎麽去改写 Option Api,我们马上来看一下。

1. 先定义 setup 函式

跟以往的 Option Api 不同,我们可以在 setup 函式里面去定义我们所有的东西,包含datamethodscomputedlifecycle 等方法。

<script>
export default {
    setup(){
        return {}
    }
}
</script>

2. 检查有多少 data

我们在改写的时候会先来看一下有多少被定义的资料,这边是我们在 Option Api 里面定义的资料的部分。

<script>
export default {
    data(){
        return {
            userData: {
                name: '',
                age: '',
                address: '',
            },
            isOpen: false,
            products: []
        }
    }
}
</script>

我们接下来来用 Vue3 的方式来改写 ,就会像这样。

<script>
import { ref, reactive } from "vue";
export default {
  setup() {
      const isOpen = ref(false)
      const products = ref([])
      const userData = reactive({
          name: '',
          age: '',
          address: '',
      })
      return {}
  },
};
</script>

在这边你会看到我定义资料是用 refreactive 来定义的,关於我们该如何选用 refreactive 呢 ? 在大多数的情况下两者皆可以互相替换使用,主要取决於个人喜好或是团队习惯,根据具体情况来决定要使用哪个好,关於 refreactive 的差异我们来看一下 :

  • ref : 可以使用任何型态的资料,但是不会对 Object 或是 Array 内部的属性做监听。
  • reactive : 只接受 ObjectArray ,可以做深层的监听,以及取资料的时候不用使用 .value,但是如果对 reactive 的 Object 使用解构的方式取得内容,就会失去了 Vue 更新绑定的机制。

我自己个人一般的情况下能尽量使用 ref就使用ref,可以更精准地去控制资料的更新,很多人不习惯用 .value的方式,但是 .value的方式可以让开发者更明确知道这段 code 再进行资料的更新。

更多具体的细节可以参考我之前写的这篇文章 ref 跟 reactive 我该怎麽选 !?

3. methods 与 this

一般我们要定义 methods 会在 methods 这个栏位里面去定义它的 function,然後要 getset可以透过 this来使用,这个 this就是指向 Vue 本身。

<script>
export default {
    data(){
        return {
            userData: {
                name: '',
                age: '',
                address: '',
            },
            isOpen: false,
            products: []
        }
    },
    methods: {
        handleTroggle(){
            this.isOpen = !this.isOpen
        },
        setProducts(prod){
            this.products = prod
        },
        showUserKey(){
            this.userData.name = 'mike'
            console.log(this.userData.name)
        }
    }
}
</script>

但是改成 Composition Api 的方式就不需要写在 methods 里面,也不需要管 this 指向的部分,整体下来看起来乾净很多。

<script>
import { ref, reactive } from "vue";
export default {
  setup() {
      const isOpen = ref(false)
      const products = ref([])
      const userData = reactive({
          name: '',
          age: '',
          address: '',
      })
      
      const handleTroggle = () => {
          isOpen.value = !isOpen.value
      }
      
      const setProducts = (prod) => {
          products.value = prod
      }
      
      const showUserKey = () => {
          userData.name = 'mike'
          console.log(userData.name)
      }
      
      return {}
  },
};
</script>

4. 换掉 mixins,把共用逻辑抽出来

以前我们会把共用的 function 或是 data 给放到 mixins,但是 mixins 最让人诟病的是对於来源的查找很不明确,只能靠 coding style 来做区别,所以我们要透过 Composition Api 的方式来处理共用的 function 。

· 使用 mixin

先新增一个 myMixin.vue的档案

// myMixin.vue
<script>
    export default {
      data(){
          return {
              point: 0
          }
      },
      methods: {
        addPoint() {
            this.point += 1
        }
      }
    }
</script>

然後再我们的 Component 去载入 myMixin.vue

// app.vue
<script>
import myMixin from "./myMixin.vue";
export default {
    mixins: [myMixin],
    data(){
        ...
    },
    methods: {
        ...
    }
}
</script>
<template>
    <div id="app">
        <h1>Point: {{ point }}</h1>
        <button @click="addPoint">click</button>	
    </div>
</template>

上面就是一般我们使用 mixins 的方式,虽然方便,但是如果多个 Component 的引入跟使用也会造成管理上的不方便,所以接下来我们使用 composition Api 来包装它。

· 使用 Composition Api

先新增一个名叫 useAddPoints.js 的档案

import { ref, readonly  } from "vue"
export function useAddPoints(){
    const point = ref(0)
    const addPoint = () => {
        point.value += 1
    }
    return {
  		point: readonly(point),
        addPoint
    }
}

这边使用了 readonly最主要是希望这个传出去的值是只可以get读取的,所以在这边把它给用 readonly 包起来,关於 readonly 的细节可以参考官方的文件

官方文件 : https://v3.vuejs.org/api/basic-reactivity.html#readonly

然後要使用的话就像是这样,非常的简单明了,完全解决了使用 mixins上面来源不清楚的问题。

<script>
import { useAddPoints } from "../composition/useAddPoints.js"
export default {
  setup() {
      const { point, addPoint } = useAddPoints();
      
      return {
          point,
          addPoint
      }
  },
};
</script>
<template>
    <div id="app">
        <h1>Point: {{ point }}</h1>
        <button @click="addPoint">click</button>	
    </div>
</template>

5. lifecycle (生命周期) 的更新

我们在 Option Api 里面有许多的 lifecycle 可以用,但是到了 composition Api 里面就有了一些变化,所以今天让我们来稍微看一下这些变化,也好在转移程序码的时候避免发生一些问题。

<script>
export default {
    beforeCreate(){},
    created(){},
    beforeMount(){},
    mounted(){},
    updated(){},
    destroyed(){}
}
</script>

但是在 composition Api 里面我们要从 vue 里面把 lifecycle 取出来在setup 使用。

<script>
import { 
    onBeforeMount, 
    onMounted, 
    onBeforeUpdate, 
    onUpdated, 
    onUnmounted 
} from "vue"

export default {
  setup() {
      
      onBeforeMount(()=> {})
      
      onMounted(()=> {})
      
      onBeforeUpdate(()=> {})
      
      onUpdated(()=> {})
      
      onUnmounted(()=> {})
      
      return {}
  },
};
</script>

不过这边要特别注意,原本 Veu2 里面 destroyed 这个生命周期函式再 Vue3 里面 改名叫做 onUnmounted,然後原本的 beforeCreatecreated 没了, 现在的 setup 这个函式就等同於 beforeCreatecreated 这两个效果一样,合再一起了。

更多完整的生命周期函式可以参考官方文件的部份
https://v3.vuejs.org/guide/composition-api-lifecycle-hooks.html#lifecycle-hooks

所以我今天如果是要在 created 里面去打 API,我就可以直接在 setup里面去做操作

<script>
import { ref } from 'vue'
import axios from "axios"
export default {
  setup() {
      const userData = ref([])
      
      // 等同於再 created 执行
      axios.get("https://test.demo.com/api/getUser").then((res)=> {
          userData.value = res.data
      })
		
      return {
          userData
      }
  },
};
</script>

或是今天你要移除原生 JS 监听的时候,就可以改用 onUnmounted

<script>
import { onMounted, onUnmounted } from 'vue'
import axios from "axios"
export default {
  setup() {
      
      const handleWinReSize = (e) => {
          console.log(e)
      }
      
      onMounted(()=> {
          window.addEventListener("resize", ,handleWinReSize)
      })
      
      // 等同於 Vue2 的 destroyed
      onUnmounted(()=> {
          window.removeEventListener("resize", ,handleWinReSize)
      })
      
      return { }
  },
};
</script>

6. Props 跟 Emit 的使用

以前在 Option Api 上面的时候都要透过 this 的方式来取得 props 还有使用 emit,但是在 Composition Api 里面可以统一透过 setup 函式来取得这两个物件。

props

定义好 props 传入的内容,我们就可以从 setup 正确的取得 props ,它会从 setup 的第一个参数传入。

<script>
import { onMounted } from 'vue'
export default {
  props: {
     userType: {
         type: String,
         default: "user"
     }  
  },
  setup(props) {
      
      onMounted(()=> {
          console.log(props.userType)
      })
      
      return { props }
  },
};
</script>
<template>
	<h1>{{ props.userType }}</h1>
</template>

emits

emit 再 setup 的第二个参数的物件里面,我们可以透过解构的方式直接取出,然後使用。

<script>
export default {
  emits: ['userName'],
  setup(props, { emit }) {
      
      const getUserName = (name) => {
          emit("userName", name)
      }
      
      return { getUserName }
  },
};
</script>
<template>
     <button @click="getUserName('mike')">mike</button>
     <button @click="getUserName('jacky')">jacky</button>
     <button @click="getUserName('andy')">andy</button>
     <button @click="getUserName('scars')">scars</button>
     <button @click="getUserName('ash')">ash</button>
</template>

先告一个段落

重构这件事情不是一个小工程,它里面有很多需要注意的细节在里面,包括如何读懂人家的逻辑以及理解这个功能开发上面的前因後果,还有就是你的经验的展现,各种问题、各种情境,除了程序码语法版本上面的差异外,还有很多都是要下功夫的,再後面的章节,我会再一一的跟大家介绍关於重构专案上面其他需要注意的地方,还有我的思考规划的方式,以及选用技术的考量等。

mike vue

那如果对於Vue3不够熟的话呢?

Ps. 购买的时候请登入或注册该平台的会员,然後再使用下面连结进入网站点击「立即购课」,这样才可以让我获得更多的课程分润,还可以帮助我完成更多丰富的内容给各位。

我有开设了一堂专门针对Vue3从零开始教学的课程,如果你觉得不错的话,可以购买我课程来学习
https://hiskio.com/bundles/9WwPNYRpz?s=tc

那如果对於JS基础不熟的朋友,我也有开设JS的入门课程,可以参考这个课程
https://hiskio.com/bundles/b9Rovqy7z?s=tc

订阅Mike的频道享受精彩的教学与分享

Mike 的 Youtube 频道
Mike的medium
MIke 的官方 line 帐号,好友搜寻 @mike_cheng


<<:  Genero:源於4GL的低代码开发平台(Low Code Development Platform)

>>:  Day 02 注册 Azure 帐号与套件安装- 免费体验30天

第2章:实验操作的环境之虚拟机器介绍

前言 本章节,将会介绍本系列文章的实验操作环境以及一些设定与建置的步骤。 实验操作环境介绍 实验操作...

【Day26】反馈元件 - Progress bar

元件介绍 Progress bar 是能够展示当前进度的进度条元件。当一个操作需要显示目前百分比,或...

Day28 - 储存帐密及自动登入

今天来做储存帐密和自动登入的功能。 提醒:今天的内容缺少了加密储存密码,是极度危险的功能,这部份预计...

为你自己学 Laravel - Day 27 专案练习 Part 7 - 文章删除

嘿~~ 各位好,我是菜市场阿龙! Youtube 频道:https://www.youtube.co...

【Day30】参赛感言

这次写了自己刚学一年半左右的前端框架搭配测试, 小菜鸟的技术上还没有到很成熟,还请各位看官多多见谅呀...