1

我想重构一些我编写的不使用任何特定模式的 JS 单页应用程序。现在我读到了一些有趣的框架(redux ...),但我的公司一般并不热衷于采用框架,这里的每个人都在使用 vanilla JS。所以我想保持自制,尽可能简单。我在旧代码中发现的最明显的缺陷是单体风格,因此引入关注分离的基于组件的架构似乎已经是一个巨大的改进。这是我想出的一个模型:

<!DOCTYPE html>
<html lang="fr">
<head>
<script>

'use strict';


let eventGenerator = (function () {
    let id = -1;
    return {
        generate: () => {
            ++id;
            return id;
        }
    };
}) ();

let dispatcher = (function () {
    let components = [];
    return {
        addComponent: (component) => {
            components.push (component);
        },
        dispatch: (id, detail = null) => {
            for (let c of components) {
                c.handleEvent (id, detail);
            }
        }
    };
}) ();


const EVT_FAKE_API_RUNNING = eventGenerator.generate ();
const EVT_FAKE_API_SUCCESS = eventGenerator.generate ();
const EVT_FAKE_API_FAILURE = eventGenerator.generate ();
const EVT_FAKE_API_ABORTED = eventGenerator.generate ();
class ComponentFakeAPI {
    constructor (param) { // param = nb de secondes à attendre
        dispatcher.addComponent (this);
        this.param = param;
        this.timer = null;
        this.result = null;
    }
    handleEvent (id, detail) {
        switch (id) {
        case EVT_FETCH_BUTTON_CLICKED:
            this.timer = setTimeout (() => {
                this.result = Math.round (Math.random () * 100);
                if (this.result >= 20)
                    dispatcher.dispatch (EVT_FAKE_API_SUCCESS, { result: this.result });
                else
                    dispatcher.dispatch (EVT_FAKE_API_FAILURE);
            }, this.param);
            dispatcher.dispatch (EVT_FAKE_API_RUNNING);
            break;
        case EVT_ABORT_BUTTON_CLICKED:
            clearTimeout (this.timer);
            dispatcher.dispatch (EVT_FAKE_API_ABORTED);
        }
    }
}


const EVT_FETCH_BUTTON_CLICKED = eventGenerator.generate ();
class ComponentFetchButton {
    constructor (elt) {
        dispatcher.addComponent (this);
        elt.innerHTML = `<button>fetch</button>`;
        this.elt = elt;
        this.but = elt.querySelector ('button');
        this.but.onclick = () => dispatcher.dispatch (EVT_FETCH_BUTTON_CLICKED);
    }
    handleEvent (id, detail) {
        switch (id) {
        case EVT_FAKE_API_RUNNING:
            this.but.disabled = true;
            break;
        case EVT_FAKE_API_SUCCESS: case EVT_FAKE_API_FAILURE: case EVT_FAKE_API_ABORTED:
            this.but.disabled = false;
            break;
        }
    }
}

const EVT_ABORT_BUTTON_CLICKED = eventGenerator.generate ();
class AbortButton {
    constructor (elt) {
        dispatcher.addComponent (this);
        elt.innerHTML = `<button disabled>abort</button>`;
        this.elt = elt;
        this.but = elt.querySelector ('button');
        this.but.onclick = () => dispatcher.dispatch (EVT_ABORT_BUTTON_CLICKED);
    }
    handleEvent (id, detail) {
        switch (id) {
        case EVT_FAKE_API_SUCCESS: case EVT_FAKE_API_FAILURE: case EVT_FAKE_API_ABORTED:
            this.but.disabled = true;
            break;
        case EVT_FAKE_API_RUNNING:
            this.but.disabled = false;
            break;
        }
    }
}

class ComponentValueDisplay {
    constructor (elt) {
        dispatcher.addComponent (this);
        elt.textContent = '';
        this.elt = elt;
    }
    handleEvent (id, detail) {
        switch (id) {
        case EVT_FAKE_API_SUCCESS:
            this.elt.textContent = detail.result;
            break;
        case EVT_FAKE_API_FAILURE:
            this.elt.textContent = 'failure !';
            break;
        case EVT_FAKE_API_ABORTED:
            this.elt.textContent = 'aborted !';
            break;
        case EVT_FAKE_API_RUNNING:
            this.elt.textContent = '';
            break;
        }
    }
}

class ComponentAverage {
    constructor (elt) {
        dispatcher.addComponent (this);
        elt.textContent = '';
        this.elt = elt;
        this.sum = 0;
        this.avg = 0;
        this.n = 0;
    }
    handleEvent (id, detail) {
        switch (id) {
        case EVT_FAKE_API_SUCCESS:
            ++ this.n;
            this.sum += detail.result;
            this.elt.textContent = Math.round (this.sum / this.n);
            break;
        }
    }
}


window.addEventListener ('load', () => {
    let componentFakeAPI = new ComponentFakeAPI (2000);
    let componentFetchButton = new ComponentFetchButton (document.querySelector ('#componentFetchButton'));
    let componentAbortButton = new AbortButton (document.querySelector ('#componentAbortButton'));
    let componentValueDisplay = new ComponentValueDisplay (document.querySelector ('#componentValueDisplay'));
    let componentAverage = new ComponentAverage (document.querySelector ('#componentAverage'));
});

</script>
<style>

#componentValueDisplay, #componentAverage {
    margin-left: 10px;
    border: 1px solid black;
    min-width: 50px;
}

</style>
</head>
<body>
<div style="display: flex">
    <div id="componentFetchButton"></div>
    <div id="componentAbortButton"></div>
    <div id="componentValueDisplay"></div>
    <div id="componentAverage"></div>
</div>
</body>
</html>

我想知道这种模式是否会在更大、更复杂的应用程序中的某个时候碰壁。有什么建议吗?

4

0 回答 0