ES6以前,JS没有出现模块化系统。html
JS主要在前端的浏览器中使用,js文件下载缓存到客户端,在浏览器中执行。前端
好比简单的表单本地验证,漂浮一个广告。node
服务器端使用ASP、JSP等动态网页技术,将动态生成数据嵌入一个HTML模板,里面夹杂着JS后react
使用 <script> 标签,返回浏览器端。es6
这时候的JS只是一些简单函数和语句的组合。npm
<html> <head> <title>magedu</title> <script src="./src/moda.js"></script> </head> <body> Welcome <script> foo() console.log(CONSTA) </script> </body> </html>
在同一目录下创建src文件夹,文件夹下创建moda.js文件编程
class A{
constructor(x){
this.x = x
}
show () {
console.log(this.x)
}
}
function foo() {
console.log("foo function");
}
const CONSTA = "aaa"
用浏览器打开html文件,在控制台就能够看到,浏览器下按F12.json
2005年以后,随着Google大量使用了AJAX技术以后,能够异步请求服务器端数据,带来了前端交互的巨大变化。浏览器
前端功能需求愈来愈多,代码也越来也多。随着js文件的增多,灾难性的后果产生了。因为习惯了随便写,js脚本中各类全局变量污染,函数名冲突,没法表达脚本之间的依赖关系,由于都是用脚本文件前后加载来实现的。亟待模块化的出现。缓存
2008年V8引擎发布,2009年诞生了Nodejs,支持服务端JS编程,但没有模块化是不能够的。
以后产生了commonjs规范。
commonjs规范,使用全局require函数导入模块,使用exports导出变量。
为了将这种模块化规范向前端开发迁移,又演化出其它的规范。例如AMD。
AMD(Asynchronous Module Definition)异步模块定义,使用异步方式加载模块,模块的加载不影响它后面语句的执行。全部依赖这个模块的语句,都须要定义在一个回调函数,回调函数中使用模块的变量和函数,等模块加载完成以后,这个回调函数才会执行,就能够安全的使用模块的资源了。其实现就是AMD/RequireJs。AMD虽然是异步,可是会预先加载和执行。
CMD(Common Module Definition),使用seajs,做者是淘宝前端玉伯,兼容并包解决了RequireJs的问题。
CMD推崇as lazy as possible,尽量的懒加载。
因为社区的模块化呼声很高,ES6开始提供支持模块的语法,可是浏览器目前支持还不够。
import语句,导入另外一个模块导出的绑定。
export语句,从模块中导出函数、对象、值的,供其它模块import导入用。
创建一个模块目录src,而后在这个目录下新建一个moda.js,内容以下:
export class A{ constructor(x){ this.x = x } show () { console.log(this.x) } } export function foo() { console.log("foo function"); } export const CONSTA = "aaa"
import {A,foo,CONSTA} from "./src/moda.js" foo() a = new A(23) console.log()
VS Code能够很好的语法支持了,可是运行环境,包括V8引擎,都不能很好的支持模块化语法。
工具链,使用JS工具。
转译就是从一种语言代码转换到另外一个语言代码,固然也能够从高版本转译到低版本的支持语句。
因为JS存在不一样版本,不一样浏览器兼容的问题,使用transpiler转译工具解决
开发中可使用较新的ES6语法,经过转译器转换为指定的某些版本代码。
参考文档 https://babeljs.io/docs/en/6.26.3/index.html
注意当前版本7.x已经有了较大的变化,请参看6.x文档
能够测试迭代器的实现,转换完的内容不须要关心。
function * counter(){ let i = 0; while(true) yield (++i); } g = counter(); console.log(g.next().value);
有以下一些预设presets,咱们先看看有哪些,一下子再进行预设的安装和配置
presets:
babel-preset-env 当前环境支持的代码,新target,所有包括es6,es2017,es测试版本等,只写这一个就能够了
react转码规则
$ npm install --save-dev babel-preset-react
ES2015转码规则
$ npm install --save-dev babel-preset-es2015
ES7不一样阶段语法提案的转码规则(共有4个阶段),选装一个
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3
在项目目录中使用
$ npm init
一直回车,会在根目录下生成一个新文件package.json,内容就是以前回车提示输入的内容
.npmrc文件
运行环境文件
能够放到npm的目录下npmrc文件中,全局影响
能够放到用户家目录中,影响用户
能够放到项目根目录中,影响项目
本次放到项目根目录中,内容以下
由于国外速度较慢,使用淘宝的
registry=https://registry.npm.taobao.org
$ echo "registry=https://registry.npm.taobao.org" > .npmrc
项目根目录下执行
$ npm install babel-core babel-cli --save-dev
--save-dev说明
当你为你的模块安装一个依赖模块时,正常状况下你得先安装他们(在模块根目录下npm install module-name),而后连同版本号手动将他们添加到模块配置文件package.json中的依赖里(dependencies)。开发用。
--save和--save-dev能够省掉你手动修改package.json文件的步骤。
spm install module-name --save 自动把模块和版本号添加到dependencies部分
spm install module-name --save-dev 自动把模块和版本号添加到devdependencies部分
package.json 里边的 devDependencies ,后边的是开发用的依赖
开发依赖前边去掉dev就是运行依赖,在打包时不要打包开发依赖
安装完后,在项目根目录下出现 node_modules目录 ,里面有babel相关模块及依赖的模块。
替换 scripts 的部分 "test": "echo \"Error: no test specified\" && exit 1" 替换为 "build": "babel src -d lib"
babel src -d lib 意思是从src目录中转译后的文件输出到lib目录
项目根目录下创建src和lib目录。
src 是源码目录;
lib 是目标目录。
在目录根目录下建立 .babelrc 文件,Json格式。
{ "presets": ["env"] }
env 能够根据当前环境自动选择。
安装依赖
$ npm install babel-preset-env --save-dev
在src中的mod.js
export class A{ constructor(x){ this.x = x } show () { console.log(this.x) } } export function foo() { console.log("foo function"); } export const CONSTA = "aaa"
src目录下新建index.js
import {A,foo,CONSTA} from "./mod.js" foo() let a = new A(23) a.show() console.log(a,a.x,CONSTA)
直接在VS Code的环境下执行出错。估计很难有可以正常运行的环境。因此,要转译为ES5的代码。
在项目根目录下执行命令
$ npm run build
$ npm run build
> my_test@1.0.0 build D:\project\testjs\my_test > babel src -d lib src\index.js -> lib\index.js src\mod.js -> lib\mod.js
转译成功
运行
$ node lib/index.js
使用babel等转译器转译JS很是流行。
开发者能够在高版本中使用新的语法特性,提升开发效率,把兼容性问题交给转译器处理
说明:导出代码都在src/mod.js中,导入代码都写在src/index.js中。
只容许一个缺省导出,缺省导出能够是变量、函数、类,但不能使用let、var、const关键字做为默认导出
// 缺省导出 匿名函数 export default function() { console.log('default export function') } // 缺省导入 import defaultFunc from './mod' defaultFunc();
// 缺省导出 命名函数 export default function xyz() { console.log('default export function') } // 缺省导入 import defaultFunc from './mod' defaultFunc();
缺省导入的时候,能够本身从新命名,能够不须要和缺省导出时的名称一致,但最好一致。
缺省导入,不须要在import后使用花括号
export class A{ constructor(x){ this.x = x } show () { console.log(this.x) } } export default function foo() { console.log("foo default function"); } export const CONSTA = "aaa"
import foo, {A,CONSTA}from "./moda" foo() let a = new A(23) a.show() console.log(a,a.x,CONSTA)
导出
/** * 导出举例 */ // 缺省导出类 export default class { constructor(x) { this.x = x; } show(){ console.log(this.x); } } // 命名导出 函数 export function foo(){ console.log('regular foo()'); } // 函数定义 function bar() { console.log('regular bar()'); } // 变量常量定义 let x = 100; var y = 200; const z = 300; // 导出 export {bar, x, y, z};
导入
/** * ~~~~~~~~~~~~~~~ * 导入举例 * as 设置别名 */ import defaultCls, {foo, bar, x, y, z as CONST_C} from './moda'; foo(); bar(); console.log(x); // x只读,不可修改,x++异常 console.log(y); // y只读 console.log(CONST_C); new defaultCls(1000).show();
也可使用下面的形式,导入全部导出,可是会定义一个新的名词空间。使用名词空间能够避免冲突。
import * as newmod from './mod';
newmod.foo(); newmod.bar(); new newmod.default(2000).show();