编写本身的Webpack Loader

本文将简单介绍webpack loader,以及如何去编写一个loader来知足自身的需求,从而也能提升对webpack的认识与使用,努力进阶为webpack配置工程师。javascript

Webpack Loader

webpack想必前端圈的人都知道了,大多数人也都或多或少的用过。简单的说就是它可以加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一块儿打包到指定的文件中。能够说,它做为一个打包工具,在前端工程化浪潮中,起到了中流砥柱的做用。css

那webpack其中很是重要的一环就是,可以对加载的资源文件,进行一些处理。好比把less、sass文件编译成css文件,负责这个处理过程的,就是webpack的loader。html

loader 用于对模块的源代码进行转换。loader 可使你在 import 或"加载"模块时预处理文件。所以,loader 相似于其余构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。前端

举个稍微复杂的例子,vue-loader,它官网介绍以下:vue

vue-loader 是一个 Webpack 的 loader,能够将指定格式编写的 Vue 组件转换为 JavaScript 模块。java

Vue组件默认分红三部分,<template><script><style>,咱们能够把一个组件要有的html,js,css写在一个组件文件中,而vue-loader,会帮助咱们去处理这个vue组件,把其中的html,js,css分别编译处理,最终打包成一个模块。node

明确本身须要什么Loader

咱们知道了webpack的强大依托于一个个强大的 loader(固然还有plugin,本文就不介绍了)。若是想真的玩溜webpack,咱们就必须掌握loader的使用。在咱们使用它们前,咱们得知道本身须要什么loader。若是想编译less,能够用less-loader;想加载html文件并打包它内链的静态文件,可使用html-loader。只要咱们想对文件进行处理时,咱们均可以去找想对应的loader。webpack

那么问题来了,万一找不到想要的loader该怎么办?git

好比我前几天遇到了一个需求,我但愿我加载的html文件,都嵌套在一个 layout.html 文件中。以下所示:github

<!-- layout.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Pure Web</title>
  <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
</head>
<body>
  <header>This is Header</header>

  <!-- 我但愿我webpack加载的html,都会被嵌套在这个{{__content__}}部分 -->
  {{__content__}}

  <footer>This is footer</header>
</body>
</html>

这样若是是编写多页应用,我就只须要编写惟一不同的中心内容,而把网站公共的部分做为layout抽离开来。

惋惜html-loader它只能帮我在一个html文件中去加载另一个html文件,像这样:

<body>
  ${require('@/htmls/header.html')}
  ${require('@/htmls/index.html')}
  ${require('@/htmls/footer.html')}
</body>

这样虽然能抽离公共部分,但我依旧须要在每一个html文件中去引用,并且为了保证html结构顺序,我得每一个文件都再引一次headerfooter,无法将他们做为一个单独的layout来引入。因此它并不彻底符合个人需求。

那该怎么办,咱们没有办法,只能本身动手写啊。

动手写一个webpack loader

首先,咱们要先阅读一遍webpack官网的介绍:如何编写一个loader?

看完后,咱们能知道,loader本质就是接收字符串(或者buffer),再返回处理完的字符串(或者buffer)的过程。webpack会将加载的资源做为参数传入loader方法,交于loader处理,再返回。

在我这个需求中,就是将我加载的html,套在我设定的layout中,再将这个处理完的html返回。大体的代码就是这样:

// {string} source: 加载的html的字符串值
module.exports = function (source) {
  return getLayoutHtml().replace('{{__content__}}', source)
}

简单思考后,发现可行,那么开始编写。

开始尝试

因此,咱们第一步,只要实现一个getLayoutHtml方法,能获得设定的layout.html文件就好。仔细想一想,layout文件应该是经过配置声明的,而后在loader里去根据配置,调用node的api去加载文件就好。

查阅node与webpack文档,咱们能够经过loader-utils来获取loader的配置项。经过node的fs.readFileSync去加载文件,那咱们的代码大概能够这样写:

module.exports = function (source) {
  const options = loaderUtils.getOptions(this)
  const layoutHtml = fs.readFileSync(options.layout, 'utf-8')
  return layoutHtml.replace('{{__content__}}', source)
}

webpack的config增长以下loader配置:

{
  test: /\.(html)$/,
  loader: 'html-layout-loader',
  include: htmlPath, // the htmls you want inject to layout
  options: {
    layout: layoutHtmlPath // the path of default layout html
  }
}

难以置信,这样就完成一个基础的html-layout-loader了。固然,这才是真正的开始,真正让loader变得可用,好用。咱们还须要考虑不少状况。

完善本身的loader

明确代码可行后,咱们得完善本身的功能点,我仔细想一想,大概须要考虑以下功能:

  1. 针对每一个加载的html,应该能够设定本身的layout文件,而不是全部的html,都必须加载同一个layout。
  2. 替换的占位符{{__content__}}也应该能够配置。

另外,还须要考虑一些异常的处理,如模板文件找不到。

完善自身的需求后,咱们又能够编写代码了,这回我就不一行行阐述代码了,直接放连接:html-layout-loader

代码也比较简单,算是实现了本身的基本需求,你们有兴趣的话能够先看看readme的介绍。

写在最后

当咱们在遇到大问题时,首先想到的老是去搜搜看有没有现成的解决方案,但现实却不免是没有解决方案。在这种状况下,咱们也能够尝试着去写一些插件、组件、或者一个通用化的解决方案,来解决自身的问题,同时对本身掌握一些知识也会有帮助。并且尝试事后可能发现,它也没那么难嘛。

另外,若是这个loader,也对读者们有帮助的话,请尽情使用,有什么问题、想法能够提issue或PR。

--阅读原文 --转载请先通过本人受权-丁香园F2E @相学长