字数: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实战中的完整配置方案,但愿对你们有所帮助,也但愿你们多多提出宝贵意见。
欢迎关注个人微信公众号: