模块pdf2image.dll加载失败_Webpack 原理从前端模块化开始

当前主流 JS 模块化方案

  • 无模块化

  • CommonJS 规范,nodejs 实现的规范

  • AMD 规范,requirejs 实现的规范

  • CMD 规范,seajs 实现的规范, seajs 与 requirejs 实现原理有很多相似的地方 u ES Modules,当前 js 标准模块化方案

注意:cjs、amd、cmd、 ES Modules 都是指规范,所以可能对应有多种实现,下面就对各个模块化方案做简单说明。

无模块化

f9505f732a1baeb3809cba071ec89e9a.png

刀耕火种年代的前端代码

无模块化带来的问题的问题

  • 污染全局作用域

  • 不便于拆分逻辑,维护成本高

  • 依赖关系不明显

  • 复用性差

CommonJS 规范

  • CommonJS 是由 node 实现的一套规范,关于 CommonJS 的提出可参考CommonJS 规范https://zhaoda.net/webpack-handbook/commonjs.html

  • require 源码解读可参考 require() 源码解读http://www.ruanyifeng.com/blog/2015/05/require.html

  • 模块包装相当于执行如下代码, compiledWrapper 是调用 node 封装的 V8 原生创建函数的方法返回的一个函数

function compiledWrapper(exports, require, module, __filename, __dirname) {  // 插入文件中的代码  // 返回导出对象  return module.exports}compiledWrapper.call(exports, exports, require, module, filename, dirname)

CommonJS 模块输出的是一个值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。如下有两个文件,执行命令node index.js,会有什么结果?

// lib.jslet counter = 3function incCounter() {  counter++}module.exports = {  counter,  incCounter}
// index.jsconst mod = require('./lib')// 此处输出值?console.log(mod.counter)mod.incCounter()// 此处输出值?console.log(mod.counter)

输出值分别为:3,3

require 函数第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象,下次加载会直接从缓存中取数据。以下是一个循环引用的例子,请问执行node main.js后会输出什么?

// a.jsconsole.log('a starting')exports.done = falseconst b = require('./b.js')console.log('in a, b.done = %j', b.done)exports.done = trueconsole.log('a done')
// b.jsconsole.log('b starting')exports.done = falseconst a = require('./a.js')console.log('in b, a.done = %j', a.done)exports.done = trueconsole.log('b done')
// main.jsconsole.log('main starting')const a = require('./a.js')const b = require('./b.js')console.log('in main, a.done = %j, b.done = %j', a.done, b.done)

输出结果如下:

main startinga startingb startingin b, a.done = falseb donein a, b.done = truea donein main, a.done = true, b.done = true

AMD 规范

  • AMD 是 Asynchronous Module Definition 的简写,即异步模块定义

  • AMD 规范的完整定义可参考 https://github.com/amdjs/amdjs-api/wiki/AMD

  • requirejs 是在浏览器中运行的,所有一些基础库需要先配置,以方便其他库调用,可以理解为 CommonJS 中的 node_modules 下的包。业务模块也可定义在其中,可认为是路径别名。paths 中的路径不能包含扩展名。

requirejs路径配置,后面再具体讲解里面参数的作用

require.config({  paths: {    // 如果第一个加载失败就会加载第二个    jquery: ['lib/jquery.min', 'lib/jquery'],    lodash: 'lib/lodash.min',    main: './mian' // 入口文件  }})

定义模块

/*** 定义模块,当依赖加载完成后执行回调* 回调可返回值,返回值会被导出到外部使用* @param {String} id 模块名称,可省略* @param {Array} dependencies 依赖的模块* @param {Function} factory 回调函数*/define(id?, dependencies?, factory);define(['jquery'], function($) {  $('body').css({ background: 'red' })  // 导出log函数  return (...args) => console.log('自定义log', ...args)})

依赖可以使用上面的config中定义paths中的key,这样就可以缩短路径,同时也便于第三方库加载依赖,例如jQuery插件打包为AMD格式的文件,引入jQuery会使用define(['jquery'], function (){ })的形式,而不用关心jQuery的真实路径。

加载模块

/** * 加载模块 * @param {Array} deps 要加载的模块 * @param {Function} callback 加载成功回调,回调参数为加载模块导出对象 * @param {Function} errback 加载失败回调 */requirejs(deps, callback, errback)require(['main'], log => {  log('我成功加载了‘)  // do something...,也可以在这里继续require其他js文件})

requirejs 使用示例可参考原文Github中的源码

CMD 规范

  • CMD 是 Common Module Definition 的简写,即通用模块定义

  • CMD 规范的完整定义可参考https://github.com/seajs/seajs/issues/242

  • CMD 的主要代表是 seajs。CMD 推崇依赖就近,AMD 推崇依赖前置。即 AMD 在定义模块的时候就必须把依赖包含进来,CMD 是在使用的时候再 require 对应的依赖

  • 当前主流的库对 CMD 支持不是很友好,都需要额外的修改才能工作

  • AMD 与 CMD 写法对比如下

// CMD// 代码写起来有同步require的感觉define((require, exports, module) => {  const $ = require('jquery‘)  $('title').text('hello')})// AMD// 明显的异步风格define(['jquery'], $ => {  $('title').text('hello')})

seajs 中 require 书写约定

  1. 正确拼写 require

// 错误!define(function(req) {  // ...}) // 正确!define(function(require) {  // ...})

使用直接量

// 错误!require(myModule) // 错误!require('my-' + 'module') // 错误!require('MY-MODULE'.toLowerCase()) // 正确!require('my-module')

不要修改 require

// 错误 - 重命名 "require"!var req = require,  mod = req('./mod') // 错误 - 重定义 "require"!require = function() {} // 错误 - 重定义 "require" 为函数参数!function F(require) {} // 错误 - 在内嵌作用域内重定义了 "require"!function F() {  var require = function() {}}

seajs 隐藏坑

如下代码输出`$`为 null

function func(require, exports, module) {  const $ = require('jquery‘)  console.log($)}func.toString = () => '() => {}'define(func)

seajs 对于 require 和 define 函数的特殊要求是由于seajs 原理导致的,seajs 的执行流程大致如下

ccb40ae0f6e7116cd4a2c6759ff7d93d.png

seajs 使用示例可参考原文Github中的源码

ES Modules

  • ES Modules 是 ECMAScript modules 的简写,也可写为 ESM。ES Modules 是 js 官方推出的标准

  • ES Modules 相比于其他模块规范是一个静态化的模块解决方案,其他模块化方案都是运行时才能确定输出内容,而 ES Modules 是编译时就确定了的。其他模块化方案导入文件都是整个导入模块,而 ES Modules 可以只导入需要的部分

  • ES Modules 会自动采用严格模式,不需要像 ES5 一样在头部加上”use strict”

  • ES Modules 可运行在服务端(node)和浏览器。目前主流浏览器都已经支持 ES Modules,node 使用 ES Modules 需要在执行时加上--experimental-modules,且要求编写的 js 文件必须以.mjs 为后缀

  • ES Modules 导出的是一个值得引用,即在模块内改变了导出值,那么下一次使用也会得到新的值

如下有两个文件,执行命令node --experimental-modules index.mjs,会有什么结果?

// lib.mjsexport let counter = 3export function incCounter() {  counter++}
// index.mjsimport * as mod from './lib’// 此处输出值?console.log(mod.counter)mod.incCounter()// 此处输出值?console.log(mod.counter)

输出结果为:3,4

循环引用

如下代码,请问执行`node --experimental-modules main.mjs`后会输出什么内容

// a.mjsimport { bar } from './b.mjs'console.log('a.mjs')console.log(bar)export let foo = 'foo'
// b.mjsimport { foo } from './a.mjs'console.log('b.mjs')console.log(foo)export let bar = 'bar'
// main.mjsimport './a.mjs'
  • 在所有的模块规范中都存在循环依赖问题,解决依赖循环的方式都相似,几乎都采用惰性导入的方式来解决。

  • 如下两个文件存在循环引用,当执行 node --experimental-modules a.mjs 时,会报错说 b 未定义,这就是由于循环依赖导致的,如果不使用 b 则不会报错,修改方案如下。其他的模块循环引用也可按照此方法进行修改。

  • CommonJS 也可以使用先导出自身,再引入其他模块的方式来避免。同时也可以把 require 放入到函数体中,即在调用的时候才去加载依赖

f738abc954665fc69048ae379da546d2.png

相关链接

  • AMD 和 CMD 的区别有哪些?- 玉伯的回答 - 知乎(https://www.zhihu.com/question/20351507/answer/14859415)

  • https://github.com/seajs/seajs/issues/277

  • https://github.com/seajs/seajs/issues/242

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

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

相关文章

ios html正则表达式,ios 正则表达式去html标签

ios 正则表达式去html标签[2021-01-27 12:53:55] 简介:php正则表达式去掉html的方法:首先使用“htmlspecialchars_decode” 将特殊的HTML实体转换回普通字符;然后通过正则表达式“preg_replace(/]>/,,$_st系统运维正则表达式概述基础正则表达式扩展正…

java开发五年面试经验_只有经验丰富的开发人员才能教您有关Java的5件事

java开发五年面试经验深入研究Java之前需要了解的所有内容的概述 有许多工具,方法,环境和功能会改变您处理代码的方式,而这些通常是在学年期间不会遇到的。 虽然它在Java开发世界中迈出了第一步,但大多数实际学习都是在工作中进行…

c++调用cplex求解例子_视频教程 | 用Python玩转运筹优化求解器IBM CPLEX(二)

编者按优化求解器对于做运筹学应用的学生来说,意义重大。然而直到今天,放眼望去,全网(包括墙外)几乎没有一个系统的Cplex中文求解器教程。作为华人运筹学的最大的社区,『运筹OR帷幄』 责无旁贷,…

markdown 行内公式_如何轻松将笔记转为思维导图(Word、Markdown)

目录简介Markdown -> XMindWord -> XMind简介思维导图是一种一种图像式思维的工具,便于我们理解知识之间的逻辑关系。在我们的学习、生活中,我们习惯于使用word、markdown等笔记软件,而较少的使用思维导图。其实,有些时候我…

apache.camel_Apache Camel 2.18发布–包含内容

apache.camel本周发布了Apache Camel 2.18.0 。 此版本是重要版本,我将在此博客文章中重点介绍。 Java 8 Camel 2.18是第一个需要Java 1.8的版本(例如,容易记住的Camel 2.18 Java1.8。Camel2.17 Java 1.7)。 我们采取了谨慎的…

cors跨域_Spring Boot 中通过 CORS 解决跨域问题

(给ImportNew加星标,提高Java技能)转自:江南一点雨今天和小伙伴们来聊一聊通过CORS解决跨域问题。同源策略很多人对跨域有一种误解,以为这是前端的事,和后端没关系,其实不是这样的,说到跨域,就不…

逻辑建模与物理建模_架构层和建模域逻辑

逻辑建模与物理建模在讨论用于建模域逻辑(例如事务脚本,表模块,域模型)的PoEAA模式时,我注意到人们对域模型模式是最好的印象(尽管印象不对)。 因此,他们开始将其应用于所有内容。 …

html文字列表,文字列表模板

文字列表模板1、如何编辑列表模板文字列表模板存放在模板包archive文件夹中,命名以list_text为前缀在模板包中找到list_text.html 模板,复制另存为一个新模板,命名为list_text_自定义名称.html自定义名称可以是英文或拼音,但不能用…

inputstreamreader未关闭会导致oom_Linux内核OOM机制分析和防止进程被OOM杀死的方法...

问题描述Linux 内核有个机制叫 OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程杀掉。典型的情况是:某天一台机器突然 ssh 远程登录不了&…

centos一键清理磁盘空间_如何清理 Docker 占用的磁盘空间

Docker 很占用空间,每当我们运行容器、拉取镜像、部署应用、构建自己的镜像时,我们的磁盘空间会被大量占用。如果你也被这个问题所困扰,咱们就一起看一下 Docker 是如何使用磁盘空间的,以及如何回收。docker 占用的空间可以通过下…

mongodb dsl_具有Java DSL的Spring Integration MongoDB适配器

mongodb dsl1引言 这篇文章解释了如何使用Spring Integration从MongoDB数据库中保存和检索实体。 为了完成此任务,我们将使用Java DSL配置扩展来配置入站和出站MongoDB通道适配器。 例如,我们将构建一个应用程序,使您可以将订单写入MongoDB存…

Oracle 数据库中较为复杂或典型的 SQL 语句的解读

文章目录批量生成 SQL 语句/拼接字符串多表关联查询 where 子句示例(一)示例(二)普通的表间内连接查询语句关键字 distinct 用法说明Oracle 数据库的分组排序查询Oracle 数据库 cast 函数Oracle 数据库 sum 函数的高级用法Oracle…

私有方法与静态私有方法_每个私有静态方法都是新类的候选人

私有方法与静态私有方法您是否有私有的静态方法来帮助您将算法分解为更小的部分? 我做。 每当我编写一个新方法时,我就会意识到它可以是一个新类。 当然,我不会从所有课程中选修课程,但这必须是目标。 私有静态方法不可重用&#…

c语言插入排序_还有这种操作?C语言插入排序算法,一点就透

插入排序算法是所有排序方法中最简单的一种算法,其主要的实现思想是将数据按照一定的顺序一个一个的插入到有序的表中,最终得到的序列就是已经排序好的数据。更多C/C资料群文件:569268376直接插入排序是插入排序算法中的一种,采用…

Mac 如何操控远程的 Windows 电脑

文章目录使用 Remote Desktop Connection for mac 客户端第 1 步:Windows 电脑进行远程设置第 2 步:Windows 电脑设置管理员账号和密码第 3 步:获取 Windows 电脑的 IP 地址第 4 步:Mac 电脑安装远程桌面连接客户端第 5 步&#x…

map iterator_一个简单的Map Iterator性能测试

map iteratorJava Map性能有很多方面可以衡量,但是关键的一个是简单的单线程扫描。 这是一些针对Iterators和Java 8 Map.forEach()简单测试代码,以及一些图形结果。 1.性能测试困难 性能测试是一项非常困难的工作,精确的可重复性测试需要Jav…

学生用计算机中sto,STO 文件扩展名: 它是什么以及如何打开它?

STO 疑难解答常见的 STO 打开问题Ecru Software PRO100 不存在你尝试加载 STO 文件并收到错误,例如 “%%os%% 无法打开 STO 文件扩展名”。 如果是这种情况,通常是因为 你的计算机上没有安装 Ecru Software PRO100 for %%os%%。 由于您的操作系统不知道如…

MacBook 使用 Loopback 录屏和录音频(MacBook 录屏教程/录视频教程/Loopback 教程)

文章目录一、下载软体二、Loopback 界面介绍三、设置系统的声音输入/输出设备(一)设置声音输入设备(二)设置声音输出设备四、录制程序中选择声音输入设备五、开始录制一、下载软体 在網路上可以找到破解版的軟體 Loopback 二、L…

惠普照片打印软件_被看错的打印机?原来打印机还可以这么玩

孩提时代,经常弄丢试卷的小值君曾频繁地与打印店打交道,那是我最早接触打印机的时候。白驹过隙,时至当下,打印设备已然成为家庭不可或缺的部分。印象中,打印机要不就是打打文档,要不就是打打照片&#xff0…

戴尔G3笔记本使用U盘重装操作系统

戴尔G3笔记本 下载安装大白菜U盘启动盘制作软件根据使用说明完成启动盘制作下载操作系统ISO文件重启电脑,连续按F12,打开如下界面后选择红色线框选项: 进入PE系统界面,打开【大白菜】,看到如下界面: