简体   繁体   English

如何使用 vanilla JS 实现可维护的反应式 UI

[英]How to achieve a maintainable, reactive UI with vanilla JS

today I've got a problem which would be easily solved by using a reactive and state managed framework like Vue.今天我遇到了一个问题,可以通过使用像 Vue 这样的反应式和状态管理框架来轻松解决。 Sadly, its not posssible to use it.可悲的是,它不可能使用它。

Following (simplified) situation ( link to codepen ): We've got a server rendered page which has a price field.以下(简化)情况(指向 codepen 的链接):我们有一个服务器呈现的页面,其中包含一个价格字段。 It has the opportunity to add or remove a note.它有机会添加或删除注释。 If we add a note, its posted to the server and the UI should update itself.如果我们添加注释,它会发布到服务器,并且 UI 应该会自行更新。 Same for removing a note.删除笔记也一样。

 const priceField = document.getElementById("priceField"); priceField.querySelector("#create-note-btn").addEventListener("click", () => { priceField.querySelector("#note-input-row").classList.toggle("hidden"); // depending on state #create-note-btn can hide/show #note-row or #node-input-row }); priceField.querySelector("#submit-note-btn").addEventListener("click", () => { priceField.querySelector("#note-row").classList.toggle("hidden"); priceField.querySelector("#note-input-row").classList.toggle("hidden"); const input = priceField.querySelector("input").value; priceField.querySelector("#note").innerHTML = input; // api call // further ui updates, like changing icon of #create-note-btn }); priceField.querySelector("#delete-note-btn").addEventListener("click", () => { priceField.querySelector("#note-row").classList.toggle("hidden"); // api call // resetting icon of #create-note-btn }); // much more logic with additional UI update operations, like recalculation of price etc.
 .hidden { display: none; }
 <div id="priceField"> <div> <span>100 €</span> <button id="create-note-btn">Create Note</button> </div> <div id="note-input-row" class="hidden"> <input></input> <button id="submit-note-btn">Submit</button> </div> <div id="note-row" class="hidden"> <span id="note">Placeholder note</span> <button id="delete-note-btn">Delete Note</button> </div> </div>

To achieve this (only!) javascript is used to update the view.为了实现这一点(仅!)javascript 用于更新视图。 Therefore many classlist.toggle("..") calls or other things are done to display / hide elements.因此,许多classlist.toggle("..")调用或其他事情都是为了显示/隐藏元素。 Additionally,there is a bunch of different operations which does also update the view on different places.此外,还有许多不同的操作也会更新不同位置的视图。

To keep the code maintainable I want to achieve, that the UI update is done at one central place and not split across different calls.为了保持代码的可维护性,我想实现 UI 更新在一个中心位置完成,而不是分散到不同的调用中。 Also state should be kept for page reload.还应保留状态以重新加载页面。

Whats an easy and maintable way to do so?什么是简单且可维护的方法?

My thoughts: Implement a little state machine (INITIAL, OPEN_NOTE, CLOSED_NOTE, ...) and a render() -function which depends on actual state.我的想法:实现一个小的状态机(INITIAL、OPEN_NOTE、CLOSED_NOTE,...)和一个依赖于实际状态的render()函数。 To keep changes for page reload, localstorage has to be used or server side rendered html needs to stateful aswell.为了保持页面重新加载的更改,必须使用 localstorage 或者服务器端呈现的 html 也需要有状态。

I followed my thoughts by implementing internal state with a render -function which holds all UI related changes.我遵循我的想法,通过使用包含所有 UI 相关更改的render函数实现内部状态。

const RenderMode = {
  INITIAL: "Initial",
  CREATE: "Create",
  OPEN: "Open",
  SHOW_NOTE: "Show note input",
  TOGGLE_PRICE: "Toggle price input",
};

render() {
    switch (this.renderMode) {
      case RenderMode.INITIAL:
        this._hideIndicatorIcon();
        this._hideIndicatorRow();
        this._hideInputRow();
        this._hidePriceInput();

        break;
      case RenderMode.CREATE:
        this._showInputRow();
        break;
      case RenderMode.OPEN:
        this._showIndicatorIcon();
        this._hideInputRow();
        this._hideIndicatorRow();
        break;
      case RenderMode.SHOW_NOTE:
        this._showIndicatorRow();
        break;
      case RenderMode.TOGGLE_PRICE:
        this._togglePriceInputs();
        break;
      default:
        console.error("No render mode defined!");
    }

State after page reload is determined from custom attributes of server side rendered html:页面重新加载后的状态由服务器端呈现的 html 的自定义属性确定:

  initializeRenderMode() {
    ...
    // if note already exists on page load switch render mode to open
    this.renderMode = existingNote ? RenderMode.OPEN : RenderMode.INITIAL;
    this._render();
  }

Its by far not the best solution, but it helps me to keep things simple.到目前为止,它不是最好的解决方案,但它帮助我保持简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM