要實現Lua的熱更新,首先要了解Lua的模塊加載機制,熱更新的核心就是替換package.loaded中的模塊。
加載規則:
包管理庫提供了從Lua中加載模塊的基礎庫。只有一個導出函數直接放在全局環境中:require。所有其他的部分都導出在表package中。
require(modname)
查詢全局緩存表package.loaded:這個函數首先查找package.loaded表,檢測modname是否被加載過。如果被加載過,require返回package.loaded[modname]中保存的值。否則,它試着爲模塊尋找加載器。
通過package.searchers查找加載器:require遵循package.searchers序列的指引來查找加載器。如果改變package.searchers中的序列,也會改變require如何查找一個模塊的方式。
查找加載器:首先require查找package.preload[modname]。如果這裏有一個值(必須是一個函數),則爲加載器。否則require會使用Lua加載器去查找package.path的路徑。如果查找失敗,接着使用C加載器去查找package.cpath的路徑。如果都失敗了,再嘗試一體化加載器(參見package.searchers詳解)
加載器調用規則:每當找到一個加載器,require都用兩個參數調用加載器:modname和一個在獲取加載器過程中得到的參數(如果是通過查找文件得到的加載器,這個額外的參數即爲文件名)。如果加載器返歸非空值,require將這個值賦給package.loaded[modname]。如果加載器沒能返回一個非空值用於賦給package.loaded[modname],require 會將true賦給 package.loaded[modname]。 無論加載器返回什麼結果,require都會爲package.loaded[modname]
設置最終值。
報錯:在加載或運行模塊時有錯誤,或是無法爲模塊找到加載器,require都會拋出錯誤。
package.loaded
存儲已經被加載的模塊:當require一個modname模塊得到的結果不爲假時,require返回這個存儲的值。require從package.loader中獲得的值僅僅是對那張表(模塊)的引用,改變這個值並不會改變require使用的表(模塊)。
package.preload
保存一些特殊模塊的加載器:這裏面的值僅僅是對那張表(模塊)的引用,改變這個值並不會改變require使用的表(模塊)。
package.path
Lua加載器的搜索路徑:使用環境變量LUA_PATH_5_3或LUA_PATH初始化。或者採用luaconf.h中的默認路徑。環境變量中的所有";;"都會被替換爲默認路徑。
package.cpath
C加載器的搜索路徑:使用環境變量LUA_CPATH_5_3或LUA_CPATH初始化。或者採用luaconf.h中定義的默認路徑。
package.searchers
require查找加載器的表:這個表內的每一項都是一個查找器函數。當加載一個模塊時,require按次序調用這些查找器,傳入modname作爲唯一參數。此方法會返回一個函數(模塊的加載器)和一個傳給這個加載器的參數。或返回一個描述爲什麼沒有找到這個模塊的字符串或者nil。
Lua共有四個查找器函數:
第一個查找器就是簡單的在package.preload表中查找;
第二個查找器用於查找Lua庫的加載庫。它使用存儲在package.path中的路徑查找工作。查找過程和函數package.searchpath描述的一致;
第三個查找器用於查找C庫的加載庫。它使用存儲在package.coath中的路徑查找工作。查找過程和函數package.searchpath描述的一致;
第四個搜索器是一體化加載器,從C路徑中查找指定模塊的根名字。
除了第一個搜索器外,每個搜索器都會返回找到的模塊的文件名。這和package.searchpath的返回值一樣,第一個搜索器沒有返回值。
package.searchpath(name, path[,sep[,,rep]])
在指定的path中搜索指定的name:路徑是一個包含一些列分號分隔的模板構成的字符串。對於每個模板,都會用name替換其中的每個問號(前提是有問號)。且將其中的sep(點".")替換爲rep(系統的目錄分隔符"/")。談候場時打開這個文件名。
例如,如果路徑是字符串 "./?.lua;./?.lc;/usr/local/?/init.lua"
搜索foo.a 這個名字將一次嘗試打開文件./foo/a.lua ./foo/a.lc 以及 /usr/local/foo/a/init.lua
返回第一個可以用讀模式打開(並馬上關閉該文件)的文件的名字。如果不存在這樣的文件,返回nil加上錯誤的消息。(錯誤消息列出了所有嘗試打開的文件名)