在webpack快入门之从零到一初体验 一文中,我们从整体了解了webpack相关特性并手动初步实现了一个可执行编译的webpack环境,这一节,我们将在此基础上继续探讨以下功能的实现:
- css的编译与加载,基于style-loader、css-loader、postcss-loader、autoprefixer以及css预处理(以less为例)。
- ES6的编译与加载,基于babel及其相关模块实现。
- css资源与js资源的分割,基于插件 extract-text-webpack-plugin。
css资源文件的编译与加载
css最基本的编译要依靠于style-loader、css-loader这两个加载器,所谓最基本,就是在不考虑使用css预处理器以及css后处理器的情况。预处理器包括less、scss、sass、stylus,后处理器如postcss的autoprefixer等。首先我们先弄明白各加载器的作用:
- style-loader,将所有处理好的css样式以行内元素style标签的格式动态注入到界面的head标签中去。
- css-loader,用来处理css样式,例如把css中类似@import()或者url()这样的引用资源进行引入或者处理。
- autoprefixer,postcss提供用来自动处理css在不同浏览器之间前缀等问题,从这里我们也可以看出,使用autoprefixer需要先引入postcss。
- 预处理-loader,所有预处理器都是以css为终点生成文件的一种专门的编程语言,为css增加了一些编程特性。
了解了这些加载器各自的职能,那我们现在先小试牛刀,用代码来体验一下。
style-loader && css-loader
1、首先,我们在src项目文件夹下新建css文件夹,并新增一样式文件common.css,并写一些全局的css
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: linear-gradient(to bottom, #abcdef, #f3f5f7);
}
2、安装css-loader和style-loader,进入到命令行,执行
cnpm i style-loader css-loader --save-dev
3、现在我们在入口文件app.js中去引入我们的common.css:
//app.js
import './css/common.css';
...
4、webpack配置,打开webpack.config.js,新增module配置项:
let path = require('path');
...
module.exports = {
entry: path.resolve(__dirname, './src/main.js'),//入口文件地址
output: {
path: path.resolve(__dirname, './dist'),
filename: 'js/[name]-[chunkhash].js'
},
module: {
rules: [ {
test: /\.css$/,//匹配所有css文件
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]//指定加载器
exclude: /node_modules///排除对node_module文件夹下面的所有资源的匹配
}]
},
...
}
需要注意,在指定加载器的时候,要注意各加载器的顺序,webpack中loader的解析是从右往左的顺序进行的,以当前为例,css文件首先是要通过css-loader进行处理,在将处理好的css交由style-loader,因此在指定加载器的时候,首先应该是style-loader,其次是css-loader。
现在,我们再次执行webpack编译命令,在浏览器中打开生成的index.html,发现页面背景已经如我们设置改变了,并且html文档中css也已经以style模式注入到head中了。
autoprefixer
上文我们已经说过,autoprefixer是css后处理器postcss提供的一个对css3中个别属性在不同浏览器下需要添加浏览器前缀的样式处理工具,因此在使用autoprefixer之前,我们需要安装postcss-loader来加载它。
cnpm i postcss-loader autoprefixer --save-dev
1、现在,我们打开webpack.config.js配置文件,在module下配置autoprefixer
...
module: {
rules: [{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{ loader: "postcss-loader" }//指定postcss加载器
],
exclude: /node_modules/
}]
}
...
在配置postcss-loader之后,我们需要注意,由于css-loader处理文件导入的方式,因此加载器postcss-loader不能与CSS模块一起使用。 为了使它们正常工作,可以添加css-loader的importLoaders选项。。
...
module: {
rules: [{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader",options: { importLoaders: 1 } },//importLoaders解决由于css-loader处理文件导入的方式导致postcss-loader不能正常使用的问题
{ loader: "postcss-loader" }//指定postcss加载器
],
exclude: /node_modules/
}]
}
...
2、接下来,我们要给postcss-loader指定加载autoprefixer的操作,在根目录新建postcss.config.js
//postcss.config.js
module.exports = {
plugins: [
require("autoprefixer")()
]
}
在postcss.config.js配置文件中,我们给引入了autoprefixer,这样一来,在postcss-loader加载的时候,就会自动去读取该配置文件里面的配置项,加载autoprefixer了。
3、所有配置项已经完成,现在我们在原有的common.css文件中去添加样式,用来测试autoprefixer
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: linear-gradient(to bottom, #abcdef, #f3f5f7);
}
.flexbox {
display: flex;
background: linear-gradient(to bottom, #abcdef, #096)
}
添加完成后,运行webpack命令进行编译,并在浏览器中打开index.html,打开控制台查看head标签下生成的style标签,已经可以看到autoprefixer已经自动为我们添加了浏览器前缀
autoprefixer 为我们提供了可以根据需求配置的参数,例如我么可以让它最终生成的css兼容最近的N个版本就好,但这不是我们本次讨论的重点,有兴趣的同学可以自己去官网查看。
//postcss.config.js 设置给最近5个版本的浏览器加前缀
module.exports = {
plugins: [
require("autoprefixer")({browsers:'last 5 version'})
]
}
css预处理(less-loader)
1、在src/css文件夹下新建style.less文件,并添加样式作为测试,并在入口文件app.js中引用该less文件
@base: #f938ab;
.box-shadow(@style, @c) when (iscolor(@c)) {
-webkit-box-shadow: @style @c;
box-shadow: @style @c;
}
.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
.box-shadow(@style, rgba(0, 0, 0, @alpha));
}
.box {
color: saturate(@base, 5%);
border-color: lighten(@base, 30%);
div { .box-shadow(0 0 5px, 30%) }
}
//app.js
import './css/common.css';//引入css
import './css/style.less';//引入less
2、安装less以及less-loader,并在webpack配置项中进行配置。
cnpm i less less-loader --save-dev
在module配置项中新增一条对less文件处理的规则:
...
module: {
rules: [{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader", options: { importLoaders: 1 } },
{ loader: "postcss-loader" },
],
exclude: /node_modules/
}, {
test: /\.less$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader", options: { importLoaders: 1 } },
{ loader: "postcss-loader" },
{ loader: "less-loader" }//less放在最后,因为要最先加载(loader从右往左加载的规则)
]
}]
},
...
配置完成后,再次运行webpack命令,并在浏览器中查看效果,可以看到在head标签内又新增了一个style标签,并且已经将less编译成css了
Babel编译ES6
babel是一个js的转码器,将ES6或者ES7转换成ES5或者ES3;babel编译主要依靠于核心库babel-core,也就是说,用ES6进行编程而不需要担心浏览器环境是否支持。
babel-preset-env代替babel-preset-ES2015
在此之前,我猜测有很多的同学使用Babel的时候,preset必然会选择ES2015,但是最近babel官方推出了babel-preset-env,并建议在使用的时候选择env代替之前的ES20**。env为我们提供了更智能的编译选择,在此我们就不展开,有兴趣的同学可以去官网深入了解。
babel-preset-env编译ES6
1、首先,我们安装babel-loader、babel-core以及babel-preset-env,并在入口文件app.js中新增一段带ES6语法的js。
cnpm install --save-dev babel-loader babel-core babel-preset-env
import './css/common.css';
import './css/style.less';
//生成一个整数随机值,数值大于4则返回成功的Promise对象,否则返回错误的promise对象
function getData() {
let promise = new Promise((resolve, reject) => {
let key = ~~(Math.random() * 10);
let temp = ['es6','babel']
if (key >= 5) {
let obj = {
msg: "ok",
data: [key,...temp]
};
resolve.call(this, obj);
} else {
let obj = {
msg: "error",
data: [key,...temp]
};
reject.call(this, obj);
}
})
return promise
}
//找到页面中的Dom
let container = document.querySelector('#app');
//获取返回的结果并打印到界面
getData().then((data) => {
container.innerHTML = JSON.stringify(data)
}, (err) => {
container.innerHTML = JSON.stringify(err)
})
在这段代码中,我么使用了Promise来返回最后生成的结果,并将结果打印到界面中,其中运用了Promise、箭头函数、解构赋值等ES6的语法
2、现在,我们去webpack配置文件中新增一条规则:
...
module: {
rules: [{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader", options: { importLoaders: 1 } },
{ loader: "postcss-loader" }
],
exclude: /node_modules/
}, {
test: /\.less$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader", options: { importLoaders: 1 } },
{ loader: "postcss-loader" },
{ loader: "less-loader" }
]
},
//新增规则,编译js
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}]
},
并且在根目录下新增.babelrc文件,用来存放babel相关的配置:
//.babelrc
{
"presets": [
["env",{
"targets": {
"chrome": 52,
"browsers": ["last 2 versions", "safari 7"]
}
}]
]//设置编译场景,并配置目标结果兼容到chrome52版本以上等等。
}
现在我们在命令行运行webpack命令,并在浏览器中刷新界面,可以看到页面输出了我们结果。
并且,在生成的js中发现,通过编译后的js已经不再是es6的写法了,变异后的核心代码如下:
...
//编译后的js
function getData() {
var _this = this;
var promise = new Promise(function (resolve, reject) {
var key = ~~(Math.random() * 10);
var temp = ['es6', 'babel'];
if (key >= 5) {
var obj = {
msg: "ok",
data: [key].concat(temp)
};
resolve.call(_this, obj);
} else {
var _obj = {
msg: "error",
data: [key].concat(temp)
};
reject.call(_this, _obj);
}
});
return promise;
}
var container = document.querySelector('#app');
getData().then(function (data) {
container.innerHTML = JSON.stringify(data);
}, function (err) {
container.innerHTML = JSON.stringify(err);
});
...
到此,通过babel编译js就基本完成,当然,babel提供了很多的插件和配置项,有兴趣的同学可以深入了解,本文暂不做深层次探讨。
CSS与JS文件分离
通过上面的操作,我们已经可以成功的将css和js进行编译打包,但是当我们再次去查看打包后的js文件时,发现js体积很大(目前js大小22kb),我们只是写了几行js而已啊,那么这又是怎么回事?
实际上,webpack在打包的时候,会把所有的模块,最终打包在一个文件里面,这也是为什么我们的js文件会如此之大,但却没看到有相应的css文件的原因。
静态资源处理器extract-text-webpack-plugin
extract-text-webpack-plugin用来实现不同文件的分离,其用法也和之前插件的用法一样。
1、安装插件
cnpm i extract-text-webpack-plugin --save-dev
2、在webpack配置项中对之前我们的css编译规则进行更改:
...
//引入插件
let extractTextPlugin = require('extract-text-webpack-plugin');
//初始化两个实例用于两处规则分别加载
let extractCSS = new extractTextPlugin('css/[name]-one.css');
let extractLESS = new extractTextPlugin('css/[name]-two.css');
...
module: {
rules: [{
test: /\.css$/,
exclude: /node_modules/,
//extractCSS实例对css进行操作
use: extractCSS.extract([
// { loader: "style-loader" },//style-loader不能和插件一起使用
{ loader: "css-loader", options: { importLoaders: 1 } },
{ loader: "postcss-loader" }
])
}, {
test: /\.less$/,
exclude: /node_modules/,
//extractLESS实例对less进行操作
use: extractLESS.extract([
// { loader: "style-loader" },
{ loader: "css-loader", options: { importLoaders: 1 } },
{ loader: "postcss-loader" },
{ loader: "less-loader" }
])
}, {
test: /\.js$/,
use: [{ loader: "babel-loader" }],
exclude: /node_modules/
}]
},
...
plugins: [
//注册插件
extractCSS,
extractLESS
]
在配置中需要注意一下几点:
- 插件不能喝style-loader同时使用,原因是style-loader的作用是获取到js内部的样式并一行内形式插入到head标签中,但是extractTextPlugin插件是将js内部的代码提取成css文件并添加外部引用的方式进行加载。
- 在多处用到extractTextPlugin插件的时候,我们需要对插件进行功能性的拆分,就比如我们的项目中有两处规则用到它,因此我们首先对插件进行两次实例化得到两个实例,并分别用到两个处理规则中。
- 别忘了在plugins下进行注册。
本节我们队webpack的加载器模块相关配置进行实战演练,学习完这一节后,您应该了解一下几点:
- css的处理相关,包括各加载器的作用、执行顺序、css预处理和后处理操作。
- ES6的编译与加载,包括babel-preset-env替换babel-preset-es2015的使用方法。
-
css资源与js资源的分割,包括插件 extract-text-webpack-plugin对静态资源的处理方式和注意事项。
下一节,我们将继续探讨webpack处理文件资源的方法,包括图片加载、图片转base64、icon及字体处理等等。
愿各位早日成为一名合格的前端高手,加油!
热门评论
纠错
1.引入css的时候路径不对应该是../
2.说明style-loader和css-loader的时候你的注释指定加载器的时候那里灭有逗号。
补充:关于第一个错误,你说app.js和css是同一级路径。很明显你的第一篇教程不是这样的,难道后来你改了?估计都是跟着你的第一篇教程来的。
纠错
1.引入css的时候路径不对应该是../
2.说明style-loader和css-loader的时候你的注释指定加载器的时候那里灭有逗号。
补充:关于第一个错误,你说app.js和css是同一级路径。很明显你的第一篇教程不是这样的,难道后来你改了?估计都是跟着你的第一篇教程来的。
非常好 谢谢啦 最近也是被构建化工具搞的一头雾水 看了您的文章恍然大悟许多了 一定不要断更啊!! 谢谢啦