在上一小节,我们将开发环境和生产环境区分开来。这一小节,我们来操作如何将样式文件的代码分割

1.安装

yarn add mini-css-extract-plugin

此插件将CSS提取到单独的文件中。它为每个包含CSS的JS文件创建一个CSS文件。它支持CSS和SourceMaps的按需加载。

它建立在新的webpack v4功能(模块类型)之上,并且需要webpack 4才能工作。

注意:MiniCssExtractPlugin 目前不支持HMR support(热重载)

2.提取css

bulid/plugins.js

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

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');    //生成html文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');  //清除
+  const MiniCssExtractPlugin = require("mini-css-extract-plugin");  //css样式提取


let plugins = [
	new HtmlWebpackPlugin({
		title: '瓦力博客',
		template: srcPath.src + '/index.html'   //以src/index.html为编译模板
	}),
+	new  MiniCssExtractPlugin({
+		filename: config.NODE_ENV == 'development'?'[name.css]': `${srcPath.css}/[name].[hash].css`,
+		chunkFilename: config.NODE_ENV == 'development'?'[id].css': `${srcPath.css}/[id].[hash].css`
+	}),   //css提取
	new CleanWebpackPlugin(),
	new webpack.HotModuleReplacementPlugin()	
]


module.exports = plugins;

build/module.js

const srcPath = require('./base/path');
const config = require('./base/config');
+  const MiniCssExtractPlugin = require("mini-css-extract-plugin");


let _module = {
	rules:[
		{
			test:/\.css$/,
			use:[
+				config.NODE_ENV == 'development'?'style-loader': MiniCssExtractPlugin.loader,
				{
					loader:'css-loader',
					options:{
						importLoaders:1
					}					
				},
				'postcss-loader'				
			]
		},
		{
			test:/\.scss$/,
			use:[
+				config.NODE_ENV == 'development'?'style-loader': MiniCssExtractPlugin.loader,
				{
					loader:'css-loader',
					options:{
						importLoaders:2
					}					
				},
				'sass-loader',
				'postcss-loader'
			]
		},
		{
			test: /\.less$/,
			use: [
+				config.NODE_ENV == 'development'?'style-loader': MiniCssExtractPlugin.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: srcPath.images,  //输出路径
						publicPath: config.NODE_ENV === 'development'?srcPath.images:srcPath.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:false  //是否压缩html
					}
				}
			]
		},
		{
			test: /(iconfont.svg)|\.(woff|woff2|eot|ttf|otf)$/,
			use:[
				{
					loader:'file-loader',
					options:{
						name:'[name].[ext]',  //[path] 上下文环境路径						
						outputPath: srcPath.iconfont,  //输出路径		
						publicPath: config.NODE_ENV === 'development'? srcPath.iconfont: srcPath.iconfont,    //公共路径						
					}
				}				
			]
		},
		{
			test: /\.js$/,
			exclude: /(node_modules|bower_components|lib)/,
			loader: 'babel-loader'
		}		
	]
}

module.exports = _module;

build/base/config.js

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'?'./':'./',
	apiUrl:'http://www.waliblog.com',
	port: 9999
}

module.exports = config;

package.json

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "webpack测试",
  "sideEffects": [
    "*.css",
+	"*.less",
+	"*.scss"
  ],
  "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",
    "mini-css-extract-plugin": "^0.7.0",
    "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"
  }
}

index.js

import './assets/css/index.css';

assets/css/index.css

h1{
    color: blue;
}

运行webpack

yarn run prod

ssl

打开dist/index.html,看到/assets/css/index.css文件样式生效了

ssl

<!--dist/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>title</title>
<link href="./assets/css/main.2df4cfd436df0ec42c4d.css" rel="stylesheet"></head>
<body>
<h1>欢迎来到瓦力博客</h1>
<span class="iconfont wali-icon-fuzhi"></span>
<img src="assets/images/wali_logo2BQg9e7.png" alt="">
<script type="text/javascript" src="./main.2df4cfd436df0ec42c4d.js"></script></body>
</html>

