Show pageOld revisionsBacklinksBack to top This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== LuCI2 (OpenWrt web 管理界面) ====== 一直以来OpenWrt都是采用[[http://en.wikipedia.org/wiki/Lua_(programming_language)|Lua]]写的web管理界面[[LuCI]],(开机速度慢不说,居然比不过腾达等弱路由器开机速度)。 LuCI需要使用多个Lua扩展(如 ''ubus'', ''luci.model.uci'', ''nixio.fs'', 等等)去存取系统信息和设置. 不幸的是这种解决方案在慢CPU和低内存的低配机器设备上是个灾难,此方案相当消耗资源且并不能很好的工作。 这导致开发了LuCI2, 一个不同架构的新的web管理节目。它不再使用Lua,而是使用静态HTML页面加JavaScript XHR方法。 这意味着从OpenWrt设备中下载后在客户端(浏览器)中构建HTML页面, 通过[[ubus]]存取各种系统数据(通过[[ubus#access.to.ubus.over.http|uhttpd-mod-ubus]]提供基于HTTP的接口API). ==== 重要说明 ==== 如上所述, LuCI2通过''ubus''和OpenWrt子系统通信(包括如''network''、''service''以及其它)。遗憾的是并非每个主要的OpenWrt工具都自己注册了''ubus'',如LuCI2不能使用''opkg''(安装包管理)。 LuCI2通过提供在附加''ubus''名称空间的''rpcd''插件解决了这个问题。前面说的''opkg''它在''ubus''中注册了一个新的''luci2.opkg''路径来访问。 综上所述, LuCI2包括两个方面: 打包的HTML/CSS/JS文件 (''htdocs'') 和一些在OpenWrt环境下运行的附加小工具。 在接下来的章节中,你会找到各种关于LuCI2开发帮助的细节。 ==== 菜单 ==== 首先需要知道的是浏览器接收到的关于LuCI2菜单并不固定写死在任何文件。代替的是通过''ubus''使用''luci.ui''路径和''menu''方法。可以通过使用以下命令来查看: <code>ubus call luci2.ui menu '{ "ubus_rpc_session": "invalid" }'</code> 内部的''rpcd''插件分析在目录''/usr/share/rpcd/menu.d''的所有文件,当前用户(基于传递的''ubus_rpc_session'')不允许增加或者删除条目。这将导致在一个二级菜单限制当前有权限访问的条目。 顶层菜单项用下面的JSON定义: <code>"foo": { "title": "Foo", "index": 12 }</code> (并且通过''index''值排序)。 第二层菜单项通过同样的方法定义: <code>"foo/bar": { "title": "Bar", "acls": [ "baz", "qux" ], "view": "foo/bar", "index": 5 }</code> 注意,第二层菜单项可以在独立的文件中定义,这样就可以方便的添加新的菜单项定义而不用修改原有的文件。 ==== 模板 ==== 每一个LuCI2子页面必须有一个存放在''/www/luci2/template/''目录下的模板。它们提供了非常简单的内容替换区的HTML文件。需要注意的是它们不包含任何变量的引用,这是JavaScript的功劳:用JavaScript去读取并写入内容。在为本地化系统(i18n)这些文件中唯一特定的语法类似下面的标记: <code><p><%:Hello world%></p></code> ==== 视图 ==== 从模板中分离,每个LuCI2子页面也需要一个定义并存放在''/www/luci2/view/''目录下的一个视图,视图是使用了子页面特定对象的''L.ui.view''扩展的JavaScript文件。Javascript中需要提供''execute''方法的实现,该方法将在膜拜加载后被执行。可选的也可以提供子页面的''title''和''description''属性。 有一点需要注意的是需要提供''execute''方法,创建视图失败有几种原因,特别是当它需要''ubus''加载额外的数据使用的时候。所以在''execute''方法中提供成功或者失败的信息是有意义的。不幸的是在异步方法加载特定数据的时候,不能简单的返回''true''或''false'',解决的方法是返回一个''Promise''对象来运行推迟提供是否成功的信息。 最简单的视图可以如下所示: <code>L.ui.view.extend({ title: L.tr('Foo'), /* 可选的标题 */ description: 'Bar', /* 可选的描述 */ execute: function() { var deferred = $.Deferred(); /* 创建一个延迟对象 */ deferred.resolve(); /* 立即Resolve,它不做任何事可以返回失败 */ return deferred.promise(); /* 返回Promise对象 (延迟对象的子集) */ } });</code> ==== 通过ubus通讯 ==== 在开始之前,需要知道的是LuCI2提供了一些存取[[zh-cn:doc:techref:uci]]系统的一些帮助。如果写一个简单的管理''/etc/config/''下配置文件不需要完全知道''ubus''的调用方法,则可以跳过该部分。 构建一些更复杂的LuCI2视图前最好先弄明白使用到的''ubus''调用。完整的对象和方法列表可以通过运行''ubus -v list''命令来得到。 下面这个简单的例子调用了''log''对象和''write''方法,它需要提供一个''event''参数传递进去。使用''ubus''命令行国内根据是,需要如下所示命令: <code>ubus call log write '{ "event": "Foo" }'</code> LuCI2为''ubus''通讯提供了一个叫做''L.rpc.declare''的工具,它可以如魔法般的帮助JavaScript访问''ubus''方法。注意,定义声明方法的时候方法并不被执行,参数也未传递,这是为以后调用准备的一个方法。下面这个示例的方法调用了''log''对象的''write''方法: <code>var writeToLog = L.rpc.declare({ object: 'log', method: 'write', params: [ 'event' ] })</code> 定义了一次这个方法后,可以在任意时间通过如下示例简单调用: <code>writeToLog('Foo');</code> 在上面的例子中执行结果被忽略了,如果视图需要处理''ubus''返回的数据时不能忽略执行结果。下面例子将通过访问''system''对象的''info''方法来描述返回结果的处理。在命令行下通过以下命令访问: <code># ubus call system info { "uptime": 123, "localtime": 1234567890, "load": [ 1, 2, 3 ], "memory": { "total": 67108864, "free": 33554432, "shared": 0, "buffered": 16777216 }, "swap": { "total": 0, "free": 0 } }</code> 在LuCI2(JavaScript)定义上面的访问如下所示: <code>var readSystemInfo = L.rpc.declare({ object: 'system', method: 'info', expect: { memory: { } } /* 可选, 只提取结果的一部分memory */ })</code> 通过简单的调用''.then''方法可访问结果数据 <code>readSystemInfo().then(function(memory) { console.log(memory); });</code> ==== 怎样测试 ==== 在"feeds.conf"中添加一个feed: <code> src-git luci2 git://git.openwrt.org/project/luci2/ui.git </code> 安装luci2包: <code>./scripts/feeds update ./scripts/feeds install luci2 </code> 在menuconfig中勾选Luci2包并编译新固件。 Last modified: 2018/06/06 19:57by tmomas