原生ES-Module在瀏覽器中的嘗試

着實瀏覽器原生模塊相干的支撐也已出了一兩年了(我第一次曉得這個事變着實2016年下半年的時刻)

可以拋開
webpack直接運用
import之類的語法

但由於算是一個比較新的東西,所以如今基礎只能本身鬧着玩 :p

但這並不能成為不去相識它的託言,照樣要體驗一下的。

首先是各大瀏覽器從什麼時候最先支撐module的:

  • Safari 10.1
  • Chrome 61
  • Firefox 54 (有可以須要你在about:config頁面設置啟用dom.moduleScripts.enabled)
  • Edge 16

數據來自
https://jakearchibald.com/2017/es-modules-in-browsers/

運用體式格局

首先在運用上,唯一的區分就是須要在script標籤上增加一個type="module"的屬性來示意這個文件是作為module的體式格局來運轉的。

<script type="module">
  import message from './message.js'

  console.log(message) // hello world
</script>

然後在對應的module文件中就是常常會在webpack中用到的那樣。
語法上並沒有什麼區分(原本webpack也就是為了讓你提早用上新的語法:)

message.js

export default 'hello world'

文雅降級

這裡有一個相似於noscript標籤的存在。
可以在script標籤上增加nomodule屬性來完成一個回退計劃。

<script type="module">
  import module from './module.js'
</script>
<script nomodule>
  alert('your browsers can not supports es modules! please upgrade it.')
</script>

nomodule的處置懲罰計劃是如許的:

支撐
type="module"的瀏覽器會疏忽包括
nomodule屬性的
script劇本實行。

而不支撐
type="module"的瀏覽器則會疏忽
type="module"劇本的實行。

這是由於瀏覽器默許只剖析
type="text/javascript"的劇本,而假如不填寫
type屬性則默許為
text/javascript

也就是說在瀏覽器不支撐
module的情況下,
nomodule對應的劇本文件就會被實行。

一些要注意的細節

但畢竟是瀏覽器原生供應的,在運用方法上與webpack的版本肯定照樣會有一些區分的。
(最少一個是運轉時剖析的、一個是當地編譯)

有用的module途徑定義

由於是在瀏覽器端的完成,不會像在node中,有全局module一說(全局對象都在window里了)。
所以說,from 'XXX'這個途徑的定義會與之前你所熟習的輕微有些相差。

// 被支撐的幾種途徑寫法

import module from 'http://XXX/module.js'
import module from '/XXX/module.js'
import module from './XXX/module.js'
import module from '../XXX/module.js'

// 不被支撐的寫法
import module from 'XXX'
import module from 'XXX/module.js'

webpack打包的文件中,援用全局包是經由過程import module from 'XXX'來完成的。
這個現實是一個簡寫,webpack會依據這個途徑去node_modules中找到對應的module並引入進來。
然則原生支撐的module是不存在node_modules一說的。
所以,在運用原生module的時刻肯定要牢記,from後邊的途徑肯定如果一個有用的URL,以及肯定不能省略文件後綴(是的,即使是遠端文件也是可以運用的,而不像webpack須要將當地文件打包到一同)。

module的文件默許為defer

這是script的另一個屬性,用來將文件標識為不會壅塞頁面襯着的文件,而且會在頁面加載完成后根據文檔的遞次舉行實行。

<script type="module" src="./defer/module.js"></script>
<script src="./defer/simple.js"></script>
<script defer src="./defer/defer.js"></script>

為了測試上邊的看法,在頁面中引入了如許三個JS文件,三個文件都邑輸出一個字符串,在Console面板上看到的遞次是如許的:

《原生ES-Module在瀏覽器中的嘗試》

行內script也會默許增加defer特徵

由於在一般的劇本中,defer關鍵字是只指針對劇本文件的,假如是inline-script,增加屬性是不見效的。
然則在type="module"的情況下,不管是文件照樣行內劇本,都邑具有defer的特徵。

可以對module範例的劇本增加async屬性

async可以作用於一切的module範例的劇本,無論是行內照樣文件情勢的。
然則增加了async關鍵字今後並不意味着瀏覽器在剖析到這個劇本文件時就會實行,而是會比及這段劇本所依靠的一切module加載終了后再實行。
import的商定,必須在一段代碼內的肇端位置舉行聲明,且不可以在函數內部舉行

也就是說下邊的log輸出遞次完全取決於module.js加載的時長。

<script async type="module" >
  import * from './module.js'
  console.log('module')
