好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ES6+ 模块化(一)

ES6+ 模块化

1. 前言

JavaScript 在设计之初主要用来开发 Web 页面 的交互、动画和表单验证等单一的 功能 ,而且程序的体积很小,大多数都是独立执行的。随着前端的发展 JavaScript 承接的 功能 越来越多,Node 的出现让 JavaScript 可以作为一门后端开发语言,程序的复杂度瞬间提升,所以有必要提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。Node 是 JavaScript 的先行者,它使用了 Commo njs 的规范来实现模块化的。在 ES6 没出来之前有很多模块化的实践,比较有名的有:Commo njs 、AMD、CMD,每个规范都有自己独立的思考。

随着 ES6 模块的发布,AMD 和 CMD 慢慢地淡出了我们的视野。现在主要常见的场景是 Node 端还采用 Commo njs 规范,这是历史原因。前端使用的是 ES6 module 规范,但是不能直接在前端使用,需要通过打包工具进行编译如:Webpack、Babel、Rollup 等。本文中我们将使用 Webpack 进行模块化编译工具,源 代码 放在 GitHub 上,仅供参考。

2. 环境搭建

现在的高级浏览器还能完全地 支持 ES6 的模块化,如何在浏览器中运行 ES6 模块呢?有两种方式:

在浏览器中直接运行 ES6 的模块化,但是需 要做 一定的工作,不能像之前直接在本地浏览器中打开 一个 html 中引入 JS 文件 ; 使用 Webpack、rollup 等模块化打包工具,html 引入编译后的 js 文件 。

这两种方式各有优缺点,第一种是原生的使用方式,浏览器兼容要求比较高,第二种使用的是第三方打包编译工具可以很好地 解决 浏览器兼容问题,但是会有一定的学习成本,并且不能直接在浏览器中运行,只能使用编译后的 文件 。

2.1 浏览器运行原生 ES6 模块

使用浏览器运行原生 ES6 模块的源码在 ES6-wiki 的 mjs 文件 中,浏览器是不能直接运行 ES6 模块化的,需 要做 一些准备工作。

首先,在引入 js 文件 时需要定义 script 的类型: type="module" 。另外,js 文件 的后缀不能使用 .js 了,需要使用 .mjs 。这样还是不能在浏览器中运行,还需要最后一步。模块化会涉及到 文件 系统,而本地打开的 html 文件 是没有服务的,所以我们要使用 node 服务的方式打开 html 文件 ,这里我们使用 node 的包 http-server 可以在相应的 文件 目录中启动 node 服务。安装如下:

 npm install --global http-server
 

安装完启动服务的工具还是会有问题,依然打不开,这是需要在浏览器中打开一些配置:浏览器地址栏输入: chrome://flags/ 然后 搜索 JavaScript 把 Experimental JavaScript 项选择 Enabled 启用状态。如下图。


到这里我们就把前期的工作做完了,如何打开 html 文件 呢?在控制台中进入对应的目录中执行:http-server 命令。本节的目录在 ES6-wiki/packages/module/mjs 下。在浏览器打开控制台返回的地址即可,本实例的地址是: http://127.0.0.1:8080/index.html

2.2 使用 Webpack

Webpack 是模块化打包工具,它兼容现在很多模块化加载方式,本节课程也主要使用 Webpack 的方式来学习 ES6 的模块化。Webpack 需要一定的学习成本可以在 官网 上学习,这里就不进行介绍了,下面给出 webpack.con fig .js 的配置如下:

  let  path  =   require  (  "path"  )  ; 
 let  HtmlWebpackPlugin  =   require  (  "html-webpack-plugin"  )  ; 

module . exports  =   { 
  entry :   "./src/index.js"  , 
  output :   { 
    filename :   "bundle.js"  , 
   }  , 
  plugins :   [ 
     new   HtmlWebpackPlugin  (  { 
      template :   './public/index.html'   // 模版 文件  
     }  ) 
   ]  , 
  module :   { 
    rules :   [ 
       { 
        test :   /\.js$/  , 
        use :   { 
          loader :   "babel-loader"  , 
         }  , 
       }  , 
     ]  , 
   }  , 
 }  ; 
 

3. 基本使用

