欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

分享一个博客园皮肤制作脚手架

程序员文章站 2022-03-12 10:24:36
前言 最近本着新年新气象的想法,想换套新的博客园皮肤。 之前自己做了一套制作皮肤的解决方案,这是当时分享皮肤以及解决方案的博客: "分享一款博客园皮肤及其解决方案" 。 不过在制作新的皮肤过程中,使用这个解决方案时还是遇到了很多问题。 而为了更方便地制作新皮肤,就升级了原先的解决方案,搭建了这套博客 ......

前言

最近本着新年新气象的想法,想换套新的博客园皮肤。

之前自己做了一套制作皮肤的解决方案,这是当时分享皮肤以及解决方案的博客:。

不过在制作新的皮肤过程中,使用这个解决方案时还是遇到了很多问题。

而为了更方便地制作新皮肤,就升级了原先的解决方案,搭建了这套博客园皮肤制作脚手架。

项目地址已从github换到了码云:。

具体的使用方式项目文档有介绍,这里就不赘述了,接下来主要是讲脚手架解决了什么问题以及解决方法。

原先的方案以及遇到的问题

原先的解决方案其实很简单,将css写在不同的less文件中,然后用webpack-dev-server保证本地开发。

在需要build最终的css时,通过mini-css-extract-plugin提取样式到最终的css文件中。

自定义js部分,就是一个单独的文件,在开发环境下通过引用这个文件来处理,构建时不需要这个文件,直接复制粘贴到博客园后台即可。

现在说一说原有方案遇到的一些问题:

  • 没有压缩css和js。早期时还好,但是到了后面功能变多,js和css文件体积在逐渐膨胀。
  • 博客园的脚本功能,实际上是通过script元素包裹的html元素,而且有的功能需要自己的html元素,所以最后js代码每次还需要手动加上相应的html元素。
  • 开发环境引用博客园的图片,页面上图片缺失,有时候报403,有时候会加载完但是不显示。这是博客园图片防盗链导致。

而我的脚手架就是针对这些问题来给出具体的解决方案。

解决css压缩问题

其实css压缩并不是个问题,主要是之前考虑到大家可以在博客里面按f12看css文件,然后方便copy功能。

但是现在我们还是需要去做一下css压缩的工作。

采用的是webpack4的常规方案:optimize-css-assets-webpack-plugin

具体的可以百度,或者直接查看我的项目代码,不展开说这个。

解决js压缩的问题(nodejs + uglify-js)

js压缩的问题不能靠webpack,因为靠webpack打包应用生成的最终js是包括一些webpack代码的,不那么纯粹。

而如果以库文件的方式打包,那么可能还需要配置两个webpack配置文件来处理,有点麻烦。

所以我采用的方案是不借助webpack,而是自己用nodejs引入uglify-js这个库来打包。

这里只写一下js压缩的关键代码:

const uglifyjs = require("uglify-js");

function buildmoudle(modulename) {
  fs.readfile(`./src/js/${modulename}.js`, (err, data) => {
    const code = data.tostring()
    let jscode = ''
    // 如果相应的模块js代码不为空,那么就进行压缩处理
    if (code !== '') {
      jscode = uglifyjs.minify(code).code
    }

    // ...
  })
}

这里实际上使用nodejs读取指定模块文件,然后使用uglify-js压缩和混淆js代码,最后再用nodejs将代码写入到指定模块文件即可。

解决js打包用html元素包裹的问题(自定义模板替换占位符)

其实到了这一步很好解决,咱们通过nodejs读取指定模板html文件的文本,将其中的占位符替换为压缩后的js代码即可。

先看看我们的模板文件:

<div id="loadingprocess"></div>
<script type="text/javascript" src="https://magi.com/assets/thirdparty/leader-line.min.js"></script>
<script type="text/javascript">
{{jscontent}}
</script>

再看看我们转换的关键代码:

/**
* 用指定模块的html代码包裹js代码
* @param {*} jscode js代码
* @param {*} moduletemplatehtml html模板代码
*/
function wrapjscodebyhtml(jscode, moduletemplatehtml) {
  return moduletemplatehtml.replace("{{jscontent}}", jscode)
}

最后将返回的最终代码输出到指定模块的html文件即可。

解决开发环境下页首文件和页尾文件包裹js注入到html中的问题(自定义webpack插件)

但是上面这一步还有点问题,咱们的模块文件改变了,还要去手动改变咱们开发环境的模板文件,要不然开发环境和最终生成的不同啊。

但是手动做这个事情太蠢了,多做几次完全就打消了我继续做皮肤的欲望了。

所以这里我们需要通过一个自定义的webpack插件,将这部分html代码和js注入到开发环境的html中:

/**
* 自制注入页首和页脚html模块代码的webpack插件
*/
function injectbloghtmlplugin() {
    console.info('html模块注入')
}

