Webpack excerpt

1. The concept of webpack

       Essentially, webpack is a static module bundler for modern JavaScript applications . When webpack processes an application, it internally builds a dependency graph from one or more entry points , and then combines each module you need in your project into one or more bundles, which are static resources for Show off your content.
       Starting from v4.0.0, webpack can package projects without introducing a configuration file . However, it is still highly configurable and can well meet your needs. Some core concepts
need to be understood before starting :

  • entry
  • output
  • loader
  • plugin
  • mode
  • browser compatibility
  • environment

1.1. Entry

       The entry point indicates which module webpack should use as a starting point for building its internal dependency graph. After entering the entry point, webpack will find out which modules and libraries are the entry point (direct and indirect) dependencies.
       The default is ./src/index.js, but you can specify one (or more) different entry points by configuring the entry property in the webpack configuration. For example:

webpack.config.js

module.exports = {
    
    
  entry: './path/to/my/entry/file.js',
};

1.2, output (output)

       The output attribute tells webpack where to output the bundle it creates. and how to name these files. The default for the main output file is "./dist/main.js", and other generated files are placed in the "./dist.js" folder by default.
       You can configure these processes by specifying an output field in the configuration:

webpack.config.js

const path = require('path')

module.exports = {
    
    
  entry: './path/to/my/entry/file.js',
  output: {
    
    
    path: path.resolve(__dirname, 'dist'), // bundle生成(emit)到哪里
    filename: 'my-first-webpack.bundle.js', // 告诉webpack bundle的名称
    ……
  }
}

1.3、loader

       webpack can only understand JavaScript and JSON files, which is a built-in capability of webpack available out of the box. The loader enables webpack to process other types of files and convert them into validtemplate, to be used by the application and added to the dependency graph.

One of the powerful features of Warnig
webpack is the ability to import any type of template (such as .css files), which may not be supported by other packers or executors. We believe this language extension is necessary because it enables developers to create more accurate dependency graphs.

At a high level, in the webpack configuration, loader has two properties:

  1. testattribute, which identifies which files will be converted.
  2. useAttribute that defines which loader should be used when doing the conversion.

webpack.config.js

moudle.exports = {
    
    
  output: {
    
    
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    
    
    rules: [{
    
    test:/\.txt$/, use: 'raw-loader'}]
  }
}

In the above configuration, a single module object is definedrulesAttribute, which contains two required attributes: test and use. Tell the webpack compiler (compiler) the following information:

When it is parsed as a .txt path in a require()/import statement, convert it with use (use) raw-loader before you package it.

Warning
Important: When defining rules in webpack configuration, define them in module.rules instead of rules. To make it easy for you to understand, webpack will warn you if you don't do it the right way.

Warning
When matching a file with a regular expression, don't put quotes around it. That is, /.txt/ and '/t ˙ xt/ and '/\.txt/ with/t˙ xt/' or "/.txt$/" are not the same. The former instructs webpack to match any file ending in .txt, the latter instructs webpack to match a single file with an absolute path '.txt'; this may not be what you intended.

1.4, plug-in (plugin)

       Loaders are used to transform certain types of modules, while plugins can be used to perform a wider range of tasks. Including: packaging optimization, resource management, injection of environment variables, etc.
       To use a plugin, you just require() it and add it to the plugins array. Most plugins can be customized through options. You can also use the same plug-in multiple times for different purposes in a configuration file. In this case, you need to create a plug-in instance by using the new operator.

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack') // 用于访问内直插件

module.exports = {
    
    
  module: [{
    
    test: /\.txt$/, use: 'raw-loader'}],
  plugins: [new HtmlWebpackPlugin({
    
    template: './src/index.html'})]
}

In the above example, html-webpack-plugin generates an HTML file for the application and automatically injects all generated bundles into this file.

1.5, mode (mode)

       By setting the mode parameter to one of developmemt , production , or none , you can enable optimizations built into webpack for that environment. Its default value is production.

module.exports = {
    
    
  mode: 'production',
}

1.6. Browser compatibility

       Webpack supports all ES5-compliant browsers (IE8 and below are not supported).
       webpack's import() and require.ensure() require a Promise. If you want to support older browsers, pre-load the polyfill before using these expressions.

1.7, environment (environment)

Webpack5 runs on Node.js v10.13.0+

2. Entry points

2.1. Single entry (shorthand) syntax

Usage: entry: string | [string]
webpack.config.js

module.exports = {
    
    
  entry: './path/to/my/entry/file.js'
}

The single entry syntax for the entry attribute is a shorthand for:

module.exports = {
    
    
  entry: {
    
    
    main: './path/to/my/entry/file.js'
  }
}

It is also possible to pass an array of file paths to the entry attribute, which will create a so-called 'multi-main entry'. This is useful when you want to inject multiple dependent files at once and map their dependencies in a 'chunk'.

module.exports = {
    
    
  entry: ['./src/file_1.js', './src/file_2.js'],
  output: {
    
    
    filename: 'bundle.js',
  },
};

2.2, object syntax

usage:

entry: {
    
     <entryChunkName> string | [string] } | {
    
    }
module.exports = {
    
    
  entry: {
    
    
    app: './src/app.js',
    adminApp: './src/adminApp.js'
  }
}

Object syntax can be cumbersome. However, this is the most scalable way to define entries in an application.

Tip
" Extensibility of webpack configurations " means that these configurations can be reused and combined with other configurations. This is a popular technique for separating concerns from environment, build target, and runtime. These are then merged using specialized tools such as webpack-merge.

"Tip"
When you generate an entry through a plugin, you can pass an empty object {} to the entry.

2.2.1. Objects describing the entry

An object used to describe the entry. You can use the following properties:

  • dependOn: The entry that the current entry depends on. They must be loaded before the entry is loaded.
  • import: Modules that need to be loaded at startup.
  • library: Specify the library option to build a library for the current entry.
  • runtime: The name of the chunk at runtime. If set, a new runtime chunk is created. This can be set to false after webpack 5.43.0 to avoid a new runtime chunk.
  • publicPath: When the output files of this entry are referenced in the browser, specify a public URL address for them. View output.publicPath.
    module.exports = {
          
          
      entry: {
          
          
        a2: 'dependingfile.js',
        b2: {
          
          
          dependOn: 'a2',
          import: './src/app.js'
        }
      }
    }
    
    runtime and dependOn should not be used on the same entry, so the following configuration is invalid and an error will be thrown:
    module.exports = {
          
          
      entry: {
          
          
        a2: './a',
        b2: {
          
          
          runtime: 'x2',
          dependOn: 'a2',
          import: './b',
       },
     },
    };
    
    Make sure that runtime cannot point to an existing entry name, for example, the following configuration will throw an error:
    module.exports = {
          
          
      entry: {
          
          
        a1: './a',
        b1: {
          
          
          runtime: 'a1',
          import: './b'
        }
      }
    }
    
    In addition, dependOn cannot be circularly referenced, and errors will also occur in the following example:
    module.exports = {
          
          
      entry: {
          
          
        a3: {
          
          
          import: './a',
          dependOn: 'b3',
        },
        b3: {
          
          
          import: './b',
          dependOn: 'a3'
        }
      }
    }
    

2.3 Common scenarios

Some entry configurations and their practical use cases are listed below:

2.3.1. Separate app and vendor (third-party library) entrances

// webpack.config.js
module.exports = {
    
    
  module.exports = {
    
    
    entry: {
    
    
      main: './src/app.js',
      vendor: './src/vendor.js',
    }
  }
}

// webpack.prod.js
module.exports = {
    
    
  output: {
    
    
    filename: '[name].[contenthash].bundle.js,
  }
}

// webpack.dev.js
module.exports = {
    
    
  module.exports = {
    
    
    filename: '[name].bundle.js'
  }
}

This is telling webpack that we want to configure 2 separate entry points (like the example above).
In this way, the necessary libraries or files (such as Bootstrap, JQuery, pictures, etc.) that have not been modified can be stored in vendor.js, and then they are packaged together into a separate chunk. Content hashes remain the same, which allows browsers to independently cache them, reducing load times.

Tip
This is discouraged in webpack < 4, usually the vendor is added as a separate entry point to the entry option to compile it into a single file (in conjunction with CommonsChunkPlugin).
This is discouraged in webpack 4. Instead, use the optimization.splitChunks option to separate the vendor and app modules and create a separate file for them. Do not create entry for vendor or other that is not the starting point of execution.

2.3.2. Multi-page application

module.exports = {
    
    
  entry: {
    
    
    pageOne: './src/pageone/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }
}

Tells webpack to expect three separate dependency graphs (like the example above).
Reason : In a multi-page application, the server will pull a new HTML document to your client. The page reloads with this new document, and the resources are re-downloaded. However, this gives us special opportunities to do things like use optimization.splitChunks to create bundles for application code that is shared between pages. Due to the increased number of entry points, multi-page applications can greatly benefit from these techniques by being able to reuse large amounts of code/modules across multiple entry points.

As a rule of thumb: use only one entry point per HTML document.

3. output

You can configure the output option to tell webpack how to write compiled files to the hard disk. Note that only one output configuration can be specified even though there can be multiple entry origins.

3.1. Usage

In the webpack configuration, the minimum requirement for the output property is to set its value to an object, and then configure the output filename as an output.filename:

module.exports = {
    
    
  output: {
    
    
    filename: 'bundle.js'
  }
}

This configuration outputs a single bundle.js file into the dist directory.

3.2. Multiple entry points

If more than one "chunk" is created in the configuration (for example, using multiple entry points or using a plugin like the CommonsChunkPlugin), substitutions should be used to ensure that each file has a unique name.

module.exports = {
    
    
  entry: {
    
    
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    
    
    filename: '[name].js',
    path: __dirname + '/dist'
  }
}
// 写入到硬盘:./dist/app.js, ./dist/search.js

3.3 Advanced Advanced

Here is a complex example using CDN and hash for resources:
config.js

module.exports = {
    
    
  output: {
    
    
    path: '/home/proj/cdn/assets/[fullhash]',
    publicPath: 'https://cdn.example.com/assets/[fullhash]'
  }
}

If you don't know what the publicPath address of the final output file is at compile time, you can leave it blank and dynamically set it at runtime through __webpack_public_path__ in the entry point file

__webpack_public_path__ = myRuntimePublicPath

4、loader

The loader is used to convert the source code of the module. loaders allow you to preprocess files when importing or "loading" modules. Thus, loaders are similar to "tasks" in other build tools and provide a powerful way to handle front-end build steps. Loaders can convert files from different languages ​​(such as TypeScript) to JavaScript or convert inline images to data URLs. The loader even allows you to import CSS files directly in JavaScript templates!

4.1. Examples

For example, you can use loaders to tell webpack to load css files, or to convert TypeScript to JavaScript. To do this, first install the corresponding loader:

npm install --save-dev css-loader ts-loader

Then instruct webpack to use css-loader for each .css file, and ts-loader for all .ts files:
webpack.config.js

module.exports = {
    
    
  module: {
    
    
    rules: [
      {
    
     test: /\.css$/, use: 'css-loader'},
      {
    
     test: /\.ts$/, use: 'ts-loader'}
    ]
  }
}

4.2. Use loaders

In your application, there are two ways to use loaders:

  • configuration method(Recommended): Specify the loader in the webpack.config.js file.
  • inline method: Display the specified loader in each import statement.
    Note that loaders were available via the CLI in webpack v4, but are deprecated in webpack v5.

4.3、Configuration

module.rules allows you to specify multiple loaders in your webpack configuration. This approach is a concise way of presenting loaders and helps keep the code clean and maintainable. At the same time, let you have a global overview of each loader:
loaders are executed from right to left , starting from sass-loader, then continuing to execute css-loader, and finally ending with style-loader.

module.exports = {
    
    
  module: {
    
    
    rules: [{
    
    
      test: /\.css$/,
      use: [
        {
    
     loader: 'style-loader'},
        {
    
     
          loader: 'css-loader',
          options: {
    
    
            modules: true,
          }
        },
        {
    
     loader: 'sass-loader' }
      ]
    }]
  }
}

4.4. Inline mode

The loader can be specified in the import statement or any reference equivalent to the import method. use! Separate loaders from resources. Each part will be equivalent to the current directory resolution.

import Styles from 'style-loader!css-loader?modules!./styles.css';

All loaders, preLoaders and postLoaders in the configuration can be overridden by prefixing the inline import statements:

  • use! prefix, will disable all configured normal loaders (normal loaders)
import Styles from '!style-loader!css-loader?modules!./styles.css'
  • use! ! prefix, will disable all configured loaders (preLoader, loader, postLoader)
import Styles from '!!style-loader!css-loader?modules!./styles.css'
  • Using the -! prefix will disable all configured preLoaders and loaders, but not postLoaders
import Styles from '-!style-loader!css-loader?modules!./styles.css'

Options can pass query parameters such as ? key=value&foo=bar, or a JSON object such as ? {'key': 'value', 'foo': 'bar'}

Tip
Use module.rules as much as possible, because this can reduce the amount of boilerplate code in the source code, and can debug and locate problems in the loader faster when errors occur.

4.5, loader characteristics

  • The loader supports chain calls. Each loader in the chain will apply the transformation to the processed resource. A set of chained loaders will be executed in reverse order. The first loader in the chain passes its result (that is, the resource with the transformation applied) to the next loader, and so on. Finally, the last loader in the chain, returns the Javascript that webpack expects.
  • Loaders can be either synchronous or asynchronous.
  • The loader runs in Node.js and can perform any operation.
  • The loader can be configured through the options object (the query parameter is still supported to set options, but this method has been deprecated).
  • In addition to the common package.json main to export npm modules as loaders, you can also use the loader field in module.rules to directly reference a module.
  • Plugins (plugins) can bring more features to the loader.
  • The loader can generate additional arbitrary files.

The preprocessing function of the loader can provide more capabilities for the JavaScript ecosystem. Users now have more flexibility to introduce fine-grained logic such as compression, packaging, language translation (or compilation), and many more features.

4.6. Parse the loader

The loader follows standard module resolution rules. In most cases, the loader will load from the module path (usually from npm install, node_modules).
We expect the loader module to be exported as a function and written as Node.js compatible Javascript. Usually npm is used to manage loaders, but it is also possible to use files in the application as custom loaders. According to regulations, loaders are usually named xxx-loader (eg json-loader).

5、plugin

Plugins are the backbone of webpack. Webpack itself is built on top of the same plugin system you use in your webpack configuration .

The purpose of the plugin is to solve other things that the loader cannot achieve. webpack provides many plugins out of the box.

Tip
If you use the webpack-sources package in the plugin, please use require('webpack').source instead of require('webpack-source') to avoid persistent cache version conflicts.

5.1. Analysis

A webpack plugin is a Javascript object with an apply method. The apply method will be called by the webpack compiler, and the compiler object can be accessed throughout the compilation lifecycle.

ConsoleLogOnBuildWebpackPlugin.js

const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
    
     
  apply(compiler) {
    
    
    compiler.hooks.run.tap(pluginName, (compilation) => {
    
    
      console.log('webpack 构建正在启动');
    })
  }
}

