最近在做electron的內(nèi)容优训,但是踩了很多坑续语,其中一個(gè)坑:
問題背景:對(duì)于進(jìn)程間的通信襟己,實(shí)現(xiàn)帶參數(shù)的輸入輸出引谜,就是直接寫一些函數(shù),可以實(shí)現(xiàn)其他文件對(duì)于函數(shù)的調(diào)用
問題內(nèi)容:其中一個(gè)js是實(shí)現(xiàn)功能函數(shù)(renderer.js)擎浴,也就是帶參數(shù)的輸入輸出员咽,另一個(gè)js是調(diào)用這些功能(hello.js),那么如何實(shí)現(xiàn)這個(gè)調(diào)用過程呢贮预?
我的hello.js和renderer.js在同一個(gè)文件夾下面贝室。
來(lái)嘗試一下網(wǎng)上的方法:
方法一:在html文件中body標(biāo)簽后加入js文件,在index.html中的body標(biāo)簽(</body>與</html>之間插入所引用的hello.js)仿吞,代碼如下:
<html>
<head>
<!---head內(nèi)容--->
</head>
<body>
<!---body內(nèi)容--->
</body>
<!---在body后面添加所引用的js文件-->
<script language="JAVASCRIPT" src='hello.js'></script>
</html>
然后滑频,對(duì)于hello.js文件,為了調(diào)用renderer.js中的函數(shù)唤冈,需要在hello.js中添加以下代碼:
new_element = document.createElement("script");
new_element.setAttribute("type","text/javascript");
new_element.setAttribute("src", "renderer.js");
document.body.appendChild(new_element);
function testBtn(struct, buttonId, msg){
//renderer.js中函數(shù)為test
test(struct, buttonId, msg);
}
testBtn('click', 'asynchronous message', 'ping');
對(duì)于代碼峡迷,網(wǎng)上解釋如下:
讓我們來(lái)分析一下關(guān)鍵的幾句代碼:首先,我們利用document.createElement("script")生成了一個(gè)script的標(biāo)簽,設(shè)置其type屬性為text/javascript绘搞,src為renderer.js(這里的renderer.js同hello.js放在同一個(gè)目錄彤避,也可放在不同的目錄)。最后將這個(gè)標(biāo)簽動(dòng)態(tài)地加入body中夯辖。如此一來(lái)琉预,我們就可以調(diào)用到不同js文件中的方法了。
注意:<script language="JAVASCRIPT" src='b.js'></script>一定要放在body下面蒿褂。
因?yàn)閔ello.js中用到了body(document.body.appendChild(new_element);)
如果將引如hello.js的代碼放在body上面圆米,也就是說,
進(jìn)入頁(yè)面后啄栓,還沒有生成body就已經(jīng)執(zhí)行hello.js里的document.body.appendChild(new_element);了娄帖。
這時(shí)body不存在就會(huì)拋javascript錯(cuò)誤。
而實(shí)際效果呢谴供,并不行(至少我沒有測(cè)試成功块茁,目前還不知道原因出現(xiàn)在哪里,求告知9鸺 J浮)
方法二:在調(diào)用者程序的開始直接加入要被調(diào)用的js文件,代碼如下:
//加入下面的代碼
document.write("<script language=javascript src='./renderer.js'></script>");
//調(diào)用函數(shù)
test('click', 'asynchronous message', 'ping');
結(jié)果呵呵了崎场。佩耳。。谭跨。干厚。還是不行。螃宙。蛮瞄。。谆扎。又是一頭霧水
方法三:在html文件中加入兩個(gè)腳本程序挂捅,注意,加入的位置在</head>和<body>兩個(gè)標(biāo)簽之間堂湖,(也有的在<body></body>兩個(gè)標(biāo)簽之間加入的)闲先,代碼如下:
</head>
<script src="hello.js"></script>
<script src="renderer.js"></script>
<body>
之后在hello.js中直接調(diào)用函數(shù)就行。
test('click', 'asynchronous message', 'ping');
然后呢无蜂?如果說前兩種方法不行我還信了伺糠,畢竟沒有看到執(zhí)行結(jié)果,但是第三種人家明明成功了斥季,而且兩種加入的方法都成功了训桶,到我這兒走不通了,幾近崩潰。渊迁。慰照。
冷靜冷靜,如果你試了試前面的方法也不行琉朽,一定要淡定,我也不知道怎么想的稚铣,然后試了一下下面這個(gè)方法箱叁。。竟然成功了L枰健耕漱!
方法四:首先,在方法三的基礎(chǔ)上抬伺,在html中直接利用require螟够,將兩個(gè)js文件直接加載進(jìn)去,然后就可以實(shí)現(xiàn)調(diào)用了峡钓。
html中的代碼:
//首先妓笙,方法三中的加入內(nèi)容不變
</head>
<script src="hello.js"></script>
<script src="renderer.js"></script>
<body>
<!--- body內(nèi)容--->
//方法四加入的內(nèi)容
<script>
// You can also require other files to run in this process
require('./renderer.js')
require('./hello.js')
</script>
</body>
</head>
至于調(diào)用函數(shù)的代碼,與方法三一樣能岩,直接在hello.js中調(diào)用即可寞宫。
解釋解釋,知其然必須知其所以然(個(gè)人理解):
對(duì)于方法三拉鹃,為什么不行呢辈赋?我打開electron調(diào)試工具的時(shí)候,第一次加載頁(yè)面時(shí)膏燕,輸出了這個(gè)信息(極為重要):
test is not defined----我的test函數(shù)沒有被定義钥屈,為什么沒有被定義,我明明已經(jīng)寫好了的坝辫,看一下方法三中的加載順序篷就,先加載的hello.js,之后加載的renderer.js阀溶,也就是說腻脏,先加載了的hello.js中的test方法沒有被定義,然后自然函數(shù)執(zhí)行不成功银锻。
看了看程序永品,發(fā)現(xiàn)方法四的加載順序與方法三完全相反,在查看了文檔之后击纬,發(fā)現(xiàn)方法三與方法四的加載的最終結(jié)果并沒有什么不同鼎姐,只不過:require引入的的文件,內(nèi)部聲明的最外層變量不屬于全局變量,而script引入的屬于全局變量炕桨。
最重要的信息:
如果用script引入需要考慮引入順序饭尝,避免變量沖突和前置依賴∠坠考慮順序钥平,考慮順序,考慮順序姊途,重要的事情說三遍(之后自己也試了試涉瘾,把方法三的順序顛倒,發(fā)現(xiàn)可以捷兰,之所以沒刪除這些內(nèi)容立叛,是想記得更清,也避免讓更多的人入坑)
哈哈哈哈贡茅,腦子真的被門給夾了秘蛇,這個(gè)坑跳的真的值(說的我自己都信了)
不過,以方法三加載腳本程序的方法并不好:
(來(lái)自阮一峰先生的日志:http://www.ruanyifeng.com/blog/2012/11/require_js.html) 首先顶考,加載的時(shí)候赁还,瀏覽器會(huì)停止網(wǎng)頁(yè)渲染,加載文件越多村怪,網(wǎng)頁(yè)失去響應(yīng)的時(shí)間就會(huì)越長(zhǎng)秽浇;其次,由于js文件之間存在依賴關(guān)系甚负,因此必須嚴(yán)格保證加載順序(比如上例的1.js要在2.js的前面)柬焕,依賴性最大的模塊一定要放到最后加載,當(dāng)依賴關(guān)系很復(fù)雜的時(shí)候梭域,代碼的編寫和維護(hù)都會(huì)變得困難斑举。
最后,介紹一種我最喜歡的方式病涨,對(duì)于功能的封裝這種方式應(yīng)該再好不過了富玷,exports和require大法好。
在nodejs中既穆,提供了exports 和 require 兩個(gè)對(duì)象赎懦,其中 exports 是模塊公開的接口,require 用于從外部獲取一個(gè)模塊的接口幻工,即所獲取模塊的 exports 對(duì)象励两。而在exports拋出的接口中,我們可以拋出變量或者函數(shù)囊颅。
如果你希望你的模塊就想為一個(gè)特別的對(duì)象類型当悔,請(qǐng)使用module.exports傅瞻;如果希望模塊成為一個(gè)傳統(tǒng)的模塊實(shí)例,請(qǐng)使用exports.xx方法盲憎;module.exports才是真正的接口嗅骄,exports只不過是它的一個(gè)輔助工具。最終返回給調(diào)用的是module.exports而不是exports饼疙。
總而言之溺森,二者的關(guān)系是:
exports 是指向的 module.exports 的引用,二者指向同一塊內(nèi)存
module.exports 初始值為一個(gè)空對(duì)象 {}窑眯,所以 exports 初始值也是 {}
require() 返回的是 module.exports 而不是 exports
下面給出代碼:
renderer.js中使用exports導(dǎo)出函數(shù):
//在這里面寫好函數(shù)的封裝儿惫,然后在hello.js中調(diào)用
var test = function(struct, buttonId, msg){
const asyncMsgBtn = document.getElementById(buttonId);
asyncMsgBtn.addEventListener(struct, function(){
switch(struct){
case 'click':
ipc.send('asynchronous-message', msg);
console.log("調(diào)用成功");
break;
default:
console.log('Error!!!')
}
})
}
//這種方式是成功的
exports.test = test;
//這種方式也是可以得
//module.exports.test = test;
而hello.js中對(duì)于代碼的使用如下:
//利用require加載模塊
const renderer = require('./renderer')
renderer.test('click', 'asynchronous message', 'ping');
renderer.test('click', 'changeView', 'change');
可以說,這種方式完全符合我們程序封裝的概念伸但,思路統(tǒng)一,結(jié)構(gòu)規(guī)整留搔,個(gè)人最愛更胖。而且,上面的程序中兩種方法都可以輸出成功隔显,其原因就在于:
exports變量是在模塊的文件級(jí)別作用域內(nèi)有效的却妨,它在模塊被執(zhí)行前被賦于 module.exports 的值。它有一個(gè)快捷方式括眠,以便 module.exports.f = ... 可以被更簡(jiǎn)潔地寫成exports.f = ...彪标。 注意,就像任何變量掷豺,如果一個(gè)新的值被賦值給exports捞烟,它就不再綁定到module.exports(其實(shí)是exports.xx會(huì)自動(dòng)掛載到?jīng)]有命名沖突的module.exports.xx)
而且,對(duì)于require函數(shù)当船,exports只是函數(shù)內(nèi)部一個(gè)局部變量题画,最后返回的仍是module.exports,這應(yīng)該就是exports稱為module.exports的引用所在德频。代碼內(nèi)容如下(應(yīng)該很清晰了):
function require(...) {
var module = { exports: {} };
((module, exports) => {
// 你的被引入代碼 Start
// var exports = module.exports = {}; (默認(rèn)都有的)
function some_func() {};
exports = some_func;
// 此時(shí)苍息,exports不再掛載到module.exports,
// export將導(dǎo)出{}默認(rèn)對(duì)象
module.exports = some_func;
// 此時(shí)壹置,這個(gè)模塊將導(dǎo)出some_func對(duì)象竞思,覆蓋exports上的some_func
// 你的被引入代碼 End
})(module, module.exports);
// 不管是exports還是module.exports,最后返回的還是module.exports
return module.exports;
}
最后钞护,這個(gè)坑跳的真的值8桥纭!患亿!