本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com
什么是 Loader
一个loader可以看做是一个node模块,也可以看做一个loader就是一个函数 (loader 会导出一个函数),众所周知webpack只能识别js文件,loader在webpack中担任的角色就是翻译工作,它可以让其它非js的资源(source)可以在webpack中通过loader顺利加载。
Loader 的方式
单一职责,一个 loader 只做一件事
调用方式,loader 是从右向左执行,链式调用
统一原则,loader 输入和输出都字符串
来看一下案例
module.exports = () => { return 343}上面这种会报错,我们上面说过laoder的方式了,统一原则,输出输入必须是字符串。而我们上面代码则输出是数字,则报错。
“
loader 导出尽量别使用箭头函数,loader 内部属性都是靠 this 来获取的,如 this.callback,this.sync
”
Webpack 手写 Loader
为什么要手写loader呢,假如有一些loader插件不满足我们的需求时,我们会采用手写loader来定制化我们功能。
开始
首先新建一个js文件
module.exports = function(source) { }- 第一个参数:是当前要处理的内容
loader 内置的方法
函数里面暴露了一些方法,this.query获取loader传过来的参数
module.exports = function(source) { console.log(this.query)}当然里面还可以引入一个库,来处理参数,该情况用于有时候我们传给loader的参数不是一个对象,可能是一个字符串。
module: { rules: [ { test: /\.css/, use: [{ loader: "testLoader", query: "前端娱乐圈" }] } ]}const loaderUtils = require('loader-utils')module.exports = function(source) { console.log(loaderUtils.getOptions(this))}webpack.config.js
module: { rules: [ { test: /\.css/, use: [ "testLoader? ] } ]}可以使用上面loaderUtils内置库获取loader的参数。
webpack.config.js
module: { rules: [ { test: /\.css/, use: [{ loader: "testLoader?, options: { name: "前端娱乐圈" } // or query: { name: "前端娱乐圈" } }] } ]}上面这两种传参形式,如果options存在,行内参数拼接则无效。上面还写了一种传参形式,query也是可以传参的,那options和query有什么区别的。这俩没啥区别就是,query是webpack老版本之前的 (2.5),options是最新支持的方式
loader 异步
loader 异步处理,假如说 loader 里面需要处理一些逻辑操作,但这个操作是异步的,那么 loader 就会编译失败,必须使用异步执行方法,来等待结果返回后,loader 则才会执行成功
module.exports = function(source) { setTimeout(() => { this.callback(1, source) }, 3000)}官方解释:this.callback 参数
this.callback(
err: Error | null, // 错误信息
content: string | Buffer, // 最终生成的源码
sourceMap?: SourceMap, // 对应的sourcemap
meta?: any // 其他额外的信息
);还有一种方法是 this.async,async 返回值也是一个 callback 所以这俩个是一样的
module.exports = function(source) { const callback = this.async() setTimeout(() => { callback(1, source) }, 3000)}Loader 起别名
resolveLoader - modules
我们现在手写的 loader 都还是写绝对路径引入进来,那么怎么直接写 loader 名呢,有两种方法,我们来看一下
module.exports = { resolveLoader: { modules: ["node_modules", "./loaders"] }, module: { rules: [ { test: /\.js/ use: { loader: "per-loader" } } ] }}我们可以看到上面,我们直接写的per-loader,我们是配置了解析loader路径,会先去node_modules里面查找,如果node_modules里面没有则会去loaders目录下查找。然后我们下面写loader: per-loader,注意:这里的per-loader就是当前loader的文件名
resolveLoader - alias
这种方法直接起别名,把路径引入过来就 ok
module.exports = { resolveLoader: { "per-loader": path.resolve(__dirname, "./loaders/per-loader.js") }, module: { rules: [ { test: /\.js/ use: { loader: "per-loader" } } ] }}实现一个 sass-loader && style-loader
sass-loader
首先安装一下node-sass插件,用于识别scss语法并编译为css
npm i node-sass新建sassLoader.js文件,并引入node-sass插件
const nodeSass = require("node-sass");const path = require("path")let result = nodeSass.renderSync({ file: path.resolve(__dirname, "../src/scss/index.scss"), outputStyle: 'expanded',});module.exports = function() { return result.css.toString()}上面采用node-sass官方配置,如异步解析.scss文件,上面对象中,file为当前要解析的文件地址,outputStyle为输出风格包含:nested(嵌套)、expanded(展开)、compact(紧凑,不换行)、compressed(压缩)。
导出result.css.toString, 这里为什么要toString,如果不toString的话返回的是一个Buffer数据。因为这里的返回值提供给下一个loader使用,为了下一个loader(style-loader) 更好的使用我们这里直接处理一下。
“
更多 Api 用法请参考 node-sass
”
style-loader
新建styleLoader.js文件
module.exports = function(source) { const style = ` let style = document.createElement("style"); style.innerHTML = ${JSON.stringify(source)}; document.head.appendChild(style) ` return style}上面导出的函数第一参数 (source) 就是我们sassLoader的返回值,然后在字符串里面写上创建 style 元素逻辑代码,并最终返回。注意这里返回值必须是字符串上,刚开始我们就说过了,输入输出都必须是字符串。
完整配置
index.js
console.log("前端娱乐圈")import "./scss/index.scss"webpack.config.js
const path = require("path");module.exports = { mode: 'development', entry: { main: './src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, resolveLoader: { alias: { "sassLoader": path.resolve(__dirname, "./loaders/sassLoader.js"), "styleLoader": path.resolve(__dirname, "./loaders/styleLoader.js") } }, module: { rules: [ { test: /\.scss/, use: ["styleLoader", "sassLoader"] } ] }}上面配置中我们用到了解析loader路径配置 (起别名),loader 是从右到左,从下到上解析执行。先是把.scss文件处理成css语法,然后在传递给styleLoader配置即可。以上一个简单完整的loader已实现完毕。如有帮助欢迎点赞 + 分享哦
感谢
谢谢你读完本篇文章,有帮助的话请**❤️关注 + 点赞 + 收藏 + 评论 + 转发❤️**
关注后回复加我好友免费领取视频教程
欢迎关注
前端巅峰,更多**「前端开发技巧」**只在公众号推送