module.exports = ConsoleLogOnBuildwebpackPlugin;

The first parameter of the tap method of the compiler hook should be the plugin name in camel case. It is recommended to use a constant for this so that it can be reused across all hooks.

5.2 Usage

Since plugins can carry parameters/options, you must pass a new instance to the plugins property in your webpack configuration.

Depending on your webpack usage, there are various ways to use plugins.

5.3 Configuration method

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack'); // 访问内置的插件
const path = require('path');

module.exports = {
    
    
  entry: './path/to/my/entry/file.js',
  output: {
    
    
    filename: 'my-first-webpack.bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    
    
    rule: [
      {
    
    
        test: /\.(js|jsx)$/,
        use: 'bable-loader',
      }
    ]
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new HtmlWebpackPlugin({
    
     tempalte: './src/index.html'})
  ]
}

ProgressPlugin is used to customize the progress report during the compilation process. HtmlWebpackPlugin will generate an HTML file and use script to introduce a JS file named my-first-webpack.bundle.js in it.

5.4. Node API method

When using the Node API, you can also pass in plugins through the plugins attribute in the configuration.
some-node-script.js

const webpack = require('webpack'); // 访问webpack运行时(runtime)
const configuration = require('./webpack.config.js')

let compiler = webpack('configuration');

new webpack.ProgressPlugin().apply(compiler)

compiler.run(function(err, stats) {
    
    
  // ...
})

6. Configuration

Online configuration example: https://createapp.dev/webpack/no-library–html-webpack-plugin

You may have noticed that very few webpack configurations look exactly the same. This is because the configuration file of webpack is a Javascript file, and a webpack configuration object is exported in the file. webpack will process according to the properties defined by this configuration.

Since webpack follows the Commonjs template specification, you can use in the configuration:

  • Introduce other files through require(...)
  • Use the utility functions downloaded by npm through require(…)
  • Use Javascript control flow expressions such as ? : operator
  • Use constant or variable assignment for value
  • Write and execute functions to generate partial configuration

Please use these functions in appropriate scenarios.

While technically possible, the following should be avoided :

  • When using the webpack CLI tool, access the CLI arguments (should write own CLI tool instead, or use --dev)
  • export indeterminate results (two calls to webpack should produce the same output file)
  • Write a long configuration (the configuration file should be split into multiple)

Tip
The most important takeaway from this documentation is that webpack configuration can have many different styles and flavors. The point is that for these configurations to be easy to maintain and understand, they need to be consistent within the team.

In the following example, it shows how webpack configuration can be expressed and configured flexibly, which is mainly due to the fact that configuration is code:

6.1. Basic configuration

webpack.config.js

const path = require('path');
module.exports = {
    
    
  mode: 'development',
  entry: './foo.js',
  output: {
    
    
    path: path.resolve(__dirname, 'dist'),
    filename: 'foo.bundle.js',
  }
}

6.2, multiple targets

In addition to exporting a single configuration as an object, function or Promise, it is also possible to export it as multiple configurations.

6.3. Use other configuration languages

Webpack supports configuration files written in a variety of programming and data languages.

7. Modules

In modular programming, developers decompose a program into functionally discrete chunks, which they call modules .

Each module has a smaller size than a complete program, making verification, debugging, and testing a breeze. Well-written modules provide solid abstraction and encapsulation boundaries, so that each module in the application has a coherent design and a clear purpose.

Node.js has supported modular programming from the beginning. However, modularization of the web is slowly being supported. There are various tools that support Javascript modularization in the web world, and each of these tools has advantages and limitations. Webpack takes lessons learned from these systems and applies the concept of modules to any file in a project.

7.1. What is a webpack module

Compared to Node.js modules, webpack modules can express their dependencies in various ways. Here are some examples:

  • ES2015 import statement
  • CommonJS require() statement
  • AMD define and require statements
  • @import statements in css/sass/less files
  • stylesheet url(…) or HTML img src=… link to an image in the file.

7.2. Supported module types

Webpack natively supports the following module types:

  • ECMAScript module
  • CommonJS modules
  • AMD module
  • Assets
  • WebAssembly module

Through the loader, webpack can support modules written in multiple languages ​​and preprocessor syntax. The loader describes to webpack how to handle non-native modules and introduces the relevant dependencies into your bundles. The webpack community has created loaders for a variety of popular languages ​​and preprocessors, including:

  • CoffeeScript
  • TypeScript
  • ESNext(Bable)
  • Sass
  • Less
  • Stylus
  • Elm

Overall, webpack provides a customizable, powerful, and rich API that allows use in any technology stack, while supporting non-intrusive workflows in development, testing, and production environments.

8. Module Resolution

resolver is a library that helps find the absolute path of a module. A module can be used as a dependent module of another module, and then referenced by the latter, as follows:

