webpack实战——预处理器(loader)【上篇】

写在前面

这是webpack实战系列笔记的第5篇记录,前几篇记录如下:

  • 打包第一个应用
  • 模块化与模块打包
  • 资源输入与输出
  • 一切皆模块

上一篇简单描述了一切皆模块的思想,学以致用,来实践下~

1. 关于 loader

每个loader本质上都是一个函数,可用公式表达其本质:

output = loader(input)

  • input可能是工程源文件的字符串,也可能是上一个loader转化后的结果,包含:
    • 字符串(转化后的结果)
    • source map
    • AST对象(抽象语法树)
  • output同样包含上述几种信息。如果是最后一个loader,其结果直接被送到webpack中做后续处理;否则作为下一个loader的input向后传递

举个例子

当使用 babel-loader 将 ES6+ 代码转化为 ES5 时,带入公式:

ES5 = babel-loader(ES6+)

上述描述说过,loader本身是一个函数那么loader是如何工作的呢?

module.exports = function loader (content, map, meta) {
    var callback = this.async();
    var result = handler(content, map, meta);
    callback(
        null,   // error
        result.content, // 转换后的内容
        result.map, // 转换source-map
        result.meta // 转换后的AST
    )
}

可以看出,该函数对接受到的内容进行转换,然后返回转换后的结果。

2. loader配置

那在应用层面应该如何实施呢?

一切皆模块中说过静态资源的类型是各式各样的,比如静态HTML/CSS/JS、图片字体音视频等,webpack如何处理这各类资源呢?ok,loader的应用场景来了。

loader,字面意思是装载器,但在webpack中实际用途则是预处理器:webpack本身只认识JavaScript,对于其他类型的资源必须先定义一个或多个loader对其进行转译,输出为webpack能够接收的形式再继续进行,因此loader做的实际上是一个预处理的工作。

2.1 引入

那loader到底应该如何使用呢?举例要在js中引入css文件:

// index.js
import './assets/common/css'
/* common.css */

body{
    width: 100vw;
    height: 100vh;
    text-align: center;
    background: gray;
}

ok,现在我们执行打包操作,build一下,会发现在终端报错:

出现报错

为解决报错,我们需要用到的就是loader了~

可以在上图报错内容除看到,提示我们没找到合适的loader来处理,并且给出css-loader提示,我们按提示安装:

// npm
npm install css-loader

// 或者 yarn   与npm二选一即可
yarn add css-loader

安装完成后,仍需在webpack.config.js中进行loader配置:

const path = require('path')

module.exports = {
    entry: {
        index: './src/index.js',
    },

    output: {
        filename: '[name].js'
    },
    mode: 'development',

    // loader配置
    module: {
        rules: [
            // css
            {
                test: /\.css$/,
                use: ['css-loader']
            }
        ]
    }
}

可以看到,对loader进行配置,配置项都在module对象中的rules模块。rules模块是一个数组,代表了要对模块进行处理的规则。在此处,我们使用到的规则有testuse

  • test:接收一个正则表达式或者一个元素为正则表达式的数组,只有正则匹配上的模块才会使用本条规则;
  • use:接收一个数组,代表该规则所使用的loader。

然后进行打包操作:

// npm
npm run build

// 或者 yarn   与npm二选一即可
yarn build

然后发现,打包错误解决。

但,新的问题出现了:
此时我们在浏览器打开index.html,发现样式并没有生效。原因是css-loader 的作用是处理css的加载语法而不是做style的样式渲染,因此我们需要添加一个 style-loader 来进行样式渲染。

2.2 链式loader

在上面我们说了需要在引入一个 style-loader 来进行样式渲染处理,先安装:

// npm
npm install style-loader

// 或者 yarn   与npm二选一即可
yarn add style-loader

接着搭配之前的webpack配置,做一些修改:

module.exports = {
    ...

    // loader配置
    module: {
        rules: [
            // css style
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
}

可以看到,我们将style-loader写在了use的数组中,并且细心的朋友可能发现写在了css-loader之前,这就是链式loader

那么为什么要区分顺序呢?在前面描述关于loader的公式中我们介绍过:

output = loader(input)

在链式webpack打包中,是按照数组从后往前的顺序将资源交给loader去处理,因此最后生效的应该放在前面。

此时,我们在执行打包操作,然后可以在浏览器中看到index.html页面加载了样式:

样式加载成功

2.3 其他配置

2.3.1 options配置

有些loader会有专门的配置项,形式上可能会有一些不同,如:

module.exports = {
    ...

    // loader配置
    module: {
        rules: [
            // css
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            // css-loader相关配置
                            ...
                        }
                    }
                ]
            }
        ]
    }
}

当然,具体配置项参数和值则需要参考相应loader的文档来进行配置,用时参阅该loader文档即可。

2.3.2 exclude 和 include

从字面意思理解,这两个分别是用来排除或者包含指定目录下模块的。

如:

module.exports = {
    ...

    // loader配置
    module: {
        rules: [
            // css
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
                exclude: /node_modules/
            }
        ]
    }
}

上面置顶了exclude: /node_modules/,则代表着该目录下的所有模块都不会被此条规则限制,也就是说node_modules中的模块不会执行该规则。

同样,include用途与此类似:

module.exports = {
    ...

    // loader配置
    module: {
        rules: [
            // css
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
                include: /src/
            }
        ]
    }
}

可以看到此处使用的是include,代表该规则只对正则匹配到的模块生效,也就是说只对src下的模块生效。

excludeinclude同时存在的情况下,exclude优先级更高!

通常情况下,在使用loader时,需要配置它,以此来加速打包速度,不配置的话打包会将所有模块打包,可能拖慢整体的打包速度。

2.3.3 resource 与 issuer

有时候,我们会在项目中看到关于resourceissuer的相关配置,那么这两个配置是做什么的呢?

其实与excludeinclude类似,都是用于规定模块作用范围的配置。但是区别是excludeinclude对规则的作用范围更加的精确。
如:

// index.js
import './src/common.css'

在webpack中,我们认为被加载模块是resource,加载者是issuer,在上述代码中,css作为被加载者,而index作为加载者。

那么具体如何使用呢?

module.exports = {
    ...

    // loader配置
    module: {
        rules: [
            // css
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
                exclude: /node_modules/,
                issuer: {
                    test: /\.js$/,
                    include: /src/pages/
                }
            }
        ]
    }
}

在上面我们看到配置了issuer对象,限制是让src/pages目录下js可以引用使用规则css-loader

我们看完后会发现一个风格问题: 代码可读性较差。可以稍加改善:

module.exports = {
    ...

    // loader配置
    module: {
        rules: [
            // css
            {
                use: ['style-loader', 'css-loader'],
                resource: {
                    test: /\.css$/,
                    exclude: /node_modules/
                },
                issuer: {
                    test: /\.js$/,
                    exclude: /node_modules/
                }
            }
        ]
    }
}

通过添加resource对象来讲外层配置包裹起来,区分resource和issuer的规则,看上去即可一目了然,但实际本质一样。可选择一种风格进行配置。

小结

本篇介绍了loader的作用和意义,以及在项目中实际使用时的一些配置,如引入、使用过程、链式loader、loader的配置等,从各大小方面均能做到对项目有优化或效率提升。
下一篇介绍几个项目常用loader以及如何自定义loader。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343