1. css-loader #

1.1 安装 #

cnpm i webpack webpack-cli webpack-dev-server html-webpack-plugin css-loader css-selector-tokenizer file-loader less less-loader postcss style-loader to-string-loader -D

2. 使用CSS #

2.1 webpack.config.js #

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    mode:'development',
    devtool:false,
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    "to-string-loader",
                    {
                        loader:'css-loader',
                        options:{
                            url:false,
                            import:false,
                            esModule: false
                        }
                    }
                ],
                include:path.resolve('src')
            }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({template:'./src/index.html'}),
    ]
}

2.2 src\index.js #

src\index.js

const css = require("./global.css");
console.log(css);

2.3 src\global.css #

src\global.css

body {
    background-color: green;
}

2.4 dist\main.js #

dist\main.js

(() => {
  var modules = {
    "css-loader/dist/cjs.js!./src/global.css": (module, exports, require) => {
      var ___CSS_LOADER_API_IMPORT___ = require("css-loader/dist/runtime/api.js");
      var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function (i) {
        return i[1];
      });
      ___CSS_LOADER_EXPORT___.push([
        module.id,
        "body {\r\n    background-color: green;\r\n  }",
        "",
      ]);
      module.exports = ___CSS_LOADER_EXPORT___;
    },
    "css-loader/dist/runtime/api.js": (module) => {
      module.exports = function () {
        var list = [];
        list.toString = function toString() {
          return this.join("");
        };
        return list;
      };
    },
    "./src/global.css": (module, exports, require) => {
      var result = require("css-loader/dist/cjs.js!./src/global.css");
      if (typeof result === "string") {
        module.exports = result;
      } else {
        module.exports = result.toString();
      }
    },
  };
  var cache = {};
  function require(moduleId) {
    if (cache[moduleId]) {
      return cache[moduleId].exports;
    }
    var module = (cache[moduleId] = {
      id: moduleId,
      exports: {},
    });
    modules[moduleId](module, module.exports, require);
    return module.exports;
  }
  (() => {
    const css = require("./src/global.css");
    console.log(css, css.toString());
  })();
})();

2.5 实现main.js #

2.5.1 第1版 #

(() => {
  var modules = {
    "./src/global.css": (module, exports, require) => {
      module.exports = "body {\r\n    background-color: green;\r\n  }";
    },
  };
  var cache = {};
  function require(moduleId) {
    if (cache[moduleId]) {
      return cache[moduleId].exports;
    }
    var module = (cache[moduleId] = {
      id: moduleId,
      exports: {},
    });
    modules[moduleId](module, module.exports, require);
    return module.exports;
  }
  (() => {
    const css = require("./src/global.css");
    console.log(css);
  })();
})();

2.5.2 第2版 #

(() => {
  var modules = {
    "./src/global.css": (module, exports, require) => {debugger
      var list = [];
      list.push([
        module.id,
        "body {\r\n    background-color: green;\r\n  }",
        "",
      ]);
      let cssWithMappingToString = item=>item[1];
      let css =  list.map(item=>cssWithMappingToString(item)).join("");
      module.exports = css;
    },
  };
  var cache = {};
  function require(moduleId) {
    if (cache[moduleId]) {
      return cache[moduleId].exports;
    }
    var module = (cache[moduleId] = {
      id: moduleId,
      exports: {},
    });
    modules[moduleId](module, module.exports, require);
    return module.exports;
  }
  (() => {
    const css = require("./src/global.css");
    console.log(css);
  })();
})();

2.5.3 第3版 #

(() => {
  var modules = {
    "css-loader.js!./src/global.css": (module, exports, require) => {
      var api = require("api.js");
      var EXPORT = api(function (item) {
        return item[1];
      });
      EXPORT.push([
        module.id,
        "body {\r\n    background-color: green;\r\n}"
      ]);
      module.exports = EXPORT;
    },
    "api.js": (module) => {
      module.exports = function (cssWithMappingToString) {
        var list = [];
        list.toString = function toString() {
          return this.map(function (item) {
            var content = cssWithMappingToString(item);
            return content;
          }).join("");
        };
        return list;
      };
    },
    "./src/global.css": (module, exports, require) => {
      var result = require("css-loader.js!./src/global.css");
      module.exports = result.toString();
    },
  };
  var cache = {};
  function require(moduleId) {
    if (cache[moduleId]) {
      return cache[moduleId].exports;
    }
    var module = (cache[moduleId] = {
      id: moduleId,
      exports: {},
    });
    modules[moduleId](module, module.exports, require);
    return module.exports;
  }
  (() => {
    const css = require("./src/global.css");
    console.log(css);
  })();
})();

