webpack4 多頁面,多環境配置

項目需求製做爲新的app的分享頁,故須要製做多頁面應用,那既然app是新的,這邊咱們也要更新上,通過多方考察(度娘)下,綜合了一些他人的優勢並結合項目實況產生了此文。css

本文省去了部分初級操做。html

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

項目目錄:

一. webpack.base.conf.js

公共配置文件webpack

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)));
})
複製代碼

雖然有註釋,可是我仍是會解釋下,是否是很貼心...…… ^_^git

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的配置,你們直接引用就好,此處就很少講了。github

// 獲取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(此處所講內容皆在此文件中,下面一樣)中通常須要這樣配置web

module.exports = {
    entry: {
                index: './src/pages/index/index.js' ,
                page1: './src/pages/index/page1.js' ,
                page2: './src/pages/index/page2.js' 
        }    
        //下面暫時忽略
        /*...*/    
}
複製代碼

這樣的話咱們每添加一個文件就須要添加一項,頁面少還好,當頁面多了之後,不管是維護仍是開發都很費勁,並且配置文件咱們通常是不推薦作修改的。npm

而爲了不這樣的操做,咱們就須要去定義這兩個方法來幫助咱們json

咱們先來說getEntry,它實際上就是獲取到咱們pages下各個頁面的index.js,而後返回一個對象,這樣咱們就不用手動添加啦。api

而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-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,到這裏基本就能夠快樂的編寫你的頁面啦。

結束啦~結束啦~...

相關文章
相關標籤/搜索