Webpack 怎么实现按需异步加载模块

news2024/11/20 2:24:10

要弄懂这个问题,需要先来看关于webpack打包的3个问题。

三个问题

第一个问题

项目中的json文件,如何使用webpack进行处理?

如果我们希望把json文件当做静态配置,例如有如下json文件

{
    "version": "1.0.0",
    "name": "config"
}

在其他模块中引用:

// index.js
import config from './config';

console.log(config); // {version: '1.0.0', name: 'config'}

要实现上面的效果,应该如何配置?

如果我们希望把json文件当做静态资源加载,例如

// index.js
import axios from 'axios';
import config from './config';

axios(config)
.then(res => {
    console.log(res.data); // {version: '1.0.0', name: 'config'}
});

这种效果又怎么实现?

第二个问题

我们知道webpack可以分包打包,而且html-webpack-plugin会帮助我们管理分包文件的加载顺序,通常打包出来的html中script会是这样的

<script src="static/js/275.ffbe1.chunk.js"></script>
<script src="static/js/main.d9933.chunk.js"></script>

因为main中模块依赖275.ffbe1 chunk中的模块,因此先加载275.ffbe1.chunk.js。

那么如果我们加载chunk的顺序有问题(先加载main),会不会报错呢?如果不会报错,webpack是怎么保证这种容错的呢?

第三个问题

对于动态加载的模块,路径是动态生成的,比如

const Component = React.lazy(() => import(`~/page/${component}`));

或者

const loadingImgSrc = require(`~/img/${skin}.loading.png`);

动态的路径只有在执行时候才能确定,webpack在打包时候并不能知道具体路径,那么webpack是如何处理的呢?

概述

上面问题虽然在平时工作的大部分项目中不会遇到,但是对于我们深入理解webpack模块化原理、应对可能遇到的疑难杂症和特殊需求以及应付有些深度的面试都大有帮助。

阅读本文可以获知上面3个问题的答案,并了解:

  1. webpack模块的解析过程(如何处理不同类型模块,用户如何根据配置控制模块解析行为)。
  2. 模块打包的原理(对于正常打包、动态依赖的打包和分片打包,webpack运行时是如何工作的)。
  3. 模块路径解析规则。

模块解析

模块解析过程

模块解析,就是分析模块的导入(依赖)和导出的过程。模块解析在webpack打包过程中有非常重要的地位。

我们先来看下webpack打包过程,大致过程可以描述为:webpack从入口开始构建依赖图,然后把不同类型的模块交给对应的loader处理,处理完成后打包到一起。

这个过程的描述有些不清晰:webpack负责构建依赖图,那么实际项目中那么多种不同类型的模块,它们的依赖如何解析?loader负责处理不同类型的模块,处理时候也要解析依赖吗,到底是webpack解析依赖,还是loader解析依赖?loader它到底做了哪些事情?最终webpack是如何将不同模块打包到一起的呢?

其实,webpack本身可以支持几种常见的模块:https://webpack.docschina.org/concepts/modules/#supported-module-types

对于这些类型的模块,webpack会对其根据后缀匹配,然后进行默认方式的解析,不需要配置loader,例如JavaScript/auto,支持ESM、CommonJS、AMD,对于asset类型的模块会输出静态资源然后导出引用地址。

对于其他类型的模块,需要对应的loader处理。

