Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

实现常用的loader #33

Open
YIngChenIt opened this issue Jul 1, 2020 · 0 comments
Open

实现常用的loader #33

YIngChenIt opened this issue Jul 1, 2020 · 0 comments

Comments

@YIngChenIt
Copy link
Owner

实现常用的loader

loader知识查漏补缺

什么是loader

webpack只能处理js的模块,如果要处理其他类型的文件,需要使用loader来进行转换。loaderwebpack中一个重要的概念,它是指用来将一段代码转化为另一段代码的webpack加载器

loader的加载顺序

默认来说loader的执行顺序是从右到左,从下到上。

但是根据loader的分类loader的执行顺序也可以为pre - normal - inline - post

inline-loader的用法

行内loader我们用的比较少,我们一般是在js文件中引入行内loader,而不是在配置文件了配置

const str = require(inline-loader!'./a.js')

表示加载a.js这个文件使用行内loader来处理

我们还需要注意行内loader的一些规则写法

const str = require(-!inline-loader!'./a.js') //-! 表示文件不会通过pre-normal来处理了
const str = require(!inline-loader!'./a.js') //! 表示文件不会通过normal来处理了
const str = require(!!inline-loader!'./a.js') //!! 表示文件只经过行内loader来说处理了

pitchLoader 和 normalLoader

每个loader都有两部分组成pitchLoadernormalLoader, pitchnormal的执行顺序是相反的

pitchLoader没有返回值

pitch1

pitchLoader有返回值

pitch2

loader的特点

  • 第一个loader要返回js脚本

  • 每个loader只做一件内容,为了让更多的loader在更多的场景链式调用

  • 每一个loader都是一个模块

  • 每个loader都是无状态的,确保loader在不同模块转换之间不保存状态

实现babel-loader

首先安装需要的包

npm i @babel/core @babel/preset-env loader-utils

我们来看下使用babel-loader时候的配置

{
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                '@babel/preset-env'
            ]
        }
    }
}   

然后我们来实现一个babel-loader

const babel = require('@babel/core')
const utils = require('loader-utils')

function loader (source) {
    const options = utils.getOptions(this) //拿到当前loader的配置项
    const cb = this.async() //因为babel转换是异步 这里用提供的方法来处理异步
    babel.transform(source, {
        ...options, //拿到配置中配置的preset
        sourceMap: true, //开启soucreMap 方便调试
        filename: this.resourcePath.split('/').pop() //拿到当前文件名作为map文件的名字
    }, function (err, res) {
        cb(err, res.code, res.map) // 将处理之后的代码返回
    })
}

module.exports = loader

实现file-loader

file-loader的大致作用是根据图片生成一个md5,发射到dist目录下,dile-loader还会返回当前的图片路径

const utils = require('loader-utils')

function loader(source) {
    const filename = utils.interpolateName(this, '[hash].[ext]', {
        content: source
    }) //将图片拼上hash戳
    this.emitFile(filename, source) //发射文件
    return `module.exports = "${filename}"` //替换生成的字符串
}

loader.raw = true //将源码转成二进制
module.exports = loader

实现url-loader

url-loader可以把某一些图片转化为base64,并且会自动判断转化为base64还是使用file-loader处理

const utils = require('loader-utils')
const mime = require('mime')

function loader(source) {
    const {limit} = utils.getOptions(this)
    if (limit && limit > source.length) { //如果设置的大小大于文件的大小,要把图片转化为base64
        return `module.exports = "data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}"`
    } else {
        return require('./file-loader').call(this, source)
    }
}
loader.raw = true
module.exports = loader

实现less-loader

let less = require('less');
function loader(source) {
  let css;
  less.render(source,function (err,r) { // r.css
    css = r.css; 
  });
  return css
}

module.exports = loader;

实现css-loader

function loader(source) {
  let reg = /url\((.+?)\)/g;
  let pos = 0;
  let current;
  let arr = ['let list = []'];
  while (current = reg.exec(source)) { // [matchUrl,g]
    let [matchUrl, g] = current;
    let last = reg.lastIndex - matchUrl.length;
    arr.push(`list.push(${JSON.stringify(source.slice(pos, last))})`);
    pos = reg.lastIndex;
    // 把 g 替换成 require的写法  => url(require('xxx'))
    arr.push(`list.push('url('+require(${g})+')')`);
  }
  arr.push(`list.push(${JSON.stringify(source.slice(pos))})`)
  arr.push(`module.exports = list.join('')`);
  return arr.join('\r\n');
}

module.exports = loader;

实现style-loader

let loaderUtils = require('loader-utils');
function loader(source) {
  // 我们可以在style-loader中导出一个 脚本
  let str = `
    let style = document.createElement('style');
    style.innerHTML = ${JSON.stringify(source)};
    document.head.appendChild(style);
  `
  return str;
}
// 在style-loader上 写了pitch
// style-loader     less-loader!css-loader!./index.less
loader.pitch = function (remainingRequest) { // 剩余的请求
  // 让style-loader 去处理less-loader!css-loader/./index.less 
  // require路径 返回的就是css-loader处理好的结果 require('!!css-loader!less-loader!index.less')
  let str = `
    let style = document.createElement('style');
    style.innerHTML = require(${loaderUtils.stringifyRequest(this, '!!' + remainingRequest)});
    document.head.appendChild(style);
  `
  return str;
}
module.exports = loader;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant