“AttentionFront-end development communityReply, ‘Collect resources‘, free Vue, small programs, Node Js, front-end development plug-ins and interview videos and other learning materials, let us learn together, progress together

Package multi-page applications

Packaging multiple pages is to package multiple HTML pages at the same time, using the HTML-webpack-plugin, but when introducing plug-ins, create multiple plug-in objects. Because an HTML-webpack-plugin plugin object can pack only one HTML page. Such as:

Module.exports = {entry: {index: "./ SRC /index.js", // Specify the chunk named index foo: "./ SRC /foo.js" // Specify the chunk name of the package output foo}, plugins: [new HtmlWebpackPlugin({template: "./ SRC /index.html", // to package the output file, you can use the relative path filename: "index.html", // after packing the output, the name of the HTML file is chunks: ["index"] // The element is the chunk name, that is, the name specified when the entry attribute value is the object, the index page only introduces index.js}), new HtmlWebpackPlugin({template: "./ SRC /index.html", // to package the output file, you can use the relative path filename: "foo.html", // after packing the output, the HTML filename is chunks: ["foo"] // The element is the chunk name, which is the name specified when the entry property value is the object, and only foo.js}),]}Copy the code

The key to packaging multiple pages is the configuration of the chunks property, because both index.html and foo.html of the packaged output will introduce both index.js and foo.js without the chunks property configured.

Therefore, the chunks attribute must be configured to specify the output module to be introduced in the HTML file after the output is packaged. The array element is the chunk name specified when the entry attribute value is an object. The index.html only introduces index.js. Foo.html only imports foo.js files

To configure the source – the map

Mapping source – a map is source code, mainly for the convenience of code debugging, because we packed online code will be compressed and other processing, lead to all code is compressed into a line, if there is an error in the code, the browser will prompt wrong position in the first row, so that we can’t really know where error in the source location. Webpack provides a devTool attribute to configure the source mapping.

let foo = 1; Console. lg(' console object method name log is written as lg '); // Error in the second line of the source file

index.js:1 Uncaught TypeError: console.lg is not a function at Object.<anonymous> (index.js:1) at o (index.js:1) at Object.<anonymous> (index.js:1) at o (index.js:1) at index.js:1 at index.js:1

Copy the code

The source code is clearly in the second line of code, and the browser error is in the first line, so if the code is very complex, we can not find the specific location of the error

There are six common configurations for Devtool:

  • 1, the source – the map:

This mode will produce a.map file, which will indicate the row and column if there is an error, which will retain the mapping between the packaged file and the original file, and the packaged output file will point to the generated.map file, telling the JS engine where the source code is, because the source code is separated from the.map file. Therefore, the browser needs to send a request to obtain the.map file, which is often used in production environments, such as:

//# sourceMappingURL=index.js.map
Copy the code

  • 2, the eval:

This is the fastest packaging mode, does not generate. Map files, uses eval to wrap the module, and adds sourceURL to the end. It is often used in development environments, such as:

//# sourceURL=webpack:///./src/index.js
Copy the code
  • 3, eval – source – the map:

Each module is executed through eval() and generates a DataUrl SourceMap (base64 encoded at the end of the eval statement), but not a.map file, which reduces network requests *. But the packaged file can be very large *.

//# sourceMappingURL=data:application/json; charset=utf-8; base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvaW5kZXguanM/YjYzNSJdLCJuYW1lcyI6WyJmb28iLCJjb25zb2xlIiwib GciXSwibWFwcGluZ3MiOiJBQUFBLElBQUlBLEdBQUcsR0FBRyxDQUFWO0FBQ0FDLE9BQU8sQ0FBQ0MsRUFBUix1RSxDQUFxQyIsImZpbGUiOiIuL3NyYy9pb mRleC5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImxldCBmb28gPSAxO1xuY29uc29sZS5sZyhgY29uc29sZeWvueixoeeahOaWueazleWQjWxvZ+WGmeaIk OS6hmxnYCk7IC8vIOa6kOaWh+S7tuesrOS6jOihjOWHuumUmVxuIl0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./src/index.jsCopy the code
  • 4, being the source – the map:

Adding cheap will only prompt errors to the rows, eliminating column information and no mapping to the imported libraries, which can improve packaging performance but will result in.map files.

  • 5, being – the module – the source – the map:

In contrast to cheap-source-map, the addition of module maps the imported library and also produces.map files for use in the production environment.

  • 6, being – the module – the eval – source – the map:

Used in development environments, using cheap mode can greatly improve the efficiency of SoureMap generation, plus Module also maps the imported libraries, eval speeds up package builds, and does not produce.map files to reduce network requests.

Any mode with eval cannot be used in production because it does not produce.map files, resulting in very large packaged files. Usually we don’t care about column information, so we use cheap mode, but we still need to map to third-party libraries to pinpoint the error location.

Watch and watchOptions are configured

Webpack can listen for file changes and recompile when they are changed. If you want to enable this function, you need to set Watch to true.

Module. exports = {watch: true, watchOptions: {poll: 1000, // Poll every second to see if the file has changed aggregateTimeout: 1000, // When the first file changes, a delay is added before the rebuild. Ignored: this option allows WebPack to aggregate any other changes made during that time into a rebuild: /node_modules/ / exclude some file listening}}Copy the code

The use of three common widgets

  • 1.clean-webpack-plugin: Clears the contents of the output directory before packaging each time, and then outputs the packaged output files to the output directory.
const {CleanWebpackPlugin} = require("clean-webpack-plugin"); Module.exports = {plugins: [new CleanWebpackPlugin() // clean up the export directory]}Copy the code

Note that the result of require(“clean-webpack-plugin “) is an object, not a class. The CleanWebpackPlugin property in this object is a class, which is used to create the plugin object.

  • 2,copy-webpack-pluginMd, history.md, and so on.
Module. exports = {plugins: [new CopyWebpackPlugin([{from: "./ readme. md", // copy readme. md files from the root directory of the project to the export directory: "// an empty string indicates an output directory}])]}Copy the code
  • 3,BannerPluginBannerPlugin is a built-in plugin for Webpack, such as:
Module.exports = {plugins: [new webpack.bannerplugin ("Copyright © 2019") // add Copyright to js header]}Copy the code

Webpack cross domain issues

Why does WebPack have cross-domain problems? Because Webpack is the front-end code, it will eventually be deployed to the front-end server, while the front-end code is usually deployed on different servers, even if deployed on the same server, using different ports. When the front-end code obtains data from the back-end server through Ajax and other means, Because the front and back end code is not in the same domain, there are cross-domain problems.

For example, we use WebPack’s devServer to run and deploy our front-end application code. DevServer starts on port 8080, and the front-end application code requests back-end data via Ajax, and the back-end server starts on port 3000.

// index.js
Copy the code

const xhr = new XMLHttpRequest(); // xhr.open("get", "http://localhost:3000/api/test"); Xhr.open ("get", "/ API /test"); / / was supposed to visit http://localhost:3000/api/test XHR. Onreadystatechange = () = > {the if (XHR. ReadyState = = = 4) { console.log(xhr.responseText); } } xhr.send();

As the front-end code is running in the browser, if in the front-end code directly through an ajax request to http://localhost:3000/api/test to get the data, then because of the influence of the browser same-origin policy, problems of cross-domain will, so you have to access/API/test. But such access will be 404 questions, because actually access is http://localhost:8080/api/test, 8080 is not the resource on the server, the solution is a proxy server through devServer configuration

Module. exports = {devServer: {proxy: {"/ API ": "http://localhost:3000" // path starting with/API to localhost:3000}}}Copy the code

http://localhost:8080/api/test will be acting to http://localhost:3000/api/test, the proxy will also support the rewriting of path, if port 3000 and no/API/test path on the server, Only the /test path, then you can rewrite the path to replace the/API

module.exports = { devServer: { proxy: { "/api": { target: "http://localhost:3000", pathRewrite: {"/api": } // replace/API with}}}}Copy the code

http://localhost:8080/api/test will be acting on http://localhost:3000/test

If the front end just wants to mock out some data and doesn’t need to actually access the back end server, we can use the before hook function provided by devServer to get the built-in server object and process the request. The built-in server object is the devServer of Webpack (port 8080), and there are no cross-domain problems because data is requested from the same server.

Get ("/ API /test", (req, res, next) => {res.json({name: "even"}); })}Copy the code

We can also start WebPack using our own server instead of using the devServer provided by WebPack.

const express = require("express"); const app = express(); const webpack = require("webpack"); // webpack const config = require("./webpack.config.js"); Const compiler = webpack(config); Const middleWare = require("webpack-dev-middleware"); Use (middleWare(compiler)); Get ("/ API /test", (req, res, next) => {res.json({name: "LHB "}); }); app.listen(3000);Copy the code

Start webPack with a custom server so that the front-end code requests data in the WebPack are in the same domain as the server’s resources.

Resolve configuration

Resolve is used to configure the resolve-related parameters of the module, and its attribute value is an object.

  • 1.modules: Tells Webpack where to search for modules when parsing them, i.e., require or import modules, write only module names, and its property value is an array. Since multiple module search paths can be configured, the search path must be absolute, for example, The SRC directory has a foo.js file and an index.js file:
// index.js

const foo = require("./foo"); // must write all foo.js module path // const foo = require("foo"); // resolve.modules: foo console.log(foo); // resolve.modules: foo console.log(foo);

Copy the code

module.exports = { resolve: { modules: [path.resolve(__dirname, "./src/"), "node_modules"] }, }

Since resolve. Modules is configured with the./ SRC directory as the search directory for modules, the foo.js module can be searched by writing only the module name in index.js

  • 2,aliasWhen the import or require module path is very long, we can set the module path or the entire path name + file name as an alias, and then directly import the alias to find the module, for example, a module is very deep
// const foo = require("./a/b/c/foo"); / / foo js in the. / SRC/a/b/c/foo/js/const foo = the require (" foo "); // SRC /a/b/c/foo.js file const foo = require("bar/foo.js"); // bar is mapped to./ SRC /a/b/c/ path console.log(foo);Copy the code

module.exports = { resolve: { alias: { "foo": path.resolve(__dirname, "./src/a/b/c/foo.js"), "bar": path.resolve(__dirname, "./src/a/b/c/") } }, }

Note that alias can map files as well as paths

  • 3,mainFields: Our package.json can have multiple fields to determine which fields are preferred for importing modules. For example, the bootstrap module contains JS and CSS. The main field in package.json file corresponds to “dist/js/bootstrap”, and the style field corresponds to “dist/ CSS /bootstrap. CSS “. We can change the default introduction by setting the mainFields field, for example:
module.exports = {
    resolve: {
        mainFields: ["style", "main"]
    },
}
Copy the code
  • 4,extensionsExtensions are used to set the order in which extensions are automatically added to modules if there is no extensions written to them.
module.exports = {
    resolve: {
        extensions: ["js", "vue"]
    },
}
Copy the code

If a project introduces the foo module, require(“./foo”), it looks for./foo.js first, and if it doesn’t find./foo.js it looks for the./foo.vue file

Setting environment Variables

The DefinePlugin plugin, a built-in plug-in provided by WebPack, is used to set the environment variable to a global value, such as:

module.exports = { plugins: [ new webpack.DefinePlugin({ DEV_MODE: Json.stringify ('development') // Set 'development' to global DEV_MODE}),]}Copy the code

After this configuration, the DEV_MODE variable can be used directly in any module and has a value of ‘development’, similar to the ProvidePlugin, which injects a module into all modules and can be used directly without importing the implementation module.

Webpack optimization

  • 1.noParse: This configuration is used as an attribute value of module, that is, it does not parse some modules. By not parsing, it does not analyze dependencies in a module, that is, it does not care whether a file is imported or not. For some independent libraries, such as jquery, there are no dependencies at all. Jquery doesn’t introduce other libraries (it’s up to you to decide if you want to parse a module based on your knowledge of it), so we can make WebPack not parse jquery dependencies to speed up packaging. For example:
Module. exports = {module: {noParse:/ /jquery/ //Copy the code

NoParse is an attribute in the Module configuration whose value is a regular expression filled with the name of the module that will not be parsed. To make it clear what noParse does, suppose we introduce the bar.js module in the entry file index.js and the foo.js module in the bar.js module, foo.js no longer depends on other modules. When webpack is packaged without noParse, the index.js module is first analyzed and found to have introduced the bar.js module, then the bar.js module is analyzed and found to have introduced the foo.js module, then the foo.js module is analyzed.

Entrypoint index = index.js
[./src/bar.js] 55 bytes {index} [built]
[./src/foo.js] 21 bytes {index} [built]
[./src/index.js] 81 bytes {index} [built]
Copy the code

If noParse is used: /bar/, the index. Js module will be analyzed when webpack is packed, and it will find that it introduced the bar.js module, but due to noParse, it will no longer parse the bar.js module. The foo.js module introduced in bar.js will not be analyzed.

Entrypoint index = index.js
[./src/bar.js] 55 bytes {index} [built]
[./src/index.js] 81 bytes {index} [built]
Copy the code
  • 2,exclude: Exclude excludes the processing of files in certain directories. That is, the loader does not use the corresponding loader to import files in a specified directory. Exclude is an attribute in Loader configuration.
module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ["@babel/preset-env"],
                            plugins: ["@babel/plugin-transform-runtime"]
                        }
                    }
                ],
                exclude: /node_modules/
            }
        ]
    }
}
Copy the code
  • 3, use,IgnorePluginTo ignore some directory in a module in the module reference, such as the introduction of a module, the module will introduce a large number of language pack, but we will not use so many language pack, if all packed into a project, then it will affect the packaging speed and the size of the final package, then the introduction of the need to use the language pack, such as:

There is a time package in the root directory of the project, and there is a lang package, which contains js files corresponding to the time output of various languages. The index.js file under the time package will import all js files under the lang package, so when we introduce the time module, Package all js files from the lang package and add the following configuration:


const webpack = require("webpack");
module.exports = {
    plugins: [
        new webpack.IgnorePlugin(/lang/, /time/)
    ]
}
Copy the code

If the time module contains the contents of the lang module, then the contents of the lang module will be ignored. Note that the /time/ is only the matching folder and the specific directory location of the time module. That is, it takes effect whenever something in the directory named time is imported.

  • 4, the use ofHappyPack: In the process of packaging, a large number of files need to be submitted to loader for processing, including parsing and conversion operations. As JS is single-threaded, these files can only be processed one by one. The working principle of HappyPack is to give full play to the multi-core function of CPU. Happypack mainly plays a role of task hijacking. When creating the Happypack instance, it needs to pass in the loader of the corresponding file, that is, the use part. HappyPack loader is used for loader configuration, for example:
const HappyPack = require("happypack"); Module.exports = {plugins: [new happypack ({// here is a wrapper id for loader that handles CSS files: Use: ["style-loader"," CSS-loader "], threads: 5 // Set the number of enabled processes})], module: {rules: [{test: /\.css$/, // Match files ending in.css use: ["happypack/loader? Id = CSS "]Copy the code

Use Happypack for optimization only when webPack has a large number of files to pack, because it takes time to start multiple processes, so when there are fewer files, use Happypack to return more time

  • 5,Remove the public module: In the case of multiple entries, if one or some modules are dependent on more than two files, then the module can be isolated. There is no need to pack the common code into each output file. This will cause code duplication and waste of traffic. They both depend on foo.js, so if the public module is not separated, the code in foo.js will be packaged into the final output of index.js and other.js, which means there will be two copies of foo.js. It is also easy to extract the common module and configure it directly in Optimization, as in:
Module.exports = {splitChunks: {cacheGroups: {// common: {// common: {// common: {// common: {// common: {// common: {// common: {// common: { "Initial ", minSize: 0, // minChunks: 2 if the file size is greater than 0 bytes, // name: "common/foo" if the file size is greater than 0 bytes, // define the name of the file to be removed}}}}Copy the code

This will remove the common foo.js module from the common directory foo.js, but if we have multiple files that rely on third-party modules such as jquery, jquery will be packaged into foo.js as well, resulting in code clutter. So we want jquery to be separate from foo.js. We can copy more than one configuration and do this by setting the weight of the code to be removed first, i.e. jquery to be removed first, as in:

Module.exports = {splitChunks: {cacheGroups: {// common: {// common: {// common: {// common: {// common: {// common: {// common: {// common: { // name: "common/foo", // define the name of the file that is to be removed}, verdor: {test: /node_modules/, priority: 1, // set the packing weight, i.e., remove chunks first: "initial", minSize: 0, // remove minChunks only when the file size is larger than 0 bytes: 2, // name: "common/jquery", // define the name of the extracted file}}}}Copy the code

This removes both foo.js and jquery.js from the common directory. Note that the code must be removed if the module is not excluded from packaging, that is, the module will be packaged into the output bundle. The preceding vendor configuration is invalid.

  • 6,According to the need to loadThat is, package the output only when it is needed, which WebPack providesimport()Method to dynamically load the specified module when webpack encounters itimport()Statements, will not immediately go to load the module, but in the use of the module, to load, that is to say, when package will be packaged together, but when loaded in the browser will not immediately load, but when used to load, for example, click the button to load a module, such as:
const button = document.createElement("button"); Button. innerText = "button me" button.addEventListener("click", Js import("./foo").then((res) => {// import() returns a Promise object console.log(res); }); }); document.body.appendChild(button);Copy the code

Import () returns a Promise object that can be dynamically loaded using JSONP, and returns a res that can be exported in a different way. The returned res is the output of module.exports; If the ES6 module output is used, that is, export default output, then the res result is res.default, as in:

// The output of the ES6 module isCopy the code

{default: "foo", __esModule: true, Symbol(Symbol.toStringTag): "Module"}

  • 7,Enable module hot update: Module hot update can be done in the case of not refreshing the web page, update the modified module, only compile the changed module, without all module repackaging, greatly improve the development efficiency, in the case of not open hot update, every modified module, will be repackaged.

To enable module hot updates, simply add hot:true to the devServer configuration. Of course, it is not enough to just enable module hot update, we need to do some operations like listening, when the listening module changes, reload the module and execute, such as:

Module. exports = {devServer: {hot: true}}
     
Copy the code

import foo from "./foo"; console.log(foo); If (module.hot) {module.hot.accept("./foo", () => {const foo = require("./foo"); // Reintroduce the module and execute console.log(foo); }); }

If you don’t use module.hot.accept listening, the page will still be refreshed when you modify module foo.

https://segmentfault.com/a/119000002032087

Author: JS_Even_JS

Past amway a few JS development tips

Please support handsome editor, reply”Add group“Can receive front-end dry goods

This article is formatted using MDNICE