项目需求制做为新的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,到这里基本就能够快乐的编写你的页面啦。
结束啦~结束啦~...