深入浅出浏览器中的ES modules

5,845 阅读2分钟

    主流浏览器陆续都已经开始支持es modules,此外一些Bundless的工具,比如snowpack、vite等。此外给予浏览器的ESM也可以实现一些前端微服务,本文主要介绍一下什么是浏览器中的ESM

  • import & export
  • import.map
  • import.meta
  • 浏览器中esm的兼容问题

一、import & export

    首先我们来看主流浏览器对于ES modules的支持情况:

Lark20201119-151747

    从上图可以看出来,主流的Edge, Chrome, Safari, and Firefox (+60)等浏览器都已经开始支持es modules。

    对于es modules,我们并不陌生,什么是es modules也不是本文的重点,一些流行的打包构建工具比如babel、webpack等早就支持es modules。

    我们来看一个最简单的es modules的写法:

//main.js
import a from 'a.js'
console.log(a)

//a.js
export let  a = 1

    上述的es modules就是我们经常在项目中使用的es modules,这种es modules,在支持es6的浏览器中是可以直接使用的。

    我们来举一个例子,直接在浏览器中使用es modules

<html  lang="en">
    <body>
        <div id="container">my name is {name}</div>
        <script type="module">
           import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js'
           new Vue({
             el: '#container',
             data:{
                name: 'Bob'
             }
           })
        </script>
    </body>
</html>

上述的代码中我们直接可以运行,我们根据script的type="module"可以判断浏览器支不支持es modules,如果不支持,该script里面的内容就不会运行。

    我们再来看一种调用方式,我们也可以通过script标签来直接引入相对路径或者绝对路径的模块。

//html
<script type="module" src="/index.js"></script>

//index.js
 import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js'
 new Vue({
    el: '#container',
    data:{
    name: 'Bob'
    }
 })

二、import.map

    上述的调用中我们通过绝对路径或者相对路径的形式来import,这种方式跟我们在之前的代码中有点区别,我们希望的是通过:

import Vue from 'vue'   

这种简单的方式来引入一个npm包,就像我们在之前bundle时书写格式一致,那么浏览器中如何支持这种写法呢,答案就是使哟过import.map。import.map的提案可以参考:github.com/WICG/import…

    我们直接来看例子:

//html
<script type="importmap">
  {
    "imports":{
      "vue":"https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js"
    }
  }
</script>
<script type="module" src="/index.js"></script>

//index.js
 import Vue from 'vue'
 new Vue({
    el: '#container',
    data:{
    name: 'Bob'
    }
 })

通过importmap,我们可以直接在业务代码中使用:

import Vue from  'vue'

三、import.meta

    在浏览器中我们有全局上下文window,在node中我们有全局上下文global,同理可以引入一个对象作为某一个module的上下文元数据。该对象就是import.meta对象,这个对象在模块中用于存取上下文数据,可读也可以写。

(1)import.meta.url

import.meta.url返回当前模块的 URL 路径。

 
 <script type="module" src="/index.js"></script>
 
 
 //index.js
 
 console.log(import.meta.url) //输出http://localhost:8080/index.js

(2) import.meta.scriptElement

    import.meta.scriptElement返回是浏览器特有的元属性,返回加载模块的那个

    此外值得注意的是import.meta不仅仅是可读的,也是可写的,整体作为模块的元数据,比如在snowpack中会往import.meta中写入以下数据:

Lark20201119161252

四、浏览器中esm的兼容问题

    对于不支持es modules或者部分支持es modules的浏览器,我们可以通过systemjs来兼容。systemjs就是对于浏览器es modules的polyfill。

    直接来看通过systemjs来实现es modules的使用方法:


//html
<script type="systemjs-importmap">
  {
    "imports":{
      "vue":"https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js"
    }
  }
</script>
<script type="module" src="/index.js"></script>

//index.js
System.register(['vue'], ()=>{
    let Vue
    return {
        setters: [ v=> Vue]
    }
 
});
new Vue({
    el: '#container',
    data:{
    name: 'Bob'
    }
 })