使用者:機智的小魚君/Help:創建你的專屬萌娘百科皮膚
這是一篇帶有整活性質的教程,但是——它是一篇真實的、具有可操作性的教程。
我真的在試圖教會你從零搭建一個單頁面應用程式 (Single Page Application)!
準備工作
獲取 wiki 的元信息
你也許已經知道了,MediaWiki 應用程式會在全局對象上綁定一個 mw.config 對象,該對象上保存了一些 wiki 的基本信息。
如果你還不知道這一點,你可以按 F12 打開你的瀏覽器控制台,列印一下這個對象。
const mwWikiData = mw.config.values
你之後會用到它。
獲取 wiki 的頁面內容
我們可以簡單直接地克隆一份文章區域的 DOM 節點:
const mwContentText = document
.querySelector('#mw-content-text')
?.cloneNode(true)
/** ↑ 注意这里的 true
* 我们顺便克隆了文章区域的DOM上绑定的事件
* 这样做可以保留住[[T:Hide]]等模板的交互性
*/
開始構建你的皮膚吧
現在你得到的數據已經足以支撐構建一個新皮膚了。
首先,讓我們找到一個根節點,我們將會用我們構建的皮膚替換其中的內容。
這個節點的最佳人選——額,我不知道該如何措辭,不過我猜你知道我的意思——就是當前皮膚所有內容除 body 元素以外最頂層的父級元素,例如對於 MoeSkin 來說,div#app 就是最好的選擇。
現在你有一個根節點了,是時候開始創作了。選擇一個你最熟悉的實現方案,或者把每一種方案都嘗試一遍,我會確保教程中的每一種實現方案都是切實可行的。
Vanilla JS
大家都喜歡 Vanilla JS。Vanilla JS 是一個快速、輕量級、跨平台的 JavaScript 框架。我們可以用它構建強大的 JavaScript 應用程式。
全世界的網際網路企業都在使用它——沒錯,所有!因此,自信點,想想看,現在你正在使用與 Google 和 Facebook 等國際企業相同的技術去編寫專屬於你自己的萌娘百科皮膚!
首先我們需要一個頁頂,裡面是網站的名字:
const siteName = document.createElement('h1')
siteName.innerText = mw.config.get('wgSiteName')
const header = document.createElement('header')
header.appendChild(siteName)
接下來,安排條目的名字和條目的內容:
const article = document.createElement('article')
const firstHeading = document.createElement('h1')
firstHeading.classList.add('firstHeading')
firstHeading.id = 'firstHeading'
firstHeading.innerText = mw.config.get('wgPageName')
article.append(
firstHeading,
mwContentText // 注意:这个变量是我们在准备工作时取得的
)
要不我們再往頁腳里添加一些東西?比如歡迎當前的用戶訪問:
const footer = document.createElement('footer')
footer.innerText = `欢迎光临,${mw.config.get('wgUserName')}`
好了,齊活了,我們用它替換根元素里的內容吧:
const root = document.querySelector('body > div:first-of-type')
root.innerHTML = ''
root.append(header, article, footer)
最後,我們將代碼封入一個立即調用函數表達式,然後來完整看一遍:
/**
* My first MGP skin - Vanilla JS ver.
* @author <你的名字>
*/
;(() => {
// Header
const siteName = document.createElement('h1')
siteName.innerText = mw.config.get('wgSiteName')
const header = document.createElement('header')
header.append(siteName)
// Article
const mwContentText = document
.querySelector('#mw-content-text')
?.cloneNode(true)
const article = document.createElement('article')
const firstHeading = document.createElement('h1')
firstHeading.classList.add('firstHeading')
firstHeading.id = 'firstHeading'
firstHeading.innerText = mw.config.get('wgPageName')
article.append(firstHeading, mwContentText)
// Footer
const footer = document.createElement('footer')
footer.innerText = `欢迎光临,${mw.config.get('wgUserName')}`
// Mount
const root = document.querySelector('body > div:first-of-type')
root.innerHTML = ''
root.append(header, article, footer)
})()
把這段代碼複製粘貼到瀏覽器控制台,執行它。沒錯,你已經成功地打造了一個簡單的皮膚。
jQuery
很多人的 JavaScript 編程之路都是從 jQuery 開始的。
雖然它是一個比較古早的工具庫,不過它真的很好上手!
鑑於實現思路與 Vanilla JS 沒有太大的差異,直接貼出最終的源碼好了:
/**
* My first MGP skin - jQuery ver.
* @author <你的名字>
*/
$(function () {
// Header
const siteName = $('<h1>').text(mw.config.get('wgSiteName'))
const header = $('<header>')
header.append(siteName)
// Article
const mwContentText = $('#mw-content-text').clone(true)
const article = $('<article>')
const firstHeading = $('<h1>', {
class: 'firstHeading',
id: 'firstHeading',
}).text(mw.config.get('wgPageName'))
article.append(firstHeading, mwContentText)
// Footer
const footer = $('<footer>').text(`欢迎光临,${mw.config.get('wgUserName')}`)
// Mount
const root = $('body > div:first-of-type')
root.empty()
root.append(header, article, footer)
})
實現看上去簡潔了不少,不得不感慨一句,不愧是 jQuery 的頂級封裝。
Vue
其實你會發現,如果沒有任何交互,使用 Vue 構建網頁和 jQuery 看上去竟然沒有多大區別!
import(
'https://cdn.bootcdn.net/ajax/libs/vue/3.2.45/vue.esm-browser.prod.min.js'
).then((Vue) => {
const { createApp, h, defineComponent, ref, onMounted, nextTick } = Vue
// Header
const siteName = h('h1', {}, mw.config.get('wgSiteName'))
const header = h('header', {}, [siteName])
// Article
const mwContentText = document
.querySelector('#mw-content-text')
?.cloneNode(true)
const firstHeading = h('h1', {
class: 'firstHeading',
id: 'firstHeading',
text: mw.config.get('wgPageName'),
})
const article = h('article', {}, [
firstHeading,
h('div', { id: 'mw-content-container' }),
])
// Footer
const footer = h('footer', {}, [`欢迎光临,${mw.config.get('wgUserName')}`])
// Mount
const App = defineComponent({
render() {
return h('div', { ref: 'appRef' }, [header, article, footer])
},
setup() {
const appRef = ref()
onMounted(async () => {
await nextTick()
appRef.value
.querySelector('#mw-content-container')
.append(mwContentText)
})
return { appRef }
},
})
createApp(App).mount('body > div:first-of-type')
})
沒錯,我正在在安利你來用 Vue
React
施工中(不過在使用純渲染函數的情況下,代碼看上去會和 vue 基本上差不多)