• webpack4 多页面,多环境配置,逐行解释


    项目需求制作为新的app的分享页,故需要制作多页面应用,那既然app是新的,这边我们也要更新上,经过多方考察(度娘)下,综合了一些他人的优点并结合项目实况产生了此文。

    本文为了解释详细,篇幅可能会较长,并省去了部分初级操作。

    ok,送上github地址 --- star,star,star我

    项目目录:

    既然是webpack4,那就主要解释下build中的文件

    一.  webpack.base.conf.js 

    公共配置文件:

    const path = require('path');
    const webpack = require("webpack");
    const glob = require("glob");
    
    require("./env-config");
    
    // 分离css
    
    //消除冗余的css
    const purifyCssWebpack = require("purifycss-webpack");
    // html模板
    const htmlWebpackPlugin = require("html-webpack-plugin");
    //静态资源输出
    const copyWebpackPlugin = require("copy-webpack-plugin");
    const rules = require("./webpack.rules.conf.js");
    // 获取html-webpack-plugin参数的方法
    var getHtmlConfig = function (name, chunks) {
        return {
            template: `./src/pages/${name}/index.html`,
            filename: `${name}.html`,
            // favicon: './favicon.ico',
            // title: title,
            inject: true,
            hash: true, //开启hash  ?[hash]
            chunks: chunks,
            minify: process.env.NODE_ENV === "development" ? false : {
                removeComments: true, //移除HTML中的注释
                collapseWhitespace: true, //折叠空白区域 也就是压缩代码
                removeAttributeQuotes: true, //去除属性引用
            },
        };
    };
    
    function getEntry() {
        var entry = {};
        //读取src目录所有page入口
        glob.sync('./src/pages/**/*.js')
            .forEach(function (name) {
                var start = name.indexOf('src/') + 4,
                    end = name.length - 3;
                var eArr = [];
                var n = name.slice(start, end);
                n = n.slice(0, n.lastIndexOf('/')); //保存各个组件的入口 
                n = n.split('/')[1];
                eArr.push(name);
                entry[n] = eArr;
            });
        return entry;
    };
    
    module.exports = {
        entry: getEntry(),
        module: {
            rules: [...rules]
        },
        resolve: {
            alias: {
                '@': path.resolve(__dirname, '../src')
            }
        },// 提取公共代码
        optimization: {
            splitChunks: {
                cacheGroups: {
                    vendor: {   // 抽离第三方插件
                        test: /node_modules/,   // 指定是node_modules下的第三方包
                        chunks: 'initial',
                        name: 'vendor',  // 打包后的文件名,任意命名    
                        // 设置优先级,防止和自定义的公共代码提取时被覆盖,不进行打包
                        priority: 10    
                    }
                }
            }
        },
        plugins: [//静态资源输出
            new copyWebpackPlugin([{
                from: path.resolve(__dirname, "../src/assets"),
                to: './assets',
                ignore: ['.*']
            }]),
            // 消除冗余的css代码
            new purifyCssWebpack({
                paths: glob.sync(path.join(__dirname, "../src/pages/*/*.html"))
            }),
    
        ]
    }
    
    //配置页面
    const entryObj = getEntry();
    const htmlArray = [];
    Object.keys(entryObj).forEach(element => {
        htmlArray.push({
            _html: element,
            title: '',
            chunks: ['vendor', element]
        })
    })
    
    //自动生成html模板
    htmlArray.forEach((element) => {
        module.exports.plugins.push(new htmlWebpackPlugin(getHtmlConfig(element._html, element.chunks)));
    })

    虽然有注释,但是我还是会逐行解释下,是不是很贴心...……  ^_^

    const path = require('path');
    const webpack = require("webpack");
    const glob = require("glob");
    
    require("./env-config"); //暂时先不管它,后面会讲
    
    // 分离css
    
    //消除冗余的css
    const purifyCssWebpack = require("purifycss-webpack");
    // html模板
    const htmlWebpackPlugin = require("html-webpack-plugin");
    //静态资源输出
    const copyWebpackPlugin = require("copy-webpack-plugin");
    const rules = require("./webpack.rules.conf.js");

    基本上就是一些变量的引用,简单解释一下glob和rules,glob是我们需要这个插件对我们多页面的路径做一个处理,这样我们打包后才会生成相应的多个文件,而rules则是一些loader的配置,大家直接引用就好,此处就不多讲了。

    // 获取html-webpack-plugin参数的方法
    var getHtmlConfig = function (name, chunks) {
        return {
            template: `./src/pages/${name}/index.html`,
            filename: `${name}.html`,
            // favicon: './favicon.ico',
            // title: title,
            inject: true,
            hash: true, //开启hash  ?[hash]
            chunks: chunks,
            minify: process.env.NODE_ENV === "development" ? false : {
                removeComments: true, //移除HTML中的注释
                collapseWhitespace: true, //折叠空白区域 也就是压缩代码
                removeAttributeQuotes: true, //去除属性引用
            },
        };
    };
    
    function getEntry() {
        var entry = {};
        //读取src目录所有page入口
        glob.sync('./src/pages/**/*.js')
            .forEach(function (name) {
                var start = name.indexOf('src/') + 4,
                    end = name.length - 3;
                var eArr = [];
                var n = name.slice(start, end);
                n = n.slice(0, n.lastIndexOf('/')); //保存各个组件的入口 
                n = n.split('/')[1];
                eArr.push(name);
                entry[n] = eArr;
            });
        return entry;
    };

    这两个方法比较重要,因为当我们使用多页面打包的时候,在module.exports里的entry(此处所讲内容皆在此文件中,下面同样)中一般需要这样配置

    module.exports = {
        entry: {
                    index: './src/pages/index/index.js' ,
                    page1: './src/pages/index/page1.js' ,
                    page2: './src/pages/index/page2.js' 
            }    
            //下面暂时忽略
            /*...*/    
    }        

    这样的话我们每添加一个文件就需要添加一项,页面少还好,当页面多了以后,无论是维护还是开发都很费劲,而且配置文件我们一般是不推荐做修改的。

    而为了避免这样的操作,我们就需要去定义这两个方法来帮助我们

    我们先来讲getEntry,它实际上就是获取到我们pages下各个页面的index.js,然后返回一个对象,这样我们就不用手动添加啦。

    而getHtmlConfig则是用来配合htmlwebpackplugin的,htmlwebpackplugin需要一些配置,而我们是多页面应用就需要产出多个同配置但是不同名的html文件,这个方法就是用我们传入的参数而产生不同的页面名配置。

    众所周知,在单页面应用中,我们只需要一个index.html就可以了,但是在多页面我们需要一一对应的页面,而去一个个new htmlwebpackplugin也违反了我们的初衷

    //配置页面
    const entryObj = getEntry();
    const htmlArray = [];
    Object.keys(entryObj).forEach(element => {
        htmlArray.push({
            _html: element,
            title: '',
            chunks: ['vendor', element]
        })
    })
    
    //自动生成html模板
    htmlArray.forEach((element) => {
        module.exports.plugins.push(new htmlWebpackPlugin(getHtmlConfig(element._html, element.chunks)));
    })

    我们的页面是有规律的,也就是index.js对应相应的index.html,那我们就可以利用之前的getEntry来获取到js文件,在生成对应的数组,利用gethtmlconfig,放入htmlwebpackplugin中就可以了。

    二.  webpack.dev.conf.js 

    开发环境配置文件:

    const path = require('path');
    const webpack = require("webpack");
    const merge = require("webpack-merge");
    const webpackConfigBase = require('./webpack.base.conf');
    
    const webpackConfigDev = {
        mode: 'development', // 通过 mode 声明开发环境
        output: {
            path: path.resolve(__dirname, '../dist'),
            // 打包多出口文件
            filename: './js/[name].bundle.js'
        },
        devServer: {
            contentBase: path.join(__dirname, "../src"),
            publicPath:'/',
            host: "127.0.0.1",
            port: "8090",
            overlay: true, // 浏览器页面上显示错误
            // open: true, // 开启浏览器
            // stats: "errors-only", //stats: "errors-only"表示只打印错误:
            hot: true, // 开启热更新
            //服务器代理配置项
            proxy: {
                '/test/*':{
                    target: 'https://www.baidu.com',
                    secure: true,
                    changeOrigin: true
                }
            }
        },
        plugins: [
            //热更新
            new webpack.HotModuleReplacementPlugin(),
            
            new webpack.DefinePlugin({
                'process.env.BASE_URL': '"' + process.env.BASE_URL + '"'
            })
              
        ],
        devtool: "source-map",  // 开启调试模式
    
    }
    module.exports = merge(webpackConfigBase, webpackConfigDev);

    引入所需

    webpack-merge,用来合并我们的webpack.base.conf.js和webpack.dev.conf.js

    proxy,因为我们启动dev环境的话,是在本地调试,会出现跨域的问题,proxy为我们做一层代理,解决跨域难题。

    webpack.DefinePlugin, 后面我们在讲

    三.  webpack.prod.conf.js 

    生产环境配置文件:

    const path = require('path');
    const webpack = require("webpack");
    const merge = require("webpack-merge");
    // 清除目录等
    const cleanWebpackPlugin = require("clean-webpack-plugin");
    
    const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
    const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
    const extractTextPlugin = require("extract-text-webpack-plugin");
    const webpackConfigBase = require('./webpack.base.conf');
    
    process.env.NODE_ENV = "test"
    
    const webpackConfigProd = {
        mode: 'production', // 通过 mode 声明生产环境
        output: {
            path: path.resolve(__dirname, '../dist'),
            // 打包多出口文件
            filename: './js/[name].[hash].js',
            publicPath: './'
        },
        devtool: 'cheap-module-eval-source-map',
        plugins: [
            //删除dist目录
            new cleanWebpackPlugin(['dist'], {
                root: path.resolve(__dirname, '../'), //根目录
                // verbose Write logs to console.
                verbose: true, //开启在控制台输出信息
                // dry Use boolean "true" to test/emulate delete. (will not remove files).
                // Default: false - remove files
                dry: false,
            }),
            new webpack.DefinePlugin({
                'process.env.BASE_URL': '"' + process.env.BASE_URL + '"'
            }),
            // 分离css插件参数为提取出去的路径
            new extractTextPlugin({
                filename: 'css/[name].[hash:8].min.css',
            }),
            //压缩css
            new OptimizeCSSPlugin({
                cssProcessorOptions: {
                    safe: true
                }
            }),
            //上线压缩 去除console等信息webpack4.x之后去除了webpack.optimize.UglifyJsPlugin
            new UglifyJSPlugin({
                uglifyOptions: {
                    compress: {
                        warnings: false,
                        drop_debugger: false,
                        drop_console: true
                    }
                }
            })
        ],
        module: {
            rules: []
        },
    
    }
    module.exports = merge(webpackConfigBase, webpackConfigProd);
    引入所需
    cleanWebpackPlugin, 我们每次build后都会产出许多不同名文件(hash不同),但我们是不需要之前的文件的,利用这个插件来清除掉我们之前的dist文件

    好了,这两个文件比较简单,就不多解释了...

    四.  env-config.js 

    一般情况下,我们配置到这个地步就已经可以使用了,但因为项目需求我们需要配置超过2个的环境变量(webpack默认两个development和production)
    而我们不同的环境可能需要不同的接口:
    ps: text ---> 请求test-api ,
        dev ---> 请求dev-api,
        pro ---> 请求api,
    ...
    这时我们就需要利用前面所没有讲的webpack.DefinePlugin了,这个插件是用来声明全局变量的,我们依据不同的打包命令定义不同的接口名称。

    'use strict'
    
    const path = require('path')
    /*
     * 环境列表,第一个环境为默认环境
     * envName: 指明现在使用的环境
     * dirName: 打包的路径,只在build的时候有用
     * baseUrl: 这个环境下面的api 请求的域名
     * assetsPublicPath: 静态资源存放的域名,未指定则使用相对路径
     * */
    const ENV_LIST = [
        {
            //开发环境
            envName: 'dev',
            dirName: 'dev',
            baseUrl: 'http://100.xxx.xxx',
            assetsPublicPath:'/'
        },
        {
            //测试环境
            envName: 'test',
            dirName: path.resolve(__dirname, '../dist'),
            baseUrl: 'http://111.xxx.xxx',
            assetsPublicPath: '/'
        },
        {
            //生产环境(命令行参数(process.arg)中prod是保留字,所以使用pro)
            envName: 'pro',
            dirName: path.resolve(__dirname, '../dist'),
            baseUrl: 'http://122.xxx.xxx',
            assetsPublicPath:'/'
        },
     
    ]
    
    const argv = JSON.parse(process.env.npm_config_argv).original || process.argv
    const HOST_ENV = argv[2] ? argv[2].replace(/[^a-z]+/ig,"") : ''
    //没有设置环境,则默认为第一个
    const HOST_CONF = HOST_ENV  ? ENV_LIST.find(item => item.envName === HOST_ENV) : ENV_LIST[0]
    // 把环境常量挂载到process.env方便客户端使用
    process.env.BASE_URL = HOST_CONF.baseUrl
    // process.env.ENV_NAME = HOST_CONF.envName
    
    module.exports.HOST_CONF = HOST_CONF
    module.exports.ENV_LIST = ENV_LIST
    
    

    我们声明一个数组,里面用来存放我们的环境变量,在将获取到的环境变量挂载到process.env上,如我所写的话,我们在客户端直接console.log(process.env.BASE_URL)就是当前环境了。

    那么程序怎么知道我们打包的是哪个环境呢?那就要去package.json中去做文章了

    "scripts": {
        "test": "npm run build --[test]",
        "dev": "cross-env NODE_ENV=development webpack-dev-server  --config build/webpack.dev.conf.js",
        "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js"
      },

    这里我只做了test环境的配置,其他的只要配置npm run build --[xxx]即可,这里提醒一点,dev和build个人觉得其实不应该算是两个环境变量,应该是你打包的手段(原谅我只能这样解释),你可以理解为一个是debug环境,一个是上线环境。

    而前面没有说的其实就是webpack.base.config.js引入我们的变量,而dev和prod中在去将我们需要的变量声明全局啦。

    ok,到这里基本就可以快乐的编写你的页面啦。

    结束啦~结束啦~...



  • 相关阅读:
    测试PHP-FPM的工作流中的疑惑点
    摘要
    Centrifugo简单试用
    react-redux的基本用法
    PHP中使用ElasticSearch(二)
    PHP中使用ElasticSearch(一)
    Vue中的状态管理器
    Laravel数据库迁移
    快速搭建一个vue开发环境
    使用cURL尝试ElasticSearch
  • 原文地址:https://www.cnblogs.com/bbbiu/p/9593854.html
Copyright © 2020-2023  润新知