用 webpack 玩轉部落格園 ⛷
前置
自定義部落格園樣式需要一下幾部分
- 頁面訂製 CSS 程式碼
- 部落格側邊欄公告(支援 HTML 程式碼) (支援 JS 程式碼)
- 頁首 HTML 程式碼
- 頁腳 HTML 程式碼
你可能不熟悉 頁首 HTML 程式碼 ,可以在此處放入一個 loading,因為頁面載入時會最先載入這部分。總之,頁面訂製 CSS 程式碼 和 部落格側邊欄公告(支援 HTML 程式碼) (支援 JS 程式碼) 是最重要的兩部分。
方式
自定義你的皮膚可以使用下面這幾種方式:
css in js
通過 webpack 我們可以實現將 css 打包到 js, 樣式由 js 動態添加。使用時只需要這樣:
- 引入打包好的 js
- 給你的皮膚添加一些配置(可選的)
<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!可以用它快速地構建、安裝、分享你的部落格園皮膚。