本系列文章已出版實體書籍:
「你的地圖會說話?WebGIS 與 JavaScript 的情感交織」(博碩文化)
WebGIS啟蒙首選✖五家地圖API✖近百個程式範例✖實用簡易口訣✖學習難度分級✖補充ES6小知識
本篇文章請搭配
[7-1] 3D地圖初探 - ArcGIS API for JavaScript
昨天介紹ArcGIS API時,講到dojo框架,並且用require語法來實現模組化管理,
究竟require發展的起源?是用來解決什麼問題?要如何使用?
今天就讓我們來一一解惑。
瀏覽器載入html頁面時,會由上到下依序載入,也就是同步載入
。
↓ html include為同步載入。
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="A.js"></script>
<script src="B.js"></script>
<script src="C.js"></script>
</head>
↓ A.js
console.log('Hello A')
↓ B.js
console.log('Hello B')
↓ C.js
console.log('Hello C')
↓ 結果
可以看到載入順序必為A → B → C,為同步載入。
這也解釋了寫在head裡面的JS必須全部載入完,才會載入寫在body中的DOM元素,因此才會需要寫$(document).ready()或window.onload來等候頁面在入完畢才執行程式。新式的寫法會把JS寫在body結束前,如此一來,就會先載入DOM元素最後才載入JS。
為了解決同步載入的困境,達到非同步模組化管理。
不但要非同步載入,還要方便每個JS的管理,而require.js就是一個實現它很好的例子。
↓ 引入require.js
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js"></script>
↓ 設定require.config
require.config({
paths: { // 每個模組的路徑
"A": "A",
"B": "B",
"C": "C"
}
});
require.config可以設定每個欲載入的模組的路徑以及它們的依賴關係。paths可以設定每個模組的路徑,屬性名稱(key)為模組名稱,屬性值(value)為路徑(省略.js)。
↓ 載入模組
require(["A", "B", "C"], function (A, B, C) {
console.log('JS OK');
});
↓ 結果
多試幾次就會發現,每次載入的順序不盡相同,可能是A → B → C,也有可能B → C → A。非同步載入每個JS載入不會受其它JS所影響,載入完的順序也是隨機。
達到了非同步載入,並解決了同步載入卡頓問題之後,還要進行模組化管理。
然而,早期JavaScript並沒有模組化的標準規範,民間眾多高手研擬出來各種版本,
其中以AMD與CommonJS為最多人使用的兩套規範。它們之間差別在於,AMD規範是針對於瀏覽器所設計,而CommonJS則是針對node.js環境所設計。
↓ 引入require.js
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js"></script>
↓ 新增test.js,define為一個模組。
define(["jquery"], function (jquery) {
var jq = {
HelloWorld: function () {
$(function () {
$(document.body).append("Hello jQuery!");
});
}
};
return jq;
});
透過define()可以定義模組,以及利用return輸出模組內容。define的第一個參數為依賴關係,也就是說要先載入jquery模組才會載入此模組。
↓ 設定require.config,將根目錄下的jquery-3.5.1.js定義為'jquery'模組。
require.config({
paths: {
'jquery': '/jquery-3.5.1'
}
});
↓ 載入test模組,並呼叫test模組輸出的物件/函式。
require(["test"], function (jq) {
jq.HelloWorld();
});
↓ 結果
↓ 新增test_CommonJS.js,透過module.exports輸出模組。
const $ = require("jquery");
const HelloWorld = () =>{
$(document.body).append("Hello jQuery!");
}
module.exports = {
Hello: HelloWorld
}
↓ 載入test_CommonJS模組,並呼叫。
const helloWorld = require("test_CommonJS.js");
helloWorld.Hello();
↓ 結果會Error,原因是瀏覽器的執行環境並沒有node.js所擁有的這四個東西。
想要在瀏覽器環境下使用CommonJS,可以使用Browserify來實現。
可是並非今天討論的重點,大家可以自己玩玩看?!
ES6出了新的語法達到了模組化的效果,也就是Import及Export,並且結合了AMD與CommonJS的優點。
↓ 新增test_export.js,透過export語法輸出模組。
export default function () { // 預設匯出函式
console.log('export');
}
↓ 載入test_export.js模組並呼叫
import fn from './test_export.js';
fn();
↓ Error
竟然Error惹!!!
Cannot use import statement outside a module解法:html的script標籤預設為type="text/javascript",要將它改為type="module"。
↓ 加入type="module"
<script type="module">
import fn from './test_export.js'; // 匯入 預設匯出 並命名為fn
fn();
</script>
↓ 成功載入
↓ 剛剛我們上面的範例為預設匯出,export後面接default。
export default function () { // 預設匯出函式
console.log('export');
}
↓ 也可以預設匯出String或Number等原始型別
export default "Hello";
↓ 匯入
import msg from './test_export.js';
console.log(msg);
↓ 結果預設匯出可以匯出原始型別、物件型別、類別(class)或函式等等,但一個模組只能有一個預設匯出。
↓ 具名匯出在匯出時帶有名稱,一個模組可以同時擁有多個具名匯出。
export const message = "Hello";
export const obj = { name: 'obj' };
↓ 匯入時用大括號寫欲匯入的內容
import { message, obj } from './test_export.js';
console.log(message);
console.log(obj);
↓ 結果
↓ 在預設匯入時還可利用as語法重新命名
import { message as msg, obj as object } from './test_export.js';
console.log(msg);
console.log(object);
↓ 結果不變
↓ 預設匯出function,具名匯出message為字串、obj為物件。
export default function () {
console.log('export');
}
export const message = "Hello";
export const obj = { name: 'obj' };
↓ 也可將具名匯出寫在最下面,用大括號加入想要匯出的變數。
const message = "Hello";
const obj = { name: 'obj' };
export default function () {
console.log('export');
}
export { message, obj };
↓ 預設匯入命名為defaultExport,具名匯出用*表示匯出全部,用as語法重新命名為namedExport。
import defaultExport, * as namedExport from './test_export.js';
defaultExport();
console.log(namedExport.message);
console.log(namedExport.obj);
↓ 結果
今天講解重點如下
明天邁入第20篇啦!依照慣例會來個番外篇!
大家期待嗎?!