我们正常开发项目会有两种模式,一种是开发模式,另一种是生产模式。在开发模式中,为了便于代码调试方便我们快速定位错误,不会压缩混淆源代码。但在生成模式中,就不需要调试代码,而是需要更快的页面加载,缓存优化等来提高用户体验。因此开发环境和生产环境需要单独配置。

1.开发和生产区别

|开发模式生产模式 
css处理css文件
添加厂商前缀
提取css
处理css文件
添加厂商前缀
提取css文件
压缩css
去除注释
less处理less文件
添加厂商前缀
提取css
处理less文件
添加厂商前缀
提取css
压缩css
去除注释
sass处理sass文件
添加厂商前缀
提取css
处理sass文件
添加厂商前缀
提取css
压缩css
去除注释
图片去缓存
压缩
去缓存
压缩
字体去缓存去缓存
html去空格去空格
压缩
去注释
jses6转es5
懒加载
js语法检测
去注释
压缩
混淆
e6转e5
懒加载
js语法检测
devServer需要不需要
清除dist清除dist目录清除dist目录

2.文件结构

webpack官方建议开发模式和生产模式的配置分开,然后将两者的公共配置提取出来。这种方法好处就是直接用文件区分环境,如webpack.dev.js就是开发环境,webpack.prod.js就是生产环境。小菜决定不使用官方推荐的模式,而是采用自己习惯的配置模式。

myProject
+|-build
+   |-base
+       |-path.js
+       |-config.js
+   |-mode.js
+   |-entry.js
+   |-devtool.js
+   |-module.js
+   |-plugins.js
+   |-devServer.js
+   |-optimization.js
+   |-output.js
 |-dist
 |-node_modules
 |-src
     |-util
        |-math.js
     |-assets
        |-css
            |-index.css
        |-less
            |-index.less     
        |-sass
            |-index.scss
        |-images
            |-wali_logo.png
     |-index.html
     |-index.js
 |-package.json
 |-webpack.config.js
 |-postcss.config.js
 |-.babelrc

3.模块化

在模块化之前,小菜先更新clean-webpack-plugin插件,最新版本时3.0.0,更新完成后去package.json看下版本是不是最新的。

yarn add clean-webpack-plugin

更新完成后,请按官方文档说明修改之前webpack.config.js的写法

+  const { CleanWebpackPlugin } = require('clean-webpack-plugin');  //清除
+  new CleanWebpackPlugin()

4.package.json

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "webpack测试",
  "sideEffects":[
	"*.css"
  ],
  "main": "index.js",
  "author": "wali",
  "private": true,
  "license": "MIT",
  "scripts": {
   "dev": "npx webpack-dev-server --colors --mode=development",
    "prod": "npx webpack --colors --mode=production",
    "build": "npx webpack --colors --mode=development",
    "analyse": "npx webpack --profile --json> stats.json --colors  --mode=development"
  },
  "dependencies": {
    "@babel/core": "^7.4.5",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.4.4",
    "@babel/polyfill": "^7.4.4",
    "@babel/preset-env": "^7.4.5",
    "@babel/runtime": "^7.4.5",
    "@babel/runtime-corejs2": "^7.4.5",
    "autoprefixer": "^9.5.1",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^2.1.1",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^3.0.1",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "image-webpack-loader": "^4.6.0",
    "json5-loader": "^2.0.0",
    "less": "^3.9.0",
    "less-loader": "^5.0.0",
    "loadsh": "^0.0.4",
    "lodash": "^4.17.11",
    "node-sass": "^4.12.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.32.0",
    "webpack-cli": "^3.3.2",
    "webpack-dev-server": "^3.4.1"
  },
  "devDependencies": {
    "webpack-bundle-analyzer": "^3.3.2"
  }
}

5.path.js

路径build/base/path.js

const path = require('path');   

let dirPath = {};
dirPath.rootDir = path.resolve(__dirname, '../../');   //根路径
dirPath.nodeModule = path.resolve(dirPath.rootDir, './node_modules');  //包路径
dirPath.src = path.resolve(dirPath.rootDir,'./src');   //源文件
dirPath.dist = path.resolve(dirPath.rootDir,'./dist'); //生成线上

dirPath.assets = 'assets';               //静态资源
dirPath.css = 'assets/css';              //css
dirPath.sass = 'assets/sass'             //sass
dirPath.less = 'assets/less';            //less
dirPath.images = 'assets/images';        //images
dirPath.iconfont = 'assets/iconfont';    //iconfont


//将srcPath 挂载出去
module.exports = dirPath;

6.config.js

路径build/base/config.js

读取cli中参数--mode=development来判断当前是开发环境还是生产环境,config一些基础配置

/****
*** config配置
****/
let _mode = process.argv[process.argv.length - 1];
let env = _mode.replace(/--mode=(.+)/g,"$1");

let config = {
	NODE_ENV: env == 'development'?'development':'production',  //development 开发 production 线上
	publicPath: env == 'development'?'/':'http://www.waliblog.com',
	apiUrl:'http://www.waliblog.com',
	port: 9999
}

module.exports = config;

7.mode.js

路径build/mode.js

设置webpack是生成模式还是开发模式

const config = require('./base/config');

let mode = config.NODE_ENV == 'development'?'development':'production';

module.exports = mode;

8.devServer.js

路径build/devServer.js

const dirPath = require('./base/path');
const config = require('./base/config');

