本WIKI由呜呜kurumi申请于2021年03月15日创建,编辑权限开放

如有内容错误,可以联系站长呜呜kurumi提交错误,赛马娘WIKI力求给大家带来最好的体验,也欢迎训练员们和我们一起建设
bugfix0531
全站通知:

进阶开发指南

阅读

    

2023-02-01更新

    

最新编辑:素子ちゃん_official

阅读:

  

更新日期:2023-02-01

  

最新编辑:素子ちゃん_official

来自赛马娘WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索
页面贡献者 :
素子ちゃん_official

模板

模板使用

第一个字符串为模板名,随后的字符串为参数,并用管道符“|”分开

{{赛马娘窗体|CSS初始化}}
{{赛马娘窗体|开始|标题=这是绿色窗体|宽=50%|高=auto|颜色=绿}}
{{赛马娘窗体|结束}}

模板建立

  • noinclude通常用于写一些模板的说明与示例,不包含在模板中
  • includeonly表示这是模板的内容,其中三重花括号表示接受一个参数,并用管道符分割表示默认值,如
{{{参数|default}}}

完整的模板如

<noinclude>
<pre>
{{带星头像|102701}}{{带星头像|104501}}<br>{{带星头像|103801}}
<//pre>
{{带星头像|102701}}{{带星头像|104501}}<br>{{带星头像|103801}}
</noinclude>
<includeonly><div style="margin:0px 75px 65px 0px;display:inline-block;"><div style="position:absolute;">[[文件:Chr_icon_{{padleft:|4|{{{1}}}}}_{{{1}}}_01.png|72px|link=]]</div><div style="position:absolute;overflow:hidden;">[[文件:Star_{{#ask:[[分类:赛马娘]][[ID::{{{1}}}]]|?初始星级|headers=hide|mainlabel=-}}_mask.png|72px|link=]]</div></div></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使用

按钮:

{{#Widget:Button|text=あ|class=small_btn|onclick=c1('あ') }}

输入框:

{{#Widget:Input|type=text|id=Text_input|style=width:300px}}

调用函数:

{{#Widget:JsText}} --引入函数类
{{#Widget:Input|type=text|id=testId|style=width:300px}} --创建一个输入框
{{#Widget:Button|text=SetText|class=small_btn|onclick=JsText.setValueById({id:'testId', text:'改变文本'});}}  --创建一个按钮,按下时调用JsText的setValueById函数

页面加载完成后再调用函数

jqurey有时需要等待其加载完成,但因加载顺序的问题会使得找不到定义。此时可以用以下widget在页面加载完成后再运行函数:

{{#Widget:页面加载完成后运行函数|function=函数名()}}

SWM

#ask使用

  • #ask用于查询页面中用#set定义的属性值
  • 对于代码{{#ask:[[分类:A]][[属性2::value]]|?属性1}}
  • [[分类:A]] 表示在A分类查询页面,[[属性2::value]] 表示查询的页面属性2的值必须为“value”,|?属性1 表示需要查询的属性
  • 我们通常用 |template=XXX 来指定接收数据的模板
{{#ask:[[分类:赛马娘]][[觉醒材料1::长距离]]|link=none}}
{{#ask:[[分类:赛马娘]][[初始星级::3]]|?ID|headers=hide|mainlabel=-}}

解析函数用法

[1]
更全版本:[2]

ResourceLoader系列模板

ResourceLoader系列模板允许用户方便的插入在MediaWiki命名空间下定义的JS、CSS文件,实现更自由更复杂的页面逻辑开发

JS、CSS

{{JS|Test}} --引入MediaWiki:Test.js
{{CSS|Test}} --引入MediaWiki:Test.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的函数

{{ES6|B}}

React

  • React模板用于导入React模块,实现Wiki的React应用开发
  • React模板调用的JS下能够使用React的Api
  • 有关在WIKI开发React App详见React开发指南
--带-babel后缀是React的JSX写法同步转码的JS文件
{{React|App1}} --引入MediaWiki:App1-babel.js
{{React|App2}} --引入MediaWiki:App2-babel.js

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:测试模块|测试|参数名=你好}}

调用后会产生这样的结果:

你好的说~

模块在调用后整个#invoke会变成调用函数的返回值。上述模块return text.."的说~",因此显示的是传入的参数+的说~。
模块最终的返回结果一定是字符串变量(string)。

模块解析优先级

由于#invoke本身算一种解析函数,因此模块返回的解析函数不能再次被解析。
例如,如果返回的字符串为:

    ...
    return "<div style='color:#FF0000'>特别周</div>{{#show:特别周|?ID}}[[特别周]][[file:Chr_icon_1001_100101_01.png|32px]]"

其产生的结果为:

特别周

{{#show:特别周|?ID}}特别周Chr icon 1001 100101 01.png


双括弧加#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:数学函数|随机数组|开始=1001|结束=1061|个数=10}}
...
我们生成了一个随机数组:{{#var:随机数组}}
...
之前的随机数组是:{{#var:随机数组}}
...

注:上述示例中,如果要生成另外一个不同的随机数组,必须再调用#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);
})

FlowDB

  • FlowDB.js是flowthread api的封装,它将某个评论页的评论视为数据库,以跨过Wiki权限限制来实现普通用户的数据存储
  • 其灵感来自于配卡讨论,有关FlowDB的使用详见FlowDB.js的注释 FlowDB.js