渐进式Web应用:新的FE系统

有时连接会成为瓶颈。

在企业环境中,我们通常认为稳定的互联网连接是理所当然的。然而,现实世界的情况经常挑战这一假设,可能会扰乱关键的业务运营。本文详细介绍了我们如何将传统的在线 ERP 系统转变为具有弹性离线解决方案的更可靠的系统。通过利用基于浏览器的存储解决方案(如 IndexedDB)、采用同步机制和使用渐进式 Web 应用程序 (PWA)。

最初,系统采用传统的客户端服务器架构,所有业务逻辑都位于后端。虽然这种架构在具有可靠连接的环境中运行良好,但它也带来了一些挑战:

  • 网络中断期间交易失败
  • 停电期间失去销售机会
  • 持续加载的用户体验不佳
  • 关键操作期间数据丢失的风险
  • 最重要的是,由于缺乏快捷的服务而失去客户。
  • 因此,在定义这一点时,我们必须即兴发挥,看看如何使事情变得更好,并且在没有连接的情况下也能做到,因为它最初是不可用的,我们使用渐进式 Web 应用程序 (PWA) 实现了一个需要一些互联网的离线系统,有效地将关键业务逻辑移动到前端,同时保持数据完整性和与核心 ERP 系统的同步。

    一些核心组件:

    **IndexedDB**:对于离线数据存储和缓存,我们通过 Dexie.js 库使用 IndexedDB 来提供支持结构化数据存储的强大客户端数据库。下面是如何使用 Dexie 设置数据库的简单示例

    // Initialize IndexedDB using Dexie.js
    import Dexie from 'dexie';
    
    interface LocalUsers{
    id:string;
    name:string;
    role:string;
    dob:string;
    phone_no:string
    }
    
    interface LocalTrx {
    id: string;
    syncId:string;
    created_at:string;
    amount:string;
    isSynced:boolean;
    modified:string;
    }
    
    export class ArticleDatabase extends Dexie {
      transaction!: Table;
      users!: Table;
      constructor(){
        super("articleDB")
        }
    
    this.version(1).stores({
    // define the fields you'll like to query by or find items by within the indexDB
        transactions: 'id, date, amount, isSynced',
        users: 'id, name, role'
     });
    }
    //create the db
    export const db=new ArticleDatabase()
    
    // Open the database
    db.open().then(() => {
        console.log("Database opened successfully!");
      })
      .catch((error) => {
        console.error("Failed to open database:", error);
      });
    
    // Adding a new transaction to IndexedDB
    import db from ../db
    async function addTransaction(transaction) {
    try {
       const trx = await db.transactions.add(transaction)
       console.log("Trx added", trx)
    } catch (err) {
        console.log("Failed creating trx", err)
    }
    }

    **服务工作者**:它们充当应用程序和网络之间的代理,通过缓存资源和拦截请求来实现离线功能,以确保关键数据在断开连接期间仍然可访问。

    `//service worksr 可以轻松设置,最近默认情况下 nextJS 应用确实带有与 vite 配合使用的服务,你可以使用 vite-pwa 插件`

    **后台同步**:这使我们能够在网络再次可用时同步数据,确保交易不会丢失并且在恢复连接后自动进行更新。

    **系统架构流程**

    该架构分为三个主要阶段:初始化、事务处理和同步。下面的流程图显示了数据在这些阶段之间的流动方式。

    Overview of workflow, intialization trasanction and sync

    *初始化阶段*

    系统启动时会检查网络连接:

    如果设备在线,它会从服务器获取最新的主数据并更新本地IndexedDB。

    如果设备处于离线状态,它会从 IndexedDB 加载数据,确保用户可以不间断地继续工作。

    交易处理

    当用户执行新交易时:

    本地数据经过验证并存储在IndexedDB中。

    乐观的 UI 更新用于立即向用户显示结果,提供流畅且响应迅速的体验。

    *同步阶段*

    当连接恢复时:

    数据会分批与服务器同步,可以通过手动单击同步按钮或​​在特定时间段之后进行。

    如果同步失败(例如由于连接速度慢),则该交易将被添加到失败交易列表中并在稍后重试。

    由于我们在前端管理一切,我们的服务对保护客户信息安全的依赖程度如何。

    **身份验证和授权**

    在任何企业系统中,保护敏感的用户信息都至关重要。我们的解决方案可确保:

    **基于 JWT 的身份验证**用于安全的用户会话。

    **基于角色的访问控制**确保只有授权用户才能执行特定的操作。

    **安全令牌存储**使用基于浏览器的机制(例如 localStorage)来处理,以增强安全性。

    为了降低使用本地存储的代币的风险,我们:

    退出时触发安全删除用户令牌。

    确保在会话结束或用户从系统注销时从 IndexedDB 中删除敏感数据。注意:如果事务未同步,我们会向登录用户显示该信息,并强制他们在注销前进行同步。

    **数据完整性和冲突解决**

    在客户端和服务器之间同步数据可能会带来数据完整性问题,尤其是当多个设备或用户离线更改同一数据时。要解决此问题:

    我们在同步之前验证所有交易细节(例如数量、金额),以确保没有差异。

    我们为每个交易分配唯一的 ID,以防止同步期间重复。

    冲突解决策略用于处理离线时在多个设备上进行数据更改的情况。例如,我们使用时间戳方法。

    //我们尝试确保首先考虑离线,因为它是系统的重要部分。

    async function resolveConflict(localTransaction, serverTransaction) {
        // Compare timestamps determine which transaction should prevail
        if (new Date(localTransaction.timestamp) > new Date(serverTransaction.timestamp)) {
            // Local transaction wins
            await syncTransactionWithServer(localTransaction);
        } else {
            // Server transaction wins
            await updateLocalTransaction(serverTransaction);
        }
    }
    
    async function updateLocalTransaction(transaction) {
        // Update the local transaction in IndexedDB to reflect the server's state
        await db.transactions.put(transaction);
    }

    **网络安全**

    鉴于连接恢复后数据将通过网络传输,我们确保:

    限制速率以防止滥用并确保过多的请求不会因 429 响应而导致服务器不堪重负,这就是我们最初使用批量更新的原因。

    使用 SSL/TLS 对传输过程中的数据进行加密。

    令牌过期和安全令牌管理,确保陈旧或过期的令牌自动从客户端存储中删除。

    PWA 和 IndexedDB 的替代品

    虽然 IndexedDB 是 PWA 中客户端数据存储的可靠选择,但根据应用程序的复杂性和要求,还有其他可用选项:

    **通过 WebAssembly (WASM) 的 SQLite:** 一些开发人员选择通过 WASM 使用 SQLite 进行更高级的数据管理,尤其是在处理更大的数据集或复杂的查询时。但是,通过 WASM 集成 SQLite 会带来额外的复杂性,例如性能问题和浏览器兼容性(例如,sqlite 如何让 Notion 更快)。

    **Web Storage API(localStorage/sessionStorage):**对于不需要复杂查询或大型数据集的简单应用程序,Web Storage API 可能是一种可行的替代方案。它更容易实现,但在存储容量和查询能力方面存在局限性。

    展望:PWA 的未来趋势

    随着 Web 技术不断发展,此类应用的可能性也在不断增加。新兴趋势包括:

  • WebAssembly 和 SQLite
  • 边缘计算
  • 高级同步协议:新兴协议,如 CRDT(无冲突复制数据类型)和 DeltaSync
  • 我本人迫不及待地想探索这些技术将如何改变离线和分布式应用程序的格局。随着功能强大的机器和笔记本电脑的快速发展,我们有机会利用这种增强的计算能力为用户提供更复杂、更高效的软件。与此同时,我们不能忘记迎合移动设备和性能较差的设备的重要性,确保我们的解决方案在所有平台上均可访问和优化。潜力是巨大的,我很高兴继续突破 PWA 的极限。

    注意:接下来的内容

    我们会着手处理。使用 Djuix.io 作为后端,使用 React / Angular 作为前端,我们将实现适当的基本流程。请继续关注更多更新,我们将继续增强构建出色应用程序的方法。

    无论如何,我希望你喜欢这篇文章并学到一些新东西。我当然喜欢。我也想听听你的想法和经历。

    直到那时。