字數:2061
閱讀時間:15分鐘
環境:webpack3.8.1
前言
本文續接文章: webpack實戰(一):真實專案中一個完整的webpack配置
上篇文章講的是框架的配置方案,本文講的是應用系統的配置方案。
這裡,我們先梳理一下框架和應用的關係。這裡我在框架配置中自定義了一個webpack外掛,其作用就是生成一個loader.js檔案,應用系統直接在頁面中載入該檔案,就會自動載入框架相應的資原始檔。即,我們這裡的目的是讓不同的應用系統可以使用同一份框架資源,從而減輕部署壓力。因此,我們所有的配置也是建立在這個基礎之上。
其次,由於我們的應用系統配置項大同小異,所以,所有的應用系統會有一個公共的配置檔案。
正文
應用系統的基本目錄結構如下:
-all
-build
-common
-webpack.common.config.js
-webpack.dev.config.js
-webpack.prod.config.js
-app1
-build
-webpack.config.js
-app2
-app3
複製程式碼
all資料夾中放置這所有的應用系統檔案。其下build資料夾放置所有應用系統的公共配置,app1、app2、app3分別表示不同的應用系統資料夾。在應用系統資料夾中,有一個build資料夾,放置應用系統的webpack配置檔案。
接下來我們就分別按照如上檔案,一一講解。
檔名 | 作用 |
---|---|
all/build/common/webpack.common.config.js | 公共配置中的基礎配置 |
all/build/common/webpack.dev.config.js | 公共配置中的開發環境配置 |
all/build/common/webpack.prod.config.js | 公共配置中的生產環境配置 |
app1/build/webpack.config.js | 應用系統中的配置 |
1.all/build/common/webpack.common.config.js
公共配置中的基礎配置,先上程式碼:
const wepackMerge = require('webpack-merge');
const Path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const Webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const ProdConfig = require('./webpack.prod.config');
const DevConfig = require('./webpack.dev.config');
//根據條件處理相關配置
const genarateConfig = (env, dirname, options) => {
//樣式loader
let cssLoader = [{
loader: 'css-loader',
options: {
sourceMap: true
}
}, {
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('postcss-cssnext')()
],
sourceMap: true
}
}, {
loader: 'less-loader',
options: {
sourceMap: true
}
}];
let styleLoader = [{
test: /\.(css|less)$/,
// exclude: /(node_modules|bower_components)/,
use: env === 'prod' ? ExtractTextPlugin.extract({
fallback: 'style-loader',
use: cssLoader
}) : [{
loader: 'style-loader',
options: {
sourceMap: true
}
}].concat(cssLoader)
}];
//指令碼loader
let jsLoader = [{
test: /\.js$/,
exclude: /(node_modules|bower_components|(\.exec\.js))/,
use: [{
loader: 'babel-loader'
}].concat(env === 'dev' ? [{
loader: 'eslint-loader'
}] : [])
}, {
test: /\.exec\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'script-loader'
}
}];
//檔案處理loader
let fileLoaderOptions = {
useRelativePath: false,
name: '[name]-[hash:5].[ext]',
publicPath: '../'
};
if (env === 'prod') {
fileLoaderOptions.limit = 8000;
}
let fileLoader = [{
test: /\.(pdf)$/,
exclude: /(node_modules|bower_components)/,
use: [{
loader: 'file-loader',
options: env === 'dev' ? fileLoaderOptions : Object.assign({}, fileLoaderOptions, {
outputPath: '../dist/pdf',
publicPath: './pdf'
})
}]
}, {
test: /\.(jpg|jpeg|png|icon|gif)$/,
exclude: /(node_modules|bower_components)/,
use: [{
loader: env === 'dev' ? 'file-loader' : 'url-loader',
options: env === 'dev' ? fileLoaderOptions : Object.assign({}, fileLoaderOptions, {
outputPath: '../dist/img',
publicPath: './img'
})
}]
}, {
//解析字型檔案
test: /\.(eot|svg|ttf|woff2?)$/,
exclude: /(node_modules|bower_components)/,
use: [{
loader: env === 'dev' ? 'file-loader' : 'url-loader',
options: env === 'dev' ? fileLoaderOptions : Object.assign({}, fileLoaderOptions, {
outputPath: '../dist/fonts',
publicPath: './fonts'
})
}]
}, {
//解析主頁面和頁面上的圖片
test: /\.(html)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src', 'img:data-src'],
minimize: true
}
}
}];
//webpack外掛
let plugins = [];
//HtmlWebpackPlugin 外掛
let htmlWebpacks = [new HtmlWebpackPlugin({
template: Path.join(dirname, '../index.ejs'),
inject: true,
filename: 'index.html',
chunks: ['index', 'loader'],
chunksSortMode: function (item1, item2) {
//先載入loader
if (item1.id === 'loader') {
return -1;
} else {
return 1;
}
}
})];
options.form === true && htmlWebpacks.push(new HtmlWebpackPlugin({
template: Path.join(dirname, '../forms/index.ejs'),
inject: true,
filename: 'forms/index.html',
chunks: ['form', 'formLoader'],
chunksSortMode: function (item1, item2) {
//先載入loader
if (item1.id === 'formLoader') {
return -1;
} else {
return 1;
}
}
}));
htmlWebpacks = options.htmlWebpackPlugins || htmlWebpacks;
plugins = plugins.concat(htmlWebpacks);
//複製資源
let copyPlugins = [
new CopyWebpackPlugin([{
from: './views',
to: 'views/'
}, {
from: './test',
to: 'test/'
}], {
ignore: ['**/.svn/**']
})
];
options.form === true && copyPlugins.push(new CopyWebpackPlugin([{
from: './forms/views',
to: 'forms/views'
}], {
ignore: ['**/.svn/**']
}));
copyPlugins = options.copyPlugins || copyPlugins;
plugins = plugins.concat(copyPlugins);
//全域性變數定義
plugins.push(new Webpack.DefinePlugin({
WEBPACK_DEBUG: env === 'dev'
}));
//友好提示外掛
plugins.push(new FriendlyErrorsPlugin());
//不打包預設載入項
plugins.push(new Webpack.IgnorePlugin(/^\.\/locale$/, /moment$/));
let entry = {
loader: './loader.js',
index: './index.js'
};
options.form === true && (entry.form = './forms/index.js', entry.formLoader = './forms/loader.js');
entry = options.entry == null ? entry : options.entry;
let config = {
devtool: 'source-map',
context: Path.join(dirname, '../'),
entry: entry,
output: {
path: Path.join(dirname, '../dist/'),
filename: env === 'dev' ? '[name]-[hash:5].bundle.js' : '[name]-[chunkhash:5].bundle.js'
},
module: {
rules: [].concat(styleLoader).concat(jsLoader).concat(fileLoader)
},
plugins: plugins
};
return config;
};
module.exports = (env, dirname, options) => {
options = options == null ? {} : options;
var config = env === 'dev' ? DevConfig(dirname, options) : ProdConfig(dirname, options);
return wepackMerge(genarateConfig(env, dirname, options), config);
};
複製程式碼
這個檔案也是我們最主要的配置內容,其中大部分內容和之前的框架配置內容一致,這裡不做贅述。
這裡有區別的就是,我們在輸出的函式中,新增了一個 options
引數,這個引數就是用來傳遞不同應用系統的定製化配置的。
其中:
options.form
是我們特殊應用的一個配置內容,是強業務相關內容,可以略過。
options.htmlWebpackPlugins
是配置 HtmlWebpackPlugin 外掛的,由於不同的應用系統的模板配置會有差異,所以我們將其作為配置項傳入。
options.copyPlugins
是配置 CopyWebpackPlugin 外掛的,不同的應用系統需要複製的內容不同。
options.entry
是配置外掛入口的,不同應用系統入口不同。
這裡,是我們的業務需求導致會有這些配置,在大家各自的業務中,這塊的配置可能都不一樣。
2.all/build/common/webpack.dev.config.js
公共配置中的開發環境配置,先上程式碼:
const Webpack = require('webpack');
module.exports = (dirname, options) => {
let gtUIPath = options.gtUIPath;
return {
devServer: {
port: '9090',
overlay: true,
//設定為false則會在頁面中顯示當前webpack的狀態
inline: true,
historyApiFallback: true,
//代理配置
proxy: {
'/gt-ui': {
target: gtUIPath,
changeOrigin: true,
logLevel: 'debug',
headers: {}
}
},
hot: true,
//強制頁面不通過重新整理頁面更新檔案
hotOnly: true
},
plugins: [
//模組熱更新外掛
new Webpack.HotModuleReplacementPlugin(),
//使用HMR時顯示模組的相對路徑
new Webpack.NamedModulesPlugin()
]
};
};
複製程式碼
這塊需要注意的就是我們傳遞了一個 options.gtUIPath
地址以作代理之用。這裡主要是為了解決字型資源等跨域的問題。
3.all/build/common/webpack.prod.config.js
公共配置中的生產環境配置,先上程式碼:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const HtmlWebpackInlineChunkPlugin = require('html-webpack-inline-chunk-plugin');
const Webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const Path = require('path');
const FileManagerPlugin = require('filemanager-webpack-plugin');
const SvnInfo = require('svn-info').sync('https://218.106.122.66/svn/application/trunk/gm-ui', 'HEAD');
module.exports = (dirname, options) => {
let pakageName = Path.basename(Path.join(dirname, '../'));
return {
plugins: [
//混淆程式碼
new UglifyJsPlugin({
sourceMap: true,
//多執行緒處理
parallel: true,
//使用快取
cache: true
}),
//提取css檔案
new ExtractTextPlugin({
filename: '[name]-[hash:5].css'
}),
new CleanWebpackPlugin(['dist'], {
root: Path.join(dirname, '../')
}),
new Webpack.NamedChunksPlugin(),
new Webpack.NamedModulesPlugin(),
//版本資訊
new Webpack.BannerPlugin({
banner: `SVNVersion: ${SvnInfo.revision}\nDate: ${new Date().toISOString().slice(0, 10)}`,
raw: false,
entryOnly: true,
include: /\.js/g
}),
//壓縮資料夾
new FileManagerPlugin({
onEnd: {
mkdir: [Path.join(dirname, '../package/')],
archive: [{
source: Path.join(dirname, '../dist'),
destination: Path.join(dirname, `../package/${pakageName}.zip`),
options: {}
}]
}
})
]
};
};
複製程式碼
這裡的配置與框架的配置基本一致,裡面的外掛也都有講解,這裡就不做贅述了。
4.app1/build/webpack.config.js
應用系統中的配置,先上程式碼:
const Path = require('path');
const wepackMerge = require('webpack-merge');
const commonConfig = require('../../build/common/webpack.common.config.js');
module.exports = env => {
//通用配置
let pConfig = commonConfig(env, __dirname, {
form: true,
//配置開發環境中框架的訪問地址
gtUIPath: 'http://localhost:8020/'
});
//基於通用配置的調整配置
let modifyConfig = {
resolve: {
alias: {
mainCtrl: Path.join(__dirname, '../controllers/main-ctrl')
}
}
};
//返回合併後的配置
return wepackMerge(pConfig, modifyConfig);
};
複製程式碼
由於公共的配置部分已經抽離出去了,所以這塊的配置就非常簡單了,這也是我們使用這種配置方案最大的優勢。
在這裡,我們可以通過options
引數和直接 merge 相應配置來做配置上的定製化調整。
如何使用配置
在package.json中做如下配置:
{
"scripts":{
"app1-d":"webpack-dev-server --env dev --config ./app1/build/webpack.config.js --open",
"app1-p":"webpack --env prod --config ./app1/build/webpack.config.js",
"app2-d":"webpack-dev-server --env dev --config ./app2/build/webpack.config.js --open",
"app2-p":"webpack --env prod --config ./app2/build/webpack.config.js"
}
}
複製程式碼
這樣,我們執行對應命令即可。
以上,就是我關於webpack實戰中的完整配置方案,希望對大家有所幫助,也希望大家多多提出寶貴意見。
歡迎關注我的微信公眾號: