OpenWrt WebUI 交互架构深度解析

在 OpenWrt 的开发中,HTML/JS (前端)、Lua (后端逻辑) 和 UCI/ubus (系统底层) 共同构成了一个完整的数据闭环。

1. 宏观架构图:数据是如何流动的?

想象一下,用户在网页上点击了"保存 Wi-Fi 密码"按钮,数据会经历以下旅程:

复制代码
graph TD
    User[用户 (HTML/CSS)] -->|1. 点击保存| JS[JavaScript (前端逻辑)]
    JS -->|2. 发送 HTTP 请求 (JSON)| Server[uhttpd (Web 服务器)]
    Server -->|3. 调用 CGI 脚本| Lua[Lua 脚本 (LuCI Controller)]
    
    subgraph "路由器内部 (后端 & 底层)"
    Lua -->|4. uci:set (修改配置)| UCI[UCI 配置文件 (/etc/config/)]
    Lua -->|4. ubus call (获取状态)| Ubus[ubus 总线 (系统状态)]
    UCI -->|5. commit & reload| System[系统内核/服务]
    end
    
    Lua -->|6. 返回结果 (JSON)| JS
    JS -->|7. 更新界面 DOM| User

2. 交互细节与数据格式

交互 A:前端 -> 后端 (JavaScript 调用 Lua)

这是最关键的一步,打通了浏览器和路由器。

  • 场景:用户填好表单,点击提交;或者页面自动刷新获取流量数据。

  • 协议:HTTP / HTTPS

  • 数据格式JSON (现代推荐) 或 application/x-www-form-urlencoded (传统 CBI)。

代码示例 (现代 JSON 方式)

1. JavaScript (发送方):

复制代码
// 前端将数据打包成 JSON
const payload = {
    ssid: "Quectel_WiFi",
    password: "new_password_123"
};

// 发送 POST 请求给 Lua 定义的 API 接口
fetch('/cgi-bin/luci/admin/api/update_wifi', {
    method: 'POST',
    body: JSON.stringify(payload) // 格式化为 JSON 字符串
})
.then(res => res.json())
.then(data => console.log("Lua 返回结果:", data));

2. Lua (接收方 - Controller):

复制代码
-- /usr/lib/lua/luci/controller/api.lua
function index()
    -- 注册路由,对应前端 fetch 的 URL
    entry({"admin", "api", "update_wifi"}, call("action_update_wifi"))
end

function action_update_wifi()
    -- 读取 HTTP 请求体
    local json_str = luci.http.content()
    -- 将 JSON 字符串解析为 Lua Table (核心转换)
    local data = luci.jsonc.parse(json_str) 
    
    -- 此时 data 就是 { ssid = "Quectel_WiFi", password = "..." }
    -- ... 后续处理 ...
end

交互 B:后端 -> 底层 (Lua 操作 UCI/ubus)

Lua 拿到数据后,通过 OpenWrt 提供的标准库与系统进行交互。

  • 场景:将 Wi-Fi 密码写入配置文件;读取当前 CPU 负载。

  • 接口:luci.model.uci (配置) 和 luci.sys / ubus (状态)。

  • 数据格式Lua Table

代码示例 1:Lua 操作 UCI (存配置)

UCI 是 OpenWrt 的统一配置接口,Lua 通过 Cursor (游标) 来操作它。

复制代码
local uci = require "luci.model.uci".cursor()

-- 1. 读取 (Get)
-- 相当于命令: uci get wireless.@wifi-iface[0].ssid
local current_ssid = uci:get("wireless", "@wifi-iface[0]", "ssid")

-- 2. 写入 (Set)
-- 相当于命令: uci set wireless.@wifi-iface[0].ssid='NewName'
uci:set("wireless", "@wifi-iface[0]", "ssid", data.ssid)
uci:set("wireless", "@wifi-iface[0]", "key", data.password)

-- 3. 提交 (Commit) - 必须提交才生效
-- 相当于命令: uci commit wireless
uci:commit("wireless")
代码示例 2:Lua 调用 ubus (读状态)

