Webpack Loader knowledge sharing

Webpack Loader knowledge sharing

Writing is not easy, and it is forbidden to reprint in any form without the author's permission!
If you think the article is good, welcome to follow, like and share!

Meet Loader

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

When loading this module, webpack doesn't actually know how to load it. We must customize the corresponding loader to complete this function.

Loader configuration method

  • Inline method:

    import "css-loader!../css/index.css"
    ; Used for loader and file path! Separate

  • Configuration method: webpack.config.js

  • The configuration method means that the configuration information is written in our webpack.config.js file

    • Module.rules allows us to configure multiple loaders (because we will continue to use other Loaders to complete the loading of other files)
    • This method can better identify the loader configuration, it is also convenient for later maintenance, and it also allows you to have a global overview of each Loader;
  • The configuration of module.rules is as follows

  • The value corresponding to the rules attribute is an array: [Rule]

  • What is stored in the array is a Rule, and Rule is an object, and multiple properties can be set in the object

    • test attribute: used to match multiple resource (resource) file names, usually set to a regular expression;
    • use attribute: the corresponding value is an array [useEntry]
      • The execution order is from back to front. For example, when parsing css, css-loader should be behind style-loader.
      • UseEntry is an object, you can set some other properties through the properties of the object
        • loader: There must be a loader attribute, and the corresponding value is a string
        • options: optional attribute, the value is a string or object, the value will be passed into the loader;
        • query: currently replaced by options
      • Passing a string (such as: use:['style-loader']) is a shorthand for the loader attribute (such as: use:[{loader:'style-loader'}])

Common Loader

CSS loader

  • We can also regard the css file as a module, and we load this module through import

  • So what kind of loader do you need?

    • For loading css files, we need a loader that can read css files

    • The most commonly used is css-loader

    • Only responsible for parsing the css file, it will not act on the page. In addition, style-loader is required to act on the page

      npm i style-loader --save

  • Installation of css-loader:

    npm install css-loader --save

  • The following is a configuration file of 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$/ , //Resource file matches regular expression use: [ { loader : "style-loader" , options : {}, }, //Shorthand: //"css-loader" { //Complete writing loader : "css-loader" , //Corresponding loader options : {}, }, ], }, ], }, }; Copy code
  • Same for less

    npm install less-loader less --save
    After installing these two plugins

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

Browser compatibility

  • If we share our configuration compatibility conditions under css compatibility and js compatibility

    • That is when we set a condition: >1% last 2 version Firefox, Chrome... not dead are all optional, multiple options are separated by || conditions; and is && relation; not conditions are available
    • It means Firefox, Chrome browsers and the latest two versions with a market share of more than 1% compatible with js and css, and browsers that are officially supported and updated within 24 months (dead filter conditions)
  • Write conditions in the root directory .browserlistrc file

  • Through the realization of the market share ratio, version requirements, etc. to configure, adapt to the browser

  • Tools used:

    • Data source: browserlist, browser market share, accurate to each version
    • Processing plug-ins: autoprefixer, babel, postcss-preset-env, etc.

Get to know the PostCSS tool

  • What is the PostCSS tool?

    • PostCSS is a tool for converting styles through JavaScript
    • This tool can help us to convert and adapt some CSS styles, such as automatically adding browser prefixes and recharging CSS styles;
    • But to achieve these functions, we need to rely on the PostCSS plug-in
  • How to use PostCSS

  1. Find the extension of PostCSS in the build tool, such as postcss-loader in webpack;
  2. Choose to add PostCss related plug-ins you need

Manually use PostCSS

Use postcss-cli operation, need to use autoprefixer plug-in

  1. npm install postcss postcss-cli --save
  2. npm install autoprefixer --save
  3. npx postcss --use autoprefixer -o'output path''output path'
    ,E.g:
    1. npx postcss --use autoprefixer -o ./src/css/result.css ./src/css/user.css

