webpack实战(二):真实项目中应用系统配置

简介:

前言

本文续接文章: 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

原文作者:老司机带你撸代码

本文来源开源中国如需转载请紧急联系作者



相关文章
|
2月前
|
前端开发 JavaScript
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
|
4月前
|
缓存 资源调度 JavaScript
万字总结webpack实战案例配置
该文章总结了Webpack在实际项目中的配置案例,包括如何配置多页面应用、使用高级插件、优化构建速度及减少输出文件大小等方面的实战经验。
|
3月前
|
前端开发 JavaScript 开发者
深入了解Webpack:现代JavaScript应用的打包利器
【10月更文挑战第11天】 深入了解Webpack:现代JavaScript应用的打包利器
|
4月前
|
设计模式 前端开发 JavaScript
webpack实战之手写一个loader和plugin
该文章详细讲解了如何从零开始编写一个自定义的Webpack Loader和Plugin,包括它们的工作原理、开发步骤以及如何将自定义的Loader和Plugin集成到Webpack配置中。
webpack实战之手写一个loader和plugin
|
5月前
webpack——通过webpack-bundle-analyzer分析项目包占比情况
webpack——通过webpack-bundle-analyzer分析项目包占比情况
47 2
webpack——通过webpack-bundle-analyzer分析项目包占比情况
|
5月前
|
缓存 JSON JavaScript
简单介绍下从零搭建 Webpack 项目
本文详细介绍了Webpack中Loader的概念及其重要性。Webpack仅支持处理JS和JSON文件,而Loader能够帮助处理其他类型的文件,如CSS、图片等,并将其转换为有效的模块。文章首先解释了Loader的基本原理,接着介绍了几种常见Loader的配置和使用方法
30 1
|
5月前
|
前端开发 JavaScript API
|
5月前
|
JavaScript 前端开发 API
解锁前端开发新境界:Vue.js携手Webpack,打造高效构建流程,你的项目值得拥有!
【8月更文挑战第30天】随着前端技术的发展,模块化与组件化趋势愈发显著。Vue.js 以其简洁的 API 和灵活的组件系统,深受开发者喜爱;Webpack 则凭借强大的模块打包能力成为前端工程化的基石。两者结合,不仅简化了组件编写与引用,还通过模块热替换、代码分割等功能大幅提升开发效率。本文将通过具体示例,展示如何利用 Vue.js 和 Webpack 构建高效、有序的前端开发环境。从安装配置到实际应用,逐步解析这一组合的优势所在。
55 0
|
5月前
|
JavaScript 测试技术
在不同 webpack 版本的 Vue 项目中配置 Storybook
在不同 webpack 版本的 Vue 项目中配置 Storybook
|
6月前
|
JavaScript Windows
安装node.js与webpack创建vue2项目
安装node.js与webpack创建vue2项目
47 1