进阶开发指南
阅读
2023-02-01更新
最新编辑:素子ちゃん_official
阅读:
更新日期:2023-02-01
最新编辑:素子ちゃん_official
模板
模板使用
第一个字符串为模板名,随后的字符串为参数,并用管道符“|”分开
模板建立
- noinclude通常用于写一些模板的说明与示例,不包含在模板中
- includeonly表示这是模板的内容,其中三重花括号表示接受一个参数,并用管道符分割表示默认值,如
完整的模板如
Widget
- widget是一种html模板,与普通页面不同,其支持完整的Html语法及类似于<!--{$name|escape:'html'}-->的模板语法
- 这意味着你能在Widget中自由的编写Html,包括使用script嵌入js代码,以及用模板语法指定组件的参数,实现更自由更复杂的页面逻辑
- widget组件定义在命名空间Widget下,通过{{#widget: XXX}}调用。
Widget建立
函数:
<script>
var 类名 = {
函数名:function (arg) {
var Element=document.getElementById(arg["id"])
Element.value = arg["text"]
}
//调用方法:类名.函数名({id:'控件ID', text:'改变的文本'});
}
</script>
控件:
<button name = "<!--{$name|escape:'html'}-->" id = "<!--{$id|escape:'html'}-->" class = "<!--{$class|escape:'html'}-->" style = "<!--{$style|escape:'html'}-->" onclick = "<!--{$onclick|escape:'html'}-->"><!--{$text|escape:'html'}--></button>
//一定要带escape,否则会把数据原封不动传到wiki上,包括js脚本。这会使得普通用户可以毫无限制地使用各种js,产生安全隐患。
//调用:{{#Widget:Button|text=あ|class="small_btn"|onclick="c1('あ')" }}
Widget使用
按钮:
输入框:
调用函数:
页面加载完成后再调用函数
jqurey有时需要等待其加载完成,但因加载顺序的问题会使得找不到定义。此时可以用以下widget在页面加载完成后再运行函数:
SWM
#ask使用
- #ask用于查询页面中用#set定义的属性值
- 对于代码{{#ask:[[分类:A]][[属性2::value]]|?属性1}}
- [[分类:A]] 表示在A分类查询页面,[[属性2::value]] 表示查询的页面属性2的值必须为“value”,|?属性1 表示需要查询的属性
- 我们通常用 |template=XXX 来指定接收数据的模板
解析函数用法
ResourceLoader系列模板
ResourceLoader系列模板允许用户方便的插入在MediaWiki命名空间下定义的JS、CSS文件,实现更自由更复杂的页面逻辑开发
JS、CSS
ES6
- ES6与JS类似,但是ES6支持文件中出现import与export,即ES6模块化 [3]
- 在多个JS文件引入,或者实现一些通用功能的代码时,我们建议使用ES6模块化
- 在多个JS文件引用时,模块化能保证加载顺序
- 在实现通用功能时,多个JS能调用这一功能,减少代码重复
例如文件A.js中定义了功能函数a并export
let a = function() {
console.log("fun from a.js");
}
export default a
文件B.js通过import声明可以调用这个函数
import a from "./index.php?title=MediaWiki:A.js&action=raw&ctype=text/javascript"; //通用模板,改文件名就行
a();
最后在页面中调用B.js 我们就运行了来自A.js的函数
React
- React模板用于导入React模块,实现Wiki的React应用开发
- React模板调用的JS下能够使用React的Api
- 有关在WIKI开发React App详见React开发指南
Lua模块
模块创建
模块一般位于Module/模块名字空间,创建以『模块:』为前缀的页面即可创建一个模块。
目前模块仅支持lua语法,详见Scribunto
一个简单的模块模板如下:
local p = {} --p代表包(package)
p["测试"]=function(frame)
local text=frame.args["参数名"]
return text.."的说~"
end
return p
模块调用
在非模块名字空间页面中,可使用#invoke来调用模块,格式为:
{{#invoke:模块名|函数名|参数1名=参数1值|参数2名=参数2值|……}}
例如,将上述模块保存到“模块:测试模块”页面中,在调用时可以这样写:
调用后会产生这样的结果:
模块在调用后整个#invoke会变成调用函数的返回值。上述模块return text.."的说~",因此显示的是传入的参数+的说~。
模块最终的返回结果一定是字符串变量(string)。
模块解析优先级
由于#invoke本身算一种解析函数,因此模块返回的解析函数不能再次被解析。
例如,如果返回的字符串为:
...
return "<div style='color:#FF0000'>特别周</div>{{#show:特别周|?ID}}[[特别周]][[file:Chr_icon_1001_100101_01.png|32px]]"
其产生的结果为:
{{#show:特别周|?ID}}特别周
双括弧加#show构成解析函数,便以字符串的形式返回了,而div标签和双中括号均正常解析。
这是由于解析函数的优先级要低于标签和双中括号,双中括号等需要在解析函数执行之后再进行解析,因此可以正常显示。
wiki解析顺序的优先级一般如下:
普通标签(如<div>)> wiki表格({|...|}) > 双中括号([[...]]) > 双括弧({{..}})> 三括弧({{{...}}})> 特殊标签(如:<pre>)
优先级越高,解析顺序越靠后。
注1:有些机制会使某些特殊的函数看上去不符合上述顺序;
注2:有时候会出现wiki表格无法解析的情况,这是因为{|...|}的判定比较严格,需要按照固定的格式,例如大括弧必须顶格,行之间必须回车等,若不符合其规则便不能成功解析。
在模块中调用解析函数
如果你想在模块中使用解析函数,可以使用frame:callParserFunction方法:
frame:callParserFunction{ name = '#函数名', args = { '参数1', '参数2' } }
frame:callParserFunction( '#函数名', { '参数1', '参数2' } )
例如,如果你想使{{#show:特别周|?ID}}在模块中生效,可以这样写:
return "特别周的ID是:"..frame:callParserFunction{name='#show',args={"特别周","?ID"}}
其结果和 特别周的ID是:{{#show:特别周|?ID}} 相同。
在模块中调用模板
可以使用frame:expandTemplate方法在模块中使用模板:
frame:expandTemplate{ title = '模板名', args = { '参数1', '参数2', 参数3名 = '参数3' } }
例如:
return "头像是:"..frame:expandTemplate{ title = '模板:带星头像', args = { '100101' ,稀有度='3'} }
其结果和 头像是:{{模板:带星头像|100101|稀有度=3}} 相同。
模块调试
在模块编辑页的下方,提供了一个可用于调试的控制台,使得在不保存页面的条件下调试代码。
可将以下语句粘贴进该控制台并回车,以对某个参数进行调试:
frame = {}
frame["args"] ={}
frame.args["参数名"]="参数值"
p["函数名"](frame)
减小模块开销
避免重复加载数据
有时一个模块需要大量数据,例如模块:翻译数据库。而且有时这些模块会在一个页面中使用多次。每次{{#invoke:}}都解析这些数据十分耗时。可使用mw.loadData()避免这个问题。
local data1 = mw.loadData( '模块:翻译数据库' ) --一个页面只会加载一次,但从加载的模块返回的值必须是表,不能是函数
local data2 = require('模块:翻译数据库') --每次调用#invoke都会加载,如果数据库大比较耗时
-- 调用数据库数据
local data = data1.text_data_147
避免重复调用
有时,一些值会在页面上多次使用,如果每次都调用#invoke十分浪费资源。此时可以使用#vardefine和#var解析函数来避免。
例如,下列代码生成一个随机数组,并把结果通过#vardefine函数保存在『随机数组』变量中:
local p={}
p["随机数组"]=function(frame)
math.randomseed(tostring(os.time()):reverse():sub(1, 7))
local left=tonumber(frame.args["开始"])
local right=tonumber(frame.args["结束"])
local sum=tonumber(frame.args["个数"])
local num
local random_array = ''
if left > right then
local tmp = right
right = left
left = tmp
end
for i=1,sum do
num = math.random(left,right)
random_array = random_array..","..num
--mw.log("已产生随机数:",num)
end
mw.log("已产生随机数组:",string.sub(random_array, 2))
frame:callParserFunction{name='#vardefine',args={"随机数组",string.sub(random_array, 2)}}
return
end
return p
在调用时只需要使用#var函数即可,不需要再次调用#invoke。
注:上述示例中,如果要生成另外一个不同的随机数组,必须再调用#invoke,并将结果重新保存在另外一个变量名中。
参考资料
官方用户手册:https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual/zh
Lua语法:https://www.runoob.com/lua/
Mediawiki API
API沙盒: https://wiki.biligame.com/umamusume/%E7%89%B9%E6%AE%8A:ApiSandbox
JS中使用api
一个简单的调用api创建页面示例
//GET操作,固定函数
function getApi(url,callback=false,callbackE=false){
let ajax=new XMLHttpRequest()
if(!callback){
ajax.open("GET",url,false)
ajax.send()
let json=JSON.parse(ajax.responseText)
return json
}
ajax.open("GET",url)
ajax.send()
ajax.onreadystatechange=function(){
if(ajax.readyState==4){
if(ajax.status==200){
callback(ajax.responseText)
}
else if(ajax.status==404&&callbackE){
callbackE(true)
}
}
}
}
//POST操作,固定函数
function postFdApi(url,fd,callback=false,callbackE=false){
let ajax=new XMLHttpRequest()
if(!callback){
ajax.open("POST",url,false)
ajax.send(fd)
let json=JSON.parse(ajax.responseText)
return json
}
ajax.open("POST",url)
ajax.send(fd)
ajax.onreadystatechange=function(){
if(ajax.readyState==4){
if(ajax.status==200){
callback(ajax.responseText)
}
else if(ajax.status==404&&callbackE){
callbackE(true)
}
}
}
}
// 检查权限
if(mw.config.get('wgUserGroups').indexOf("sysop")==-1){
console.log("你没有管理员权限!")
}
// 设置变量
var UserToken = ""
var Title_OCC = "Sandbox"
var Text_OCC = "This is the second test!"
// 数据
var fd=new FormData()
fd.append("action","edit")
fd.append("title",Title_OCC)
fd.append("summary","Create by API")
fd.append("text",Text_OCC)
fd.append("format","json")
console.log(fd)
// 获取token
getApi("https://wiki.biligame.com/umamusume/api.php?action=query&meta=tokens&format=json",function(json){
let token=JSON.parse(json).query.tokens.csrftoken;
console.log(token)
UserToken = token;
})
//添加token
fd.append("token",UserToken)
//POST
postFdApi("https://wiki.biligame.com/umamusume/api.php",fd,function(json){
console.log(json);
//取得操作结果
let result=JSON.parse(json).edit.result;
console.log(result);
})