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

字数: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 应用系统中的配置

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实战中的完整配置方案,但愿对你们有所帮助,也但愿你们多多提出宝贵意见。

欢迎关注个人微信公众号:

相关文章
相关标签/搜索