3. @import #

3.1 src\index.js #

src\index.js

//const css = require("./index.css");
const css = require("./index.less");
console.log(css);

3.2 src\index.css #

src\index.css

@import "./global.css";
body {
    color: red;
}

3.3 src\global.css #

src\global.css

body {
    background-color: green;
}

3.4 src\index.less #

src\index.less

@import "./global.less";
body {
    color: red;
}

3.5 src\global.less #

src\global.less

body {
    background-color: green;
}

3.6 webpack.config.js #

webpack.config.js

module.exports = {
+    resolveLoader:{
+        alias:{
+            'css-loader':path.resolve(__dirname,'loaders','css-loader.js')
+        }
+    },
+    module:{
+        rules:[
+            {
+                test:/\.css$/,
+                use:[
+                    "to-string-loader",
+                    {
+                        loader:'css-loader',
+                        options:{
+                            url:false,
+                            import:true,
+                            esModule: false
+                        }
+                    }
+                ],
+                include:path.resolve('src')
+            },
+            {
+                test:/\.less$/,
+                use:[
+                    "to-string-loader",
+                    {
+                        loader:'css-loader',
+                        options:{
+                            import:true,
+                            url:false,
+                            esModule: false,
+                            importLoaders:1
+                        }
+                    },
+                    {
+                        loader:'less-loader'
+                    }
+                ],
+                include:path.resolve('src')
+            },
+        ]
+    }
}

3.7 dist\main.js #

dist\main.js

(() => {
  var modules = {
    "css-loader.js!./src/global.css": (module, exports, require) => {
      var api = require("api.js");
      var EXPORT = api(function (i) {
        return i[1];
      });
      EXPORT.push([
        module.id,
        "body {\r\n    background-color: green;\r\n}\r\n",
        "",
      ]);
      module.exports = EXPORT;
    },
    "css-loader.js!./src/index.css": (module, exports, require) => {
      var api = require("api.js");
+     var GLOBAL_CSS = require("css-loader.js!./src/global.css");
      var EXPORT = api(function (item) {
        return item[1];
      });
+     EXPORT.i(GLOBAL_CSS);
      EXPORT.push([
        module.id,
        "body {\r\n    color: red;\r\n}",
        "",
      ]);
      module.exports = EXPORT;
    },
    "api.js": (module) => {
      module.exports = function (cssWithMappingToString) {
        var list = [];
        list.toString = function toString() {
          return this.map(function (item) {
            var content = cssWithMappingToString(item);
            return content;
          }).join("");
        };
+        list.i = function (modules) {
+          for (var i = 0; i < modules.length; i++) {
+            list.push(modules[i]);
+          }
+        };
        return list;
      };
    },
    "./src/index.css": (module, exports, require) => {
      var result = require("css-loader.js!./src/index.css");
      module.exports = result.toString();
    },
  };
  var cache = {};
  function require(moduleId) {
    if (cache[moduleId]) {
      return cache[moduleId].exports;
    }
    var module = (cache[moduleId] = {
      id: moduleId,
      exports: {},
    });
    modules[moduleId](module, module.exports, require);
    return module.exports;
  }
  (() => {
    const index = require("./src/index.css");
    console.log(index);
  })();
})();

3.8 loaders\to-string-loader.js #

loaders\to-string-loader.js

var loaderUtils = require("loader-utils");
module.exports = function() {}
module.exports.pitch = function(remainingRequest) {
    return `
        var result = require(${loaderUtils.stringifyRequest(this, "!!" + remainingRequest)});
        if (result && result.__esModule) {
            result = result.default;
        }
        if (typeof result === "string") {
            module.exports = result;
        } else {
            module.exports = result.toString();
        }
    `;
};

3.9 loaders\css-loader.js #

loaders\css-loader.js

