用 webpack 玩轉部落格園 ⛷

前置

自定義部落格園樣式需要一下幾部分

  • 頁面訂製 CSS 程式碼
  • 部落格側邊欄公告(支援 HTML 程式碼) (支援 JS 程式碼)
  • 頁首 HTML 程式碼
  • 頁腳 HTML 程式碼

你可能不熟悉 頁首 HTML 程式碼 ,可以在此處放入一個 loading,因為頁面載入時會最先載入這部分。總之,頁面訂製 CSS 程式碼部落格側邊欄公告(支援 HTML 程式碼) (支援 JS 程式碼) 是最重要的兩部分。

方式

自定義你的皮膚可以使用下面這幾種方式:

css in js

通過 webpack 我們可以實現將 css 打包到 js, 樣式由 js 動態添加。使用時只需要這樣:

  1. 引入打包好的 js
  2. 給你的皮膚添加一些配置(可選的)
<script src="//guangzan.gitee.io/awescnb/index.js"></script>
<script>$.awesCnb({
             // 給你的皮膚添加一些配置
        })
</script>

如果不暴露配置直接引入一個 js 就好了,是不是非常簡單?

缺點

頁面不能及時渲染 css,因為 css in js,但是我們可以加個 loading 解決。 😀

優點

這樣做甚至可以實現瞬間切換多套皮膚,這裡有我以前做的一個例子供您查看。點擊查看切換效果.

css && js

上面那樣做的缺點很明顯, 需要一個 loading, 如果我們將 css 和 js 分離,把 css 放到 頁面訂製 CSS 程式碼,js 放到 部落格側邊欄公告(支援 HTML 程式碼) (支援 JS 程式碼) 就不會出現這種情況了。通過 webpack plugin MiniCssExtractPlugin 可以將皮膚程式碼分別打包出一個 js 文件和一個 css 文件。

webpack

如何寫一個架子方便開發部落格園皮膚呢,下面我把 webpack 配置放出來。

options.js

我把需要經常更改的配置單獨抽離一個文件,這樣做能節省我很多時間。

module.exports = {
  themeName: 'reacg',
  template: 'post',
  eslint: true,
  sourceMap: false,
  openAnalyzer: false,
  cssExtract: false,
}
  • themeName 創建的主題文件夾名稱 (運行 npm start 會啟動它)
  • template 本地開發要啟動的頁面 ‘index’ -> 首頁 ‘post’ -> 隨筆詳情頁 ‘tag’ -> 標籤頁 …
  • eslint 是否開啟 eslint
  • sourceMap 是否開啟 sourcemap
  • openAnalyzer build 時開啟 size 分析
  • cssExtract 是否單獨抽離 css

cssExtract 如果沒有開啟,build 會打包生成一個 dist, dist 下僅有 js 文件,如上, 這是 css in js 的方式,通過 js 動態添加 style ; 如果設為 true ,會在 dist 目錄下創建一個 ext 文件夾, 下面放了你的皮膚 js 和 css 文件。

正如你所見, ext 下每一個皮膚對應 js 和 css 兩個文件。

webpack.base.js

不言而喻, webpack.base.js 是開發環境和生產環境依賴的公共配置。

const path = require('path')
const {themeName, eslint} = require('./options')

const jsLoader = [
  {
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env'],
    },
  },
]

if (eslint) {
  jsLoader.push({
    loader: 'eslint-loader',
    options: {
      cache: true,
    },
  })
}

module.exports = {
  entry: {
    // 多出口
    index: './src/main.js',
    acg: './src/themes/acg/index.js',
    reacg: './src/themes/reacg/index.js',
    gshang: './src/themes/gshang/index.js',
    element: './src/themes/element/index.js',
    [themeName]: `./src/themes/${themeName}/index.js`,
  },
  output: {
    filename: '[name].js',
    path: path.join(__dirname, '..', 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: jsLoader,
      },
    ],
  },
  resolve: {
    alias: {
      '@': path.resolve('src'),
      '@awescnb': path.resolve('src/awescnb'),
      '@tools': path.resolve('src/assets/utils/tools'),
      '@plugins': path.resolve('src/plugins'),
      '@constants': path.resolve('src/constants'),
    },
  },
}

這裡主要關注多個 entry, 方便打包多個皮膚。

webpack.dev.js

開發環境配置

const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {template, themeName, sourceMap} = require('./options')

module.exports = merge(baseWebpackConfig, {
  mode: 'development',
  devtool: sourceMap ? 'inline-source-map' : '',
  devServer: {
    host: 'localhost',
    port: 8080,
    contentBase: path.join(__dirname, 'dist'),
    open: true,
    hot: true,
    disableHostCheck: true,
    proxy: {},
    before() {},
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: `src/templates/${template}.html`,
      inject: 'body',
      chunks: [`${themeName}`],
    }),
    new webpack.HotModuleReplacementPlugin({}),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
            },
          },
          'postcss-loader',
        ],
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
            },
          },
          'postcss-loader',
          'sass-loader',
        ],
      },
    ],
  },
})

webpack.prod.js

const path = require('path')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base')
const {openAnalyzer, cssExtract} = require('./options')
// const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const plugins = [
  // new CleanWebpackPlugin()
]
let output = {
  filename: '[name].js',
  path: path.join(__dirname, '..', 'dist'),
}
let cssLoader = [
  'style-loader',
  {
    loader: 'css-loader',
    options: {
      importLoaders: 1,
    },
  },
  'postcss-loader',
]
let scssLoader = [
  'style-loader',
  {
    loader: 'css-loader',
    options: {
      importLoaders: 2,
    },
  },
  'postcss-loader',
  'sass-loader',
]

if (openAnalyzer) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  plugins.push(
    new BundleAnalyzerPlugin({
      analyzerMode: 'server',
      analyzerHost: '127.0.0.1',
      analyzerPort: 8888,
      reportFilename: 'report.html',
      defaultSizes: 'parsed',
      openAnalyzer: true,
      generateStatsFile: false,
      statsFilename: 'stats.json',
      statsOptions: null,
      logLevel: 'info',
    })
  )
}

if (cssExtract) {
  output.path = path.join(__dirname, '..', 'dist/ext')
  const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  plugins.push(
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
      ignoreOrder: false,
    })
  )
  const MiniCssExtractPluginLoader = {
    loader: MiniCssExtractPlugin.loader,
    options: {
      publicPath: '../',
      hmr: process.env.NODE_ENV === 'development',
    },
  }
  cssLoader[0] = MiniCssExtractPluginLoader
  scssLoader[0] = MiniCssExtractPluginLoader
}

module.exports = merge(baseWebpackConfig, {
  mode: 'production',
  output,
  plugins,
  externals: {
    jquery: 'window.jquery',
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: cssLoader,
      },
      {
        test: /\.scss$/,
        use: scssLoader,
      },
    ],
  },
})

通過簡單的配置就可以實現任何你想要的結果。webpack 越來越火不是沒有原因的啊,快速上手, 生態豐富。如果 cli 用慣了,不如自己嘗試打造自己的工作流。如果你沒有時間造這個輪子,我已經將他封裝好了, 分享給大家。 free to use!可以用它快速地構建、安裝、分享你的部落格園皮膚。

GZ/awescnb