简图:

image.png

解释:

1.VUE文件只定义组件,以及模板中所需要的回调方法(例如@click等)。
2.useInit.js,只定义监听器、计算属性、生命周期等,相当于VUE2的watch、compute、mounted(等等)等,但用到的方法和依赖的均来源于useTools.js。
3.useTools.js,只定义依赖和方法,相当于VUE2的data、props、methods。
4.VUE文件从useInit.js中获取模板中用到的依赖。
5.useInit.js从useTools.js文件中获取初始化用到的方法和依赖,以及需要传递到VUE文件中的依赖。
6.返回方法和依赖。
7.返回依赖。

举例:

App.vue

<template>
  <div
    class="test"
    :style="{
      width: state.fatherWidth + 'vw',
      background: state.fatherBgc,
    }"
  >
    <button @click="changeBgc">变色</button>
    <child></child>
  </div>
</template>

<script setup>
import Child from "./components/child.vue";
import { defineComponent } from "vue";
import useInit from "./utils/App/useInit";

defineComponent({
  Child,
});

let { state } = useInit();

function changeBgc() {
  state.fatherBgc = "lightblue";
}
</script>

<style scoped>
.test {
  height: 100vh;
  overflow: hidden;
}
</style>

child.vue

<template>
  <div class="test">
    <button @click="updateWidth">变窄</button>
  </div>
</template>

<script setup>
import PubSub from "pubsub-js";
function updateWidth() {
  PubSub.publish("updateWidth")
}
</script>

<style scoped>

</style>

useInit.js

import PubSub from "pubsub-js";
import useTools from "./useTools";
import { onMounted, onUnmounted, watch } from "vue";

export default function useInit() {
    const {
        init,
        pubsubOptions,
        state,
    } = useTools();

    watch(
        () => state.fatherWidth,
        val => {
            if (val % 2 === 1) {
                state.fatherBgc = "pink"
            }
        }
    )

    onMounted(() => {
        init();
    });

    onUnmounted(() => {
        for (let psevt in pubsubOptions) {
            PubSub.unsubscribe(psevt);
        }
    });

    return {
        state
    }
}

useTools.js

import PubSub from "pubsub-js"
import { reactive } from "vue";

export default function useTools() {
    const pubsubOptions = {
        updateWidth: null,
    }
    const state = reactive({
        fatherWidth: 100,
        fatherBgc: 'yellowgreen'
    })

    function updateWidth() {
        state.fatherWidth --
        state.fatherBgc = 'lightblue';
        if (state.fatherWidth <= 80) {
            state.fatherWidth = 100
            state.fatherBgc = 'yellowgreen'
        }
    }

    return {
        init: function() {
            pubsubOptions.updateWidth = PubSub.subscribe("updateWidth", () => {
                updateWidth()
            })
        },
        pubsubOptions: pubsubOptions,
        state: state,
    };
}

总结:

最初我的想法是延伸optionAPI的写法,将data、props、components、watch、生命周期钩子等都写到vue组件中,然后将这些依赖以传递的方式传递到useInit等,但是一顿打磨之后发现传递其实可以有终点。比如useTools,把所有的data、methods、都定义到useTools中,然后再返回到外部。其实思路上就是一个正反的区别,但是向外传递的方式能让data和methods的关系更紧密,也更直观。但是这两种方法都没有按功能模块进行拆分,几乎是把所有的方法都堆到一起。最后再抛出一个问题,有些系统的原型设计就已经很大程度的既定了代码的设计,言外之意就是代码的耦合度等和系统的设计有很大关系。在这种耦合度必然存在的情况下要怎么拆分功能模块,让代码更容易管理和维护。或者是否有更好的代码设计的思路或方案啊,绞尽脑汁就是想不出啊~

一些想法:

1.useTools按照相关功能继续拆分成单个功能模块
2.公共的依赖保留在useTools中向外传递
3.单个功能模块仅定义依赖的data、props、components、watch、生命周期钩子等,使这些相关的东西高度聚合在一起


野望
18 声望4 粉丝

一个为了写出漂亮代码而努力的前端人