loader主要做了两件事:

  1. 转译代码。
  2. 将模块化代码转为webpack可以识别的格式。例如对于css,@import等引用其他css的语法webpack并不能识别,css-loader会将这些模块引用语法转换为require,这样就能够被webpack识别了。所以,最终负责依赖解析的还是webpack,loader只是将模块转为webpack能识别的模块。loader还有一种方式可以让webpack知道某个模块的依赖,就是使用this.resolve/this.getResolve。(https://webpack.docschina.org/contribute/writing-a-loader/#module-dependencies)

webpack打包过程可以图示如下

  1. webpack对原始资源做默认处理。
  2. 交给loader处理,将依赖的语句改成require或者通过this.resolve解析依赖。也就是说loader得负责把模块转成webpack能够识别的模块化语法(包括导入和导出语法),这样webpack才能根据导入语句分析依赖,才能根据导出语句进行包装然后打包。
  3. loader处理完交给webpack,webpack解析依赖,然后递归处理依赖模块。
  4. 添加运行时代码,打包。

模块解析相关配置

1. Rule.type

webpack支持通过设置Rule.type控制模块的默认解析方式:https://webpack.docschina.org/configuration/module/#ruletype。

例如前言中提到的第一个问题,我们可以什么都不配置,webpack就会按照默认方式,把json解析成js对象。相当于

export default {
    "version": "1.0.0",
    "name": "config"
};

如果希望把json作为静态资源解析,则要配置Rule.type

// webpack.config.js
module.exports = {
  // other config...
  module: {
      rules: [
        {
          test: /.json$/,
          type: 'asset/resource'
        }
      ]
    }
};

如果模块类型(后缀)和配置的type不匹配,webpack会不进行默认处理,而是交给相应的loader处理。

2. module.noParse

顾名思义,用于模块没有依赖的场景,模块不会被webpack进行解析,直接被打包到bundle。
对于那些模块中确定没有其他依赖的js模块,可以不继续进行模块解析。例如大型的、已经打包好的、以global方式引入的第三方库,设置该配置可以避免模块解析工作从而提升构建性能。

module.exports = {
  //...
  module: {
    noParse: /jquery|lodash/,
  },
};

3. Rule.exclude

该配置用于模块本身已经经过处理,不需要loader再次处理,可以直接让webpack处理。这个选项也可以用来缩小构建目标。

模块打包

依赖解析完成之后,代码也转译完成后,剩下的就是打包了。

普通模块打包

webpack解析好模块后,会将代码都包装成commonjs格式的模块,本质就是闭包。通过webpack运行时代码完成模块导出导入。

例如有这样的代码

// index.js
import lib from './lib';
console.log(lib);


// lib.js
export default 'lib';

webpack配置

// webpack.config.js
module.exports = {
    entry: './src/index.js',
    mode: 'development',
};

在development模式下打包结果如下(经过简化)

(() => {
    "use strict";

    var __webpack_modules__ = ({
        "./src/index.js":
            ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
                __webpack_require__.r(__webpack_exports__);
                var _lib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/lib.js");
                console.log(_lib__WEBPACK_IMPORTED_MODULE_0__["default"]);
            }),
        "./src/lib.js":
            ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
            __webpack_require__.r(__webpack_exports__);
            __webpack_require__.d(__webpack_exports__, {
                "default": () => (__WEBPACK_DEFAULT_EXPORT__)
            });
            const __WEBPACK_DEFAULT_EXPORT__ = ('lib'); 
        })
    });

    var __webpack_module_cache__ = {};

    function __webpack_require__(moduleId) {
        var cachedModule = __webpack_module_cache__[moduleId];
        if (cachedModule !== undefined) {
            return cachedModule.exports;
        }
        var module = __webpack_module_cache__[moduleId] = {
            exports: {}
        };

        __webpack_modules__[moduleId](module, module.exports, __webpack_require__);

        return module.exports;
    }
    (() => {
        __webpack_require__.d = (exports, definition) => {
            for(var key in definition) {
                if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
                    Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
                }
            }
        };
    })();

    (() => {
        __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
    })();

    (() => {
        __webpack_require__.r = (exports) => {
            if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
            }
            Object.defineProperty(exports, '__esModule', { value: true });
        };
    })();

    var __webpack_exports__ = __webpack_require__("./src/index.js");
})();

我们对上面打包结果进一步改造,提取关键代码

(() => {
    "use strict";

    var modules = ({
        "./src/index.js":
            ((exports, require) => {
                var lib = require("./src/lib.js");
                console.log(lib["default"]);
            }),
        "./src/lib.js":
            ((exports, require) => {
                exports.default = 'lib'; 
            })
    });

    var modulesCache = {};

    function require(moduleId) {
        var cachedModule = modulesCache[moduleId];
        if (cachedModule !== undefined) {
            return cachedModule.exports;
        }
        var module = modulesCache[moduleId] = {
            exports: {}
        };

        modules[moduleId](module.exports, require);

        return module.exports;
    }

    require("./src/index.js");
})();

可以发现webpack打包其实做了几件事:

  1. 实现了CommonJS规范的require方法,导入的模块缓存起来,如果下次再导入直接返回结果。
  2. 每个模块引用语句都改成了require引用。
  3. 模块被包装成闭包,按照CommonJS格式(module.exports)导出。

关于模块化语法更多细节可以参考这个文章:https://segmentfault.com/a/1190000010349749

动态引入的模块打包

我们看这样的代码

// index.js
import('./lib').then(
    res => {
        console.log(res);
    }
);
// lib.js
export default 'lib';