</script>
<script async src="./defer/async.js"></script>

一個module只會加載一次

這個module是不是唯一的定義是資本對應的完全途徑是不是一致。
假如當前頁面途徑為https://www.baidu.com/a/b/c.html,則文件中的/module.js../../module.jshttps://www.baidu.com/module.js都邑被以為是同一個module
然則像這個例子中的module1.jsmodule1.js?a=1就被認定為兩個module,所以這個代碼實行的結果就是會加載兩次module1.js

<script type="module" src="https://blog.jiasm.org/module-usage/example/modules/module1.js"></script>
<script type="module" src="/examples/modules/module1.js"></script>
<script type="module" src="./modules/module1.js"></script>
<script type="module" src="./modules/module1.js?a=1"></script>
<script type="module">
  import * as module1 from './modules/module1.js'
</script>

在線Demo

import和export在運用的一些小提示

不管是瀏覽器原生供應的版本,亦或許webpack打包的版本。
importexport基礎上照樣共通的,語法上基礎沒有什麼差異。

下邊列出了一些可以會幫到你更好的去運用modules的一些技能。

export的重命名

在導出某些模塊時,也是可以像import時運用as關鍵字來重命名你要導出的某個值。

// info.js
let name = 'Niko'
let age = 18

export {
  name as firstName,
  age
}

// import
import {firstName, age} from './info.js'

Tips: export的挪用不像node中的module.exports = {}
可以舉行屢次挪用,而且不會掩蓋(key重名除外)。

export { name as firstName }
export { age }

如許的寫法兩個key都邑被導出。

export導出的屬性均為可讀的

也就是說export導出的屬性是不可以修正的,假如試圖修正則會獲得一個非常。
然則,相似const的結果,假如某一個導出的值是援用範例的,對象或許數組之類的。
你可以操縱該對象的一些屬性,比方對數組舉行push之類的操縱。

export {
  firstName: 'Niko',
  packs: [1, 2]
}
import * as results from './export-editable.js'

results.firstName = 'Bellic' // error

results.packs.push(3)        // success

如許的修正會致使其他援用該模塊都邑受到影響,由於運用的是一個地點。

export在代碼中的遞次並不影響終究導出的結果

export const name = 'Niko'
export let age = 18

age = 20

const 或許 let 關於 挪用方來講沒有任何區分

import {name, age} from './module'

console.log(name, age) // Niko 20

import獵取default模塊的幾種姿態

獵取default有以下幾種體式格局都可以完成:

import defaultItem from './import/module.js'
import { default as defaultItem2 } from './import/module.js'
import _, { default as defaultItem3 } from './import/module.js'

console.log(defaultItem === defaultItem2) // true
console.log(defaultItem === defaultItem3) // true

默許的規則是第一個為default對應的別號,但假如第一個參數是一個解構的話,就會被剖析為針對一切導進項的一個匹配了。
P.S. 同時存在兩個參數示意第一個為default,第二個為悉數模塊

導出悉數的語法以下:

import * as allThings from './iport/module.js'

相似index的export文件編寫

假如你碰到了相似如許的需求,在某些處所會用到十個module,假如每次都import十個,肯定是一種糟蹋,視覺上也會給人一個不好的覺得。
所以你可以會寫一個相似index.js的文件,在這個文件中將其引入到一塊,然後運用時import index即可。
一般來講可以會這麼寫:

import module1 from './module1.js'
import module2 from './module2.js'

export default {
  module1,
  module2
}

將一切的module引入,並導出為一個Object,如許確着實運用時已很方便了。
然則這個索引文件依然是很貌寢,所以可以用下面的語法來完成相似的功用:

export {default as module1} from './module1.js'
export {default as module2} from './module2.js'

然後在挪用時修正為以下花樣即可:

import * as modules from './index.js'

在線Demo

小記

想到了近來爆紅的deno,其中有一條特徵也是提到了,沒有node_modules,依靠的第三方庫直接經由過程收集要求的體式格局來獵取。
然後瀏覽器中原生供應的module也是相似的完成,都是朝着更天真的方向在走。
祝賀揚棄webpack來舉行開闢的那一天早日到來 :)

參考資料

  1. es modules in browsers
  2. es6 modules in depth
  3. export – JavaScript | MDN
  4. import – JavaScript | MDN

文中示例代碼的GitHub堆棧:傳送陣

    原文作者:賈順名
    原文地址: https://segmentfault.com/a/1190000015237377
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