Writing is not easy, without the permission of the author is prohibited in any form of reprint!


If you think this article is good, welcome to follow, thumb up and share!


Link to the original Nuggets

Know the Loader

Loader can be used to convert the source code of a module;

Webpack doesn’t really know how to load this module when we load it, so we have to customize the corresponding loader to do this.

Loader configuration mode

  • Inline mode:import "css-loader! . /css/index.css"; Loader and file paths used! separated
  • Configuration method: webpack.config.js
  • The configuration mode means to specify the configuration information in our webpack.config.js file

    • Module. rules allows us to configure multiple loaders (because we will continue to use other loaders to load other files).
    • This allows you to better identify the configuration of the Loader and facilitate maintenance later. It also gives you a global overview of the individual Loaders.
  • The configuration of module.rules is as follows
  • The rules property corresponds to an array of values: [Rule]
  • A Rule is an object in which multiple properties can be set

    • Test property: used to match multiple resource filenames, usually set to a regular expression;
    • Use property: The corresponding value is an array [useEntry]

      • The order of execution is back-to-front, such as parsing CSS, CSS -loader should follow style-loader.
      • UseEntry is an object, and you can set some other properties through the properties of the object

        • Loader: Must have a loader property, the corresponding value is a string
        • Options: Optional properties whose value is a string or an object that will be passed into the loader;
        • Query: is now replaced by options
      • Passing string (e.g. : use:[‘style-loader’]) is shorthand for the loader attribute (e.g. : use:[{loader:’style-loader’}])

Common Loader

CSS loader

  • We can also think of the CSS file as a module, which we load through import
  • So what kind of loader is needed

    • To load the CSS file, we need a loader that can read the CSS file
    • The most commonly used is CSS-Loader
    • It is only responsible for parsing the CSS file and does not apply to the page. In addition, it also needs the style-loader to apply to the page

      npm i style-loader --save

  • Installation of CSS-Loader:

    npm install css-loader --save

  • The following is the configuration file for a CSS-loader
