項目需求製做爲新的app的分享頁,故須要製做多頁面應用,那既然app是新的,這邊咱們也要更新上,通過多方考察(度娘)下,綜合了一些他人的優勢並結合項目實況產生了此文。css
本文省去了部分初級操做。html
送上github地址 --- star,star,star我node
公共配置文件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中就能夠了。
開發環境配置文件:
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, 後面咱們在講
生產環境配置文件:
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文件
好了,這兩個文件比較簡單,就很少解釋了...
通常狀況下,咱們配置到這個地步就已經可使用了,但由於項目需求咱們須要配置超過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,到這裏基本就能夠快樂的編寫你的頁面啦。
結束啦~結束啦~...