在 Custom Elements 中使用 Flux 架构模式的应用实例

前言

Web 开发中,我们通常使用 MVC(Model-View-Controller)模式进行项目的设计和开发,其核心思想是将应用程序分为三部分:模型(Model)、视图(View)、控制器(Controller),分别处理应用中的业务逻辑、界面展示和流程控制。但随着前端应用的逐渐复杂,MVC 模式往往不能很好地满足开发的需求。

因此,Flux 架构模式应运而生。Flux 模式是 Facebook 提出的一种前端应用开发模式,在该模式中,将应用程序分为四部分:dispatcherstoreviewaction。该模式的核心思想是数据的单向流动,通过这种方式来降低应用的复杂度。

本文就将通过一个应用实例,详细讲解在 Custom Elements 中使用 Flux 架构模式的技术实现方案。

实例介绍

假设我们要实现一个简单的 Todo List 应用,该应用具有以下功能:

  1. 用户可以添加、删除、编辑待办事项。
  2. 用户可以将待办事项标记为已完成、未完成。
  3. 用户可以查看已完成和未完成的待办事项。

技术实现方案

1. 构建 Custom Elements

首先,我们需要构建一个 Custom Elements,它将是我们应用的主要组成部分。

class TodoList extends HTMLElement {
  constructor() {
    super();

    // TODO: 构建 TodoList 代码
  }
}

customElements.define('todo-list', TodoList);

由于这个 Todo List 应用只有一个组件,可以直接编写。但是当我们开发一个大型应用时,可能需要拆分更多的组件。

2. 设计 Store

接下来,我们来设计 Store,Store 是 Flux 中存储数据的中央管理器。我们可以使用 Redux 作为 Store,这里我们只需要安装 redux 包即可。

npm install redux

在 Store 中,我们需要定义初始的状态和针对不同 Action 的更新函数。对于 Todo 应用,我们可以定义如下的状态:

const initialState = {
  todos: [],
  filter: 'ALL'
};

然后,我们定义更新函数:

function reducer(state = initialState, action) {
  switch(action.type) {
    case 'ADD_TODO':
      // 处理 ADD_TODO Action
      return newState;
    case 'DELETE_TODO':
      // 处理 DELETE_TODO Action
      return newState;
    case 'UPDATE_TODO':
      // 处理 UPDATE_TODO Action
      return newState;
    case 'TOGGLE_TODO':
      // 处理 TOGGLE_TODO Action
      return newState;
    case 'SET_FILTER':
      // 处理 SET_FILTER Action
      return newState;
    default:
      // 如果没有匹配的 Action,返回原始状态
      return state;
  }
}

const store = createStore(reducer);

3. 设计 Action

接下来,我们需要设计 Action,Action 表示用户行为和事件,会触发对 Store 中数据的更新。在 Todo 应用中,我们可以定义如下的 Action:

const ADD_TODO = 'ADD_TODO';
const DELETE_TODO = 'DELETE_TODO';
const UPDATE_TODO = 'UPDATE_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
const SET_FILTER = 'SET_FILTER';

function addTodo(todo) {
  return { type: ADD_TODO, todo };
}

function deleteTodo(id) {
  return { type: DELETE_TODO, id };
}

function updateTodo(todo) {
  return { type: UPDATE_TODO, todo };
}

function toggleTodo(id) {
  return { type: TOGGLE_TODO, id };
}

function setFilter(filter) {
  return { type: SET_FILTER, filter };
}

这里我们定义了 5 个 Action,分别对应新增、删除、更新、切换完成状态和设置筛选条件。

4. 构建 View

在 View 中,我们将 Store 中的数据展示到界面上,并处理用户的交互事件。

class TodoList extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: 'open' });
    const template = document.createElement('template');
    template.innerHTML = `
      <style>
        /* some styles */
      </style>
      <div class="todo-list">
        <h2>Todo List</h2>
        <input type="text" id="todo-input">
        <button id="add-button">Add</button>
        <ul class="todo-items"></ul>
        <div class="filter">
          <button class="all-filter">All</button>
          <button class="completed-filter">Completed</button>
          <button class="incompleted-filter">Incompleted</button>
        </div>
      </div>
    `;
    this.shadow.appendChild(template.content.cloneNode(true));

    // 获取 DOM 节点
    this.input = this.shadow.getElementById('todo-input');
    this.addButton = this.shadow.getElementById('add-button');
    this.todoList = this.shadow.querySelector('.todo-items');
    this.filterAll = this.shadow.querySelector('.all-filter');
    this.filterCompleted = this.shadow.querySelector('.completed-filter');
    this.filterIncompleted = this.shadow.querySelector('.incompleted-filter');

    // 监听按钮点击事件
    this.addButton.addEventListener('click', () => {
      if (this.input.value) {
        store.dispatch(addTodo({
          id: Date.now(),
          text: this.input.value,
          completed: false
        }));
        this.input.value = '';
      }
    });

    // 监听过滤条件点击事件
    this.filterAll.addEventListener('click', () => {
      store.dispatch(setFilter('ALL'));
    });

    this.filterCompleted.addEventListener('click', () => {
      store.dispatch(setFilter('COMPLETED'));
    });

    this.filterIncompleted.addEventListener('click', () => {
      store.dispatch(setFilter('INCOMPLETED'));
    });

    // 监听 Store 改变事件,更新 DOM
    store.subscribe(() => {
      const { todos, filter } = store.getState();

      // 根据过滤条件过滤待办事项
      const filteredTodos = todos.filter(todo => {
        if (filter === 'COMPLETED') {
          return todo.completed;
        }
        if (filter === 'INCOMPLETED') {
          return !todo.completed;
        }
        return true;
      });

      // 更新待办事项列表
      this.todoList.innerHTML = '';
      filteredTodos.forEach(todo => {
        const li = document.createElement('li');
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = todo.completed;
        checkbox.addEventListener('click', () => {
          store.dispatch(toggleTodo(todo.id));
        });
        const span = document.createElement('span');
        span.textContent = todo.text;
        span.addEventListener('dblclick', () => {
          const newText = prompt('Enter new text:');
          if (newText) {
            store.dispatch(updateTodo({ ...todo, text: newText }));
          }
        });
        const button = document.createElement('button');
        button.textContent = 'Delete';
        button.addEventListener('click', () => {
          store.dispatch(deleteTodo(todo.id));
        });
        li.appendChild(checkbox);
        li.appendChild(span);
        li.appendChild(button);
        this.todoList.appendChild(li);
      });
    });

  }
}

customElements.define('todo-list', TodoList);

在上面的代码中,我们监听了用户的新增、删除、更新和切换待办事项完成状态的操作,并使用 Redux 的 API 来改变 Store 中的数据。同时,我们也监听了 Store 的变化事件,在 Store 变化时更新界面。

总结

以上就是在 Custom Elements 中使用 Flux 架构模式的应用实例,实现了一个简单的 Todo 应用,该方案具有以下优势:

  1. 通过单向数据流,降低了应用的复杂度,使得数据和逻辑更为清晰。
  2. 每个组件都是独立的,组件之间的耦合度降低,便于重构和维护。
  3. 采用 Store 统一管理数据,便于跨组件共享数据。

至此,我们已经掌握了使用 Flux 架构模式在 Custom Elements 中开发应用的技术方案,希望对广大前端开发者有所帮助。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a33ad6add4f0e0ffb566d9


纠错反馈