var postcss = require("postcss");
var loaderUtils = require("loader-utils");
var Tokenizer = require("css-selector-tokenizer");
const { getOptions } = require("loader-utils");
const cssLoader = function (inputSource) {
  let loaderOptions = getOptions(this) || {};
  const cssPlugin = (options) => {
    return (root) => {
      root.walkAtRules(/^import$/i, (rule) => {
        rule.remove();
        options.imports.push(rule.params.slice(1, -1));
      });
    };
  };

  let callback = this.async();
  let options = { imports: [] };
  let pipeline = postcss([cssPlugin(options)]);
  pipeline.process(inputSource).then((result) => {
    let { loaders, loaderIndex } = this;
    let {importLoaders=0} = loaderOptions;
    const loadersRequest = loaders
    .slice(
      loaderIndex,
      loaderIndex + 1 + (typeof importLoaders !== "number" ? 0 : importLoaders)
    )
    .map((x) => x.request)
    .join("!");
    let importCss = options.imports
      .map((url) =>`list.push(...require(`+loaderUtils.stringifyRequest(this, `-!${loadersRequest}!${url}`)+`));`).join("\r\n");
    let script = `
      var list = [];
      list.toString = function () {return this.join('')}
      ${importCss}
      list.push(\`${result.css}\`);
      module.exports = list;
    `;     
    callback(null, script);
  });
};

module.exports = cssLoader;

4.@url #

4.1 webpack.config.js #

module.exports = {
    mode:'development',
    devtool:false,
    resolveLoader:{
        alias:{
            'css-loader':path.resolve(__dirname,'loaders','css-loader.js')
        },
         modules:[path.resolve(__dirname,'loaders'),'node_modules']
    },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    "to-string-loader",
                    {
                        loader:'css-loader',
                        options:{
                            url:true,
                            import:true,
                            esModule: false
                        }
                    },
                    "logger1-loader",
                    "logger2-loader"
                ],
                include:path.resolve('src')
            },
            {
                test:/\.less$/,
                use:[
                    "to-string-loader",
                    {
                        loader:'css-loader',
                        options:{
                            import:true,
+                            url:true,
                            esModule: false,
                            importLoaders:1
                        }
                    },
                    {
                        loader:'less-loader'
                    }
                ],
                include:path.resolve('src')
            },
+            {
+                 test:/\.jpg/,
+                 use:[{
+                     loader:'file-loader',
+                     options:{
+                         esModule:false
+                     }
+                 }]
+             }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({template:'./src/index.html'}),
    ]
}

4.2 src\index.css #

src\index.css

body {
    color: red;
}
+#root{
+    background-image: url(./images/kf.jpg);
+    width:100px;
+    height:100px;
+}

4.3 dist\main.js #

dist\main.js

(() => {
  var modules = {
    "css-loader.js!./src/index.css": (module, exports, require) => {
      var api = require("api.js");
      var EXPORT = api(function (i) {
        return i[1];
      });
      EXPORT.push([
        module.id,
+        "body {\r\ncolor: red;\r\n}\r\n#root{\r\nbackground-image: url(" +require("./src/images/kf.jpg") +");\r\nwidth:100px;\r\n    height:100px;\r\n}"
      ]);
      module.exports = EXPORT;
    },
    "api.js": (module) => {
      module.exports = function (cssWithMappingToString) {
        var list = [];
        list.toString = function toString() {
          return this.map(function (item) {
            var content = cssWithMappingToString(item);
            return content;
          }).join("");
        };
        list.i = function (modules) {
          for (var i = 0; i < modules.length; i++) {
            list.push(modules[i]);
          }
        };
        return list;
      };
    },
    "./src/images/kf.jpg": (module, exports, require) => {
      module.exports = require.p + "4a783413fe7beffd284711d5fdfd1549.jpg";
    },
    "./src/index.css": (module, exports, require) => {
      var result = require("css-loader.js!./src/index.css");
      module.exports = result.toString();
    },
  };
  var cache = {};
  function require(moduleId) {
    if (cache[moduleId]) {
      return cache[moduleId].exports;
    }
    var module = (cache[moduleId] = {
      id: moduleId,
      exports: {},
    });
    modules[moduleId](module, module.exports, require);
    return module.exports;
  }
  require.p="";
  const index = require("./src/index.css");
  console.log(index);
})();

