vue3学习之hook

由于使用vue3.2已经有段时间,对于setup-api的应用也有了基础的认识,最近一直疑惑的一个问题就是作者为什么从2x 升级到3x 直接改变了vue项目文件的形态。要知道这样做会增加vue的学习成本,那么就必然是老版本有痛点,新版本有优势。在3x版本被重点提及的hook又是什么?如何使用?我们一起来探讨。

一、Hook是什么?

首先我们来看看官方对hook的定义:

当构建前端应用时,我们常常需要复用公共任务的逻辑。例如为了在不同地方格式化时间,我们可能会抽取一个可复用的日期格式化函数。这个函数封装了无状态的逻辑:它在接收一些输入后立刻返回所期望的输出。

很清晰明了的说明了hook是做什么的,但是又觉得有什么不对的地方,这不是和2x版本的mixin的功能有些相似么?那接下来我们写一个功能对比下看看。

二、编写自己的hook
使用2x实现联动选择城市功能
<!-- html -->
<template>
    <div style="padding:100px 40%">
        联动选择省市
        <el-select v-model="province" placeholder="选择省份">
            <el-option v-for="(item,index) in provinceList" :key="index" :value="item" :label="item">
            </el-option>
        </el-select>
        <el-select v-model="city" placeholder="选择市">
            <el-option v-for="(item, index) in cityList" :key="index" :value="item" :label="item">
            </el-option>
        </el-select>
    </div>
</template>
// js 部分 
export default {
    data () {
        return {
            province: '',
            city: '',
            provinceList: [],
            cityList: []
        }
    },
    mounted () {
        this.getProvinceList()
    },
    watch: {
        province () {
            this.getCityList()
        }
    },
    methods: {
        getProvinceList () {
            this.provinceList = ['河南', '安徽', '西安', '杭州']
        },
        getCityList () {
            this.city = ''
            let data = {
                '河南': ['郑州', '洛阳', '周口', '开封', '商丘'],
                '安徽': ['合肥', '阜阳', '六安'],
                '西安': ['西安', '宝鸡', '延安'],
            }
            this.cityList = data[this.province]
        }
    }
}
抽离封装mixin
export default {
    data () {
        return {
            province: '',
            city: '',
            provinceList: [],
            cityList: []
        }
    },
    mounted () {
        this.getProvinceList()
    },
    watch: {
        province () {
            this.getCityList()
        }
    },
    methods: {
        getProvinceList () {
            this.provinceList = ['河南', '安徽', '西安', '杭州']
        },
        getCityList () {
            this.city = ''
            let data = {
                '河南': ['郑州', '洛阳', '周口', '开封', '商丘'],
                '安徽': ['合肥', '阜阳', '六安'],
                '西安': ['西安', '宝鸡', '延安'],
            }
            this.cityList = data[this.province]
        }
    }
}
import mixin from './mixin'
export default {
    mixins: [mixin],
    data () {
        return {
            // 其他数据
        }
    },
    mounted () {
        // 编写其他逻辑
    }
}
官方hook Demo

使用组合式 API 实现鼠标跟踪功能

<script setup>
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
  // 被组合式函数封装和管理的状态
  const x = ref(0)
  const y = ref(0)

  // 组合式函数可以随时更改其状态。
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // 一个组合式函数也可以挂靠在所属组件的生命周期上
  // 来启动和卸载副作用
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // 通过返回值暴露所管理的状态
  return { x, y }
}
</script>
<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>
编写自己的联动选择城市hook
import {ref, onMounted, watch} from 'vue'
export const usePeovicenAndCity = () => {
    
    // province数据相关
    const province = ref('')
    const provinceList = ref([])
    const getProvinceList = () => {
        provinceList.value = ['河南', '安徽', '西安', '杭州']
    }
    watch(province, () => {
        getCityList()
    })

    // city数据相关
    const city = ref('')
    const cityList = ref([])
    const getCityList = () => {
        city.value = ''
        let data = {
            '河南': ['郑州', '洛阳', '周口', '开封', '商丘'],
            '安徽': ['合肥', '阜阳', '六安'],
            '西安': ['西安', '宝鸡', '延安'],
        }
        cityList.value = data[province.value]
    }

    // mounted 调取省份数据
    onMounted(() => {
        getProvinceList()
    })

    return {province, city, provinceList, cityList}
}
import {ref} from 'vue'
import {usePeovicenAndCity} from './hook'
let {province, city, provinceList, cityList} = usePeovicenAndCity()
三、hook与mixin的对比

从上面的代码实现来看,mixin复用通用代码看起来好像更优雅,简洁。那么我们对比下页面有多个复用的情况。

mixin

import mixin1 from './mixin1'
import mixin2 from './mixin2'
import mixin3 from './mixin3'
import mixin4 from './mixin4'
export default {
    mixins: [mixin1,mixin2,mixin3,mixin4],
    data () {
        return {
            // 其他数据
        }
    },
    watch: {
        key () {
            // do something
        }
    }
}

hook

import {ref} from 'vue'
import {fun1, fun2, fun3, fun4,} from './hook'
let {data1, a1} = fun1()
let {data2, a2} = fun2()
let {data3, a3} = fun3()
let {data4, a4} = fun4()

如果页面上某个数据异常,而这个数据是从mixin里面获取的,那么就需要在多个mixin里面一点点找,如果项目庞大的话,这将是个灾难。
对比3x的hook数据源清晰明了,每个数据源都能找到对应的代码片段。

四、vue3学习和项目迁移(个人理解):
1.3x版本的优势

复用相同的逻辑太舒服啦,一个功能模块的代码都在一起,不用上下来回找。

2.3x版本的问题

1、.value的赋值/访问形式太难受了,希望官方后期可以解决;
2、如果有老项目迁移,需要开发者付出一部分精力去学习,对于项目来讲成本增加了很多

3.阅读性和维护

首先从项目的阅读性和维护难度来讲,2x版本的下限高,上限低。3x版本的上限很高,下限很低理想状态下 从上至下的代码可以简化为 功能A-功能B-功能C… 各个功能之间没有耦合的代码,而2x版本的代码功能是分散在data、watch、methods 各个模块之间的,所以3x版本相较于2x上限会高很多,同样下限也会低与2x, 因为各种api没有了约束,对于不熟悉的开发者会造成功能代码更乱等问题。

4.哪些项目适合使用3x

新项目:综合学习成本和项目复杂度来决定。新的版本必定要有一定的学习成本,这个时间对于项目来讲