webpack配置

// webpack.config.js
module.exports = {
    entry: './src/index.js',
    mode: 'development',
};

上面代码index模块动态引入lib模块,这种打包会生成两个chunk,切片之间通过webpack运行时异步加载。

打包生成main.js和src_lib_js.js,这里只给出提取关键代码并简化后的结果

// main.js
(() => {
 	var modules = ({
        "./src/index.js":
            ((exports, require) => {

                require.ensureChunk("src_lib_js")
                .then(() => {
                    return require("./src/lib.js");
                })
                .then(
                    res => { console.log(res.default); }
                );
            })
 	});
 	var modulesCache = {};
 	
 	function require(moduleId) {
        var cachedModule = modulesCache[moduleId];
        if (cachedModule !== undefined) {
            return cachedModule.exports;
        }
        var module = modulesCache[moduleId] = {
            exports: {}
        };

        modules[moduleId](module.exports, require);

        return module.exports;
 	}

    var installedChunks = {
        "main": 0
    };
 	
    require.ensureChunk = (chunkId) => {
        // 拼接url
        function getScriptUrl(chunkId) {
            var scriptUrl;
            // web worker
            if (window.importScripts) scriptUrl = window.location + "";
            var document = window.document;
            if (!scriptUrl && document) {
                if (document.currentScript)
                    // 以当前的script为基准加载chunk
                    scriptUrl = document.currentScript.src
                if (!scriptUrl) {
                    var scripts = document.getElementsByTagName("script");
                    if(scripts.length) scriptUrl = scripts[scripts.length - 1].src
                }
            }
            if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
            // 去掉路由后面的内容(hash、query和多余的斜杠)
            scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/?.*$/, "").replace(//[^/]+$/, "/");
    
            return scriptUrl + chunkId + '.js';
        }
        // 如果未安装过,则通过script标签下载js文件
        if (installedChunks[chunkId] === undefined) {
            return new Promise((resolve, reject) => {
                var script = document.createElement('script');
                script.src = getScriptUrl(chunkId);
                document.head.appendChild(script);
                script.onload = resolve;
                script.onerror = reject;
            });
        }
        return Promise.resolve();
    };

    var webpackJsonpCallback = (data) => {
        var [chunkId, moreModules] = data;
        for(moduleId in moreModules) {
            modules[moduleId] = moreModules[moduleId];
        }
        installedChunks[chunkId][0]();
    }
 		
    var chunkLoadingGlobal = self["webpackChunkwebpackmodule3"] = self["webpackChunkwebpackmodule3"] || [];
    chunkLoadingGlobal.forEach(data => {
        webpackJsonpCallback(data);
    });
    var push = chunkLoadingGlobal.push;
    chunkLoadingGlobal.push = data => {
        push(data);
        webpackJsonpCallback(data);
    };
    
    require("./src/index.js");
})();


// src_lib_js.js
"use strict";

(self["webpackChunkwebpackmodule3"] = self["webpackChunkwebpackmodule3"] || [])
.push([
    'src_lib_js',
    {
        "./src/lib.js":
            ((exports, require) => {
                exports.default = 'lib';
            })
    }
]);

下面解释一下webpack对于动态依赖的打包的关键处理。

动态依赖会把依赖的模块单独打包成一个chunk,chunk就是一个文件,一个chunk中包含1个或多个module。

动态依赖语句会被转为一个promise,通过动态创建script标签异步加载chunk。

