webpack
打包时只能处理 js
文件,对于其余类型的文件如 jsx
, css
, scss
, vue
, png
等文件,须要专门的东西处理一下再传入 webpack
,这个东西就是 loader
。css
loader 用于对模块的源代码进行转换。loader 可使你在 import 或"加载"模块时预处理文件。所以,loader 相似于其余构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 能够将文件从不一样的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至容许你直接在 JavaScript 模块中 import CSS文件!
在开发本身 loader
以前,咱们得知道 loader
是啥html
loader 是导出为一个函数的 node 模块。该函数在 loader 转换资源的时候调用。给定的函数将调用 loader API,并经过 this 上下文访问。
说白了,loader
就是一个函数,接收源模块,而后处理一番,再导出去,给下一个 loader
或者 webpack
。前端
module.exports = function(source) { // handle source ... return handled source }
关于开发一个 loader
遵循的一些原则,你们能够去看文档,本文以一个处理 txt
文件的小例子来讲明如何开发一个 loader。目录结构以下vue
// webpack.config.js const path = require('path') module.exports = { mode: 'development', entry: __dirname + "/src/app.js", output: { path: __dirname + "/dist", filename: "bundle.js" }, module: { rules: [ { test: /\.txt$/, use: [ 'text-loader' ] } ] } }
在 loaders
文件中存放咱们的 txt-loader.js
node
// txt-loader.js module.exports = function(source) { console.log(source) }
源文件 name.txt
webpack
// name.txt hello [name]!
入口文件 app.js
web
// app.js const name = require('./name.txt') console.log(name)
先执行走一波,终端执行数组
./node_modules/.bin/webpack
确定会报错,由于咱们的 loader
尚未写完,可是源文件内容已经打印出来了浏览器
报错信息也说,这个 loader
没有返回 Buffer
或者 String
。sass
txt-loader
要作的事情就是将任何 .txt
文件中的 [name]
直接替换为咱们想要的名字,而后返回包含默认导出文本的 JavaScript
模块。
须要注意的是,咱们不能再 loader
里面将这个名字写死,而应该在使用 loader
的时候以配置的形式传进去,咱们平时看到的 loader
通常都有个 options
选项,就是为了传些配置进去
{ test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } },
咱们给 txt-loader
加个配置选项
// webpack.config.js ... { test: /\.txt$/, use: { loader: path.resolve(__dirname, './src/loaders/txt-loader.js'), options: { name: 'Jay' } } } ...
那咱们在 txt-loader.js
怎么接收配置呢?webpack
提供了一个 loader
工具库
// txt-loader.js const loaderUtils = require('loader-utils') module.exports = function(source) { this.cacheable && this.cacheable() const options = loaderUtils.getOptions(this) || {} console.log(options) source = source.replace(/\[name\]/g, options.name) console.log(source) return source }
执行一下
发现咱们期待的结果打印出来了,可是仍是报错了,报错信息说还须要额外的 loader
去处理当前 loader
的结果。
有时咱们处理某种类型的文件须要多个 loader
,这些 loader
的执行顺序和 use
数组中 loader
书写顺序是相反的,如解析 scss
文件时
{//处理.scss文件 test: /\.scss$/, use: [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "sass-loader" // compiles Sass to CSS }] },
上面例子 webpack
是先通过 sass-loader
,而后将结果传入 css-loader
,最后再进入 style-loader
。
链路中间的 loader
返回什么样的结果都行,只要下一个接收的 loader
可以正常处理就行,可是最后一个调用 loader
的结果是须要传入至 webpack
中,webpack
指望它返回 JS
代码,以及可选的source map
。
注意:若是是处理顺序排在最后一个的 loader,那么它的返回值将最终交给 webpack 的 require,换句话说,它必定是一段可执行的 JS 脚本 (用字符串来存储),更准确来讲,是一个 node 模块的 JS 脚本。
// 处理顺序排在最后的 loader module.exports = function (source) { // 这个 loader 的功能是把源模块转化为字符串交给 require 的调用方 return `module.exports = ${JSON.stringify(source)}` }
本例处理 txt
文件只有一个 txt-loader
,最终传入至 webpack
中的是 hello Jay!
,不是个可执行的 JS 脚本。最终代码以下
module.exports = function(source) { this.cacheable && this.cacheable() const options = loaderUtils.getOptions(this) || {} source = source.replace(/\[name\]/g, options.name) return `module.exports = ${JSON.stringify(source)}` }
将 dist
中生成的 bundle.js
文件放入浏览器控制台中运行一下,能够看到输出 hello Jay!
咱们再看一个使用多个 loader
的例子,处理 html
文件并压缩,解析 html
并使之成为 JS
可执行的脚本的任务就交给现有的 html-loader
,压缩的任务就我们本身来实现,就叫 html-optimize-loader
吧。
修改一下 webpack.config.js
// webpack.config.js ... module: { rules: [ { test: /\.txt$/, use: { loader: 'name-loader', options: { name: 'Jay' } } }, { test: /\.html$/, use: ['html-loader', { loader: 'html-optimize-loader', options: { comments: false } }] } ] }, resolveLoader: { // html-loader 在 'node_modules' modules: ['node_modules', path.resolve(__dirname, './src/loaders')] }, ...
这里咱们改为多个 loader
配置的模式,也在咱们新加的 html-optimize-loader
中加入了配置,压缩时是否保留注释。
在 src
中新建 test.html
文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!-- 这里是 main 内容 --> </body> </html>
入口文件 app.js 改为 test.html
const html = require('./test.html') console.log(html)
在 loaders
文件中新建 html-optimize-loader.js
// hmtl-optimize-loader.js const Minimize = require('minimize') const loaderUtils = require('loader-utils') module.exports = function (source) { var callback = this.async() this.cacheable && this.cacheable() var options = loaderUtils.getOptions(this) || {} var minimize = new Minimize(options) console.log(source) console.log(minimize.parse(source)) return minimize.parse(source, callback) }
这里 loader
咱们采用异步的方式,执行一下
发现 source
和压缩后的 source
都打印出来了,这里咱们直接将压缩后 source
直接传入 html-loader
中去处理了。你们能够将 options
中的 comment
设成 true
,发现注释就会保留了,最终生成的 bundle
文件也能够丢进浏览器的控制台跑一下。
就这样咯,下一篇写实现一个 webpack
plugin