專案需求製作為新的app的分享頁,故需要製作多頁面應用,那既然app是新的,這邊我們也要更新上,經過多方考察(度娘)下,綜合了一些他人的優點並結合專案實況產生了此文。
本文省去了部分初級操作。
送上github地址 --- star,star,star我
專案目錄:
一. 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-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,到這裡基本就可以快樂的編寫你的頁面啦。
結束啦~結束啦~...