1. 程式人生 > 其它 >require 動態載入_Javascript 5種動態庫

require 動態載入_Javascript 5種動態庫

技術標籤:require 動態載入

要解決的問題

  • 複用其他人的工作
  • 減少 executable 的體積
  • 不共享原始碼的前提下複用

解決方案

exectuable不會攜帶所有的演算法實現。它會構建在executor提供的builtin api的基礎上。同時也會利用第三方的 dynamic library 依賴 executor 的 dynamic linker 動態連結進來使用。這個動態連結其他人的工作的機制在很多種機器中都有提供。

構成

  • executable:動態連結發起的源頭,executor第一個載入的東西
  • dynamic library:提供被複用的演算法,動態連結庫
  • dynamic library linker:一般是executor的一個元件,提供動態連結的能力。也可能是第三方利用executor的 api 獨立實現的linker。

解決方案案例

JavaScript 主流的動態連結庫有以下幾種,各自使用的linker是不同的

f5ce235a75ae202947edbac20201de38.png

傳統瀏覽器

  • executable:直接嵌入到 html 的 <script> 標籤的 JavaScript 程式碼
  • dynamic library:由 html 檔案的 <script src="xxx"> 引用的 .js 檔案
  • dynamic linker:瀏覽器自身,通過全域性變數實現彼此的互通。script標籤的順序決定了載入順序,後被載入的js可以引用前面js定義的全域性變數。
// http://localhost/library.js
function call_library() { 
  console.log('i am the library')
}
// http://localhost/index.html
<html>
<head>
<script src="./library.js"></script>
<script>
console.log('i am the executable')
call_library() // window.call_library()
</script>
</head>
<body>
</body>
</html>

executable 和 dynamic library 之間的通過全域性變數 window 上的全域性變數實現互相呼叫。 call_library 這個函式其實就是定義在 window 上的變數。 用瀏覽器訪問 http://localhost/index.html 在瀏覽器的控制檯輸出

i am the executable
i am the library

JavaScript 程式碼內沒有直接的動態載入的支援,用 script 標籤載入的 url 無法動態計算出來。一個hack的方法是通過 DOM API 建立 script 標籤。

// http://localhost/library.js
console.log('i am the library')
// http://localhost/index.html
<html>
<head>
<script>
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.type= 'text/javascript';
script.src= './library.js';
head.appendChild(script);
</script>
</head>
<body>
</body>
</html>

CJS 的代表 nodejs

  • executable:直接用引數傳遞給 node 命令的 js 檔案
  • dynamic library:js檔案 或者 package.json 定義的 js package
  • dynamic linker:用 node 提供的全域性 require api 載入其他的 js 檔案

用 js 檔案做為動態連結庫

// /opt/executable.js
console.log('i am the executable') 
require('./library.js')
// /opt/library.js
console.log('i am the library')
node /opt/executable.js
// Output:
// i am the executable
// i am the library

用包含 package.json 的目錄做為動態連結庫

目錄結構如下

  • executable.js
  • node_modules
    • library
      • package.json
      • library.js
// /opt/executable.js
console.log('i am the executable') 
require('library')
// /opt/node_modules/library/package.json
{ 
  "main": "library.js"
}
// /opt/node_modules/library/library.js
console.log('i am the library')
node /opt/executable.js
// Output:
// i am the executable
// i am the library

AMD 的代表 require.js

require.js 是基於傳統瀏覽器之上,用js自身實現的一個動態連結庫的linker。

  • executable:<script data-main="exectuable.js" src="https://unpkg.com/[email protected]/require.js">
  • dynamic library:用 define(function(require, exports, module) {}) 包裝的 js 檔案
  • dynamic library linker:require.js

使用例子如下

// http://localhost/index.html
<html> 
<head>
<script data-main="executable.js" src="https://unpkg.com/[email protected]/require.js"></script>
</head>
<body>
</body>
</html>
// http://localhost/executable.js
define(function(require, exports, module) { 
  const lib = require('./library.js')
  console.log(lib.greeting)
})
// http://localhost/library.js
define(function(require, exports, module) {
  exports.greeting = 'hello'
})

用瀏覽器訪問 http://localhost/index.html,在瀏覽器的控制檯裡輸出

hello

在傳統瀏覽器裡實現了 nodejs 的 require/exports 的語法。底層使用的還是動態生成 script 標籤的方式。

支援 ES6 Module 的瀏覽器

支援 ES6 Module 的瀏覽器,Chrome 從61起

  • exectuable:直接嵌入到 html 的 <script type="module"> 標籤的 JavaScript 程式碼
  • dynamic library:用 url 可以訪問到的 javascript 檔案
  • dynamic library linker:瀏覽器根據 JavaScript 頭部宣告的 import {yyy} from './xxx.js',動態載入相對當前 url 的 xxx.js

和傳統瀏覽器不同,import 引用的 js 會被瀏覽器載入,無需用 script 標籤 src 引用進來。

// http://localhost/library.js
console.log('i am the library')
// http://localhost/index.html
<html>
<head>
<script type="module">
import './library.js'
</script>
</head>
<body>
</body>
</html>

用瀏覽器訪問 http://localhost/index.html 在瀏覽器的控制檯輸出

i am the library

支援 ES6 Module 的 nodejs

  • executable:直接用引數傳遞給 node 命令的 js 檔案
  • dynamic library:mjs檔案 或者 package.json 定義的 js package
  • dynamic library linker:用 ES6 Module 的 import 語法呼叫 nodejs 的 require 機制
// /opt/executable.mjs
import './library.mjs' 
console.log('i am the executable')
// /opt/library.mjs
console.log('i am the library')
node --experimental-modules executable.mjs
// Output:
// i am the executable
// i am the library

nodejs 使用 ES6 Module 的語法進行動態連結需要兩個條件

  • 檔名字尾從 .js 變成 .mjs
  • 新增 --experimental-modules 的命令列引數

如果 import 的包不是相對路徑,也是從 node_modules 裡查詢 package.json

實現 System.register 的 system.js

基於 CJS 或者 AMD 都無法模擬實現 ES6 動態連結庫的全部特性。System.register 是另外一種定義動態連結庫的格式,它基於 ES5 的語法實現,可以完全模擬 ES6 Module 提供的特性。

system.js 實現了 System.register 這種格式,執行在支援 Promise 的瀏覽器裡。

  • executable:瀏覽器html內嵌的script標籤
  • dynamic library:由 System.register 定義
  • dynamic library linker:s.js (system.js 的核心部分)

例如

// http://localhost/index.html
<html> 
<head>
<script src="https://unpkg.com/[email protected]/dist/s.js"></script>
<script>
(async () => {
  let lib = await System.import('./library.js')
  console.log(lib.greeting) 
})()
</script>
</head>
<body>
</body>
</html>
// http://localhost/library.js
System.register([], function($__export, $__moduleContext) { 
  let greeting = 'hello'
  $__export({
    greeting: greeting
  });
  return {
    // setters: [], // (setters can be optional when empty) 
    execute: function() {
    }
  };
});

用瀏覽器訪問 http://localhost/index.html,會在瀏覽器控制檯列印

hello