字數:2061css
閱讀時間:15分鐘html
環境:webpack3.8.1node
本文續接文章: webpack實戰(一):真實項目中一個完整的webpack配置webpack
上篇文章講的是框架的配置方案,本文講的是應用系統的配置方案。web
這裏,咱們先梳理一下框架和應用的關係。這裏我在框架配置中自定義了一個webpack插件,其做用就是生成一個loader.js文件,應用系統直接在頁面中加載該文件,就會自動加載框架相應的資源文件。即,咱們這裏的目的是讓不一樣的應用系統可使用同一份框架資源,從而減輕部署壓力。所以,咱們全部的配置也是創建在這個基礎之上。json
其次,因爲咱們的應用系統配置項大同小異,因此,全部的應用系統會有一個公共的配置文件。跨域
應用系統的基本目錄結構以下:緩存
-all
-build
-common
-webpack.common.config.js
-webpack.dev.config.js
-webpack.prod.config.js
-app1
-build
-webpack.config.js
-app2
-app3
複製代碼
all文件夾中放置這全部的應用系統文件。其下build文件夾放置全部應用系統的公共配置,app一、app二、app3分別表示不一樣的應用系統文件夾。在應用系統文件夾中,有一個build文件夾,放置應用系統的webpack配置文件。微信
接下來咱們就分別按照如上文件,一一講解。babel
文件名 | 做用 |
---|---|
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 | 應用系統中的配置 |
公共配置中的基礎配置,先上代碼:
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
是配置插件入口的,不一樣應用系統入口不一樣。
這裏,是咱們的業務需求致使會有這些配置,在你們各自的業務中,這塊的配置可能都不同。
公共配置中的開發環境配置,先上代碼:
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
地址以做代理之用。這裏主要是爲了解決字體資源等跨域的問題。
公共配置中的生產環境配置,先上代碼:
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: {}
}]
}
})
]
};
};
複製代碼
這裏的配置與框架的配置基本一致,裏面的插件也都有講解,這裏就不作贅述了。
應用系統中的配置,先上代碼:
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實戰中的完整配置方案,但願對你們有所幫助,也但願你們多多提出寶貴意見。
歡迎關注個人微信公衆號: