置顶公告:【置顶】关于临时开启评论区所有功能的公告(2022.10.22) | 【置顶】关于本站Widget恢复使用的公告
  • 你好~!欢迎来到萌娘百科镜像站!如需查看或编辑,请联系本站管理员注册账号。
  • 本镜像站和其他萌娘百科的镜像站无关,请注意分别。

用戶:機智的小魚君/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 基本上差不多)