Previous articles:

  • Relearn webpack4 principle analysis

  • Relearn webpack4 basics

  • Re-learn webpack4 loader development

  • Relearn webpack4 plugin development

  • Webpack plug-in development second open cache plug-in

  • Relearn webpack4 packaging libraries and components

  • Relearn webpack4 build speed and volume optimization

loader

Loader is simply a JS module exported as a function

module.exports = function(source) {
  return source
}
Copy the code

Multiple loaders are executed in serial order from back to front

use: ['style-loader'.'css-loader'.'less-loader']
Copy the code

Why from back to front?

Two cases of combinations of functions

  • In the Unix pipline
  • Compose (webpack uses this) grilled Onions
compose = (f,g) = > (. args) = >f(g(... args))Copy the code
Implement a small example: loader-order
├ ─ ─ dist │ ├ ─ ─ index. The js │ └ ─ ─ the main, js ├ ─ ─ loaders │ ├ ─ ─ a - loader. The js │ └ ─ ─ b - loader. Js ├ ─ ─ package \ copy json ├ ─ ─ └─ SRC │ ├─ ├─ webpack.config.jsCopy the code

package.json

{
  "name": "loader-order"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "build": "webpack"
  },
  "keywords": []."author": ""."license": "ISC"."devDependencies": {
    "loader-utils": "^ 2.0.0." "."webpack": "^ 4.43.0"."webpack-cli": "^" 3.3.12}}Copy the code

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js'.output: {
    path: path.join(__dirname, 'dist'),
    filename: 'main.js'
  },
  module: {
    rules: [{test: /\.js$/,
        use: [
          path.resolve('./loaders/a-loader'),
          path.resolve('./loaders/b-loader']}]}}Copy the code

a-loader.js

const loaderUtils = require('loader-utils');

module.exports = function(source) {
  console.log('Loader a is excuted! ');

  const url = loaderUtils.interpolateName(this.'[name].[ext]', source);
  console.log(url);
  this.emitFile(url, source);
  return source;
}
Copy the code

b-loader.js

module.exports = function(source) {
  console.log('Loader b is excuted! ');
  return source;
}
Copy the code

index.js

const a = 1
Copy the code

NPM run build. Logs are printed

Loader b is excuted!
Loader a is excuted!
Copy the code

Use loader-runner to develop and debug loader locally

  • Loader-runner allows loaders to run without installing webpack
  • As a WebPack dependency, it is used in WebPack to execute the Loader and to develop and debug the loader

Implementation of a small example: raw-loader

├ ─ ─ package. Json ├ ─ ─ the run - loader. Js └ ─ ─ the SRC ├ ─ ─ async - loader. Js ├ ─ ─ async. TXT ├ ─ ─ demo. TXT └ ─ ─ raw - loader. JsCopy the code

package.json