index.html文件中可以看到css文件被提取出来,并且被引用。

3.提取sass

上面配置已经写了,在plugins.jsmodule.js文件中配置了,这里就简单的验证一下

修改index.js

import './assets/sass/index.scss';

assets/sass/index.scss

body{
  h1{
    transform: translate(100px, 100px);
  }
}

运行webpack

yarn run prod

打开dist/index.html发现欢迎来到瓦力博客几个字发生偏移,说明/assets/sass/index.scss样式已经生效

ssl

4.提取less

上面配置已经写了,在plugins.jsmodule.js文件中配置了,这里就简单的验证一下

修改index.js

import './assets/less/index.less';

assets/less/index.less

body{
  h1{
    transform: rotate(90deg);
    background: red;
    text-align: center;
  }
}

运行webpack

yarn run prod

打开dist/index.html发现欢迎来到瓦力博客几个字旋转90度,背景变红,字体居中,说明/assets/less/index.less样式已经生效

ssl

5.生产环境css压缩

安装optimize-css-assets-webpack-plugin

yarn add optimize-css-assets-webpack-plugin

optimization.js

+  const config = require('./base/config');
+  const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

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
			}
		}
	}
}

+if(config.NODE_ENV != 'development'){
+	optimization['minimizer'] = [new OptimizeCssAssetsPlugin({})]
+}

module.exports = optimization

运行开发环境

yarn run build

发现没有发生什么改变,样式都在head标签内

运行生产环境

yarn run prod

发现dist目录下有css目录生成,打开css文件,看到css被压缩了。

6.总结

测试这小节其实遇到几个问题,小菜写出来:

调试

这小节的测试命令都变成yarn run prod,是测试生产环境下webpack配置,生产环境下css,less,scss都会被压缩,去注释,index.html文件也会被压缩,去注释,还要添加域名。这样一来从生成的文件方面就不容易看出webpack到底是否配置正确。所以小菜将config.js中修改

+	publicPath: env == 'development'?'./':'./',

就是生成后index.html文件能直接在本地打开而不报错。

build/module.js修改

+	minimize:config.NODE_ENV === 'development'? false:false  //是否压缩html

生成后index.html文件不要被压缩,还保留树形结构,便于查找

less和scss文件样式不起作用

小菜起初测试css文件,发现打包后css样式被提取出来,样式也生效了,但是换成

import './assets/less/index.less';
//或
import './assets/sass/index.scss';

时,在运行打包命令时,样式没有被提取出来。后来经过认真分析,发现/webpack/2019/05/24/webpack-10.html一节中配置了Tree ShakingTree shaking作用就是当我在index.js文件中使用import './assets/less/index.less'写法时,会检测在js文件中是否使用了引入来的less,很明显在index.js文件中没有使用,只是单纯的引入,所以Tree shaking就会自动帮我们过滤掉这行代码,就导致最终生成的js文件中没有样式,也就提取不出来样式了。

看了上面的解释,有的小伙伴可能会问import './assets/css/index.css'也在index.js文件中只引用没有使用,为什么css文件中的样式就能够生效呢?原因还是在/webpack/2019/05/24/webpack-10.html一节中,小菜在package.json中配置了css,没有配置less,scss

  "sideEffects": [
    "*.css",
+	"*.less",
+	"*.scss"
  ],

当我们配置*.css,*.less,*.scss后,在Tree shaking时,webpack就会知道忽略css,less,scss所有文件,即使在js文件中css,less,scss只引用没有使用,也不要删除这行代码。

7.代码回滚

build/module.js

- minimize:config.NODE_ENV === 'development'? false:false  //是否压缩html
+ minimize:config.NODE_ENV === 'development'? false:true  //是否压缩html

build/base/config.js

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'?'./':'./',
+	publicPath: env == 'development'?'./':'http://www.waliblog.com/',
	apiUrl:'http://www.waliblog.com',
	port: 9999
}

module.exports = config;

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) webpack 解析resolve(20) webpack Library的打包(21) webpack dll(22) webpack 多页面打包配置(23) webpack 搭建vue脚手架(24)