Webpack Loader

一.Webpack与Loader

Webpack希望对所有依赖资源一视同仁,抹平针对不同类型资源的处理差异:

Unlike most bundlers out there, the motivation behind Webpack is to gather all your dependencies (not just code, but other assets as well) and generate a dependency graph.

负责抹平这种差异的,就是Loader

Webpack: 无论引入了个什么东西,都给我转成JS Module,不然就胡闹(报错) Loaders: 好

配置结构

Entry

依赖图的入口

entry: './src/index.js'
// 或者多入口的形式
entry: {
 main: './src/index.js'
}

Output

输出产物

output: {
 filename: 'main.js',
 path: path.resolve('./build')
}
// 对应多入口的形式
output: {
 filename: '[name].js',
 path: path.resolve('./build')
}

Loaders

依赖处理器,拦截依赖项并进行预处理

比如这个场景:

// index.js file
import helpers from '/helpers/main.js';// Hey Webpack! I will need these styles:
import 'main.css';

Webpack不认识CSS(无法直接处理),就需要先由Loader加工一遍(预处理)

常见的Loader配置:

module: {
 rules: [
   {
     test: /\.css$/,
     use: [
       { loader: 'style-loader' },
       { loader: 'css-loader' },
       { loader: 'less-loader' }
     ]
   }
 ]
}

Loader应用顺序是less-loader, css-loader, style-loader

P.S.除了通过配置文件来指定Loader与资源类型的关系外,还可以在引入资源时添上loadername!前缀,例如import image from 'file-loader!./my-img.png'

Plugins

Loader不够用/不好用或者做不到的时候,通过自定义插件来扩展

例如extract-text-webpack-plugin用来改变样式规则被打进bundle的style-loader默认行为,把CSS收集起来,通过<link>标签作为外部资源引用:

var ExtractTextPlugin = require('extract-text-webpack-plugin');plugins: [
 new ExtractTextPlugin('main.css')
]

html-webpack-plugin用来生成入口HTML:

var HTMLWebpackPlugin = require('html-webpack-plugin');plugins: [
 new HTMLWebpackPlugin()
]

内置的DefinePlugin用来区分环境:

new webpack.DefinePlugin({
 'process.env.NODE_ENV': JSON.stringify(ENV)
})

内置的UglifyJsPlugin用来压缩源码:

new webpack.optimize.UglifyJsPlugin()

注意,还有个长得差不多的uglifyjs-webpack-plugin,与内置的UglifyJsPlugin细微的版本差异,具体见Is webpack.optimize.UglifyJsPlugin() UglifyJsPlugin() ?

二.Loader

Loader主要用来处理非JS资源依赖

webpack enables use of loaders to preprocess files. This allows you to bundle any static resource way beyond JavaScript.

与Plugin的区别

Loader只负责处理特定类型的依赖,“处理”包括解析,转换等,把Webpack不认识的东西(各种非JS依赖)转换成可打进bundle的JS

Plugin更强大一些,能够跨Loader进一步加工Loader的输出,在打包前/后或过程中扩展增强

实质

A loader is a node module that exports a function. This function is called when a resource should be transformed by this loader. The given function will have access to the Loader API using the this context provided to it.

Loader是用来转换依赖资源的函数,这个函数能够通过Loader API拿到bundle过程中的一些上下文信息(比如目标原始资源内容或前一个loader的输出、loader配置项等),以及调用Webpack内部方法(例如通过this.resolve()处理二级依赖)

特点

  • 职责单一
  • 可组合
  • 无状态

因为希望Loader可组合,所以要求其职责单一(操作工序可灵活配置),且无状态(避免副作用影响随意组合)

三.链式Loader(Chaining loaders)

Webpack 1支持通过3种方式声明Loader的应用顺序:

loaders: [
 { test: /\.scss$/, loader: 'style' },
 { test: /\.scss$/, loader: 'css' },
 {
   test: /\.scss$/, loader: 'autoprefixer',
   query: { browsers: 'last 2 version' }
 },
 {
   test: /\.scss$/, loader: 'sass',
   query: { outputStyle: 'expanded' }
 }
]

或者

loaders: [
 {
   test: /\.scss$/,
   loaders: [
     'style',
     'css',
     'autoprefixer?browsers=last 2 version',
     'sass?outputStyle=expanded',
   ]
 }
]

或者

loaders: [
 {
   test: /\.scss$/,
   loader: 'style!css!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded',
 },
]

从右向左应用(与grunt/gulp的task定义顺序相反),相当于函数组合形式的style(css(file)),与管道类似,例如last!second!first表示:

last Loader最先应用,能够拿到原始资源内容
second Loader能够拿到前一个执行的Loader的返回结果
first Loader最后应用,要求返回一个JS Module和可选的source map

相当于echo $resource_content | first | second | last,输入原始资源内容,输出JS Module(CMD模块或ES模块),中间可以流经n个Loader

在后来版本中换成了:

use: [
 'style-loader',
 {
   loader: 'css-loader',
   options: {
     importLoaders: 1
   }
 },
 {
   loader: 'less-loader',
   options: {
     noIeCompat: true
   }
 }
]

但出于兼容性考虑,query属性还保留着,并且作为options的别名存在,具体见UseEntry

P.S.关于链式loaders的更多信息,请查看Chaining loaders

四.loader示例

比如,有个场景是要去掉JSONC(JSON with JavaScript style comments)中的注释,像加载JSON一样加载JSONC,例如:

// settings.json
{
 // Format a file on save. A formatter must be available, the file must not be auto-saved, and editor must not be shutting down.
 "editor.formatOnSave": false
}