{
  "name": "raw-loader"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "test": "node ./run-loader.js"
  },
  "keywords": []."author": ""."license": "ISC"."dependencies": {
    "loader-runner": "^ 3.1.0"
  },
  "devDependencies": {
    "loader-utils": "^ 2.0.0." "}}Copy the code

run-loader.js

const { runLoaders } = require('loader-runner')
const fs = require('fs')
const path = require('path')

runLoaders({
  resource: path.join(__dirname, './src/demo.txt'),
  loaders: [{loader: path.join(__dirname, './src/raw-loader.js'),
      // Pass parameters to loader
      options: {
        name: 'test'}}, {loader: path.join(__dirname, './src/async-loader.js')},context: {
    emitFile: () = >{}},readResource: fs.readFile.bind(fs)
}, (err, result) = > {
  err ? console.log(err) : console.log(result)
})
Copy the code

demo.txt

foobar
Copy the code

raw-loader.js

const loaderUtils = require('loader-utils')
const fs = require('fs')
const path = require('path')

module.exports = function(source) {
  const { name } = loaderUtils.getOptions(this)
  console.log(name)

  Loader / / synchronization
  const json = JSON.stringify(source)
        .replace('foo'.' ')
        .replace(/\u2028/g.'\\u2028')
        .replace(/\u2029/g.'\\u2029');
  
  //return `export default ${json}`
  
  // throw new Error('Error');

  // this.callback(new Error('Error'), json)
  
  this.callback(null, json, 2.3.4)}Copy the code

async.txt

async
Copy the code

async-loader.js

const fs = require('fs')
const path = require('path')

module.exports = function(source) {
  const callback = this.async()
  
  fs.readFile(path.join(__dirname, './async.txt'), 'utf-8'.(err, data) = > {
    if (err) {
      callback(err, ' ')
    }
    callback(null, data)
  })
}
Copy the code

NPM run test, log printing

test { result: [ '"async"', 2, 3, 4 ], resourceBuffer: <Buffer 66 6f 6f 62 61 72>, cacheable: true, fileDependencies: ['/Users/bytedance/mywork/new - life/webpack - rest/write loader and plug-ins/raw - loader/SRC/demo. TXT '], contextDependencies: [], missingDependencies: [] }Copy the code

Obtaining Loader Parameters

  • Obtain the value by using the getOptions method of loader-utils
module.exports = function(source) {
  const { name } = loaderUtils.getOptions(this)}Copy the code

Asynchronous loader

module.exports = function(source) {
  const callback = this.async()
  callback(null, source)
}
Copy the code

Caching is used in loader

  • Webpack enables loader caching by default
    • You can use this.cacheable(false) to turn off caching
  • Cache condition: The loader result has a certain output with the same input
    • A dependent loader cannot use the cache

How does loader export files

  • This.emitfile writes files
const loaderUtils = require('loader-utils')
const url = loaderUtils.interpolateName(this."[name].[ext]", {
  source,
})
this.emitFile(path.join(__dirname, url), source);
const path = `__webpack_public_path__+The ${JSON.stringify(url)}; `
return `export default ${path}`
Copy the code

Finally, a loader for image processing

├ ─ ─ dist │ ├ ─ ─ index. The CSS │ └ ─ ─ Sprite. JPG ├ ─ ─ package. The json ├ ─ ─ the run - loader. Js └ ─ ─ the SRC ├ ─ ─ images │ ├ ─ ─ 1. JPG │ └ ─ ─ 2. JPG ├ ─ ─ index. CSS └ ─ ─ Sprite - loader. JsCopy the code

package.json

{
  "name": "sprite-loader"."version": "1.0.0"."main": "index.js"."scripts": {
    "test": "node ./run-loader.js"
  },
  "keywords": []."author": ""."license": "ISC"."description": ""."devDependencies": {
    "run-loader": "^ 0.0.6"
  },
  "dependencies": {
    "loader-utils": "^ 2.0.0." "."spritesmith": "^ 3.4.0"}}Copy the code

run-loader.js

const { runLoaders } = require('loader-runner')
const fs = require('fs')
const path = require('path')

runLoaders({
  resource: path.join(__dirname, './src/index.css'),
  loaders: [
    path.join(__dirname, './src/sprite-loader')].readResource: fs.readFile.bind(fs)
}, (err, result) = > (err ? console.log(err) : null))
Copy the code

index.css

After the picture? __sprite, which represents the images to be merged

.img1 {
  background: url(./images/1.jpg?__sprite);
}
.img2 {
  background: url(./images/2.jpg?__sprite);
}
Copy the code

sprite-loader.js

const Spritesmith = require('spritesmith')
const fs = require('fs')
const path = require('path')

module.exports = function(source) {
  const callback = this.async()
  const imgs = source.match(/url\((\S*)\? __sprite/g)
  const matchedImgs = []

  for (let i = 0; i < imgs.length; i++) {
    const img = imgs[i].match(/url\((\S*)\? __sprite/) [1]
    matchedImgs.push(path.join(__dirname, img))
  }

  Spritesmith.run({
    src: matchedImgs
  }, (err, result) = > {
    fs.writeFileSync(path.join(process.cwd(), 'dist/sprite.jpg'), result.image)
    source = source.replace(/url\((\S*)\? __sprite/g.mach= > {
      return `url("dist/sprite.jpg"`
    })
    
    fs.writeFileSync(path.join(process.cwd(), 'dist/index.css'), source)
    callback(null, source)
  })
}
Copy the code

NPM run test

  • sprite.jpg

  • index.css
.img1 {
  background: url("dist/sprite.jpg");
}
.img2 {
  background: url("dist/sprite.jpg");
}
Copy the code

I’m not going to deal with position for the image

Join us at ❤️

Bytedance Xinfoli team

Nice Leader: Senior technical expert, well-known gold digger columnist, founder of Flutter Chinese community, founder of Flutter Chinese community open source project, well-known developer of Github community, author of dio, FLY, dsBridge and other well-known open source projects

Wechat scan code: look forward to your participation, together with technology to change life!!

We look forward to your joining us to change our lives with technology!!

Recruitment link: job.toutiao.com/s/JHjRX8B