4.4 loaders\css-loader.js #

loaders\css-loader.js

var postcss = require("postcss");
var loaderUtils = require("loader-utils");
var Tokenizer = require("css-selector-tokenizer");
const { getOptions } = require("loader-utils");
const cssLoader = function (inputSource) {
  let loaderOptions = getOptions(this) || {};
  const cssPlugin = (options) => {
    return (root) => {
      root.walkAtRules(/^import$/i, (rule) => {
        rule.remove();
        options.imports.push(rule.params.slice(1, -1));
      });
+      root.walkDecls((decl) => {
+          var values = Tokenizer.parseValues(decl.value);
+          values.nodes.forEach(function (value) {
+            value.nodes.forEach((item) => {
+              if (item.type === "url") {
+               let url = loaderUtils.stringifyRequest(this, item.url);
+               item.stringType = "'";
+               item.url ="`+require("+url+")+`";
+              }
+            });
+          });
+          let value = Tokenizer.stringifyValues(values);
+          decl.value = value;
+      });
    };
  };

  let callback = this.async();
  let options = { imports: [] };
  let pipeline = postcss([cssPlugin(options)]);
  pipeline.process(inputSource).then((result) => {
    let { loaders, loaderIndex } = this;
    let {importLoaders=0} = loaderOptions;
    const loadersRequest = loaders
    .slice(
      loaderIndex,
      loaderIndex + 1 + (typeof importLoaders !== "number" ? 0 : importLoaders)
    )
    .map((x) => x.request)
    .join("!");
    let importCss = options.imports
      .map((url) =>`list.push(...require(`+loaderUtils.stringifyRequest(this, `-!${loadersRequest}!${url}`)+`));`).join("\r\n");
    let script = `
      var list = [];
      list.toString = function () {return this.join('')}
      ${importCss}
      list.push(\`${result.css}\`);
      module.exports = list;
    `;     
    callback(null, script);
  });
};

module.exports = cssLoader;

5.style-loader #

5.1 webpack.config.js #

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    mode:'development',
    devtool:false,
    resolveLoader:{
        alias:{
            'css-loader':path.resolve(__dirname,'loaders','css-loader.js'),
+            'style-loader':path.resolve(__dirname,'loaders','style-loader.js')
        }
    },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
+                    "style-loader",
                    {
                        loader:'css-loader',
                        options:{
                            url:true,
                            import:true,
                            esModule: false
                        }
                    }
                ],
                include:path.resolve('src')
            },
            {
                test:/\.less$/,
                use:[
+                    "style-loader",
                    {
                        loader:'css-loader',
                        options:{
                            import:true,
                            url:true,
                            esModule: false,
                            importLoaders:1
                        }
                    },
                    {
                        loader:'less-loader'
                    }
                ],
                include:path.resolve('src')
            },
            {
                 test:/\.jpg/,
                 use:[{
                     loader:'file-loader',
                     options:{
                         esModule:false
                     }
                 }]
             }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({template:'./src/index.html'}),
    ]
}

5.2 style-loader.js #

loaders\style-loader.js

let loaderUtils = require("loader-utils");
function loader(source) {}

loader.pitch = function (remainingRequest, previousRequest, data) {let style = `
    var style = document.createElement("style");
    style.innerHTML = require(${loaderUtils.stringifyRequest(
      this,
      "!!" + remainingRequest
    )});
    document.head.appendChild(style);
 `;
  return style;
};
module.exports = loader;

5.3 src\index.html #

src\index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>css-loader</title>
</head>
<body>
+    <div id="root">root</div>
</body>
</html>

7. PostCSS #

7.1 类型 #

7.2 AST节点 #

7.3 操作 #

var postcss = require("postcss");
const cssPlugin = (options) => {
    return (root,result) => {
      root.walkAtRules();//遍历所有的@Rule @import
      root.walkDecls((decl) => {
        if(decl.value.endsWith('px')){
            decl.value=parseFloat(decl.value)/75+'rem';
        }
      });
    };
  };
let options = {};
let pipeline = postcss([cssPlugin(options)]);
let inputSource = `
#root{
    width:750px;
}`;
pipeline.process(inputSource).then((result) => {
    console.log(result.css);
})