2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

User:机智的小鱼君/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 基本上差不多)