import foo from 'path/to/module';
// 或者
require('path/to/module'

A dependent module can be code from an application or a third-party library. The resolver helps webpack find the module code that needs to be introduced into the bundle from each require/import statement. When bundling modules, webpack uses enhanced-resolve to resolve file paths.

8.1. Parsing rules in webpack

Using enhanced-resolve, webpack can resolve three file paths:
absolute path

import '/home/me/file';
import 'c:\\Users\\me\\file';

Since the absolute path of the file has been obtained, no further parsing is required.
relative path

import '../src/file1';
import './file2';

In this case, the directory where the source file using import or require is considered as the context directory, and the relative path given in import/require will be spliced ​​with this context path to generate the absolute path of the module.
module path

import 'module';
import 'module/lib/file';

Retrieve modules in all directories specified in resolve.modules. You can replace the initial module path by configuring an alias, please refer to the resolve.alias configuration option for details.

  • If the package contains a package.json file, the fields specified in the resolve.exportsFields configuration option will be searched in turn, and the first field in package.json will confirm the export available in the package according to the package export specification.

Once the path is resolved according to the above rules, the resolver will check whether the path points to a file or a folder. If the path points to a file:

  • If the file has an extension, the file is packed directly.
  • Otherwise, file extensions will be resolved using the resolve.extensions option, which tells the parser which extensions are acceptable (eg .js, .jsx).

If the path points to a folder, proceed as follows to find a file with the correct extension:

  • If the folder contains a package.json file, it will be searched according to the field order in the resolve.mainFields configuration
    , and the file path will be determined according to the first field in package.json that meets the configuration requirements.
  • If there is no package.json file or resolve.mainFields does not return a valid path, it will search according to the file name order specified in the resolve.mainFiles configuration option to see if an existing file name can be matched in the import/require directory.
  • File extensions are then resolved in a similar fashion using the resolve.extensions option.

Webpack provides sensible defaults for these options, depending on the build target.

8.2. Parsing the loader

The loader's parsing rules also follow specific specifications. But the resolveLoader configuration item can set independent parsing rules for the loader.

8.3. Caching

Every filesystem access to a file is cached, allowing for faster firing of multiple parallel or serial requests for the same file. In watch mode, only modified files are removed from the cache. If watch mode is turned off, the cache will be cleared before each compilation.

9、Module Fedration

9.1. Motivation

Multiple independent builds can compose an application, there should be no dependencies between these independent builds, so they can be developed and deployed independently.

9.2, the underlying concept

We distinguish between local modules and remote modules. Local modules are normal modules that are part of the current build. Remote modules are not part of the current build and are loaded at runtime from so-called containers.

Loading a remote module is considered an asynchronous operation. When using remote modules, these asynchronous operations will be placed in the load operation of the next chunk between the remote module and the entry. Remote modules cannot be used without a chunk load operation.

Loading of chunks is usually done by calling import(), but older syntax like require.ensure or require([…]) subclasses are also supported.

Containers are created with a container entry that exposes asynchronous access to specific modules. Exposed access is divided into two steps:

  1. load module (asynchronously)
  2. execute module (synchronous)

Step 1 will be done during chunk loading. Step 2 will be done during interleaved execution with other (local and remote) modules. This way, the order of execution is not affected by the conversion of modules from local to remote or remote to local.

Containers can be nested, and containers can use modules from other containers. There can also be circular dependencies between containers.

9.3 Advanced concepts

Each build acts as a container, and can also have other builds as containers. In this way, each build can access modules exposed by other containers by loading modules from the corresponding container.

A shared module is a module that is both rewritable and provides rewrites to nested containers. They usually point to the same modules in each build, such as the same libraries.

The packageName option allows to find the required version by setting the package name. By default, it automatically infers module requests, when you want to disable automatic inference, please set requiredVersion to false.

9.4. Building blocks

ContainerPlugin (low level)
This plugin uses the specified public module to create an additional container entry.

ContainerReferencePlugin (low level)
This plugin adds specific references to containers as externals and allows remote modules to be imported from these containers. It also calls the override API of these containers to provide them with overloads (via __webpack__override__ or the override API when the build is also a container) and the specified overload is provided to all referenced containers.

ModuleFederationPlugin(high level)
ModuleFederation组合了ContainerPlugin和ContainerReferencePlugin。

9.5. Conceptual goals

  • It can both expose and use any module type supported by webpack.
  • Chunk loading should load everything it needs in parallel (web: single round trip to the server)
  • Control from the user to the container
    • Rewriting modules is a one-way operation
    • Sibling containers cannot override each other's modules
  • Concept applies independently of the environment
    • Available for web, Node.js, etc.
  • Relative and Absolute Requests in Shares
    • will always be provided, even if not used
    • Will resolve the relative path to config.context
    • requiredVersion will not be used by default
  • Module Requests in Shared
    • only available when using
    • will match all equal module requests used in the build
    • will provide all matching modules
    • The requiredVersion will be extracted from package.json at this location in the diagram
    • Multiple different versions can be provided and used when you have nested node_modules
    • A module request with a trailing / in a share will match all module requests with this prefix

9.6. Use Cases

Each page is built individually
Each page of a single page application is exposed from the container in a separate build. The main application (application shell) is also built independently and will reference all pages as remote modules. In this way, each page can be deployed individually. The main application is deployed when routes are updated or new routes are added. The main application defines common libraries as shared modules to avoid duplication in page building.

Component libraries as containers
Many applications share a common component library that can be built as a container exposing all components. Each application uses components from the component library container. Changes to component libraries can be deployed individually without redeploying all applications. The application automatically uses the latest version of the component library.

Dynamic Remote Containers
The container interface supports get and init methods. init is an async-compatible method that contains only one parameter when called: the shared scope object. This object is used as a shared scope in the remote container and is populated by modules provided by the host. It can be used to dynamically connect remote containers to host containers at runtime.

(async () => {
    
    
  // 初始化共享作用域(shared scope)用提供的已知此构建和所有远程的模块填充它
  await __webpack_init_sharing__('default');
  const container = window.someContainer; // 或从其他地方获取容器
  // 初始化容器 它可能提供共享模块
  await container.init(__webpack_share_scopes__.default);
  const module = await container.get('./module');
})();

The container tries to provide a shared module, but warns if the shared module is already in use, and ignores the provided shared module. Containers can still use it as a downgrade module.

You can implement A/B testing by dynamically loading different versions of a shared module.

Tip
Before attempting to dynamically connect to a remote container, make sure the container is loaded.

Example:
init.js

function loadComponent(scope, module) {
    
    
  return async () => {
    
    
    // 初始化共享作用域(shared scope)用提供的已知此构建和所有远程的模块填充它
    await __webpack_init_sharing__('default');
    const container = window[scope]; // 或从其他地方获取容器
    // 初始化容器 它可能提供共享模块
    await container.init(__webpack_share_scopes__.default);
    const factory = await window[scope].get(module);
    const Module = factory();
    return Module;
  };
}

loadComponent('abtests', 'test123');

9.7, Dynamic Remote based on Promise

In general, remote is configured using a URL, examples are as follows:

module.exports = {
    
    
  plugins: [
    new ModuleFederationPlugin({
    
    
      name: 'host',
      remotes: {
    
    
        app1: ''
      }
    })
  ]
}

But you can also pass a promise to the remote, which will be called at runtime. You should call this promise with a template that conforms to the get/init interface described above. For example, if you want to pass which version of the federation module you should use, you can do something via a query parameter.

9.8. Dynamic Public Path

Providing a host API to set the publicPath
allows the host to set the publicPath of the remote module at runtime by exposing the method of the remote module.

This approach is especially useful when you mount independently deployed sub-applications on subpaths of the host domain.

Scenario:
You have a host app at https://my-host.com/app/* and a subapp at https://foo-app.com. Sub-apps are also mounted on the host domain, so https://foo-app.com can be accessed via https://my-host.com/app/foo-app and https://my-host. com/app/foo-app/* can redirect to https://foo-app.com/* through a proxy.

Example:
webpack.config.js (remote)

module.exports = {
    
    
  entry: {
    
    
    remote: './public-path',
  },
  plugins: [
    new ModuleFederationPlugin({
    
    
      name: 'remote', // 该名称必须与入口名称相匹配
      exposes: ['./public-path'],
      // ...
    }),
  ],
};

public-path.js (remote)

export function set(value) {
    
    
  __webpack_public_path__ = value;
}

src/index.js (host)

const publicPath = await import('remote/public-path');
publicPath.set('/your-public-path');

//bootstrap app  e.g. import('./bootstrap.js')

10. More

https://webpack.docschina.org/

Guess you like

Origin blog.csdn.net/weixin_44767973/article/details/128230949