加载好chunk后,会做几件事情:

  1. 把自己注册到已安装依赖中(webpackChunkwebpackmodule3
  2. 加载chunk中的所有模块
  3. 把加载chunk的promise resolve

加载好chunk后(即promise resolve后),意味着模块也已经加载好,接下来就会通过require加载模块(第9行)。然后就可以正常地使用模块了。

我们看到webpack通过全局变量webpackChunkwebpackmodule3来管理多chunk的加载过程,那如果多个用webpack打包的项目工作在同一个浏览器中,会不会全局变量冲突呢?webpack考虑到了这个问题,支持用户通过output.jsonpFunction选项配置全局变量的名称,这样就可以避免冲突。

多chunk打包

wepback支持分包加载,可以把项目打包成多个chunk,多个chunk的加载和动态依赖类似,也是要通过一个全局变量管理chunk,所以chunk的加载过程都是一样的。

多chunk需要注意一个问题,因为多个chunk是有依赖关系的,如果我们在html中加载chunk的顺序正确,那么执行的顺序和打成一个bundle一样,如果加载顺序和依赖的关系不一致,就需要通过某种机制保证依赖的chunk加载完,再执行模块。

我们看下面代码打包的示例

// index.js
import lib from './lib';
console.log(lib);
// lib.js
export default 'lib';

webpack配置分包

// webpack.config.js
module.exports = {
    entry: './src/index.js',
    mode: 'development',
    optimization: {
        splitChunks: {
            chunks: 'all',
            minSize: 0,
            cacheGroups: {
              lib: {
                test: /lib/,
              }
            },
          },
    }
};

打包产物如下(代码经过简化)

// main.js
(() => {
    "use strict";

    var modules = ({
        "./src/index.js":
            ((exports, require) => {
                var lib = require("./src/lib.js");
                console.log(lib["default"]);
            })
    });

    var modulesCache = {};

    // The require function
    function require(moduleId) {
        var cachedModule = modulesCache[moduleId];
        if (cachedModule !== undefined) {
            return cachedModule.exports;
        }
        var module = modulesCache[moduleId] = {
            exports: {}
        };

        modules[moduleId](module.exports, require);
        return module.exports;
    }

    var installedChunks = {
        "main": 0
    };

    // 如果依赖的chunk未加载完成,保存在deferred中
    var deferred = [];
    // 如果依赖的chunk加载完,执行回调
    // 否则保存该模块,等待依赖的chunk都加载完后再执行回调
    require.Onload = (chunkIds, fn) => {
        var result;
        deferred.push([chunkIds, fn]);
        // 遍历deferred,将每个依赖chunk加载完成的模块执行
        for (var i = 0; i < deferred.length; i++) {
            var [chunkIds, fn] = deferred[i];
            var fulfilled = true;
            for (var j = 0; j < chunkIds.length; j++) {
                if ((installedChunks[chunkIds[i]] === 0) {
                    chunkIds.splice(j--, 1);
                }
                else {
                    fulfilled = false;
                }
            }
            if(fulfilled) {
                deferred.splice(i--, 1)
                var r = fn();
                if (r !== undefined) result = r;
            }
        }
        return result;
    };

    var webpackJsonpCallback = (data) => {
        var [chunkId, moreModules] = data;
        for(moduleId in moreModules) {
            modules[moduleId] = moreModules[moduleId];
        }
        installedChunks[chunkId][0]();
        return require.Onload();
    }

    var chunkLoadingGlobal = self["webpackChunkwebpackmodule3"] = self["webpackChunkwebpackmodule3"] || [];
    chunkLoadingGlobal.forEach(data => {
        webpackJsonpCallback(data);
    });
    var push = chunkLoadingGlobal.push;
    chunkLoadingGlobal.push = data => {
        push(data);
        webpackJsonpCallback(data);
    };
    // startup
    require.Onload(["lib-src_lib_js"], () => (require("./src/index.js")));
})();

// lib-src_lib_js.js
(self["webpackChunkwebpackmodule3"] = self["webpackChunkwebpackmodule3"] || []).push([["lib-src_lib_js"],{
    "./src/lib.js":
        ((exports, require) => {
            exports.default = 'lib';
        })
}]);

可以看到分片打包之后,不能像只有一个bundle那样直接通过require引用模块,因为依赖的模块所在的chunk可能没有加载完,因此要先通过require.Onload方法确保chunk已经加载完,再去执行当前模块,由于chunk加载完时,chunk内的所有模块都会被加载,因此这时候通过require引用依赖的模块是没有问题的。

require.Onload方法就是把每个模块依赖的chunk和回调都保存起来,并且检查当前所有的模块,如果发现某个模块依赖的chunk都已经加载完,就执行其回调。每当某个chunk加载完,都会调用require.Onload,以便依赖它的模块可以马上执行。

这样,webpack就可以保证分包的chunk在页面加载顺序和依赖顺序不一致时候,也可以正常工作,同步地执行。

模块路径解析

webpack可以解析三种文件路径:绝对路径、相对路径和模块路径,匹配 路径后还会匹配扩展名。

https://www.webpackjs.com/concepts/module-resolution/#webpack-%E4%B8%AD%E7%9A%84%E8%A7%A3%E6%9E%90%E8%A7%84%E5%88%99

下面看如何使用resolve选项控制路径解析。

resolve选项

下面列举几个常用的resolve选项,更多更详细的说明参考官方文档:
https://www.webpackjs.com/configuration/resolve/

resolve.modules

指定webpack模块解析的目录,默认是['node_modules']。webpack会尝试从resolve.module指定的列表中查找模块路径。

resolve.alias

设置路径别名,设置该选项后,让模块引用更简单。

alias: {
  "@": path.resolve(__dirname, 'src'),
  "~": path.resolve(__dirname, 'src')
}

resolve.extensions

配置扩展名

{
  extensions: [".js", ".json"]
}

webpack会对没有扩展名的路径按照extensions依次匹配,所以通常要把常用的文件扩展名放在前面,以减少尝试匹配的次数。

动态路径的打包

对于动态的路径,如import()/require()。会打包相应目录下所有文件,然后在代码运行的时候动态拼接起来加载。

因此应该注意不要让路径太过模糊,否则会打包出非常多的chunk,最极端情况是整个路径都是一个变量,这意味着webpack会打包所有模块。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1389521.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Android]实现一个权限申请类

[Android]实现一个权限申请类 导言 在引入了动态权限申请之后&#xff0c;Android的权限申请就变得尤为繁琐&#xff0c;若是按照原有的方法一板一眼地进行申请&#xff0c;样板代码未免太多。因此本篇文章就使用ActivityResult API&#xff0c;来实现一个简单的权限申请类来帮…

国标视频监控平台EasyCVR如何通过接口调用下载设备录像文件

安防监控系统国标GB28181协议EasyCVR视频监控平台采用了开放式的网络结构&#xff0c;平台可支持Windows/Linux(CentOS ubuntu)/国产麒麟系统&#xff0c;能在局域网、公网、专网等复杂的网络环境中&#xff0c;将场景中分散的海量网络监控设备进行统一接入与汇聚管理&#xff…

USB Cable导致连接识别不良

2根USB线&#xff0c;连接USB2RS232芯片&#xff0c;有根线能够识别&#xff0c;另外一根不能识别。 好的线识别如下&#xff1a; 另外一根就不能识别

GZ036 区块链技术应用赛项赛题第1套

2023年全国职业院校技能大赛 高职组 “区块链技术应用” 赛项赛卷(1卷) 任 务 书 参赛队编号: 背景描述 随着消费需求的不断变化,消费者对食品安全的关注度越来越高,希望能参与食品供应链管理,让每个环节都透明化。但传统的供应链管理依靠纸张记录,保存数…

[自动驾驶算法][从0开始轨迹预测]:二、自动驾驶系统中常用的坐标系及相应的转换关系

自动驾驶中常见的坐标系与坐标转换 1. 传感器坐标系1.1 相机坐标系统1) 相机相关基础知识2) 相机各坐标系图像/像素坐标系相机坐标系像平面坐标系 3) 相机各坐标系之间的转换像平面坐标系到像素坐标系的转换&#xff08;平移缩放变换&#xff09;相机坐标系转像平面坐标系&…