let devServer = {
	contentBase: dirPath.dist,
	clientLogLevel: 'info',
	open:true,  //启动时默认打开浏览器
	host:'localhost', //域名 0.0.0.0局域网可访问
	port:config.port || '9999',
	inline:true, //实时更新
	hot:true,    //热替换
	hotOnly:false, //true禁止浏览器自动刷新,false浏览器刷新
	proxy:{
		'/':{
			target: config.apiUrl
		},
		'/upload':{
			target: config.apiUrl
		}
	}
}

module.exports = devServer

9.entry.js

路径build/entry.js

const dirPath = require('./base/path');

let entry = {
	main: dirPath.src + '/index.js'
}

module.exports = entry;

10.devtool.js

路径build/devtool.js

const config = require('./base/config');

const devtool = config.NODE_ENV === 'development'?'cheap-eval-source-map':'cheap-source-map';

module.exports = devtool;

11.module.js

路径build/module.js

const dirPath = require('./base/path');
const config = require('./base/config');

let _module = {
	rules:[
		{
			test:/\.css$/,
			use:[
				'style-loader',
				{
					loader:'css-loader',
					options:{
						importLoaders:1
					}					
				},
				'postcss-loader'
				
			]
		},
		{
			test:/\.scss$/,
			use:[
				'style-loader',
				{
					loader:'css-loader',
					options:{
						importLoaders:2
					}					
				},
				'sass-loader',
				'postcss-loader'
			]
		},
		{
			test: /\.less$/,
			use: [
				'style-loader',
				{
					loader:'css-loader',
					options:{
						importLoaders:2
					}					
				},
				'less-loader',
				'postcss-loader'
			]
		},
		{
			test:/\.(png|svg|jpeg|jpg|gif)$/,
			use:[		
				{
					loader:'file-loader',
					options:{
						name:'[name][sha512:hash:base64:7].[ext]',  //[path] 上下文环境路径
						outputPath: dirPath.images,  //输出路径	
						publicPath: config.NODE_ENV === 'development'?dirPath.images:dirPath.images   //公共路径													
					}
				},
				{
					loader: 'image-webpack-loader',
					options: {
						bypassOnDebug: true, // [email protected]
						disable: true,       // [email protected] and newer
					},
				},
			]
		},
		{
			test: /\.html$/,
			use:[
				{
					loader:'html-loader',
					options:{
						arrts:['img:src','img:data-src'],
						minimize: config.NODE_ENV === 'development'? false:true  //是否压缩html
					}
				}
			]
		},
		{
			test: /(iconfont.svg)|\.(woff|woff2|eot|ttf|otf)$/,
			use:[
				{
					loader:'file-loader',
					options:{
						name:'[name].[ext]',  //[path] 上下文环境路径
						outputPath: dirPath.iconfont,  //输出路径		
						publicPath: config.NODE_ENV === 'development'? dirPath.iconfont: dirPath.iconfont,    //公共路径							
					}
				}				
			]
		},
		{
			test: /\.js$/,
			exclude: /(node_modules|bower_components|lib)/,
			loader: 'babel-loader'
		}
	]
}

module.exports = _module;

12.pligins.js

路径build/pligins.js

const dirpath = require('./base/path');

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');    //生成html文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');  //清除


let plugins = [
	new HtmlWebpackPlugin({
			title: '瓦力博客',
			template: dirpath.src + '/index.html'   //以src/index.html为编译模板
	}),
	new CleanWebpackPlugin(),
	new webpack.HotModuleReplacementPlugin()
]

module.exports = plugins;

13.optimization.js

路径build/optimization.js

let optimization = {
	usedExports: true,
	splitChunks: {
		chunks: 'all',
		minSize: 30000,
		maxSize: 0,
		minChunks: 1,
		maxAsyncRequests: 5,
		maxInitialRequests: 3,
		automaticNameDelimiter: '~',
		name: true,
		cacheGroups: {
			vendors: {
				test: /[\\/]node_modules[\\/]/,
				priority: -10
			},
			default: {
				minChunks: 2,
				priority: -20,
				reuseExistingChunk: true
			}
		}
	}
}

module.exports = optimization

14.output.js

路径build/output.js

const srcPath = require('./base/path');
const config = require('./base/config');

let output = {
	path: srcPath.dist,
	filename: '[name].[hash].js',
	publicPath: config.publicPath
}

module.exports = output;

15.webpack.config.js

const _mode = require('./build/mode');
const _devtool = require('./build/devtool');
const _entry = require('./build/entry');
const _module = require('./build/module');
const _plugins = require('./build/plugins');
const _devServer = require('./build/devServer');
const _optimization = require('./build/optimization');
const _output = require('./build/output');



module.exports = {
	mode: _mode,
	devtool: _devtool,
	entry: _entry,	
	module: _module,
	plugins: _plugins,
	devServer: _devServer,
	optimization: _optimization,
	output: _output	
}

将webpack配置拆分成单个js文件,然后在组装在一起,通过cli参数来区分是开发模式还是生产模式。拆分好后,在package.json文件中找到scripts运行对应的命令,如果能够运行成功,说明我们目前拆分的没有问题。如果报错了,请认真检查报错原因。

webpack4.x 教程

webpack 准备工作(1) webpack 手写最基本的配置(2) webpack 打包样式(3) webpack 让打包更便捷(4) webpack 打包图片(5) webpack iconfont打包(6) webpack devServer本地开发(7) webpack 处理ES6语法(8) webpack SourceMap配置(9) webpack Tree Shaking(10) webpack js代码分割(11) webpack 懒加载(12) webpack 打包分析,Preloading,Prefetching(13) webpack 模块化区分开发和生产(14) webpack 样式文件的代码分割(15) webpack 浏览器缓存(16) webpack shimming(17) webpack 环境变量(18) webpack Esline(19)