Webpack configuration file uses PostCSS

  1. Install postcss-loader and autoprefixer
    • npm install postcss-loader autoprefixer --save
  2. Write configuration file
    • Use postcss-loader before css-loader processing
    • In postcssOptions of options, configure plugins and use autoprefixer
{ test : /\.css$/ , //Resource file matches regular expression use: [ { loader : "style-loader" , options : {}, }, //Shorthand: //"css-loader" { //Complete writing loader : "css-loader" , //Corresponding loader options : {}, }, { loader : "postcss-loader" , options : { postcssOptions : { plugins : [ require ( "autoprefixer" )], }, }, }, ], }, Copy code

postcss-preset-env

  • In fact, when configuring postcss-loader, we do not need to use autoprefixer to configure plugins.

  • We can use another plugin: postcss-preset-env

    • postcss-preset-env is also a postcss plugin;

    • It can help us convert some modern CSS features into CSS recognized by most browsers, and will add the required polyfill according to the target browser or runtime environment;

      • For example, an eight-digit hexadecimal color will help us convert to RGBA
    • It also includes automatically helping us to add autoprefixer (so equivalent to having built-in autoprefixer);

    • 1. we need to install postcss-preset-env:

      npm install postcss-preset-env --save

    • After that, we can directly modify the previous autoprefixer:

  • Simplified configuration

    • 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$/ , //Resource file matches regular expression use: [ { loader : "style-loader" , options : {}, }, //Shorthand: //"css-loader" { //Complete writing loader : "css-loader" , //Corresponding loader options : {}, }, "postcss-loader" , ], }, { test : /\.less$/ , use: [ "style-loader" , "css-loader" , "postcss-loader" , "less-loader" ], }, ], }, }; Copy code

postcss.config.js

module .exports = { plugins : [ //import //require("postcss-preset-env") //shorthand for "postcss-preset-env" ], }; Copy code

effect

Before

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

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, eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInVzZXIuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztBQUVBOztBQUZBOztBQUVBOztBQUZBOztBQUVBOztBQUVBO0lBQ0kseUJBQWlCO09BQWpCLHNCQUFpQjtRQUFqQixxQkFBaUI7WUFBakIsaUJBQWlCO0lBQ2pCLHVCQUF1QjtBQUMzQiIsImZpbGUiOiJyZXN1bHQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiOmZ1bGxzY3JlZW57XG5cbn1cblxuLmNvbnRlbnR7XG4gICAgdXNlci1zZWxlY3Q6IG5vbmU7XG4gICAgdHJhbnNpdGlvbjogYWxsIDJzIGVhc2U7XG59Il19 */ duplicated code

importLoaders

  • When normal css file parsing encounters @import, it will not re-parse from the last loader in the use array

  • Recursive analysis can be achieved by setting importLoaders

  • The number filled in is the number of loaders going forward in the use array

  • E.g:

const path = require ( "path" ); module .exports = { entry : "./src/js/main.js" , output : { filename : "bundle.js" , path : path.resolve(__dirname, "build" ), }, module : { rules : [ { test : /\.css$/ , //Resource file matches regular expression use: [ { loader : "style-loader" , options : {}, }, //Shorthand: //"css-loader" { //Complete writing loader : "css-loader" , //Corresponding loader options : { importLoaders : 1 , }, }, "postcss-loader" , ], }, { test : /\.less$/ , use: [ "style-loader" , { //Complete writing loader : "css-loader" , //Corresponding loader options : { importLoaders : 2 , }, }, "postcss-loader" , "less-loader" , ], }, ], }, }; Copy code

File Loader

Want to parse and read files, such as pictures

  1. Install file-loader
    • npm install --save file-loader
  2. Write configuration file

rule:

  • Options explanation:
    • name: output name, [name] name reference,. character, [hash:6] hash information truncated 6 digits, [ext] suffix name
    • ouputPath: output folder, you can also add img/before the file name
{ test : /\.(png|jpe?g|gif|svg)$/ , use: [ { loader : "file-loader" , options :{ name : "[name].[hash:6].[ext]" , outputPath : "img" //limit(url-loader) } } ], }, Copy code
  • There is also url-loader that has the same function as file-loader
    • The difference is that instead of packaging, it converts the picture to BASE64 encoding.
    • You can write the limit attribute under the options of the use object to limit the size, and perform BASE4 encoding on small pictures to reduce the number of requests.

Asset module type

  • The webpack version we currently use 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 to replace the above loaders;
  • Asset module type, replace all these loaders by adding 4 new module types:

    • asset/resource sends a separate file and exports the URL. Previously achieved by using file-loader;
    • asset/inline exports the data URI of a resource. Previously achieved by using url-loader;
    • asset/source Export the source code of the resource. Previously achieved by using raw-loader;
    • asset automatically chooses between exporting a data URI and sending a separate file. Previously by using url-loader and configuring resources
  • For example, to load pictures, we can use the following methods:

//Pack the picture { test : /\.(png|jpe?g|gif|svg)$/ , type: "asset/resource" , generator : { filename : "img/[name].[hash:6][ext]" , }, }, //BASE64 encode and limit the size { test : /\.(png|jpe?g|gif|svg)$/ , type: "asset/inline" , parser : { dataUrlCondition : { maxSize : 100 * 1024 , }, }, }, //Mixed use, limit the size, determine the packaging method { test : /\.(png|jpe?g|gif|svg)$/ , type: "asset" , generator : { filename : "img/[name].[hash:6][ext]" , }, parser : { dataUrlCondition : { maxSize : 100 * 1024 , }, }, }, Copy code
  • But how can I customize the output path and file name of the file?
    • Method 1: Modify output and add assetModuleFilename attribute;
    • Method 2: In the Rule, add a generator attribute and set the filename;
//Method one ouput object output : { filename : "bundle.js" , path : path.resolve(__dirname, "build" ), assetModuleFilename : "img/[name].[hash:6][ext]" , }, //Method two rule object (recommended) { test : /\.(png|jpe?g|gif|svg)$/ , type: "asset/resource" , generator : { filename : "img/[name].[hash:6][ext]" , }, } Copy code

Load font file

rule object:

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

Custom Loader

Loader is used to transform the source code of the module. Many Loaders have been used before, such as css-loader, style-loader, babel-loader, etc.

  • Loader is essentially a JavaScript module exported as a function

  • The Loader runner library will call this function, and then pass in the result or resource file generated by the previous loader.

  • Writing a custom Loader will receive 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" ] } ] } } Copy code
  • If you want to load the loader folder directly, you can configure the restoreLoader property
module : { rules :[ { test : /\.js$/i , use:[ "demoLoader" ] } ] }, resolveLoader :{ modules :[ "./loaders" , "node_modules" ] } Copy code
  • Multiple Loaders use execution order
    • From back to front, from right to left
rules:[ { test : /\.js$/i , use:[ "demoLoader1" , "demoLoader2" , "demoLoader3" , ] } ] Copy code
  • result

Pitch-Loader and enforce

In fact, there is another Loader called PitchLoader

  • In fact, this is why the execution order of loader is reversed.

    • Run-loader first executes PitchLoader first, and performs loaderIndex++ when executing PitchLoader;
    • NormalLoader will be executed after run-loader, and loaderIndex-- will be executed when NormalLoader is executed;
  • So, how to change their order of execution?

    • Can be split into multiple Rule objects and change their order through enforce
    • Enforce is an attribute of the rule object.
  • There are four ways to enforce

    • All loaders are normal by default
    • Loaders set in the line are all inline
    • You can also set pre and post through enforce
  • In Pitch and Normal, their execution order is respectively

    • post, inline, normal, pre

    • pre, normal, inline, post

Synchronous Loader

  • What is a synchronous loader?

    • The Loader created by default is the synchronous Loader
    • This Loader must return the result through return or this.callback, and hand it over to the next loader for processing
    • Usually in case of error, this.callback will be used
  • The usage of this.callback is as follows

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

Asynchronous Loader

  • What is an asynchronous Loader

    • Sometimes use Loader for some asynchronous operations
    • We hope to return the result processed by this loader after the asynchronous operation is completed
    • At this time, we will use asynchronous Loader
  • The loader-runner has provided us with a method when executing the loader, so that the loader becomes an asynchronous loader

  • loader.js

module .exports = function ( content ) { const callback = this .async() setTimeout ( ()=> { console .log( "async loader" ,content) callback( null , content) }) } Copy code

Incoming and getting parameters

  • Pass in parameters when using loader,

  • You can use a parsing library loader-utils officially provided by webpack

    • npm i loader-utils
  • webpack.config.js

{ test : /\.js$/i , use:[ "syncLoader" , "asyncLoader" , { loader : "optionLoader" , options :{ type : "options" , desc : "demo" } } ] } Copy code
  • 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'} copy the code

Parameter verification

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

    • npm i schema-utils
  • schema.json (validation rules)

{ "type" : "objcet" , "properties" : { "type" : { "type" : "string" , "description" : "Please enter the correct parameter type" }, "desc" : { "type" : "string" , "description" : "Description must be a string type" } }, "additionalProperties" : true } Copy code
  • 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) }) } Copy code

babel-loader case

  • We know that babel-loader can perform JS code conversion

  • Next, try to define your own babel-loader

  • 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) } }) } Copy code
  • babelSchema.json
{ "type" : "object" , "properties" : { "presets" : { "type" : "array" } }, "additinalProperties" : true } Copy code
  • 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" ] } } ] } ] }, Copy code

Custom md-loader

  • Implement markdownLoader with the help of 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" ] }, ] Copy code
  • mdLoader.js
    • Use marked and highlight to process content, and return to 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; } Copy code
  • main.js
    • At the same time introduce the css file
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) Copy code
  • effect