首页 > 其他分享 >模块化开发之 ES Modules

模块化开发之 ES Modules

时间:2022-08-23 14:24:55浏览次数:91  
标签:console name 模块化 Modules module js export import ES

ES Modules

ES Modules 特性

  • 自动采用严格模式,忽略 'use strict'
  • 每个 ESM 模块都是单独的私有作用域
  • ESM 是通过 CORS 去请求外部 JS 模块的
  • ESM 的 script 标签会延迟执行脚本
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ES Module - 模块的特性</title>
</head>
<body>
  <!-- 通过 script 添加 type = module 的属性,就可以以 ES Module 的标准执行其中的 JS 代码 -->
  <script type="module">
    console.log('this is es module')
  </script>

  <!-- 1. ESM 自动采用严格模式,忽略 'use strict' -->
  <!-- script[type=module] -->
  <script type="module">
    console.log(this) // undefined
  </script>

  <script>
    console.log(this) // window
  </script>

  <!-- 2. 每个 ES Module 都是运行在单独的私有作用域中 -->
  <script type="module">
    var foo = 100
    console.log(foo)
  </script>

  <script type="module">
    console.log(foo) // foo is not defined
  </script>

  <!-- 3. ESM 是通过  CORS  的方式请求外部 JS 模块的 -->
  <script type="module" src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
  <!-- Access to script at 'https://libs.baidu.com/jquery/2.0.0/jquery.min.js' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. -->
  <!-- 支持 CORS  -->
  <script type="module" src="https://unpkg.com/[email protected]/dist/jquery.min.js"></script>

  <!-- 4. ESM 的 script 标签会延迟执行脚本,相当于 defer -->
  <script type="module" src="demo.js"></script>
  <p>需要显示的类容</p>
</body>
</html>

ES Modules 导出

// ./module.js
const foo = 'es modules'
export { foo }
// ./app.js
import { foo } from './module.js'
console.log(foo) // => es modules

export default

  • export default 向外暴露的成员,可以使用任意的变量名来接收
  • 在一个模块中,export default 只允许向外暴露1次
  • 在一个模块中,可以同时使用 export default 和 export 向外暴露成员

导出:

var info = {
  name: 'zs',
  age: 20
}
export default info

导入:

aaa 可以任意命名,可以在控制台输出 info 对象

import aaa,  from './module.js'
console.log(aaa)

export var name = ‘**’

  • 使用 export 向外暴露的成员,只能使用 { } 的形式来接收
  • 使用 export 导出的成员,导入时,必须严格按照导出时候的名称使用 {} 按需接收
  • 使用 export 导出的成员,如果就想换个名称来接收,可以使用 as 来起别名

导出的第一种写法

导出:

export var name = 'foo module'

export function hello() {
  console.log('hello')
}

export class Person {}

导入:

import { name, hello, Person } from './module.js'

导出的第二种写法(推荐)

能看直观描述该模块向外提供了哪些成员

var name = 'foo module'

function hello() {
  console.log('hello')
}

class Person {}

export { name, hello, Person }

导出重命名

导出:

export {
  name as default,
  hello as fooHello
}

导入:

// default 是 js 的关键词,需重命名
import { fooName as fooName, fooHello } from './module.js'

导出注意事项

  • export {} 为固定语法,并不是对象字面量,并非导出了一个对象

    var name = 'robert'
    var age = 18
    
    var obj = {
      name,
      age
    }
    
    export {
      name,
      age
    }
    
    // 导出一个对象的写法
    export default { name, age }
    
  • 导出的并非值,而是导出值所存放的地址,外部引入的值,会受到导出模块内部修改的影响

    var name = 'robert'
    var age = 18
    
    setTimeout(() => {
      name = 'robert1'
    }, 1000);
    
    export { name, age }
    
  • import {} 并非解构,而是固定的语法

    import { name, age } from './module.js'
    console.log(name, age)
    
  • 导入的成员是只读的,并不能修改,可利用该特性,解决应用中常量的问题,例如全局的配置文件

    name = 'tom' // app.js:20 Uncaught TypeError: Assignment to constant variable.
    
    setTimeout(() => {
      console.log(name, age) // robert1 18
    }, 1500);
    

导入注意事项

// ./module.js
var name = 'robert'
var age = 18

console.log('module action')

// 同时导出命名成员和默认成员

export {
  name,
  age
}