injectbloghtmlplugin.prototype.apply = function (compiler) {
    compiler.hooks.compilation.tap('injectbloghtmlplugin', (compilation) => {
        compilation.hooks.htmlwebpackpluginbeforehtmlprocessing.tapasync(
            'injectbloghtmlplugin',
            function (data, callback) {
                const headerjstext = fs.readfilesync(`./src/js/header.js`).tostring()
                const headerhtmltext = fs.readfilesync(`./src/template/header.html`).tostring()

                const footerjstext = fs.readfilesync(`./src/js/footer.js`).tostring()
                const footerhtmltext = fs.readfilesync(`./src/template/footer.html`).tostring()

                data.html = data.html.replace('{{headerhtml}}', headerhtmltext.replace("{{jscontent}}", headerjstext))
                data.html = data.html.replace('{{footerhtml}}', footerhtmltext.replace("{{jscontent}}", footerjstext))

                callback(null, data)
            }
        )
    })
};

htmlwebpackpluginbeforehtmlprocessing是编译时的一个hook,在html处理之前。

这里会将未压缩的代码和模板html一起注入到生成的html中。

解决改变页首模板文件和页尾模板文件不能热更新的问题(raw-loader)

到了这一步,这个生成页首和页尾模板文件的功能已经差不多了。

然而你会发现在开发环境下改变页首模板文件,页面并不会热更新。

这是因为页首html模板文件与入口文件不存在依赖关系,所以不能热更新。

这里你可能会有疑问,明明html-webpack-plugin依赖的页面模板文件就没有问题,修改之后可以热更。

那是因为html-webpack-plugin依赖的模板内部是做了处理的,所以改html-webpack-plugin的页面模板文件可以热更新。

但是我们这里的页首模板文件和页尾模板文件肯定不能这么处理,那么我们让他们存在依赖即可。

在入口文件app.js中引入相应的模板文件。

import './src/template/header.html'
import './src/template/footer.html'

但是很显然这样会引起编译报错,所以我们需要修改webpack配置用raw-loader进行处理一下。

module: {
    rules: [
        //...
        {
            test: /\.(htm|html)$/,
            use: [
                'raw-loader'
            ]
        }
    ],
},

这样一来修改页首模板和页尾模板就可以实现热更新了。

解决博客园图片防盗链导致开发环境图片加载错误

关于这个问题需要分两点去解决,第一点是img元素的图片加载问题,另外一点是css中background-image中图片的加载问题。

首先我们需要明白是什么原因导致的,这两个点都是因为同一个原因:防盗链处理。

当我们加载博客园的图片资源时,请求头部会有个referrer policy的头域,默认值为no-referrer-when-downgrade

它会向服务器发送我们当前引用这个资源的页面的地址,而博客园服务器会对不是博客园的地址做过滤处理,我们的图片也就加载不出来。

第一点很好解决,在我们的categorylist.htmlindex.htmlread.html三个页面模板文件的头部加上下面这段代码即可:

<meta name="referrer" content="no-referrer"/>

这样一来,我们图片的请求,不会再向图片服务器传递 referrer值。

第二点不好解决,因为图片是在css中的,上面的方式对它无效。

最开始的想法是有没有办法拦截图片请求然后做处理,然而并没有什么太好的办法。

后来我又想着本地开启一个代理,用nodejsexpress去处理图片资源,但是太过麻烦。

最后的处理方式比较质朴:

将css图片中引用的图片下载下来放在本地,开发的时候加载本地图片,生成的时候通过webpack的publicpath生成博客园的地址。

其实这种玩法是借鉴了我日常操作中的场景:开发的时候加载本地图片,发布的时候将本地图片上传到cdn之后,引用cdn地址。

而现在博客园的图片资源就是作为这个cdn地址存在的。

这里说起来简单,其实改造起来还是做了不少事的。

首先要将css中引用的图片下载下来,存放在src文件夹下的imgs文件夹中。

然后修改所有css中的博客园图片地址为本地图片地址。

因为之前都是用的网络图片地址,现在用本地图片的话,那么就要用file-loader处理一下。

需要在webpack.config.js中配置publicpath,但是考虑到咱们的webpack配置在生产和开发时都会用到,所以需要区分开发和生产环境。

这里没有用两套配置,因为毕竟改动不大,所以直接使用cross-env添加node_env环境变量,然后在webpack.config.js中加以判断。

这个方案其实不太满意,因为有一个手动下载图片,并修改开发代码的步骤。

不过也还能接受吧,毕竟只有第一次可能工作量大一点,您如果有更好的方法希望可以提一下建议。

不足之处

仍然有一点不足之处,但是这个不足之处仅仅只针对除我之外的使用者。

因为我自己的皮肤力求简洁,所以很多博客功能我没有用到,也就没有在页面模板中加入这方面的html代码。

如果您有需要的话,可以下载脚手架后,修改template文件夹中的三个html模板即可:

  • index.html 博客首页模板
  • read.html 博客详情页面模板
  • categorylist.html 分类列表页

我自己用的是博客园的blank皮肤模板,如果您用的是博客园其他的皮肤模板的话,可能也需要进行相应修改。

总结

最后吐槽一下,当我改造旧项目时,发现有些引用自博客园的css路径变动导致引用css失效。

而且有一次我发现自己博客里面的样式有些奇怪,原因是博客园的html结构中id还是class来的变动了一个名字。

这些会导致我需要去更新一下受到影响的页面模板,略显烦躁。

不过总的来说,问题不大,还是最喜欢博客园,主要是可以定制皮肤以及玩出各种新高度,现在还能锻炼下自己搭建前端脚手架的能力。

诸位要是对这个脚手架有什么好的建议,也希望不吝赐教。