webpack处理样式文件
前面讲到了webpack默认只能处理JS和JSON文件,那么项目中的其他资源有怎么处理呢?我们先来看看样式文件的处理方式吧。
把demo项目的目录定义的规范一些:
把index.js移动到js目录中,新建了一个styles目录,里面新建一个index.css,简单写一些样式。
index.css:
html, body {
margin: 0;
padding: 0;
}
.box1 {
width: 100px;
height: 200px;
}
在index.html里面添加一个div,class为box1。
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
</head>
<body>
<div class="box1"></div>
</body>
<!-- <script src="./index.js"></script> -->
<script src="./build/build.js"></script>
</html>
在index.js中引入index.css。
index.js:
import '../styles/index.css'
import {sum} from './modules/foo'
console.log(sum(1, 2))
上一篇中提到webpack.config.js的核心概念中,有一个loader,就是用来针对处理各种的资源,CSS样式文件需要用到两个loader,style-loader和css-loader,需要先安装这两个loader,然后修改webpack.config.js的配置。
安装style-loader和css-loader:
npm i style-loader css-loader -D
说明一下两个loader的作用:
- css-loader:将CSS样式添加到JS文件中。
- style-loader:将JS中的样式作为style标签插入html中。
所有必须要先执行css-loader,再执行style-loader。
在webpack.config.js中添加两个loader的配置。
webpack.config.js:
// nodejs自带的路径解析工具
const {resolve} = require('path')
// commonjs的规范
module.exports = {
// 项目的入口JS文件
entry: './js/index.js',
// 输出配置
output: {
// 构建好的文件输出的目录
// 这里需要绝对路径,所以要用到resolve函数来计算项目根目录的绝对路径
// 输出到项目根目录下的build目录下
path: resolve(__dirname, 'build'),
// 输出文件名
filename: 'build.js'
},
module: {
// loader会写rules数组里面
rules: [
// css样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.css$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: ['style-loader', 'css-loader']
}
]
},
// 插件会卸载plugins数组里面
plugins: [],
// 构建模式
mode: 'development'
}
使用webpack命令构建项目:
webpack
用浏览器打开index.html看到样式已经生效。
再检查元素,看看dom的结构,可以看到我们的样式以style标签插入到html中了。
再看看构建处理的build.js,会发现样式被构建到了JS里面了,所以index.html只需要引入build.js,再build.js执行时,就会把样式作为style标签插入index.html中。
但是项目中往往会存在很多CSS代码的,如果全部都构建到JS中,会使JS文件变得很大,影响页面加载速度,因此我们一般希望把CSS代码提取出来,另外构建,这时我们需要用到mini-css-extract-plugin这个插件。
提取CSS插件
首先要安装mini-css-extract-plugin。
npm i mini-css-extract-plugin -D
为了更好看到效果,我们添加多一个CSS文件。
box2.css:
.box2 {
width: 100px;
height: 100px;
background-color: deeppink;
}
再index.html中添加一个div,class为box2。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
</head>
<body>
<div class="box1"></div>
<div class="box2"></div>
</body>
<!-- <script src="./index.js"></script> -->
<script src="./build/build.js"></script>
</html>
再index.js中引入box2.css。
index.js:
import '../styles/index.css'
import '../styles/box2.css'
import {sum} from './modules/foo'
console.log(sum(1, 2))
在webpack.config.js中添加mini-css-extract-plugin的相关配置。
webpack.config.js:
// nodejs自带的路径解析工具
const { resolve } = require('path')
// 引入提取css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// commonjs的规范
module.exports = {
// 项目的入口JS文件
entry: './js/index.js',
// 输出配置
output: {
// 构建好的文件输出的目录
// 这里需要绝对路径,所以要用到resolve函数来计算项目根目录的绝对路径
// 输出到项目根目录下的build目录下
path: resolve(__dirname, 'build'),
// 输出文件名
filename: 'build.js'
},
module: {
// loader会写rules数组里面
rules: [
// css样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.css$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: [
// 用MiniCssExtractPlugin.loader代替style-loader
{
loader: MiniCssExtractPlugin.loader,
options: {
// 建议指定构建后的build目录相对路径
// 相对于提取的build.css的相对路径
// 这样做可以避免CSS中使用url加载其他资源时,路径错误的问题
publicPath: '../'
}
},
'css-loader'
]
}
]
},
// 插件会卸载plugins数组里面
plugins: [
// 配置提取css插件
new MiniCssExtractPlugin({
// 提取的CSS代码输出在build/styles/build.css中
filename: 'styles/build.css'
})
],
// 构建模式
mode: 'development'
}
步骤说明:
- 用require函数引入mini-css-extract-plugin。
- 在plugins中添加该插件的配置。
- 用MiniCssExtractPlugin.loader替换style-loader,因为已经不需要用JS来插入style标签了。
用webpack命令构建项目。
webpack
构建完成后,发现build目录中多了一个styles目录,里面有一个build.css,这是我们在plugins中配置的CSS代码输出文件,所以CSS代码都提取到里面去了。
build.js中对index.css构建的代码也变成了下面这样,说明用mini-css-extract-plugin处理了。
实际上CSS的提取工作已经完成了,但是我们会发现index.html根本没有引入build.css,样式肯定不会生效的,难道我们每次都要手动把资源引入到index.html中吗?当然不用,我们可以使用html-webpack-plugin来帮我们自动化引入资源。
html文件处理插件
首先还是要先安装html-webpack-plugin。
npm i html-webpack-plugin -D
在webpack.config.js中添加相应配置。
webpack.config.js:
// nodejs自带的路径解析工具
const { resolve } = require('path')
// 引入提取css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 引入html文件处理插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// commonjs的规范
module.exports = {
// 项目的入口JS文件
entry: './js/index.js',
// 输出配置
output: {
// 构建好的文件输出的目录
// 这里需要绝对路径,所以要用到resolve函数来计算项目根目录的绝对路径
// 输出到项目根目录下的build目录下
path: resolve(__dirname, 'build'),
// 输出文件名
filename: 'build.js'
},
module: {
// loader会写rules数组里面
rules: [
// css样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.css$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: [
// 用MiniCssExtractPlugin.loader代替style-loader
{
loader: MiniCssExtractPlugin.loader,
options: {
// 建议指定构建后的build目录相对路径
// 相对于提取的build.css的相对路径
// 这样做可以避免CSS中使用url加载其他资源时,路径错误的问题
publicPath: '../'
}
},
'css-loader'
]
}
]
},
// 插件会卸载plugins数组里面
plugins: [
// 配置提取css插件
new MiniCssExtractPlugin({
// 提取的CSS代码输出在build/styles/build.css中
filename: 'styles/build.css'
}),
// html文件处理插件
new HtmlWebpackPlugin({
// 指定要处理的html文件
template: './index.html'
})
],
// 构建模式
mode: 'development'
}
步骤说明:
- 引入用requrie函数引入html-webpack-plugin。
- 在plugins中添加相应配置。
html-webpack-plugin会自动加载所以用到的资源到指定的html中,所以index.html中的script标签也要删除,否则会重复加载。
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
</head>
<body>
<div class="box1"></div>
<div class="box2"></div>
</body>
</html>
我们重新构建项目,看到build目录中多了一个index.html。
里面会自动加载build.js和build.css。
这样我们写的JS和CSS都能生效了。
对于前端来说最头疼的莫过于浏览器兼容了,这会让我们做很多枯燥乏味的事情,现在我们只需要引入一个postcss-loader就可以通过webpack来自动完成大部分的兼容工作,解放生产力!
样式浏览器兼容性出路
我们需要用到postcss-loader和postcss-preset-env两个依赖,安装一下。
npm i postcss-loader postcss-preset-env -D
在webpack.config.js的css样式处理部分中追加postcss-loader的配置。
webpack.config.js:
// nodejs自带的路径解析工具
const { resolve } = require('path')
// 引入提取css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 引入html文件处理插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// commonjs的规范
module.exports = {
// 项目的入口JS文件
entry: './js/index.js',
// 输出配置
output: {
// 构建好的文件输出的目录
// 这里需要绝对路径,所以要用到resolve函数来计算项目根目录的绝对路径
// 输出到项目根目录下的build目录下
path: resolve(__dirname, 'build'),
// 输出文件名
filename: 'build.js'
},
module: {
// loader会写rules数组里面
rules: [
// css样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.css$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: [
// 用MiniCssExtractPlugin.loader代替style-loader
{
loader: MiniCssExtractPlugin.loader,
options: {
// 建议指定构建后的build目录相对路径
// 相对于提取的build.css的相对路径
// 这样做可以避免CSS中使用url加载其他资源时,路径错误的问题
publicPath: '../'
}
},
'css-loader',
// css浏览器兼容性处理
// !!!需要在package.json中添加browserslist配置指定浏览器兼容策略
{
loader: 'postcss-loader',
options: {
// 固定写法,不管它
iden: 'postcss',
// 使用postcss-preset-env来处理样式兼容问题
// 这里需要写一个返回数组的函数
// 固定写法,记住就行
plugins: () => [require('postcss-preset-env')()]
}
}
]
}
]
},
// 插件会卸载plugins数组里面
plugins: [
// 配置提取css插件
new MiniCssExtractPlugin({
// 提取的CSS代码输出在build/styles/build.css中
filename: 'styles/build.css'
}),
// html文件处理插件
new HtmlWebpackPlugin({
// 指定要处理的html文件
template: './index.html'
})
],
// 构建模式
mode: 'development'
}
在package.json中添加browserslist配置,指定浏览器兼容策略。
package.json(不要全部复制,只需要复制browserslist项就行哦):
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^4.2.2",
"html-webpack-plugin": "^4.3.0",
"mini-css-extract-plugin": "^0.10.0",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^1.2.1",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
},
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.1%",
"not dead",
"not op_mini all"
]
}
}
browserslist的配置分成开发环境和生产环境两部分,开发环境我们简单配置:last 1 chrome version就是最后一个版本的chrome浏览器的意思,后面同理。
生产环境这里的配置是:
-
0.1%:兼容99.9%以上的浏览器。
- not dead:不兼容已经被弃用的浏览器。
- not op_mini all:不兼容opera mini所有版本浏览器,因为已经基本没人用了。
npm官网有browserslist的详细说明,大家有兴趣去了解一下。
为了看到效果,我们在index.css中添加一些存在兼容性问题的样式。
index.css:
html, body {
margin: 0;
padding: 0;
}
.box1 {
width: 100px;
height: 200px;
background-color: pink;
display: flex;
backface-visibility: hidden;
}
.box2 {
width: 100px;
height: 100px;
background-color: deeppink;
}
现在用webpack命令构建项目。
webpack
我们看到build.css里面自动添加了浏览器兼容的样式代码。
要注意的是,postcss-loader默认使用的是生产环境,其配置于webpack.config.js中的mode没有关系,而是使用nodejs的运行环境,所以想要切换成开发环境,需要在webpack.config.js中添加代码:process.env.NODE_ENV = 'development’
CSS压缩
当然,项目中的CSS都是很庞大的,我们往往希望能将其压缩一下,此时需要一个名字很长的插件帮助我们:optimize-css-assets-webpack-plugin。
首先安装一下:
npm i optimize-css-assets-webpack-plugin -D
然后在webpack.config.js中添加插件配置。
webpack.config.js:
// nodejs自带的路径解析工具
const { resolve } = require('path')
// 引入提取css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 引入html文件处理插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入css压缩插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
// browserslist的开发和生产环境切换根webpack的mode没有关系,而是读取nodejs的运行环境
process.env.NODE_ENV = 'development'
// commonjs的规范
module.exports = {
// 项目的入口JS文件
entry: './js/index.js',
// 输出配置
output: {
// 构建好的文件输出的目录
// 这里需要绝对路径,所以要用到resolve函数来计算项目根目录的绝对路径
// 输出到项目根目录下的build目录下
path: resolve(__dirname, 'build'),
// 输出文件名
filename: 'build.js'
},
module: {
// loader会写rules数组里面
rules: [
// css样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.css$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: [
// 用MiniCssExtractPlugin.loader代替style-loader
{
loader: MiniCssExtractPlugin.loader,
options: {
// 建议指定构建后的build目录相对路径
// 相对于提取的build.css的相对路径
// 这样做可以避免CSS中使用url加载其他资源时,路径错误的问题
publicPath: '../'
}
},
'css-loader',
// css浏览器兼容性处理
{
loader: 'postcss-loader',
options: {
// 固定写法,不管它
iden: 'postcss',
// 使用postcss-preset-env来处理样式兼容问题
// 这里需要写一个返回数组的函数
// 固定写法,记住就行
plugins: () => [require('postcss-preset-env')()]
}
}
]
}
]
},
// 插件会卸载plugins数组里面
plugins: [
// 配置提取css插件
new MiniCssExtractPlugin({
// 提取的CSS代码输出在build/styles/build.css中
filename: 'styles/build.css'
}),
// html文件处理插件
new HtmlWebpackPlugin({
// 指定要处理的html文件
template: './index.html'
}),
// css压缩插件
new OptimizeCssAssetsWebpackPlugin()
],
// 构建模式
mode: 'development'
}
步骤说明:
- 引入optimize-css-assets-webpack-plugin插件。
- 在plugins中实例化。
构建项目看看效果,看到build.css被压缩成一行了。
对CSS样式的处理基本完成了,但是我们很多时候会使用到less、sass等CSS预处理工具,这些样式又怎么处理呢?下面我们以less为例说明。
CSS预处理工具的构建
其实很简单,只需要添加对应的loader就可以了。以less为例,我们需要安装less-loader,其作用是将less代码转换成css代码。
npm i less-loader -D
如果项目中没有less,还需要安装less。
npm i less -D
我们在styles文件夹中添加index.less文件。
index.less:
html, body {
background-color: #666666;
div {
margin-bottom: 10px;
}
}
在webpack.config.js中的rules中添加对less文件的处理。
webpack.config.js:
// nodejs自带的路径解析工具
const { resolve } = require('path')
// 引入提取css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 引入html文件处理插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入css压缩插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
// browserslist的开发和生产环境切换根webpack的mode没有关系,而是读取nodejs的运行环境
process.env.NODE_ENV = 'development'
// commonjs的规范
module.exports = {
// 项目的入口JS文件
entry: './js/index.js',
// 输出配置
output: {
// 构建好的文件输出的目录
// 这里需要绝对路径,所以要用到resolve函数来计算项目根目录的绝对路径
// 输出到项目根目录下的build目录下
path: resolve(__dirname, 'build'),
// 输出文件名
filename: 'build.js'
},
module: {
// loader会写rules数组里面
rules: [
// css样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.css$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: [
// 用MiniCssExtractPlugin.loader代替style-loader
{
loader: MiniCssExtractPlugin.loader,
options: {
// 建议指定构建后的build目录相对路径
// 相对于提取的build.css的相对路径
// 这样做可以避免CSS中使用url加载其他资源时,路径错误的问题
publicPath: '../'
}
},
'css-loader',
// css浏览器兼容性处理
{
loader: 'postcss-loader',
options: {
// 固定写法,不管它
iden: 'postcss',
// 使用postcss-preset-env来处理样式兼容问题
// 这里需要写一个返回数组的函数
// 固定写法,记住就行
plugins: () => [require('postcss-preset-env')()]
}
}
]
},
// less样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.less$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: [
// 用MiniCssExtractPlugin.loader代替style-loader
{
loader: MiniCssExtractPlugin.loader,
options: {
// 建议指定构建后的build目录相对路径
// 相对于提取的build.css的相对路径
// 这样做可以避免CSS中使用url加载其他资源时,路径错误的问题
publicPath: '../'
}
},
'css-loader',
// css浏览器兼容性处理
{
loader: 'postcss-loader',
options: {
// 固定写法,不管它
iden: 'postcss',
// 使用postcss-preset-env来处理样式兼容问题
// 这里需要写一个返回数组的函数
// 固定写法,记住就行
plugins: () => [require('postcss-preset-env')()]
}
},
'less-loader'
]
}
]
},
// 插件会卸载plugins数组里面
plugins: [
// 配置提取css插件
new MiniCssExtractPlugin({
// 提取的CSS代码输出在build/styles/build.css中
filename: 'styles/build.css'
}),
// html文件处理插件
new HtmlWebpackPlugin({
// 指定要处理的html文件
template: './index.html'
}),
// css压缩插件
new OptimizeCssAssetsWebpackPlugin()
],
// 构建模式
mode: 'development'
}
说明:在rules中添加less文件的处理,我们可以看到其实跟css的出路几乎是一样的,只不过在后面添加了一个less-loader而已,less-loader只是把less文件转换成css文件而已,然后再按照css的方法来处理即可。这里的代码显得有点重复臃肿,可以将公共部分提取出来。
webpack.config.js:
// nodejs自带的路径解析工具
const { resolve } = require('path')
// 引入提取css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 引入html文件处理插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入css压缩插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
// browserslist的开发和生产环境切换根webpack的mode没有关系,而是读取nodejs的运行环境
process.env.NODE_ENV = 'development'
// 提取样式处理的公共代码
const commonCssLoader = [
// 用MiniCssExtractPlugin.loader代替style-loader
{
loader: MiniCssExtractPlugin.loader,
options: {
// 建议指定构建后的build目录相对路径
// 相对于提取的build.css的相对路径
// 这样做可以避免CSS中使用url加载其他资源时,路径错误的问题
publicPath: '../'
}
},
'css-loader',
// css浏览器兼容性处理
{
loader: 'postcss-loader',
options: {
// 固定写法,不管它
iden: 'postcss',
// 使用postcss-preset-env来处理样式兼容问题
// 这里需要写一个返回数组的函数
// 固定写法,记住就行
plugins: () => [require('postcss-preset-env')()]
}
}
]
// commonjs的规范
module.exports = {
// 项目的入口JS文件
entry: './js/index.js',
// 输出配置
output: {
// 构建好的文件输出的目录
// 这里需要绝对路径,所以要用到resolve函数来计算项目根目录的绝对路径
// 输出到项目根目录下的build目录下
path: resolve(__dirname, 'build'),
// 输出文件名
filename: 'build.js'
},
module: {
// loader会写rules数组里面
rules: [
// css样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.css$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: [...commonCssLoader]
},
// less样式处理
{
// 正则表达式匹配需要处理的文件
// 匹配.css结尾的文件
test: /\.less$/,
// 需要多个loader是用use配置
// loader的执行顺序是从数组的最后一个元素开始
// 所以第一个元素是style-loader,第二个元素是css-loader
use: [
...commonCssLoader,
'less-loader'
]
}
]
},
// 插件会卸载plugins数组里面
plugins: [
// 配置提取css插件
new MiniCssExtractPlugin({
// 提取的CSS代码输出在build/styles/build.css中
filename: 'styles/build.css'
}),
// html文件处理插件
new HtmlWebpackPlugin({
// 指定要处理的html文件
template: './index.html'
}),
// css压缩插件
new OptimizeCssAssetsWebpackPlugin()
],
// 构建模式
mode: 'development'
}
重新构建项目看效果,发现less的样式也起作用了。
至此webpack对样式的处理基本完成了,喜欢的点个赞呗!
下一篇讲解图片的处理,拜拜啦!