ES6 的模块化设计思想是尽量 静态化 ,使得编译时就能确定模块的依赖关系,只能在顶级作用域。模块系统中,每个 文件 都是 一个 模块,模块之间都是相互独立。在 ES6 模块中 自动 采用 严格模式 ,不知道的同学可以去看看,对于学习 JavaScript 语言有一定的帮助。

3.1 export/import

export 是导出语法,import 是导入语法。看下面的实例:

  // a.js 
 export   let  x  =   ; 
 export   let  y  =   ; 

 // main.js 
 import   { x ,  y }   from   './a.js'  ; 
console .  log  ( x ,  y ) 
 

上面 代码 中,a.js 文件 中使用 export 导出 x 和 y 两个变量,在 mian.js 文件 中使用 import 进行导入。a.js 中还可以使用对象的方式导出:

  let  a  =   ; 
 let  b  =   ; 
 export   { 
	a , 
  b , 
 } 
 

从上面的 main.js 文件 中可以看出,export 使用的是引用方式进行导出的,导出的是 一个 接口,所以不能直接导出 一个 值。我们如下实例:

  let  a  =   ; 
 export  a ; 	 // 编译报错 
 // 正确的方式如下 
Export  let  a  =   ; 
 

虽然使用 export 不能直接导出 一个 值,但是可以使用 export default 导出 一个 特定的值:

  export   default   ; 
 

export 模块导出的是 一个 接口,在模块内如果数据更新,则所依赖的地方的值都是最新的。

  // a.js 
 let  a  =   ; 
 setInterval  (  (  )   =>   { 
  a ++ 
 }  ) 
 export   { 
	a
 } 

 // main.js 
 import   {  a  }   from   './a.js'  ; 
 setInterval  (  (  )   =>   { 
  consolog .  log  ( a ) 
 }  ) 
 

import 有声明的特点,类似 var 的特点,可以实现变量提升,但是不能 修改 变量对应的值。

  // main.js 
console .  log  ( a ) 
 import   {  a  }   from   './a.js'  ; 
a  =   ; 	 // 这样赋值是 错误 的 
 

使用 export + from 命令的方式,提供了一种便捷的方式在当前的模块导出其他模块的 内容 ,不能在当前模块下使用导出的变量。

  // b.js 
 let  a  =   ; 
 let  b  =   ; 
 export   { 
	a , 
  b , 
 } 

 // a.js 
 export   { a , b }   from   './b.js'  ; 
 export   c   =   (  )   =>   {  } 
 // 等价于使用import 先导入,然后再使用 export 导出 
 import   {  a ,  b  }   from   './b'  ; 
 export   { 
	a , 
  b , 
 } 

 // main.js 
 import   { a ,  b ,  c }   from   './a.js' 
 

export 和 import 命令规定要处于模块顶层,一旦出现在块级作用域内,就会报错。

  // a.js 
 { 
	 export   let  a  =   ; 
 } 

 // main.js 
 { 
   import   {  a  }   from   './a'  ; 
 } 
 //控制台答应 错误  内容 : 'import' and 'export' may only appear at the top level 
 

上面的 代码 中 export 和 import 都放在块级作用域中的,执行时会报错,提升你 export 和 import 只能在顶级出现。

3.2 export default 命令

export default 命令用来导出模块中 默 认变量或 方法 ,上面我们也提到了使用 export 导出的是 一个 对象不能导出 一个 值类型。

  // a.js 
 export   default   'imooc'  ; 
 // main.js 
 import  name  from   './a.js' 
console .  log  ( name )  ; 	 // imooc 
 

export default 命令声明的 函数 可以是匿名的。

  export   default   function   (  )   { 
  console .  log  (  'imooc'  )  ; 
 } 
 // 等价 
 function   fn  (  )   { 
  console .  log  (  'imooc'  )  ; 
 } 
 export   default  fn ; 
 

也可以是 一个 类:

  // a.js 
 export   default   class   { 
   constructor  (  )   { 
    console .  log  (  'imooc'  ) 
   } 
   // ... 
 } 

 // main.js 
 import  A  from   './a'  ; 
 const  a  =   new   A  (  )  ; 
console .  log  ( a ) 
 

