重拾 Webpack(中卷)
之前在上卷中,我主要梳理了 webpack 入门知识,在此我将更接近于工程中的知识实际梳理一遍
本文持续更新中…
前置知识:重拾 Webpack(上卷)
预处理器(loader)
loader 概述
- 每一个 loader 本质上都是一个函数,在 webpack4 之前,loader 函数的输入输出都必须为字符串,但在 webpack4 之后,loader 也同时支持 抽象语法树(AST) 的传递,以此来减少代码的重复解析
用公式可以表示为:output = loader(input)
- 在此,我们来看一下 loader 的源码结构,以此阐释 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 }; };
(可能包含 sourcr map 和 AST 对象)
loader 的配置
- webpack 只认识 JavaScript,对于其他类型的资源,比如 CSS、图片等等,必须预先定义一个或多个 loader 来进行转译,经过 loader 输出为 webpack 能够接受的形式再继续进行
因此,loader 做的实际上是一个预处理的工作 - loader 的引入:
- 假设在我们的目录中,有以下两种文件:
|-app.js |-style.css
- 我们要做的是将 css 文件引入到 js 文件中使用,那么跟着我开始吧!
- 安装第三方模块
- webpack 本身并不含有任何的 loader,而所有的 loader 都是第三方 npm 模块,所以我们必须先安装它
npm install --save-dev css-loader
- webpack 本身并不含有任何的 loader,而所有的 loader 都是第三方 npm 模块,所以我们必须先安装它
- 引入到工程中
- 在
webpack.config.js
中配置:module.exports = { // ... module: { rules: [{ test: /\.css$/, use: ['css-loader'], }], }, };
- 在
- 配置项的说明:
- module.rules 代表着模块的处理规则,每条规则都包含着许多配置项,这里我们只用最重要的两项:test 和 use
- test 接受一个正则表达式或者元素为正则表达式的数组,如果匹配这条正则表达式,那么就会使用与之对应的规则,比如这里的
/\.css$/
用来匹配所有的.css
结尾的文件 - use 接受一个数组,数组中包含着对应 test 会使用到的所有 loader,比如这里的 css 文件会使用到
css-loader
,如果只有一个 loader,那么也可以省略数组,只写成字符串
- 假设在我们的目录中,有以下两种文件:
- 链式 loader:
- 上述的案例是不成功的,因为我们还差一样东西:style-loader
- 每一个 loader 的功能都是独特而局限的,也就是说,一种类型的模块可以需要多种 loader 共同完成,在处理 css 文件时同样如此,需要
style-loader
和css-loader
共同完成 - 安装
npm install --save-dev style-loader
- 添加配置
在 webpack 打包时,是按照数组从后往前的顺序将资源交给 loader 处理的,因此要把最后生效的 style-loader 放在最前面// webpack.config.js module.exports = { // ... module: { rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'], }], }, };
- exclude 与 include
- 在项目中我们经常会用到
babel-loader
来处理 ES6+ 语言特性,将其编译为 ES5 来适应浏览器,但是对于 node_modules 中的 js 文件来说,很多都是已经编译为 ES5 的,因此没必要再使用 babel-loader 来进行额外的处理 - exclude 与 include 就是用来排除规则的,他们可接收正则表达式或者字符串(字符串即文件的绝对路径),以及由他们组成的数组,如:
rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /node_modules/, }],
- 这里的含义是:对于匹配到正则表达式的模块,若在 exclude 指定的目录中(此处为 node_modules 目录),则不会执行该 use 规则
- 除了 exclude 之外,使用 include 配置也能达到相同的效果,如:
rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'], include: /src/, }],
- 值得注意的是,include 的逻辑似乎与 exclude 是相反的,对于匹配到正则表达式的模块,若在 include 指定的目录中(此处为 src 目录),才执行该 use 规则
- exclude 与 include 同时存在时,exclude 的优先级更高,如:
rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /node_modules/, include: /node_modules\/awesome/, }]
- 此时,exclude 使得 node_modules 目录已经被排除了,虽然 include 之后想要让该 use 规则对 node_modules 中的某一个模块生效,也是无济于事的,因为 include 是无法覆盖 exclude 的
- 要实现原本的需求,我们可以改为:
这令人头疼的正则,哎,,Ծ^Ծ,,rules: [{ test; /\.css/, use: ['style-loader', 'css-loader'], // 用 exclude 排除 node_modules 中除了 foo 和 bar 以外的所有模块 exclude: /node_modules\/(?!(foo|bar)\/).*/, }]
- 另外,由于 exclude 的优先级跟高,所以我们可以对 include 的子目录进行排除,如:
此处的结果就是:该 use 规则只对 src 目录生效,同时排除 src 目录中的 lib 目录rules: [{ test; /\.css/, use: ['style-loader', 'css-loader'], exclude: /src\/lib/, include: /src/, }]
- 注:exclude 与 include 的配置项往往是必须考虑的,也是常加的,否则可能会拖慢整体的打包速度
- 在项目中我们经常会用到
- resource 与 issuer
- resource 与 issuer 可以用于更加精确地确定模块规则的作用范围,如:
在 webpack 中,被加载模块是 resource,而加载者是 issuer,在上面的例子中,resource 是// index.js import './style.css'
style.css
,加载者是index.js
。 - 前面介绍的 test、exclude、include 在本质上就是对 resource 的配置,如果想要对 issuer 增加条件的话,需要额外地写配置,比如,我们只想让
/src/pages
目录下的 js 文件可以引用 css,本质想就是改变加载者的范围,配置如下:rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /node_modules/, issuer: { test: /\.js$/, include: /src/pages/, }, }], // 只有 /src/pages/ 下面的 js 文件引用 css 文件才能使此 use 规则生效
- 事实上,上述代码的可读性较差,我们可以改为:
注:此风格和上面的代码风格无法并存的,只能选择一种风格配置rules: [{ use: ['style-loader', 'css-loader'], resource: { test: /\.css$/, exclude: /node_modules/, }, issuer: { test: /\.js$/, exclude: /node_modules/, }, }],
- 事实上,上述代码的可读性较差,我们可以改为:
- resource 与 issuer 可以用于更加精确地确定模块规则的作用范围,如:
- enforce
- webpack 中的 loader 按照执行顺序可分为:pre、inline、normal、post四种,之前我们直接定义的 loader 都属于 normal 类型,官方已不推荐 inline 形式,而 pre 和 post 形式则需我们使用 enforce 指定,如:
这段代码的解释为:对于所有匹配正则表达式的模块,首先使用的 loader 规则就是 eslint-loaderrules: [{ test: /\.css$/, enforce: 'pre', use: 'eslint-loader', // eslint-loader 的功能是对源代码进行质量检测,这种检测工作往往在其他 loader 之前执行 }]
- 概述起来就是,enforce 可以强制指定 loader 的执行顺序
- pre 能强制 loader 首先执行
- post 能强制 loader 最后执行
- webpack 中的 loader 按照执行顺序可分为:pre、inline、normal、post四种,之前我们直接定义的 loader 都属于 normal 类型,官方已不推荐 inline 形式,而 pre 和 post 形式则需我们使用 enforce 指定,如:
- 常用 loader 介绍
- loader 官方文档
- 模板 loader:
- html-loader : 将HTML文件导出编译为字符串,可供js识别的其中一个模块
- pug-loader : 加载pug模板
- jade-loader : 加载jade模板(是pug的前身,由于商标问题改名为pug)
- ejs-loader : 加载ejs模板
- handlebars-loader : 将Handlebars模板转移为HTML
- 样式 loader:
- css-loader : 解析css文件中代码
- style-loader : 将css模块作为样式导出到DOM中
- less-loader : 加载和转义less文件
- sass-loader : 加载和转义sass/scss文件
- postcss-loader : 使用postcss加载和转义css/sss文件
- 脚本转换编译 loader:
- script-loader : 在全局上下文中执行一次javascript文件,不需要解析
- babel-loader : 加载ES6+ 代码后使用Babel转义为ES5后浏览器才能解析
- typescript-loader : 加载Typescript脚本文件
- coffee-loader : 加载Coffeescript脚本文件
- JSON加载 loader:
- json-loader : 加载json文件(默认包含)
- json5-loader : 加载和转义JSON5文件
- Files文件 loader:
- raw-loader : 加载文件原始内容(utf-8格式)
- url-loader : 多数用于加载图片资源,超过文件大小显示则返回data URL
- file-loader : 将文件发送到输出的文件夹并返回URL(相对路径)
- jshint-loader : 检查代码格式错误
- 加载框架 loader:
- vue-loader : 加载和转义vue组件
- angualr2-template–loader : 加载和转义angular组件
- react-hot-loader : 动态刷新和转义react组件中修改的部分,基于webpack-dev-server插件需先安装,然后在webpack.config.js中引用react-hot-loader
样式处理
正在更新中…
本作品采用《CC 协议》,转载必须注明作者和本文链接