Oracle基础查询介绍

1、oracle语句分为&#xff1a; DCL&#xff1a;数据控制语言&#xff0c;关键字有 grant、revoke 如&#xff1a;grant create table to test2; DDL&#xff1a;数据定义语言&#xff0c;关键字有 create、alter、drop、truncate 如&#xff1a;create table test1; DML&am…

接口测试用例设计 - 实战篇

一&#xff0e;接口测试流程 1&#xff0e;需求讨论 2&#xff0e;需求评审 3&#xff0e;场景设计 4&#xff0e;数据准备 5&#xff0e;执行 二&#xff0e;分析接口文档中哪些元素 1&#xff0e;接口名称 2&#xff0e;接口地址 3&#xff0e;支持格式 4&#xff0…

IDEA2023的激活与安装(全网最靠谱,最快捷的方式)

前言&#xff1a; 相信很多小伙伴已经开始了java的学习之旅&#xff0c;想要更快乐的学习当然少不了IDEA这个得力的开发工具软件。但是IDEA是付费的&#xff0c;免费版功能有太少&#xff0c;怎么才能既免费&#xff0c;又能使用上正式版呢&#xff01;当然还是激活啦&#xf…

【昕宝爸爸小模块】深入浅出之JDK21 中的虚拟线程到底是怎么回事(一)

➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan 欢迎优秀的你&#x1f44d;点赞、&#x1f5c2;️收藏、加❤️关注哦。 本文章CSDN首发&#xff0c;欢迎转载&#xff0c;要注明出处哦&#xff01; 先感谢优秀的你能认真的看完本文&…

