loader

2019-10-09  本文已影响0人  田成力

1. loader运行的总体流程

loader

2.babel-loader

属性
this.request /loaders/babel-loader.js!/src/index.js'
this.userRequest /src/index.js
this.rawRequest ./src/index.js
this.resourcePath /src/index.js
$ cnpm i @babel/preset-env @babel/core -D
const babel = require("@babel/core");
function loader(source,inputSourceMap) {
    //C:\webpack-analysis2\loaders\babel-loader.js!C:\webpack-analysis2\src\index.js
    const options = {
        presets: ['@babel/preset-env'],
        inputSourceMap:inputSourceMap,
        sourceMaps: true,//ourceMaps: true 是告诉 babel 要生成 sourcemap
        filename:this.request.split('!')[1].split('/').pop()
    }
    //在webpack.config.js中 增加devtool: 'eval-source-map'
    let {code,map,ast}=babel.transform(source,options);
    return this.callback(null,code,map,ast);
}
module.exports = loader;
resolveLoader: {
    alias: {//可以配置别名
      "babel-loader": resolve('./build/babel-loader.js')
    },//也可以配置loaders加载目录
    modules: [path.resolve('./loaders'), 'node_modules']
},
{
    test: /\.js$/,
    use:['babel-loader']
}

3.pitch

pitch与loader本身方法的执行顺序图

|- a-loader `pitch`
  |- b-loader `pitch`
    |- c-loader `pitch`
      |- requested module is picked up as a dependency
    |- c-loader normal execution
  |- b-loader normal execution
|- a-loader normal execution

3.1 loaders\loader1.js

loaders\loader1.js

function loader(source) {
    console.log('loader1',this.data);
    return source+"//loader1";
}
loader.pitch = function (remainingRequest,previousRequest,data) {
    data.name = 'pitch1';
    console.log('pitch1');
}
module.exports = loader;

3.2 loaders\loader2.js

loaders\loader2.js

function loader(source) {
    console.log('loader2');
    return source+"//loader2";
}
loader.pitch = function (remainingRequest,previousRequest,data) {
    console.log('remainingRequest=',remainingRequest);
    console.log('previousRequest=',previousRequest);
    console.log('pitch2');
    //return 'console.log("pitch2")';
}
module.exports = loader;

3.3 loaders\loader3.js

loaders\loader3.js

function loader(source) {
    console.log('loader3');
    return source+"//loader3";
}
loader.pitch = function () {
    console.log('pitch3');
}
module.exports = loader;

3.4 webpack.config.js

 {
    test: /\.js$/,
    use: ['loader1', 'loader2', 'loader3']
 }

4.loader-runner

4.1 loader类型

4.2 特殊配置

符号 变量 含义
-! noPreAutoLoaders 不要前置和普通loader Prefixing with -! will disable all configured preLoaders and loaders but not postLoaders
! noAutoLoaders 不要普通loader Prefixing with ! will disable all configured normal loaders
!! noPrePostAutoLoaders 不要前后置和普通loader,只要内联loader Prefixing with !! will disable all configured loaders (preLoaders, loaders, postLoaders)

4.2 查找规则执行

let path = require("path");
let nodeModules = path.resolve(__dirname, "node_modules");
let request = "-!inline-loader1!inline-loader2!./styles.css";
//首先解析出所需要的 loader,这种 loader 为内联的 loader
let inlineLoaders = request
  .replace(/^-?!+/, "")
  .replace(/!!+/g, "!")
  .split("!");
let resource = inlineLoaders.pop();//// 获取资源的路径
let resolveLoader = loader => path.resolve(nodeModules, loader);
//从相对路径变成绝对路径
inlineLoaders = inlineLoaders.map(resolveLoader);
let rules = [
  {
    enforce: "pre",
    test: /\.css?$/,
    use: ["pre-loader1", "pre-loader2"]
  },
  {
    test: /\.css?$/,
    use: ["normal-loader1", "normal-loader2"]
  },
  {
    enforce: "post",
    test: /\.css?$/,
    use: ["post-loader1", "post-loader2"]
  }
];
let preLoaders = [];
let postLoaders = [];
let normalLoaders = [];
for(let i=0;i<rules.length;i++){
    let rule = rules[i];
    if(rule.test.test(resource)){
        if(rule.enforce=='pre'){
          preLoaders.push(...rule.use);
        }else if(rule.enforce=='post'){
          postLoaders.push(...rule.use);
        }else{
          normalLoaders.push(...rule.use);   
        }
    }
}
preLoaders = preLoaders.map(resolveLoader);
postLoaders= postLoaders.map(resolveLoader);
normalLoaders = normalLoaders.map(resolveLoader);

let loaders = [];
//noPrePostAutoLoaders  忽略所有的 preLoader / normalLoader / postLoader
if(request.startsWith('!!')){
  loaders = inlineLoaders;//只保留inline
//noPreAutoLoaders 是否忽略 preLoader 以及 normalLoader
}else if(request.startsWith('-!')){
  loaders = [...postLoaders,...inlineLoaders];//只保留post和inline
//是否忽略 normalLoader  
}else if(request.startsWith('!')){
  loaders = [...postLoaders,...inlineLoaders,...preLoaders];//保留post inline pre
}else{
  loaders = [...postLoaders,...inlineLoaders,...normalLoaders,...preLoaders];
}
console.log(loaders);

4.4 run-loader