默认的JSON依赖处理不支持带注释的:

Module build failed: SyntaxError: Unexpected token / in JSON at position 0

这时可以手搓一个简单的loader(如果没找到现成的):

import { getOptions } from 'loader-utils';
import { parse as parseJSONC, stripComments } from 'jsonc-parser';export default function parse(source) {
 const opts = getOptions(this);
 let json = stripComments(source);
 if (opts.extRule) {
   const filePath = this.resourcePath;
   const matched = opts.extRule.test(filePath);
   if (!matched) {
     let errors = [];
     json = parseJSONC(source, errors, {
       disallowComments: true
     });
     if (errors.length) {
       throw new Error(`Unexpected COMMENTS at ${filePath}`);
     }
   }
 } return `export default ${json}`;
}

借助MS的jsonc-parser解析JSONC/JSON,还支持一个配置项extRule做后缀名规则检查:

{
 test: /\.json$/,
 use: {
   loader: path.join(__dirname, './loaders/jsonc-loader.js'),
   options: {
     extRule: /.jsonc?$/i
   }
 }
}

P.S.除了同步形式的loader,还有回调注入形式的Asynchronous Loaders

P.S.更多Loader API,请查看The Loader Context

五.常用loader

实际应用场景有很多经典的配合,比如:

  • style-loader!css-loader:收集App依赖的CSS,并在运行时通过<style>标签插入页面
  • file-loader:生成多文件的方式(奇怪的是这个事情竟然也由Loader来做,而不是主配置支持)
  • file-loader!html-loader:引入HTML,进行模板替换等预处理,再生成输出文件

官方介绍了7类loader:

文件

  • raw-loader:直接取文件内容
  • val-loader:加载JS代码,要求CMD模块形式
  • url-loader:与file-loader类似,支持针对小文件返回data URL
  • file-loader:把文件拷贝到output目录,并返回相对URL

JSON

  • json-loader:默认内置了,用来加载JSON文件
  • json5-loader:加载并转译JSON 5文件(ES5.1 JSON语法)
  • cson-loader:加载并转译CSON文件

转译

  • script-loader:在global环境执行JS文件(像script标签一样),里面的require不会被转换
  • babel-loader:加载ES2015+代码,并用Babel转译到ES5
  • buble-loader:加载ES2015+代码,并用Bublé转换到ES5
  • traceur-loader:加载ES2015+代码,并用Traceur转换到ES5
  • ts-loader或awesome-typescript-loader:加载TypeScript 2.0+代码
  • coffee-loader:加载CoffeeScript代码

模板

  • html-loader:把require引用的HTML静态资源作为字符串导出
  • pug-loader:加载Pug模板,返回个函数
  • jade-loader:加载Jade模板,返回个函数
  • markdown-loader:把Markdown编译成HTML
  • react-markdown-loader:用markdown-parse解析器把Markdown编译成React组件
  • posthtml-loader:加载并用PostHTML转换HTML文件
  • handlebars-loader:把Handlebars编译成HTML
  • markup-inline-loader:把SVG/MathML文件内容塞进HTML,用icon font或给SVG应用CSS动画时很有用

样式

  • style-loader:把模块输出作为style(标签)插入DOM
  • css-loader:加载CSS文件返回CSS,支持imports
  • less-loader:加载并编译LESS文件
  • sass-loader:加载并编译SASS/SCSS文件
  • postcss-loader:加载并用PostCSS转换CSS/SSS文件
  • stylus-loader:加载并编译Stylus文件

Lint检查及测试

  • mocha-loader:用mocha在浏览器/NodeJS环境进行测试
  • eslint-loader:预加载器,用ESLint进行Lint检查
  • jshint-loader:预加载器,用JSHint进行Lint检查
  • jscs-loader:预加载器,用JSCS进行代码风格检查
  • coverjs-loader:预加载器,用CoverJS确定测试覆盖度

框架

  • vue-loader:加载并编译Vue组件
  • polymer-loader:用可配置的预处理器处理HTML和CSS,支持像引入一般模块一样require()Web Components
  • angular2-template-loader:加载并编译Angular组件

P.S.更多第三方loader,见awesome-webpack

参考资料

  • Writing a Loader
  • Introduction to Webpack: Entry, Output, Loaders, and Plugins
  • What is the loader order for webpack?

本文分享自微信公众号 - 前端向后(backward-fe)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-03-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习实践二三事

java设计模式之创建型模式

(1)创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

10140
来自专栏琦小虾的Binary

JavaSE 基础学习之一 —— Java 的简介

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/...

10350
来自专栏机器学习入门

LeetCode Weekly Contest 36解题思路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

7530
来自专栏机器学习入门

算法细节系列(34):再见字符串(2)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

5630
来自专栏运维前线

java线上服务问题排查总结

版权声明:本文为木偶人shaon原创文章,转载请注明原文地址,非常感谢。 https://b...

22630
来自专栏机器学习入门

LWC 66: 758. Bold Words in String

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

9240
来自专栏运维前线

CentOS 7.2 部署Node.js开发环境

版权声明:本文为木偶人shaon原创文章,转载请注明原文地址,非常感谢。 https://b...

24820
来自专栏琦小虾的Binary

JavaSE 基础学习之二 —— Java 的部分基本语法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/...

8520
来自专栏琦小虾的Binary

JavaSE 基础学习之四 —— 异常处理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/...

11820
来自专栏琦小虾的Binary

Java 学习之路——前言

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/...

8430

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励