在开发Electron
程序时,在引入jQuery
和Bootstrap
后,控制台报错:
1 | Uncaught TypeError: Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript. |
而反复查看代码没发现问题,引用的顺序没有问题:
1 | <script src="js/jquery.min.js"></script> |
后在网上找到了一些方法,解决了我的疑惑。
方法一
在引入jQuery
之前,添加如下代码:
1 | <script> |
方法二
使用require
方式引入jquery
:
1 | window.$ = window.jQuery = require('./js/jquery.min.js') |
虽然问题是解决了,但是是什么引发了这个问题呢?
经过深入了解,发现原来electron
里面还是和浏览器有区别的。在electron
里面使用的是CommonJS
标准,这也是后端服务器采用的标准,而在jQuery
中有如下代码:
1 | if ( typeof module === "object" && typeof module.exports === "object" ) { |
判断如果在CommonJS
中,jQuery
就绑定到module
下,否者就绑定到window
下,我们经常使用的$
就是在window
的下。
而Bootstrap
是一个(首发共众号)前端工具,它依赖jQuery
,直接从window
下有没有jQuery/$
来判断有没有jQuery
,而在这里就行(正义的程序猿)不通了,因为jQuery
是在module
下的,所以就有了如上的两种方法,个人更倾向于第二种方法。
有没有其他方法?有!
方法三
因为electron
中集成了node
相关的东西,可以把这个集成关闭或者把相关对象绑定到新的对象上:
1 | const win = new BrowserWindow({ |
上面这种方式,window
对象下就没有module
了。
当然我们也可以把绑定的对象替换掉,这样也能使用node
相关的API
:
1 | <head> |
这只是一种解决思路,不建议大家这么做。
既然都谈到CommonJS
了,有必要进一步了解前端的几个规范。
CommonJs,AMD和CMD
CommonJS
是nodejs
也就是服务器端广泛使用的模块化机制,模块必须通过module.exports 导出对外的变量或接口,通过require()
来导入其他(首发公zhong号)模块的(正义的程序猿,欢迎关注)输出到当前模块作用域中,此方式是同步的AMD
是RequireJS
在推广过程中对模块定义的规范化产出,提前执行(异步加载:依赖先执行)+延迟执行CMD
是SeaJS
在推广过程中对模块定义的规范化产出,延迟执行(运行到需加载,根据顺序执行)
CommonJS
CommonJS
模块加载是同步的,后面的模块必须等前面的加载完成才能执行。
定义模块:
1 | // 模块 a.js |
加载模块:
1 | // 模块 b.js |
CommonJS模块的加载(首发公zhong号)机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。(正义的程序猿)看如下例子:
1 | // b.js |
1 | // a.js |
还有,CommonJS
模块重复引入的模块并不会重复执行,再次获取模块只会获得之前获取到的模块的缓存。
AMD
全称:Asynchronous Module Definition
,中文翻译:异步模块定义,javascript
原生不支持这种规范,使用AMD规范进行页面开发需要用到对应的库函数,鼎鼎大名的就是RequireJS
,
定义模块:
1 | // 独立模块 |
独立模块即定义的模块不依赖其他模块,非独立模块就是会依赖其他模块。我们定义的模块依赖foo
和bar
,第二个参数是一个函数,仅当依赖加载成功后才会调用,函数的参数与前面的依赖数组一一对应,函数必须返回一个对象,给其他模块调用。
加载模块:
AMD
加载模块也是用require
,由于是异步的所以只能用回调函数的方式:
1 | require(['foo','bar'], function(foo,bar){ |
回调函数中才能使用依赖的模块,这里是在定义的时候加载模块了,相当于把依赖前置。
require()
里面可以配置一些参数:
1 | require.config({ |
- paths: 指定模块的位置,可以是文件路径,也可以是一个网址
- shim: 有些库不兼容
AMD
的写法,可以配置shim
来解决,可以理解成“垫片”,帮助require.js
加载非AMD规范的库
CMD
即Common Module Definition
通用模块定义,这个规范也是国内发展起来的,有个浏览器的实现SeaJS
,CMD
和AMD
要解决的问题都是一样的,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同。
和其他规范一样,CMD
中也是一个模块一个文件:
1 | define(function(require, exports, module) { |
require
是可以把其他模块导入进来的一个参数;而exports
是可以把模块内的一些属性和方法导出的;module
是一个对象,上面存储了与当前模块相关联的一些属性和方法。
AMD
是依赖关系前置,在定义模块的时候就要声明其依赖的模块,CMD
是按需加载依赖,只有在用到某个模块的时候再去require
,看看CMD
的例子:
1 | // model1.js |
CommonJS
主要用于服务端,模块文件都存在本地硬盘,所以加载起来比较快,不用考虑用异步加载,但是在浏览器中,受限于网络原因,更合理的方案应该是异步加载。为了方便模块化开发,这时武林中出现了AMD
和CMD
两大门派,分别代表了两种不同的思想,一是依赖前置,二是按需加载依赖,各有利弊,这两大门派都像争夺武林盟主,按理说是要比武论高下的,但半路杀出个程咬金,选举委员会直接自己推了一个规范:ES Module
,告知天下以后要听ES Module
的,他才是大哥。
这小子又是谁?
ES Module
ES modules(ESM)是 JavaScript 官方的标准化模块系统,能和CommonJS
混合使用,意味着能在node
环境下执行,因为他是标准,所以未来很多浏览器都会支持。
模块定义:
1 | // module.js |
模块加载:
1 | // index.js |
加载模块的方式还有很多种,比如:
1 | // 加载单个模块 |
在ESM
中模块导出是值的引用,看如下例子:
1 | // b.js |
1 | // a.js |
import/export
使用又一个限制,就是不能在其他语句/表达式的内部使用,比如if
语句里面,所以一般都在最底部,原因是ESM
使用javascript引擎静态分析。
至此,ESM
已经是事实上的盟主,使用该规范无论是node
环境还是浏览器环境都能很好兼容,而且前端也不要再引入requirejs
或sea.js
来进行模块开发。
参考:
公众号:正义的程序猿
https://www.jianshu.com/p/d67bc79976e6
https://blog.csdn.net/crystal6918/article/details/74906757
https://cloud.tencent.com/developer/article/1589084