本文不会介绍loader的一些使用方法,不熟悉的同学请自行查看Webpack loader
首先我们来看一下为什么需要loader,以及他能干什么?
webpack 只能理解 JavaScript 和 JSON 文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。
本质上来说,loader 就是一个 node 模块,这很符合 webpack 中「万物皆模块」的思路。既然是 node 模块,那就一定会导出点什么。在 webpack 的定义中,loader 导出一个函数,loader 会在转换源模块resource的时候调用该函数。在这个函数内部,我们可以通过传入 this 上下文给 Loader API 来使用它们。最终装换成可以直接引用的模块。
前面我们已经知道,由于 Webpack 是运行在 Node.js 之上的,一个 Loader 其实就是一个 Node.js 模块,这个模块需要导出一个函数。 这个导出的函数的工作就是获得处理前的原内容,对原内容执行处理后,返回处理后的内容。
一个简单的loader源码如下
module.exports = function(source) {
// source 为 compiler 传递给 Loader 的一个文件的原内容
// 该函数需要返回处理后的内容,这里简单起见,直接把原内容返回了,相当于该 Loader 没有做任何转换
return source;
};
由于 Loader 运行在 Node.js 中,你可以调用任何 Node.js 自带的 api,或者安装第三方模块进行调用:
const xml2js = require('xml2js');
const parser = new xml2js.Parser();
module.exports = function(source) {
this.cacheable && this.cacheable();
const self = this;
parser.parseString(source, function (err, result) {
self.callback(err, !err && "module.exports = " + JSON.stringify(result));
});
};
这里我们事简单实现一个xml-loader;
注意:如果是处理顺序排在最后一个的 loader,那么它的返回值将最终交给 webpack 的 require,换句话说,它一定是一段可执行的 JS 脚本 (用字符串来存储),更准确来说,是一个 node 模块的 JS 脚本,所以我们需要用module.exports =导出。
整个过程相当于这个 loader 把源文件
// 这里是 source 模块
转化为
// example.js
module.exports = '这里是 source 模块';
然后交给 require 调用方:
// applySomeModule.js
var source = require('example.js');
console.log(source); // 这里是 source 模块
写完后我们要怎么在本地验证呢?下面我们来写个简单的demo进行验证。
首先我们创建一个根目录xml-loader,此目录下 npm init -y生成默认的package.json文件 ,在文件中配置打包命令
"scripts": {
"dev": "webpack-dev-server"
},
之后npm i -D webpack webpack-cli,安装完webpack,在根目录 创建配置文件webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.xml$/,
use: ['xml-loader'],
}
]
},
resolveLoader: {
modules: [path.join(__dirname, '/src/loader')]
},
devServer: {
contentBase: './dist',
overlay: {
warnings: true,
errors: true
},
open: true
}
}
在根目录创建一个src目录,里面创建index.js,
import data from './foo.xml';
function component() {
var element = document.createElement('div');
element.innerhtml = data.note.body;
element.classList.add('header');
console.log(data);
return element;
}
document.body.appendChild(component());
同时还有一个foo.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Mary</to>
<from>John</from>
<heading>Reminder dd</heading>
<body>Call Cindy on Tuesday dd</body>
</note>
最后把上面的xml-loader放到src/loader文件夹下。完整的demo源码请看
最终我们的运行效果如下图
至此一个简单的webpack loader就实现完成了。当然最终使用你可以发布到npm上。
当我们配置loader时我们经常会看到有这样的配置
ules: [{
test: /\.html$/,
use: [ {
loader: 'html-loader',
options: {
minimize: true
}
}],
}]
那么我们在loader中怎么获取这写配置信息呢?答案是loader-utils。这个由webpack提供的工具。下面我们来看下使用方法
const loaderUtils = require('loader-utils');
module.exports = function(source) {
// 获取到用户给当前 Loader 传入的 options
const options = loaderUtils.getOptions(this);
return source;
};
没错就是这么简单。
1、path.resolve
可以简单通过在 rule 对象设置 path.resolve 指向这个本地文件
{
test: /\.js$/
use: [
{
loader: path.resolve('path/to/loader.js'),
options: {/* ... */}
}
]
}
2、ResolveLoader
这个就是上面我用到的方法。ResolveLoader 用于配置 Webpack 如何寻找 Loader。 默认情况下只会去 node_modules 目录下寻找,为了让 Webpack 加载放在本地项目中的 Loader 需要修改 resolveLoader.modules。
假如本地的 Loader 在项目目录中的 ./loaders/loader-name 中,则需要如下配置:
module.exports = {
resolveLoader:{
// 去哪些目录下寻找 Loader,有先后顺序之分
modules: ['node_modules','./loaders/'],
}
}
加上以上配置后, Webpack 会先去 node_modules 项目下寻找 Loader,如果找不到,会再去 ./loaders/ 目录下寻找。
3、npm link
npm link 专门用于开发和调试本地 npm 模块,能做到在不发布模块的情况下,把本地的一个正在开发的模块的源码链接到项目的 node_modules 目录下,让项目可以直接使用本地的 npm 模块。 由于是通过软链接的方式实现的,编辑了本地的 Npm 模块代码,在项目中也能使用到编辑后的代码。
完成 npm link 的步骤如下:
链接好 Loader 到项目后你就可以像使用一个真正的 Npm 模块一样使用本地的 Loader 了。(npm link不是很熟,复制被人的)
在有些情况下,有些转换操作需要大量计算非常耗时,如果每次构建都重新执行重复的转换操作,构建将会变得非常缓慢。 为此,Webpack 会默认缓存所有 Loader 的处理结果,也就是说在需要被处理的文件或者其依赖的文件没有发生变化时, 是不会重新调用对应的 Loader 去执行转换操作的。
如果你想让 Webpack 不缓存该 Loader 的处理结果,可以这样:
module.exports = function(source) {
// 关闭该 Loader 的缓存功能
this.cacheable(false);
return source;
};
在默认的情况下,Webpack 传给 Loader 的原内容都是 UTF-8 格式编码的字符串。 但有些场景下 Loader 不是处理文本文件,而是处理二进制文件,例如 file-loader,就需要 Webpack 给 Loader 传入二进制格式的数据。 为此,你需要这样编写 Loader:
module.exports = function(source) {
// 在 exports.raw === true 时,Webpack 传给 Loader 的 source 是 Buffer 类型的
source instanceof Buffer === true;
// Loader 返回的类型也可以是 Buffer 类型的
// 在 exports.raw !== true 时,Loader 也可以返回 Buffer 类型的结果
return source;
};
// 通过 exports.raw 属性告诉 Webpack 该 Loader 是否需要二进制数据
module.exports.raw = true;
以上代码中最关键的代码是最后一行 module.exports.raw = true;,没有该行 Loader 只能拿到字符串。
Loader 有同步和异步之分,上面介绍的 Loader 都是同步的 Loader,因为它们的转换流程都是同步的,转换完成后再返回结果。 但在有些场景下转换的步骤只能是异步完成的,例如你需要通过网络请求才能得出结果,如果采用同步的方式网络请求就会阻塞整个构建,导致构建非常缓慢。
在转换步骤是异步时,你可以这样:
module.exports = function(source) {
// 告诉 Webpack 本次转换是异步的,Loader 会在 callback 中回调结果
var callback = this.async();
someAsyncOperation(source, function(err, result, sourceMaps, ast) {
// 通过 callback 返回异步执行后的结果
callback(err, result, sourceMaps, ast);
});
};
参考
webpack 在前端工程中随处可见,当前流行的 vue, react, weex 等解决方案都推崇 webpack 作为打包工具。前端工具云集的时代,这是你值得选择的之一。
webpack是前端工程构建的一套工具,为什么一个程序称之为一套呢,是因为webpack其实是npm的一个模块,使用起来的话,这期间还需要很多其它模块来进行支持,所以我称之为一套工具。
本文从一个小Demo开始,通过不断增加功能来说明webpack的基本配置,只针对新手。webpack基本的配置就可以熟悉了,会引入loader,配置loader选项,会设置alias,会用plugins差不多。
Plugins是webpack的基础,我们都知道webpage的plugin是基于事件机制工作的,这样最大的好处是易于扩展。讲解如果扩展内置插件和其他插件,以及我们常用的Plugins插件
webpack技巧的总结:进度汇报、压缩、复数文件打包、分离app文件与第三方库文件、资源映射、输出css文件、开发模式、分析包的大小、更小的react项目、更小的Lodash、引入文件夹中所有文件、清除extract-text-webpack-plugin日志。
Webpack 作为目前最流行的前端构建工具之一,在 vue/react 等 Framework 的生态圈中都占据重要地位。在开发现代 Web 应用的过程中,Webpack 和我们的开发过程和发布过程都息息相关,如何改善 Webpack 构建打包的性能也关系到我们开发和发布部署的效率。
新版 Webpack 中我们所做的每一个更新目的都在于此,为了当大家在使用 Webpack 的时候敏捷连续毫无顿挫感。 webpack 4 进行构建性能测试,得出的结果非常有趣。结果很惊人,构建时间降低了 60%-98%!
Webpack 是一个现代 JavaScript 应用程序的模块打包器 (module bundler) 。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块
Tobias Koppers是一位自由软件开发者,家住德国纽伦堡。他因写出webpack这个已有数百万开发者使用的开源软件而名噪一时。他目前专注于JavaScript和开源项目。以下是我对他个人的专访,希望对大家有所启发。
本文讲述css-loader开启css模块功能之后,如何与引用的npm包中样式文件不产生冲突。比如antd-mobilenpm包的引入。在不做特殊处理的前提下,样式文件将会被转译成css module。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!