前言
本文续接文章: 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实战中的完整配置方案,希望对大家有所帮助,也希望大家多多提出宝贵意见。
原文发布时间:06/22
原文作者:老司机带你撸代码
本文来源开源中国如需转载请紧急联系作者