开可以导出的是 一个 对象:

  const  obj  =   { 
  name :   'imooc'  , 
  getLession :   function  (  )   { 
		console .  log  (  'ES6 imooc'  )  ;  
   } 
 } 
 export   default  obj ; 
 

3.3 as 命令

as 命令是用来 重命名 的,在使用 import 命令导入时可以使用 as 来更改变量名。

  // a.js 
 let  a  =   ; 
 let  b  =   ; 

 export   { 
	a , 
  b , 
 } 

 // main.js 
 import   {  a ,  b  as  y  }   from   './a'  ; 
console .  log  ( a ,  y )  ; 	 // 1,2 
 

如果模块中同时有 export default 导出和 export 导出时,在导入时可以使用 as 对 默 认导出进行 重命名 。

  // a.js 
 let  a  =   ; 
 let  b  =   ; 

 export   { 
	a , 
  b , 
 } 

 export   default   let  c  =   ; 


 // main.js 
 import   {  a ,  b ,   default   as  c  }   from   './a' 
 // 等价于下面直接在外面进行使用 
 import  c ,   {  a ,  b  }   from   './a' 
 

默 认导出的 内容 也可以放在 export 导出的对象中,但是需要使用 as default 命令:

  // a.js 
 let  a  =   ; 
 let  b  =   ; 
 let  c  =   'imooc'  ; 

 export   { 
	a , 
  b , 
  c  as   default  , 	 // 相当于 export default 'imooc',给导出的对象 增加  一个 default 属性  
 } 
 

4. 小结

本节主要讲解了 ES6 Module 的使用,通过对 export、import、default、as 命令的讲解学习了 ES6 Module 的基本 用法 ,基本上涵盖了日常使用的场景。

查看更多关于ES6+ 模块化(一)的详细内容...

  阅读:43次

上一篇

下一篇

第1节:ES6+ 简介    第2节:ES6 环境配置    第3节:ES6+ let    第4节:ES6+ const    第5节:ES6+ 展开语法    第6节:ES6+ 剩余参数    第7节:ES6+ 解构赋值    第8节:ES6+ 模版字符串    第9节:ES6+ 箭头函数    第10节:ES6+ 数值扩展    第11节:ES6+ isFinite()&isNaN()    第12节:ES6+ Number 对象的方法    第13节:ES6+ Math 对象的扩展    第14节:ES6+ includes()    第15节:ES6+ 字符串的扩展    第16节:ES6+ startsWith()    第17节:ES6+ endsWith()    第18节:ES6+ repeat()    第19节:ES6+ padStart()    第20节:ES6+ padEnd()    第21节:ES6+ trim()    第22节:ES6+ Array.from()    第23节:ES6+ of()    第24节:ES6+ find()和findIndex()    第25节:ES6+ copyWithin()    第26节:ES6+ fill()    第27节:ES6+ isArray()    第28节:ES6+ 对象的扩展    第29节:ES6+ flat()    第30节:ES6+ 可选链操作符    第31节:ES6+ Object.is()    第32节:ES6+ Object.assign()    第33节:ES6+ Object.keys()    第34节:ES6+ Object.values()    第35节:ES6+ Object.entries()    第36节:ES6+ 数据结构扩展    第37节:ES6+ Set    第38节:ES6+ WeakSet    第39节:ES6+ Map    第40节:ES6+ WeakMap    第41节:ES6+ Symbol    第42节:ES6+ for...of    第43节:ES6+ 迭代协议    第44节:ES6+ 实现一个简版的 Promise    第45节:ES6+ Promise 基础    第46节:ES6+ Promise 进阶    第47节:ES6+ Generator 基础    第48节:ES6+ Generator 函数应用    第49节:ES6+ async/await    第50节:ES6+ Class 前置知识    第51节:ES6+ Class    第52节:ES6+ Proxy    第53节:ES6+ Reflect(一)    第54节:ES6+ Reflect(二)    第55节:ES6+ 模块化(一)    第56节:ES6+ 模块化(二)    第57节:ES6实战1-实现Vue3 reactive 源码    第58节:ES6实战2-实现 Vue3 effect 源码    第59节:ES6 实战2-封装请求    第60节:ES6+ 实战3-代码整洁之道    第61节:ES6 Map原理分析    第62节:ES6module语法加载importexport    第63节:ES6的循环与可迭代对象示例详解