export default 'default export'
  • 导入的路径,必须使用完整的文件名称,不能省略 .js

    // bad
    import { name } from './module'
    console.log(name) // GET http://localhost:3000/04-import/module net::ERR_ABORTED 404 (Not Found)
    
    // good
    import { name } from './module.js'
    
  • import index.js , 不可省略 index.js 文件 ,不同于 common.js 可省略 index.js

    // bad
    import { lowercase } from './utils' 
    console.log(lowercase('HHH')) // GET http://localhost:3000/04-import/utils/ net::ERR_ABORTED 404 (Not Found)
    
    // good
    import { lowercase } from './utils/index.js'
    
  • 引用相对路径的文件,./ 不可省略,因为省略 ./ 后,文件则以字母开头,那么 ESM 会以为是引用第三方库

    // bad
    import { name } from 'module.js'
    console.log(name) // Failed to resolve module specifier "module.js". Relative references must start with either "/", "./", or "../".
    
    // good
    import { name } from './module.js'
    
  • 引用文件也可以使用 / 开头的绝对路径,表示从网站根目录下开始寻找

    import { name } from '/04-import/module.js'
    
  • 引用文件也可以使用完整的 url ,意味着我们可以直接引用 cdn 上的一些模块文件

    import { name } from 'http://localhost:3000/04-import/module.js'
    
  • 如只想执行某个模块,而不需要提取模块中的某个成员,那么保持 import 后的 {} 为空即可

    import { } from './module.js'
    // 以上简写语法
    import './module.js'
    
  • 使用 * 号提取出导出模块中的所有成员

    import * as mod from './module.js'
    console.log(mod) // Module {Symbol(Symbol.toStringTag): 'Module'}
    
  • 动态导入

    // 不能 import from 一个变量
    var modulePath = './module.js'
    import { name } from modulePath
    console.log(name) // Unexpected identifier
    
    // import 关键词只能出现在最顶层
    if (true) {
      import { name } from './module.js' // 导入声明只能在模块的顶层使用
    }
    
    import('./module.js').then(function(module) {
      console.log(module)
    })
    
  • 同时导入命名成员和默认成员

    import { name, age, default as title } from './module.js'
    // 以上简写,左侧导入默认成员命名可任意取
    import title, { name, age  } from './module.js'
    console.log(name, age, title)
    

导出导入成员

一般在 index 文件使用较多,将某个散落的模块组织在一起将其导出给外部一起使用

// ./components/avatar.js
export var Avatar = 'Avatar Components'

// ./components/button.js
export var Button = 'Button components'

// ./components/index.js
import { Button } from './button.js'
import { Avatar } from './avatar.js'

export {
  Button,
  Avatar
}

// 以上可简写成如下
export { Button } from './button.js'
export { Avatar } from './avatar.js'

导入:

// import { Button } from './components/button.js'
// import { Avatar } from './components/avatar.js'

// console.log(Button)
// console.log(Avatar)

import { Button, Avatar } from './components/index.js'

console.log(Button)
console.log(Avatar)

浏览器环境 Polyfill

ES Module 是2014年才被提出来的,意味着早起的浏览器不支持该特性,如 IE 和某些国产浏览器,那么就需要使用 Polyfill 让浏览器直接支持 ES Module 特性。

image

browser-es-module-loader

通过 unpkg 查找对应的 npm 包的 cdn 服务链接

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ES Module 浏览器环境 Polyfill</title>
  <!-- 支持 module 的浏览器,设定上就不会执行 nomodule 属性的 script 脚本,所以它只会跑上方的 app.js 脚本 -->
  <!-- 而老破旧的浏览器不支持 type="module",会跳过这个 script 标签;同时又由于它不认识 nomodule 属性,反倒会执行 nomodule script 里的 classic-bundle.js 文件了  -->
  <script nomodule src="https://unpkg.com/[email protected]/dist/polyfill.min.js"></script>
  <script nomodule src="https://unpkg.com/[email protected]/dist/babel-browser-build.js"></script>
  <script nomodule src="https://unpkg.com/[email protected]/dist/browser-es-module-loader.js"></script>
</head>
<body>
  <!-- https://unpkg.com/[email protected]/dist/ -->
  <!-- unpkg 简介: https://www.jianshu.com/p/671c00b5c65d -->
  <script type="module">
    import { foo } from './module.js'
    console.log(foo)
  </script>
</body>
</html>
var foo = 'foo'

export {
  foo
}

附录

Browser-sync 安装与使用

Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。

官网文档

安装 BrowserSync

npm install -g browser-sync

启动 BrowserSync

browser-sync start --server --files "*" / browser-sync . --files **/*.js

更多命令行用法查看官网

标签:console,name,模块化,Modules,module,js,export,import,ES
From: https://www.cnblogs.com/dwyWeb/p/16610099.html

相关文章