Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How style-loader works with css-loader?

Tags:

webpack

I know that style-loader adds CSS to a dom by injecting a tag. And css-loader gets css as string when it meets require('./style.css');.

But, How style-loader plays with css-loader??

I'm reading style-loader source code and css-loader source code. But I cannot understand how they play together.

How the css string which css-loader gets from style.css is passed to style-loader?

like image 979
Byeongin Yoon Avatar asked Apr 22 '19 06:04

Byeongin Yoon


People also ask

What is the use of style-loader?

The style loader takes CSS and actually inserts it into the page so that the styles are active on the page.

How do I load a CSS file into webpack?

To be able to use CSS in your webpack app, you need to set up a new loader. Out-of-the-box, webpack only understands Javascript and JSON. With a loader, you can translate another type of file to a format that webpack understands and can work with. There are many webpack loaders and each loader has a specific purpose.

How do webpack loaders work?

They allow you to pre-process files as you import or “load” them. Thus, loaders are kind of like “tasks” in other build tools and provide a powerful way to handle front-end build steps. Loaders can transform files from a different language (like TypeScript) to JavaScript or load inline images as data URLs.

How do I bundle CSS with webpack?

By default, Webpack will inline your CSS as Javascript tags that injects the CSS into the page. This sounds strange but it lets you do hot reloading in development. In production you extract them to a file using the ExtractTextPlugin, and require it with a normal link tag.


1 Answers

Good question. I've done quite some homework in order to properly answer it. Here's what I found.

Normal Loader

The common understanding of webpack loaders, is that they are units chained-up to form a pipeline. Each loader processes the input source code, transforms it, then passes the result down to the next unit in the pipeline. And this process repeats until the last unit done its job.

But above is only part of the whole picture, only true for normal loaders. style-loader is not a normal loader, because it also has a pitch method.


The pitch Method of Loader

Note, there's no such thing as pitch loader, cus every loader can have a "normal side" and "pitch side".

Here's the not-very-helpful webpack doc on pitching loader. The most useful info is the concept of "pitch phase" and "normal phase" and their execution order.

|- 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

You've seen style-loader's source code, the export looks like:

module.exports = {}
module.exports.pitch = function loader(request) {
  /* ... */
  return [/* some string */].join('\n')
}

The only related part in the doc to above source code:

if a loader delivers a result in the pitch method the process turns around and skips the remaining loaders.

It's still quite unclear on how exactly this pitch thing works.

Digging Deeper

I finally came across this blog post (written in chinese tho) talks about the detail. Specifically, it analyses the exact case like in style-loader where the pitch method returns something.

As per the blog, the pitch method is mainly used to access and modify metadata early in the loader process. Returning from pitch method is indeed rare, and poorly documented. But when it does return sth other than undefined, here's what happens:

# Normal execution order is disrupted.
|- style-loader `pitch` # <-- because this guy returns string early
# below steps are all canceled out
  |- css-loader `pitch`
    |- requested module is picked up as a dependency
  |- css-loader normal execution
|- style-loader normal execution

Then the return value from styleLoader.pitch just becomes a new in-memory file entry. This file is then loaded like a normal file and transformed using a brand new load process.

If you check, the content of this on-the-fly generated file from styleLoader.pitch looks something like

var content = require("!!./node_modules/css-loader/dist/cjs.js??ref--8-3!./index.css");

You'll notice every require request is fully configured using inline query. Thus these request won't go through any test in webpackConfig.module.rules.

Conclusion

Basically, this is what style-loader does:

  1. it captures a request early by exposing a pitch method.
  2. it then understands what this request is about, read the config of all following-up loaders, transforms all config to inline-queried require(...)
  3. it then issues a new file on-the-fly, and by doing this, the original request is effectively canceled then replaced by a new request to this in-memory file.

I don't know any better, all truth is held in the source code of loader-runner module. If anyone has better ref sources or understanding, please comment, post an answer or edit mine.

like image 128
hackape Avatar answered Nov 10 '22 15:11

hackape