let readFile = require("fs");
let path = require("path");
function createLoaderObject(loader) {
  let obj = { data: {} };
  obj.request = loader;
  obj.normal = require(loader);
  obj.pitch = obj.normal.pitch;
  return obj;
}
function runLoaders(options, callback) {
  let loaderContext = {};
  let resource = options.resource;
  let loaders = options.loaders;
  loaders = loaders.map(createLoaderObject);
  loaderContext.loaderIndex = 0;
  loaderContext.readResource = readFile;
  loaderContext.resource = resource;
  loaderContext.loaders = loaders;
  let isSync = true;
  var innerCallback = (loaderContext.callback = function(err, args) {
    loaderContext.loaderIndex--;
    iterateNormalLoaders(loaderContext, args, callback);
  });
  loaderContext.async = function async() {
    isSync = false;
    return innerCallback;
  };
  Object.defineProperty(loaderContext, "request", {
    get: function() {
      return loaderContext.loaders
        .map(function(o) {
          return o.request;
        })
        .concat(loaderContext.resource)
        .join("!");
    }
  });
  Object.defineProperty(loaderContext, "remainingRequest", {
    get: function() {
      return loaderContext.loaders
        .slice(loaderContext.loaderIndex + 1)
        .map(function(o) {
          return o.request;
        })
        .concat(loaderContext.resource || "")
        .join("!");
    }
  });
  Object.defineProperty(loaderContext, "currentRequest", {
    enumerable: true,
    get: function() {
      return loaderContext.loaders
        .slice(loaderContext.loaderIndex)
        .map(function(o) {
          return o.request;
        })
        .concat(loaderContext.resource || "")
        .join("!");
    }
  });
  Object.defineProperty(loaderContext, "previousRequest", {
    get: function() {
      return loaderContext.loaders
        .slice(0, loaderContext.loaderIndex)
        .map(function(o) {
          return o.request;
        })
        .join("!");
    }
  });
  Object.defineProperty(loaderContext, "data", {
    get: function() {
      return loaderContext.loaders[loaderContext.loaderIndex].data;
    }
  });
  iteratePitchingLoaders(loaderContext, callback);
  function iteratePitchingLoaders(loaderContext, callback) {
    if (loaderContext.loaderIndex >= loaderContext.loaders.length) {
      loaderContext.loaderIndex--;
      return processResource(loaderContext, callback);
    }

    let currentLoaderObject = loaderContext.loaders[loaderContext.loaderIndex];
    let fn = currentLoaderObject.pitch;
    if (!fn) return iteratePitchingLoaders(options, loaderContext, callback);

    let args = fn.apply(loaderContext, [
      loaderContext.remainingRequest,
      loaderContext.previousRequest,
      currentLoaderObject.data
    ]);
    if (args) {
      loaderContext.loaderIndex--;
      return iterateNormalLoaders(loaderContext, args, callback);
    } else {
      loaderContext.loaderIndex++;
      iteratePitchingLoaders(loaderContext, callback);
    }
    function processResource(loaderContext, callback) {
      let buffer = loaderContext.readResource.readFileSync(
        loaderContext.resource,
        "utf8"
      );
      iterateNormalLoaders(loaderContext, buffer, callback);
    }
  }
  function iterateNormalLoaders(loaderContext, args, callback) {
    if (loaderContext.loaderIndex < 0) return callback(null, args);

    var currentLoaderObject = loaderContext.loaders[loaderContext.loaderIndex];
    var fn = currentLoaderObject.normal;
    if (!fn) {
      loaderContext.loaderIndex--;
      return iterateNormalLoaders(loaderContext, args, callback);
    }
    args = fn.apply(loaderContext, [args]);
    if (isSync) {
      loaderContext.loaderIndex--;
      iterateNormalLoaders(loaderContext, args, callback);
    }
  }
}

let entry = "./src/world.js";

let options = {
  resource: path.join(__dirname, entry),
  loaders: [
    path.join(__dirname, "loaders/loader1.js"),
    path.join(__dirname, "loaders/loader2.js"),
    path.join(__dirname, "loaders/loader3.js")
  ]
};

runLoaders(options, (err, result) => {
  console.log(result);
});

5. file

5.1 file-loader

const { getOptions, interpolateName } = require('loader-utils');
function loader(content) {
  let options=getOptions(this)||{};
  let url = interpolateName(this, options.filename || "[hash].[ext]", {content});
  this.emitFile(url, content);
  return `module.exports = ${JSON.stringify(url)}`;
}
loader.raw = true;
module.exports = loader;

5.2 url-loader

let { getOptions } = require('loader-utils');
var mime = require('mime');
function loader(source) {
    let options=getOptions(this)||{};
    let { limit, fallback='file-loader' } = options;
    if (limit) {
      limit = parseInt(limit, 10);
    }
    const mimetype=mime.getType(this.resourcePath);
    if (!limit || source.length < limit) {
        let base64 = `data:${mimetype};base64,${source.toString('base64')}`;
        return `module.exports = ${JSON.stringify(base64)}`;
    } else {
        let fileLoader = require(fallback || 'file-loader');
        return fileLoader.call(this, source);
    }
}
loader.raw = true;
module.exports = loader;

6 css

6.1 less-loader.js

let less = require('less');
function loader(source) {
    let callback = this.async();
    less.render(source, { filename: this.resource }, (err, output) => {
        callback(err, output.css);
    });
}
module.exports = loader;

6.2 style-loader

let loaderUtils=require("loader-utils");
 function loader(source) {
    let script=(`
      let style = document.createElement("style");
      style.innerHTML = ${JSON.stringify(source)};
      document.head.appendChild(style);
    `);
    return script;
} 
module.exports = loader;
上一篇 下一篇

猜你喜欢

热点阅读