module 配置项
在 module 中主要配置 loader,loader
是模块转换器,比如把 sass 转成 css。配置了相关资源的 loader
,我们就可以直接在程序中用 import
的方式引入非 js 资源。loader
能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中。
需要下载的 loader
以及作用:
- babel-loader:解析 JavaScript,包括 js 的新特性、新语法;
- css-loader:处理 css 语法,比如
@import
、url()
等; - style-loader:将 css 引入到
head
标签中的style
标签里; - sass-loader:处理 sass 文件,将 sass 语法编译成 css;
- less-loader:处理 less 文件,将 less 语法编译成 css;
- ts-loader:处理 typescript 文件,将 ts 语法编译成 js;
- postcss-loader:接收样式原码并交由编译插件处理,比如层架浏览器前缀,使用 css 新语法;
- eslint-loader:规范 js 代码;
- html-loader:将一个 html 文件通过 JS 加载进来;
- file-loader:加载图片、字体等资源;
- url-loader:与 file-loader 功能相同;
- html-withimg-loader:转化 HTML 中的 img 图片路径;
- expose-loader:用于暴露全局变量;
- vue-loader:处理 vue 文件;
- raw-loader:将文件作为字符串导入;
需要注意的是,使用 sass 时,除了要下载 sass-loader,还要下载 node-sass;使用 less 时,除了要下载 less-loader,还要下载 less;
在 module.rules 这个配置项中配置 loader,下面详细介绍各个 loader 的配置。
style-loader
和 css-loader
两者有很大不同,css-loader
的作用仅仅是处理 CSS 的各种加载语法,例如 @import
和 url()
等。而 style-loader
才是真正让样式起作用的 loader(会将 CSS 引入到 head 标签里的 style 标签中)。因此这两个一般配合使用:
{
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
}
]
}
}
还需要注意的是:webpack打包时是按数组从后往前的顺序将资源交给loader处理的(先处理数组的最后一项),因此要把最后生效的放在前面。
loader options
有时候使用一个 loader 时,可能要对它进行一些配置,例如 babel-loader
,babel 的一些配置就可以写在 options 里,当然也可以建一个 .babelrc
文件进行配置。当一个 loader 需要配置时,它就不能在 use 属性里是个单纯的字符串了,而应是一个对象。
{
module: {
rules: [
{
test: /\.css$/,
use: [{
loader: 'style-loader',
options: {
// 将样式放到顶部
// 当 HTML 模板中也有 CSS 样式(通过 style 标签写的),你又不想被覆盖掉,可以将 引入的 CSS 放到最顶部防止原来的样式被覆盖。
insertAt: 'top',
}
},{
// 对 css-loader 配置时,是个对象
loader: 'css-loader',
options: {
// css-loader 的配置项
}
}]
}
]
}
}
需要注意的是,css 中还不能书写背景图片路径(例如:
background: url()
)。不然会报错。因为加载的不是样式,而是图片,在 webpack 中,想要加载图片,还需要使用file-loader
,之后会介绍。
CSS Module
在书写 css 规则时,可能有些元素的类名会重复,导致样式被覆盖,为了解决全局污染的问题,可以把 class
命名写长一点、加一层父级选择器、降低冲突的几率。
在 css-loader 中就可以配置 css module,达到降低类名冲突的目的。
{
loader: 'css-loader',
options: {
modules: true,
}
}
css-loader 默认的哈希算法是 [hash:base64],会将类名编译成 base64 的字符串。除此之外,还可以定制。
{
loader: 'css-loader',
options: {
mudules: true,
localIdentName: '[name]__[local]--[hash:base64:5]'
}
}
// 或者使用这种的方式:
{
loader: 'css-loader?modules&localIdentName=[name]__[local]--[hash:base64:5]'
}
配置项含义:
[name]
css 的模块名;[local]
指原本的编译器标识符,比如 css 类名;[hash:base64:5]
表示是一个 5 位的 hash 值,这个值是根据模块名和标识符计算的,因此不同模块中相同的标识符也不会造成样式冲突;
如果在某些样式中不想使用 css module,可以使用:global(.className)
的语法,声明一个全局规则。凡是这样声明的 class,都不会被编译成哈希字符串。
sass-loader
和 less-loader
sass
和 less
是 CSS 的预处理器,需要安装。而且最终会编译成 CSS,因此我们还需要 style-loader
和 css-loader
。。
module: {
rules: [
{
test: /\.(sa|sc)ss$/,
use: ["style-loader", "css-loader","sass-loader"]
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"]
}
]
}
postcss-loader
下载:npm install postcss-loader
配置:
// 不需要再次创建新的 loader 对象,应该在之前的 style-loader css-loader 之后直接添加 postcss-loader 即可
{
test: /\.(c|sa|sc)ss)$/,
// 顺序很重要
use: ['style-loader','css-loader','postcss-loader', 'sass-loader'],
},{
test: /\.(c|le)ss)$/,
use: ['style-loader','css-loader','postcss-loader', 'less-loader'],
}
需要注意的是:使用多个 loader 时,loader 的加载是有顺序的,loader 的加载是从右到左。因此,less-loader 或者 sass-loader 先执行,让代码先转成原生的CSS,然后使用 postcss-loader 优化CSS属性(比如添加属性后缀),然后是 css-loader 将CSS文件中 @import
导入的文件添加进来,最后使用 style-loader 将 CSS 样式添加到 html 的 style 标签中。
配置 PostCSS
这里需要创建一个文件 —— postcss.config.js
在项目根目录下。
自动添加后缀 —— autoprefixer
使用之前需要先下载。
const autoprefixer = require('autoprefixer');
module.exports = {
plugins: [
autoprefixer({
// 需要支持的特性(这里添加了 grid 布局)
grid: true,
// 浏览器兼容
overrideBrowserList: [
'>1%', // 浏览器份额 大于 1% 的。
'last 3 versions', // 兼容最后三个版本
'android 4.2',
'ie 8'
],
})
]
};
postcss-preset-env
插件
这个插件可以让我们在应用中使用最新的 CSS 语法特性。同样需要下载:
yarn add postcss-preset-env -D
使用:
// postcss.config.js
const autoprefixer = require('autoprefixer');
const postcssPresetEnv = require('postcss-preset-env');
module.exports = {
plugins: [
autoprefixer({
grid: true,
overrideBrowserList: [
'>1%',
'last 3 versions',
'android 4.2',
'ie 8'
],
}),
postcssPresetEnv({
state: 3,
features: {
'color-mod-function': {
unresolved: 'warn'
},
browsers: 'last 2 versions'
}
})
]
};
babel-loader
babel-loader 很重要,使用 babel-loader可以让我们写的 JS 代码更加兼容浏览器环境。配置 babel-loader 时需要下载好几个其他的包 。这三个是最核心的模块。
yarn add babel-loader @babel/core @babel/preset-env -D
主要作用如下:
babel-loader
它是 babel 与webpack协同工作的模块;@babel/core
babel 编译器的核心模块;@babel/preset-env
它是官方推荐的预置器,可根据用户设置的目标环境自动添加所需的插件和补丁来编译 ES6+ 代码。
具体配置如下:
rules: [
{
test: /\.js$/,
// 不要编译 node_modules 下面的代码
exclude: path.join(__dirname,'../node_modules'),
use: {
loader: "babel-loader",
options: {
// 当为 true 时,会启动缓存机制,
// 在重复打包未改变过的模块时防止二次编译
// 这样做可以加快打包速度
"cacheDirectory": true,
}
}
}
]
对于 options 其它部分,可以在项目根目录下新建一个 .babelrc
文件。.babelrc 文件相当于一个 json 文件。它的配置项大概是这样的:
{
"presets": [],
"plugins": [],
}
比如要配置的一个内容:
{
"presets": [
["@babel/env", // 每一个 preset 就是数组的每一项
// 当有的 preset 需要配置时,这一项将也是一个数组
// 数组的第一项是 preset 名称,第二项是该 preset 的配置内容,是一个对象
{ // @babel/preset-env 会将 ES6 module 转成 CommonJS 的形式
// 将 mudules 设置成 false,可以禁止模块语句的转化
// 而将 ES6 module 的语法交给 webpack 本身处理
"mudules": false,
// targets 可以指定兼容的各个环境的最低版本
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
}
}
]
],
"plugins": [
// 语法转换(ES6转ES5)将常用到这个包
// 在开发环境下载
// 下载这个插件后还需要下载另一个包:@babel/runtime
// @babel/runtime 需要下载到生产环境中(--save)。不需要配置
"@babel/plugin-transform-runtime",
]
}
env 的 targets 属性,可以配置的环境名称有:
chrome
,opera
,edge
,firefox
,safari
,ie
,ios
,android
,node
,electron
。当然 targets 的值也可以是一个字符串,例如:"targets": "> 0.25%, not dead"
表示仅包含浏览器具有> 0.25%市场份额的用户所需的polyfill和代码转换。
除了使用 @babel/preset-env
解决语法兼容性问题之外,也可以使用 @babel/plugin-transform-runtime
+ @babel/runtime
的方式来解决。使用前需要先下载。
关于 babel-runtime 的作用可以参考:@babel/preset-env 与@babel/plugin-transform-runtime 使用及场景区别
ts-loader
使用 ts-loader
可以让我们使用 typescript 来编写 js 代码。安装该 loader 后,还要安装 typescript。
yarn add ts-loader typescript -D
rules: [
{
test: /\.ts$/,
use: "ts-loader",
}
]
可以使用 tsc --init
命令生成 ts 配置文件,进一步地配置 ts 语言环境。
eslint-loader
eslint 是 JS 语法的校验器,它提供了一个 loader:eslint-loader
。使用之前需要先下载:
yarn add eslint eslint-loader
配置如下:
{
rules: [
{
test: /\.js$/,
use: {
laoder: 'eslint-loader',
}
}
]
}
设置好 loader 后,还要在项目根目录下建一个 .eslintrc.json
文件再进行其他配置。
当然,也可以来到这个网址 https://eslint.org/demo/,下载默认的配置文件。下载好后把文件修改成 .eslintrc.json
名称(名称前有一个点),然后把该文件剪切到项目根目录下。
需要注意的是,loader 的执行顺序是从右到左(对于一个规则,多个loader的情况,配置 .css laoder时,use 项中有多个 loader),从下到上(对于一个多个规则,比如同是处理 .js 文件的配置,写了好几个规则(test)),因此,eslint-loader 应该放在所有 .js 规则中的最后一个(先检验,再做别的事情)。
{
rules: [
{
test: /\.js$/,
use: [
loader: "babel-loader",
]
},{
test: /\.js$/,
use: [
loader: "eslint-loader",
]
}
]
}
也可以使用 options 中的 enforce 配置项:
{
rules: [
{
test: /\.js$/,
use: [
loader: "eslint-loader",
options: {
// 强制让这个 loader 最先执行
enforce: "pre"
}
]
},{
test: /\.js$/,
use: [
loader: "babel-loader",
]
}
]
}
enforce
默认值是 normal
,除了 pre
和 normal
之外,还有 post
,表示强制最后执行在 normal 之后执行这个loader。
处理 react jsx 语法:@babel/preset-react
下载: yarn add @babel/preset-react -D
。当然,如果想使用 react,也要下载。在 .babelrc
的presets项中添加一个preset:
{
"presets": [
["@babel/env",
{
"modules": false,
"targets": {
"ie": 9
}
}
],
"@babel/react"
]
}
这个时候就可以愉快的使用 react 了!
处理 .jsx
的文件
用 react 写的文件不光可以使用 .js
后缀,也可以使用 .jsx
文件后缀。但想要使用,这需要配置,不然会报错。来到 webpack 配置文件,添加一个 loader 项:
{
test: /\.jsx?$/,
use: "babel-loader",
},{
test: /\.tsx?$/,
use: "ts-loader"
}
暴露全局变量
在 webapck 中使用 jquery 时,可以这么引入:
import $ from 'jquery';
但是这个 $
变量并不在全局下(window)。如果我们想要将改变量暴露到全局中,需要使用 expose-loader
。
下载:yarn add expose-loader
。将 jquery 模块暴露出来:
import $ from "expose-loader?$!jquery";
?$!
中的 $
就是指被暴露的变量名(expose-loader
?
!
是固定格式)。
当然,如果不想这么写,也可以在 rules 中进行配置:
{
rules: [
test: require('jquery'),
use: 'expose-loader?$'
]
}
配置好后,使用jQuery时,还需要进行引入:import $ from 'jquery'
。如果不想每次都引入(或说不用引入),可以使用一个插件:provide-plugin
。使用时不需要下载,webpack 自带,然后在 plugins 配置项中配置:
{
plugins: [
new webpack.ProvidePlugin({
$: 'jquery'
})
]
}
如果你在 HTML 中引入了第三方模块使用 script 标签,但在开发中如果再使用 import $ from 'jquery'
,webpack 就会多打包一次。为了不让 webpack 这样做,可以添加一个配置:
module.exports = {
plugins: [],
// ...
externals: {
jquery: 'jQuery'
}
}
html-loader
有了这个 loader,我们可以将一个 html 文件通过 JS 加载进来。比如这样:
rules: [
{
test: /\.html$/,
use: 'html-loader'
}
]
// loader.html
<h1>Hello World!</h1>
// index.js
import html from './loader.html';
document.write(html);
file-loader
和 url-loader
file-loader
用于打包文件类型的资源。比如 CSS 的背景图片和字体、HTML 的 img 标签中的 src 路径等。
rules: [
{
test: /\.(png|jpg|gif)$/,
use: "file-loader",
}
]
这样就可以对 png、jpg、gif类型的图片文件进行打包,而且可以在 JS 中加载图片。
file-loader 中的 options
主要有两个配置项:
name
,指定打包后文件的名字,默认是 hash 值加上文件后缀。也可以制定成:[name].[ext]
表示原来的名字和文件后缀。publicPath
这里的 publicPath 与 output 中的 publicPath 一样,在这里指定后,会覆盖原有的 output.publicPath。 比如:
rules: [
{
test: /\.(png|jpg|gif)/,
use: {
loader: "file-loader",
options: {
name: '[name].[ext]',
publicPath: "",
}
}
}
]
url-loader
与 file-loader
作用类似,唯一的不同是:url-loader
可以设置一个文件大小的阈(yù)值。当大于该阈值时与 file-loader 一样返回 publicPath,而小于阈值时则返回文件的 base64
形式编码。比如:
{
test: /\.(png|jpg|gif)$/,
use: {
loader: "url-loader",
options: {
// 当文件小于这个值时,使用 base64 编码形式
// 大于该值时,使用 publicPath
// 这个属性在 file-loader 中是没有的。
limit: 10240, // 10KB
name: '[name].[ext]',
// 将所有的图片打包到 img 目录下
outputPath: 'img/',
}
}
}
html-withimg-loader
当我们在 HTML 模板中有 img 标签时,img 标签的 src
的路径并不会被 webpack 转化(因为没有被依赖,图片将不会被打包),可以使用 html-withimg-loader
打包 src 对应的资源,使用之前同样需要先下载。然后配置:
{
rules: [
test: /\.html/,
use: 'html-withimg-loader'
]
}
vue-loader
它可以将 .vue
文件转换成 JavaScript 模块。配置如下:
{
test: /\.vue$/,
use: 'use-loader',
}
在 vue 中,可以使用 vue-style-loader
代替 style-loader
。配置如下:
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// ...
{
rules: {
test: /\.vue$/,
loader: 'vue-loader',
}
}
// ...
plugins: {
new VueLoaderPlugin()
}