ubus 是 OpenWrt 的系统总线,用于进程间通信(例如获取网络接口的实时流量)。

复制代码
local ubus = require "ubus"
local conn = ubus.connect()

if conn then
    -- 调用 network.interface dump 方法
    -- 相当于命令: ubus call network.interface dump
    local status = conn:call("network.interface", "dump", {})
    
    -- status 是一个巨大的 Lua Table,包含所有接口的 IP、Mac、流量等信息
    local wan_ip = status.interface[1].ipv4_address[1].address
end

交互 C:后端 -> 前端 (Lua 返回数据给 JS)

Lua 处理完业务逻辑后,需要将结果告诉前端。

  • 场景:告诉用户"保存成功";返回最新的网速数据。

  • 数据格式JSON

代码示例

Lua (发送方):

复制代码
-- 准备返回的数据 (Lua Table)
local result = {
    code = 0,
    msg = "保存成功",
    timestamp = os.time()
}

-- 设置 HTTP 头
luci.http.prepare_content("application/json")
-- 将 Lua Table 编码回 JSON 字符串并输出
luci.http.write_json(result)

JavaScript (接收方):

复制代码
// 前端 fetch 的 .then(data => ...) 接收到的就是上面的 result 对象
if (data.code === 0) {
    alert(data.msg); // 弹出 "保存成功"
}

3. 两种开发模式的区别

根据您的项目(React),您主要使用的是 模式二

模式一:传统 LuCI CBI (Server-Side Rendering)

  • 特点:Lua 代码中直接定义表单结构(Map, Section, Value)。

  • 渲染:Lua 运行并在服务器端生成完整的 HTML 发给浏览器。

  • 交互:配置定义非常快,但界面风格死板,很难做成您视频中那样炫酷的效果。

  • 核心文件:/usr/lib/lua/luci/model/cbi/xxx.lua

模式二:现代前后端分离 (Client-Side Rendering) - 推荐

  • 特点:HTML/JS 是静态资源,React 负责渲染。

  • 渲染:浏览器运行 JS 生成界面。

  • 交互JS 通过 JSON API 与 Lua 通信

  • 优势:可以完全自定义 UI(如华为风格、Quectel 风格),用户体验极其流畅。

  • 核心:JS 负责美貌,Lua 负责干活,中间用 JSON 传话。

4. 总结:交互字典

|------------|---------------|--------------|---------------|----------------------------------|
| 交互动作 | 发起方 | 接收方 | 传输格式 | 关键函数/库 |
| 保存配置 | JavaScript | Lua | JSON | fetch(POST) -> luci.jsonc.parse |
| 获取状态 | JavaScript | ubus (或 Lua) | JSON-RPC | ubus call (JS库) 或 fetch(GET) |
| 修改系统配置 | Lua | UCI | Lua Table | uci:set, uci:commit |
| 读取系统状态 | Lua | ubus | Lua Table | ubus_conn:call |
| 页面渲染 | React/Browser | DOM | HTML/CSS | ReactDOM.render |

相关推荐
随风一样自由1 小时前
React中实现iframe嵌套登录页面:跨域与状态同步解决方案探讨
javascript·react.js·ecmascript
pale_moonlight1 小时前
九、Spark基础环境实战(下)
大数据·javascript·spark
Aerelin1 小时前
爬虫图片采集(自动化)
开发语言·前端·javascript·爬虫·python·html
Highcharts.js1 小时前
Renko Charts|金融图表之“砖形图”
java·前端·javascript·金融·highcharts·砖型图·砖形图
含若飞1 小时前
列表弹窗实现方案整理
前端·javascript·vue.js
EB_Coder1 小时前
2025前端面试题-JavaScript基础篇
前端·javascript·面试
shaohaoyongchuang1 小时前
vue_05axios
前端·javascript·vue.js
梓沂1 小时前
playEdu自定义接口需要满足的格式
前端·javascript·react.js
杀死那个蝈坦1 小时前
Redis 多级缓存:架构设计、核心问题与落地实践
开发语言·spring·青少年编程·golang·kotlin·maven·lua