const path = require("path"); module.exports = { entry: "./src/js/main.js", output: { filename: "bundle.js", path: Path. Resolve (__dirname, "build"),}, module: {rules: [{test: /\.css$/, // use: [{loader: "Style-loader ", options: {},}, // For short: //" CSS -loader" {// Full Loader: "CSS -loader", // The corresponding loader options: {},},],},],},};
  • Less in the same way

    NPM install less-loader less –save

  • Write style-loader, css-loader, less-loader into the use array in turn

Browser compatibility

  • What if we shared our configuration compatibility criteria under CSS compatibility and JS compatibility

    • When we set a condition: >1% Last 2 version Firefox, Chrome… Not dead are optional, multiple options to, divided into | | conditions; And is &&; Available NOT condition
    • JS, CSS compatible with Firefox, Chrome and the latest two versions of the market share of more than 1%, and 24 months of official support and update browsers (Dead)
  • Write the condition in the.browserlistrc file at the root directory
  • Through the realization of market share ratio to go, version requirements, such as configuration, adapt to the browser
  • Tools used:

    • Source: BrowserList, browser market share, accurate to every version
    • Processing plug-ins: Autoprefixer, Babel, PostCSS-preset-env, etc

Know PostCSS tools

  • What is the PostCSS tool?

    • PostCSS is a tool for converting styles using JavaScript
    • This tool can help us with some CSS style conversion and adaptation, such as automatically add browser prefix, CSS style recharge;
    • But to do this, we need the PostCSS plugin
  • How do I use PostCSS
  1. Look for extensions to PostCSS in build tools, such as postCSS-loader in Webpack;
  2. Select the PostCSS-related plugins you want to add

Use PostCSS manually

To use the PostCSS-CLI operation, you need the Autoprefixer plug-in

  1. npm install postcss postcss-cli --save
  2. npm install autoprefixer --save
  3. NPX postcss –use autoprefixer-o ‘output path’, for example:

    1. npx postcss --use autoprefixer -o ./src/css/result.css ./src/css/user.css

Webpack configuration files use PostCSS

  1. Install PostCSS-Loader and Autoprefixer

    • npm install postcss-loader autoprefixer --save
  2. Writing configuration files

    • PostCSS-loader is used to process CSS before using postCSS-loader
    • Under PostCSSOptions, configure plugins to use Autoprefixer
{test: /\.css$/, // resource file matching regular expression use: [{loader: "style-loader", options: {},}, // Short for: // "css-loader" {// Full Loader: "css-loader", // The corresponding loader options: {},}, {loader: "postcss-loader", options: { postcssOptions: { plugins: [require("autoprefixer")], }, }, }, ], },

postcss-preset-env

  • In fact, we do not need to use Autoprefixer to configure the plug-in when configuring PostCSS-Loader.
  • We can use another plugin :postcss-preset-env

    • PostCSS-preset-env is also a PostCSS plugin;
    • It helps you turn some of the modern CSS features into CSS that most browsers recognize, and adds the necessary polyfills depending on the target browser or runtime context.

      • For example, eight-bit hexadecimal colors will help us convert them to RGBA
    • Also included will automatically help us add Autoprefixer (so Autoprefixer is already built in);
    • First, we need to install postcss-preset-env:

      npm install postcss-preset-env --save

    • After that, we can directly modify the previous Autoprefixer:
  • Configuration to simplify the

    • Write only postcss-loader in use
    • Create postcss.config.js in the project root directory and write the configuration

webpack.config.js

const path = require("path"); module.exports = { entry: "./src/js/main.js", output: { filename: "bundle.js", path: Path. Resolve (__dirname, "build"),}, module: {rules: [{test: /\.css$/, // use: [{loader: "Style-loader ", options: {},}, // For short: //" CSS -loader" {// Full Loader: "CSS -loader", // The corresponding loader options: {}, }, "postcss-loader", ], }, { test: /\.less$/, use: ["style-loader", "css-loader", "postcss-loader", "less-loader"], }, ], }, };

postcss.config.js

Exports = {plugins: [// import // require(" postCSS-preset-env ") // "postCSS-preset-env"],}; exports = {plugins: [// import // require(" postCSS-preset-env ") //

The effect

Before

:fullscreen{

}

.content{
    user-select: none;
    transition: all 2s ease;
}

After

:-webkit-full-screen{ } :-ms-fullscreen{ } :fullscreen{ } .content{ -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; transition: all 2s ease; } /*# sourceMappingURL=data:application/json; base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInVzZXIuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztBQUVBOztBQUZBOztBQUVBOztBQ UZBOztBQUVBOztBQUVBO0lBQ0kseUJBQWlCO09BQWpCLHNCQUFpQjtRQUFqQixxQkFBaUI7WUFBakIsaUJBQWlCO0lBQ2pCLHVCQUF1QjtBQUMzQiIsImZpb GUiOiJyZXN1bHQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiOmZ1bGxzY3JlZW57XG5cbn1cblxuLmNvbnRlbnR7XG4gICAgdXNlci1zZWxlY3Q6IG5vbmU7X G4gICAgdHJhbnNpdGlvbjogYWxsIDJzIGVhc2U7XG59Il19 */

importLoaders

  • Normal CSS file parsing will not start from the last loader in the use array when @import is encountered
  • Recursive parsing can be achieved by setting importLoaders
  • The number you fill in is the number of subsequent loaders in the use array
  • Such as:
const path = require("path"); module.exports = { entry: "./src/js/main.js", output: { filename: "bundle.js", path: Path. Resolve (__dirname, "build"),}, module: {rules: [{test: /\.css$/, // use: [{loader: "Style-loader ", options: {},}, // For short: //" CSS -loader" {// Full Loader: "CSS -loader", // The corresponding loader options: {importLoaders: 1,}}, "postcss - loader,"],}, {test: / \. Less $/, use: [" style - loader ", {/ / complete writing loader: "Css-loader ", // The corresponding loader options: {importLoaders: 2,},}, "postcss-loader", "less-loader",],},],},},};

File Loader

You want to parse and read files, such as images

  1. The installation file – loader

    • npm install --save file-loader
  2. Writing configuration files

Rule:

  • The options:

    • Name: Output name, [name] name reference,. Character, [hash:6] hash truncated to 6 bits, [ext] suffix
    • OutputPath: The output folder, or img/ before the filename
{ test: /\.(png|jpe? g|gif|svg)$/, use: [{loader: file - "loader", the options: {name: "[name] [hash: 6] [ext]", outputPath: "img" / / limit (url - loader)}}],},
  • The same function as file-loader also has URL-loader

    • The difference is that instead of packaging, it converts the image to BASE64 encoding.
    • You can reduce the number of requests by writing the limit attribute under the options of the use object to limit the size and BASE4 encode small images.

Asset module type

  • The current version of Webpack we are using is Webpack5:

    • Before Webpack5, we need to use some loaders to load these resources, such as raw-loader, url-loader, file-loader;
    • After Webpack5, we can directly use the Asset Module Type, instead of the above loader;
  • Asset Module Type, which replaces all of these Loaders by adding four new module types:

    • Asset /resource sends a separate file and exports the URL. This was done earlier by using file-loader;
    • Asset /inline exports the data URI of a resource. This was done earlier by using URL-loader;
    • Asset /source exports the source code for the resource. This was done earlier by using Raw-Loader;
    • Asset automatically chooses between exporting a data URI and sending a separate file. This was done by using URL-loader and configuring the resources
  • For example, to load an image, we can use the following methods:
/ / packaging picture {test: / \. (PNG |, jpe." G | | GIF SVG) $/, type: "asset/resource", the generator: {filename: "img / [name] [6] hash: [ext]",},}, {/ / BASE64 encoding and limit size test: /\.(png|jpe? g|gif|svg)$/, type: "asset/inline", parser: { dataUrlCondition: { maxSize: 100 * 1024,}}}, / / mixed use, limited size, determine packaging {test: / \. (PNG |, jpe." g|gif|svg)$/, type: "asset", generator: { filename: "img/[name].[hash:6][ext]", }, parser: { dataUrlCondition: { maxSize: 100 * 1024, }, }, },
  • However, how can you customize the output path and file name of a file?

    • To modify output, add AssetModuleFileName;
    • In Rule 2, add a Generator property and set FileName.
Output: {filename: "Bundle. Js ", path: path.resolve(__dirname, "build"), assetModuleFilename: "Img / [name] [6] hash: [ext]",}, second rule object (recommended) {/ / way test: / \. (PNG |, jpe." g|gif|svg)$/, type: "asset/resource", generator: { filename: "img/[name].[hash:6][ext]", }, }

Load the font file

Rule object:

{ test: /\.ttf|eot|woff2? $/i, type: "asset/resource", generator: { filename: "font/[name].[hash:6][ext]", }, },

Custom Loader

Loaders are used to convert the source code of the module. We have used many loaders before, such as CSS-loader, style-loader, babel-loader, etc

  • Loader is essentially a JavaScript module that is exported as a function
  • The Loader Runner library calls this function and passes in the result or resource file from the previous Loader.
  • Writing a custom Loader accepts three parameters

    • Content: Resource file parameters
    • Map: SourceMap related data
    • Meta: Some metadata
  • Note: The path passed in is related to content

    • webpack.config.js
{ context: path.resolve(__dirname, ".. /"), entry: "./src/main.js", output: { path:path.resolve(__dirname,".. /build"), filename:"bundle.js", }, module: { rules:[ { test:/\.js$/i, use:[ "./loaders/demoLoader.js" ] } ] } }
  • If you want to load the Loader folder directly, you can configure the ResoveLoader property
module: {
        rules:[
            {
                test:/\.js$/i,
                use:[
                    "demoLoader"
                ]
            }
        ]
},
resolveLoader:{
    modules:["./loaders", "node_modules"]
}
  • Multiple Loaders use execution order

    • From back to front, right to left
rules:[
    {
        test:/\.js$/i,
        use:[
            "demoLoader1",
            "demoLoader2",
            "demoLoader3",
        ]
    }
]
  • The results of

Pitch – Loader and enforce

There is actually another type of Loader, called PitchLoader

  • In fact, this is why the loader is executed in reverse order

    • Run-Loader first executes PitchLoader, while PitchLoader executes LoaderIndex+ +;
    • NormalLoader is executed after the run-loader, and loaderIndex is executed when the normalLoader is executed.
  • So, how do you change the order of their execution?

    • You can split multiple Rule objects and use Enforce to change their order
    • Enforce is a property of the Rule object.
  • There are four ways to use Enforce

    • All Loaders are Normal by default
    • Loaders set inline are all inline
    • You can also set PRE and POST through Enforce
  • In Pitch and Normal the order of execution is

    • Post, inline, normal, pre
    • Pre, normal, inline, post

Synchronization of the Loader

  • What is a synchronous loader?

    • The default created Loader is the synchronous Loader
    • This Loader must return the result either through return or this.callback, which the next Loader can process
    • This. Callback is usually used when there is an error
  • This. Callback is used as follows

    • The first argument is ERR or null
    • The second argument is either string or buffer
    • loader.js
module.exports = function (content){
    console.log("loader", content)
    this.callback(null,content)
}

Asynchronous Loader

  • What is an asynchronous Loader

    • Sometimes the Loader is used for some asynchronous operations
    • We want to return the results processed by this loader after the asynchronous operation is completed
    • This is the time to use an asynchronous Loader
  • Loader-runner has already given us methods to make the loader an asynchronous one when executing the loader
  • loader.js
module.exports = function (content){
    const callback = this.async()
    setTimeout(()=>{
        console.log("async loader",content)
        callback(null, content)
    })
}

Passing in and getting parameters

  • Passing in parameters when using loader,
  • You can use a parse library loader-utils provided by Webpack officials

    • npm i loader-utils
  • webpack.config.js
{
    test:/\.js$/i,
    use:[
        "syncLoader",
        "asyncLoader",
        {
            loader:"optionLoader",
            options:{
                type:"options",
                desc:"demo"
            }
        }
    ]
}
  • optionLoader.js
const { getOptions }  = require("loader-utils")

module.exports = function (content){
    const callback = this.async();

    const options = getOptions(this)

    setTimeout(()=>{
        console.log("asyncLoader",content, options)
        callback(null, content)
    })
}

//  asyncLoader console.log("main.js") { type: 'options', desc: 'demo' }

Parameter calibration

  • You can use the official verification library schema-utils provided by wepack

    • npm i schema-utils
  • Schema.json (validation rule)
{" type ":" objcet, "" properties" : {" type ": {" type" : "string", "description" : "please enter the correct parameter type"}, "desc" : {" type ": "String ", "description": "description must be of type string"}}, "additionalProperties": true}
  • loader.js
const { getOptions } = require("loader-utils") const { validate } = require("schema-utils") const loaderSchema = require(".. /schema/schema.json") module.exports = function (content){ const callback = this.async(); const options = getOptions(this) validate(loaderSchema, options) setTimeout(()=>{ console.log("asyncLoader",content, options) callback(null, content) }) }

Babel – loader case

  • We know that babel-loader can perform JS code conversion
  • Next try to define a babel-loader of your own
  • myBabelLoader.js
const babel = require("@babel/core") const { getOptions } = require("loader-utils") const { validate } = require("schema-utils") const babelSchema = require(".. /schema/babel.json") module.exports = function (content){ const callback = this.async(); const option = getOptions(this); validate(babelSchema, option) babel.transform(content, option, (err, res) =>{ if(err){ callback(err) }else{ callback(null, res.code) } }) }
  • babelSchema.json
{
  "type": "object",
  "properties": {
    "presets": {
      "type": "array"
    }
  },
  "additinalProperties": true
}
  • webapck.config.js
module: {
    rules:[
        {
            test:/\.js$/i,
            use:[
                "syncLoader",
                "asyncLoader",
                {
                    loader:"optionLoader",
                    options:{
                        type:"options",
                        desc:"demo"
                    }
                },
                {
                    loader:"myBabelLoader",
                    options: {
                        presets:["@babel/preset-env"]
                    }
                }
            ]
        }
    ]
},

Custom md – loader

  • MarkDownloader is implemented with Marked and Highlight libraries

    • npm i marked highlight.js
  • webpack.config.js

    • Configure CSS loading and MD loading rules
rules:[
    {
      test:/\.css$/i,
      use:[
          "style-loader",
          "css-loader"
      ]
    },
    {
      test:/\.md$/i,
      use:["mdLoader"]
    },
]
  • mdLoader.js

    • Process the content with marked and highlighted, and return the modular code
const marked = require("marked") const hljs = require("highlight.js") module.exports = function (content){ marked.setOptions({ highlight:function (code,lang){ return hljs.highlight(lang, code).value } }) const htmlContent = marked(content) const innerContent = "`" + htmlContent + "`" const moduleCode = `var code=${innerContent}; export default code; ` console.log(moduleCode) return moduleCode; }
  • main.js

    • Also introduce CSS files
import md from "./index.md"
import "highlight.js/styles/default.css"

console.log("main.js")

const ele = document.createElement("div")
ele.innerHTML = md;

document.body.appendChild(ele)
  • The effect

  • Link to the original Nuggets
  • Nuggets: Front end LeBron
  • Zhihu: Front-end Leon
  • Continue to share technical blog posts, follow the WeChat official account 👇