今天說好要提到 Webpack 的部分,然後,我最近其實有點疲憊,所以今天就先講一下目前在使用的 Webpack 就好了。
只是說,我可能不會多加解釋就是。
'use strict'
/**
* @author Scar Wu, Hina Chen.
*/
// Load Libraries
const path = require('path')
const webpack = require('webpack')
const webpackLodashPlugin = require('lodash-webpack-plugin')
const webpackOptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const webpackMiniCssExtractPlugin = require('mini-css-extract-plugin')
const vueLoaderPlugin = require('vue-loader/lib/plugin')
function cssLoaders (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS
? [cssLoader, postcssLoader]
: [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
return [webpackMiniCssExtractPlugin.loader].concat(loaders)
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
function styleLoaders (options) {
const output = []
const loaders = cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
let extEntries = {}
if (process.env.NODE_ENV === 'production') {
extEntries = {
}
}
// Merge Webpack Config
const webpackConfig = {
mode: process.env.NODE_ENV,
context: path.resolve(__dirname, './'),
entry: Object.assign({
app: './src/vue/main.js'
}, extEntries),
output: {
filename: 'js/[name].js',
chunkFilename: 'js/[name].js',
publicPath: '/vue/'
},
devtool: (process.env.NODE_ENV !== 'production') ? 'eval-source-map' : false,
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': path.resolve('./src/vue'),
'@assets': path.resolve('./src/vue/assets')
}
},
optimization: (process.env.NODE_ENV === 'production') ? {
splitChunks: {
chunks: 'all',
name: 'vendor'
},
runtimeChunk: {
name: 'manifest'
}
} : {},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: cssLoaders({
sourceMap: (process.env.NODE_ENV !== 'production'),
extract: (process.env.NODE_ENV === 'production')
}),
cssSourceMap: (process.env.NODE_ENV !== 'production'),
cacheBusting: true,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
},
...(styleLoaders({
sourceMap: (process.env.NODE_ENV !== 'production'),
extract: (process.env.NODE_ENV === 'production'),
usePostCSS: true
})),
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory=true'
},
{
test: /\.pug$/,
loader: 'pug-plain-loader'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'img/[name].[hash:7].[ext]'
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name].[hash:7].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name].[hash:7].[ext]'
}
}
]
},
plugins: [
new vueLoaderPlugin(),
...((process.env.NODE_ENV !== 'production') ? [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.NoEmitOnErrorsPlugin()
] : []),
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpackMiniCssExtractPlugin({
filename: 'css/[name].css'
}),
new webpackOptimizeCSSPlugin({
cssProcessorOptions: (process.env.NODE_ENV !== 'production')
? { safe: true, map: { inline: false } }
: { safe: true }
}),
new webpackLodashPlugin({
caching: true,
collections: true,
paths: true,
shorthands: true
})
],
node: {
setImmediate: false,
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
module.exports = webpackConfig
裡面的 extEntries
大抵上就是動態載入的部分,所以你會發現他只有在 Production 的環境底下才會被加入。因為在開發模式下,這種動態載入是會失效的,你還是需要在 main.js
裡面暫時的把他 require
進來方便開發。
然後在正式環境下,也會有 splitChunks
, runtimeChunk
這兩組設定,所以,他會把從 entry
進來的檔案做好分割,這裡就是我們的 Vue 可以通用的原因。就是不會出現我 Vue 不是你的 Vue 的那種狀況。
至於其他的,就看看就好(欸)。
然後,如果你想用昨天的 CSS Modules 的話,可以更動 use: laoder
的部分,簡單的寫法大概像是這樣:
// 前略 ...
output.push({
test: new RegExp('\\.' + extension + '$'),
oneOf: [
{
resourceQuery: /module/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[local]_[hash:base64:5]'
}
},
'sass-loader'
]
},
{
use: loader
}
]
})
// 後略 ...
至於那個 localIdentName
請不要用來惡搞你的客戶,感恩。不然大家好像都在我這邊學到一些不三不四的設定,這樣感覺超糟糕的。
我是糟糕的人沒關係,你們不可以!
我最近比較沒梗一點,明天聊到大型資料載入的部分,在跟大家分享一些算是八奇實例。