Leetcode22-旅行终点站(1436)

1、题目 给你一份旅游线路图&#xff0c;该线路图中的旅行线路用数组 paths 表示&#xff0c;其中 paths[i] [cityAi, cityBi] 表示该线路将会从 cityAi 直接前往 cityBi 。请你找出这次旅行的终点站&#xff0c;即没有任何可以通往其他城市的线路的城市。 题目数据保证线路…

【Macos系统】安装VOSviewer及使用VOSviewer教程!!以ESN网络的研究进行案例分析

【Macos系统】安装VOSviewer及使用VOSviewer教程 以ESN网络的研究进行案例分析 本文介绍如何安装和使用VOSviewer软件&#xff0c;并以ESN&#xff08;Echo State Network&#xff09;网络的研究为案例进行分析。利用VOSviewer对相关文献进行可视化分析&#xff0c;并深入了解…

最新可用GPT-3.5、GPT-4、Midjourney绘画、DALL-E3文生图模型教程【宝藏级收藏】

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

麒麟KYLINOS域名解析失败的修复方法

原文链接&#xff1a;麒麟KYLINOS域名解析失败的修复方法 hello&#xff0c;大家好啊&#xff01;今天我要给大家介绍的是在麒麟KYLINOS操作系统上修复域名解析的方法。在日常使用中&#xff0c;我们可能会遇到由于系统配置问题导致的域名解析失败&#xff0c;这在内网环境下尤…

世微AP5160宽电压 LED 降压型恒流芯片14-18V 3A 电源PCB线路

这是一款14-18V 3A 电流的PCB设计方案. 运用的是世微AP5160 电源驱动IC,这是一款效率高&#xff0c;稳定可靠的 LED 灯恒流驱动控制芯片&#xff0c;内置高精度比较器&#xff0c;固定 关断时间控制电路&#xff0c;恒流驱动电路等&#xff0c;特别适合大功率 LED 恒流驱动。 …

selenium模拟浏览器查询导出参考文献

通过使用Selenium和BeautifulSoup&#xff0c;在CNKI网站上&#xff0c;以"知识图谱"为关键词&#xff0c;通过自动化工具在搜索页面提取相关文章信息。点击清楚并全选进行文献导出&#xff0c;随后从导出页面和管理导出的页面提取参考文献。 浏览器及WebDriver下载…

集合框架(一)

集合体系概述 集合体系结构 Collection代表单列集合&#xff0c;每个元素&#xff08;数据&#xff09;只包含一个值。Map代表双列集合&#xff0c;每个元素包含两个值&#xff08;键值对&#xff09;。 Collection集合体系 Collection集合特点 List系列集合&#xff1a;添加…

主流进销存系统有哪些?企业该如何选择进销存系统?

主流进销存系统有哪些&#xff1f;企业该如何选择进销存系统&#xff1f; “永久免费”的软件&#xff0c;这个可能还真不太可能有。而且就算有&#xff0c;也只能说是相对免费。 因为要么就是数据存量有限&#xff0c;要么就是功能有限数据、信息都不保障&#xff0c;并且功…

最小矩阵宽度 - 华为OD统一考试

OD统一考试(C卷) 分值: 200分 题解: Java / Python / C++ 题目描述 给定一个矩阵,包含N*M个整数,和一个包含K个整数的数组。 现在要求在这个矩阵中找一个宽度最小的子短阵,要求子矩阵包含数组中所有的整数。 输入描述 第一行输入两个正整数 N, M 表示矩阵大小。 接下…

Go后端开发 -- 面向对象特征:结构体 继承 多态 interface

Go后端开发 – 面向对象特征&#xff1a;结构体 && 继承 && 多态 && interface 文章目录 Go后端开发 -- 面向对象特征&#xff1a;结构体 && 继承 && 多态 && interface一、Go的结构体1.结构体的声明和定义2.结构体传参 二、将…

嵌入式一开始要怎么学?

今日话题&#xff0c;嵌入式一开始要怎么学&#xff1f;废话不多说&#xff0c;我认为学习嵌入式编程无需观看视频&#xff0c;拥有好的文档比视频更有帮助。掌握一门编程语言是学好嵌入式的关键。现今有许多计算机编程语言可供选择&#xff0c;比如C语言和C等。如果你刚刚入门…