Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端引入es6 #8

Open
Jeffersondyj opened this issue Nov 2, 2016 · 0 comments
Open

前端引入es6 #8

Jeffersondyj opened this issue Nov 2, 2016 · 0 comments

Comments

@Jeffersondyj
Copy link
Owner

Jeffersondyj commented Nov 2, 2016

背景

什么是es6

ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JS。

实际上后两者是ECMA-262标准的实现和扩展。Javascript包含EcmaScript、DOM(Node,Element)、BOM(window,location),ECMA-262标准的发布过程:

  • ECMAScript 2.0 1998年6月发布
  • ECMAScript 3.0 1999年12月发布,成为JavaScript的通行标准,得到了广泛支持。
  • ECMAScript 4.0 2007年10月发布
  • ECMAScript 5.0 2009年12月发布
  • ECMAscript 5.1 2011年6月发布,并且成为ISO国际标准(ISO/IEC 16262:2011)
  • ECMAScript 6 2015年6月17日发布正式版本,即ECMAScript 2015

为何需要es6

语法糖

:::javascript
var array = [1, 2, 3];
array.forEach(v = > console.log(v));

增加程序可读性

减少程序代码出诡异错误的机会,例如:

  • 没有块级作用域 let 避免 var 的提升
  • 类写法比较怪异 彻底面向对象,可以写传统的类继承
  • 异步调用写法 yield 避免“回调地狱”
  • 没有模块管理 ......

符合未来趋势

如何引入es6

需要做什么

babel是一个JavaScript编译器,用于转化你的JS代码。
facebook也使用了es6 + babel编译。
baiju-web的系统中,我们需要:

  • 本地服务 在mockup环境下,edp-webserver可以利用babel将es6代码实时转es5
  • 线上打包 在build环境下,edp-build用babel将es6代码转为es5后,再模块化打包压缩

本地服务

新版的edp-webserver已经提供了babelHandler,注意到已经暴露了babel全局句柄,那我们只需要在edp-webserver-config.js里面,加上:

:::javascript
{
    location: /\.es6|\.jsx\.js/, // babel 同时对付es6和react
    handler: [
        babel({
            sourceMaps: 'both'
        }, { // forceTransform 无论是否有`define`都强制转成UMD/AMD模块
            forceTransform: true
        })
    ]
}

我现在是1.2.8版本,如果是老版本edp-webserver(无babel版),你可以:

:::javascript
npm uninstall -g edp-webserver
npm install -g edp-webserver

美中不足的是,原先启动webserver可以用edp ws start快捷启动,现在只能edp webserver start了。

线上打包

需要在edp-build-config.js里面的moduleCompiler之前加一个自定义Processor,命名为PaBabelProcessor。
也有参考过ecomfeedp-build的BabelProcessor,不过发现那里require的babeljs是5.*版的,最新的babeljs是6.18版,我们还是用更新的版本为宜,只是配置和官方Processor的源码略有不同而已。

:::javascript
/**
 * 初始化配置
 */
PaBabelProcessor.prototype.name = 'PaBabelProcessor';
PaBabelProcessor.prototype.files = ['**/*.es6.js', '**/*.jsx.js'];
PaBabelProcessor.prototype.babel = require('babel-core');
PaBabelProcessor.prototype.compileOptions = {
    compact: false,
    ast: false,
    presets: ['es2015']
};

/**
 * 构建处理
 *
 * @param {FileInfo} file 文件信息对象
 * @param {ProcessContext} processContext 构建环境对象
 * @param {Function} callback 处理完成回调函数
 */
PaBabelProcessor.prototype.process = function (file, processContext, callback) {
    var options = {};
    for (var attr in this.compileOptions) {
        if (this.compileOptions.hasOwnProperty(attr)) {
            options[attr] = this.compileOptions[attr];
        }
    }
    options.filename = file.fullPath;
    file.setData(this.babel.transform(file.data, options).code);
    callback();
};

这里edp-build不用换版本,维持你现在的环境即可。

引入import export特性

本地服务

考虑到es6也有强大的模块管理功能,拟将原来的define require写成import export形式

:::javascript
{
    location: /\.es6|\.jsx\.js/, // babel 同时对付es6和react
    handler: function (context) {
        var url = context.request.url;
        if (url.indexOf('\/output\/') >= 0) {
            file()(context);
            return;
        }
        var option = {sourceMaps: 'both'};
        url.indexOf('dsp') >= 0 || (option.modules = 'amd');
        babel(option, {
            // forceTransform 无论是否有`define`都强制转成UMD/AMD模块
            forceTransform: true
        })(context);
    }
}

意思也比较好理解:如果是build后的代码,则不管。如果是dsp下的代码(没来得及换成import),则用老办法转换(见上面代码)
否则则用babel5里面的配置:modules: 'amd'进行转换。这个是 babel5 ,倒是无需加plugin。

线上打包

需要在package.json里面加2个plugin:

"babel-plugin-add-module-exports": "^0.2.1"
"babel-plugin-transform-es2015-modules-amd": "^6.22.0"

然后BabelProcessor中的compileOptions配plugins: ['add-module-exports', 'transform-es2015-modules-amd']

第二个插件是转成amd模块,第一个插件是为了fix babel6编译对export default的支持。
鉴于伊始编译的代码死活没法正常工作,经过排查和跟踪,比对编译后的代码定位问题,所需的是类但得到的是{default: 类}:

在 babel5 时代,export default {}; 除了会被转译成 exports.default = {};,还会加一句 module.exports = exports.default(关键)
但在 babel6 时代做了一个区分,后面这句不再添加。在我看来,主要是为了区分 commonJS 和 es6 的模块定义,也就是 commonJS的 require 和 module.exports 搭配使用
这样一个相当于是 babel6 约定俗成的规范,导致了这个问题所在。

其他

踩过的坑

你当然可以在全局安装babel,但我没有这样做,而是选择在项目node_modules下局部安装。
注意光有babel-core,babel-preset-es2015依然要报错,还需要安装各个plugins。package.json下加了茫茫多这些:

:::javascript
"devDependencies": {
    "babel-core": "^6.18.0",
    "babel-plugin-add-module-exports": "^0.2.1",
    "babel-plugin-check-es2015-constants": "^6.8.0",
    "babel-plugin-transform-es2015-arrow-functions": "^6.8.0",
    "babel-plugin-transform-es2015-block-scoped-functions": "^6.8.0",
    "babel-plugin-transform-es2015-block-scoping": "^6.10.1",
    "babel-plugin-transform-es2015-classes": "^6.9.0",
    "babel-plugin-transform-es2015-computed-properties": "^6.8.0",
    "babel-plugin-transform-es2015-destructuring": "^6.9.0",
    "babel-plugin-transform-es2015-for-of": "^6.8.0",
    "babel-plugin-transform-es2015-function-name": "^6.9.0",
    "babel-plugin-transform-es2015-literals": "^6.8.0",
    "babel-plugin-transform-es2015-modules-amd": "^6.22.0",
    "babel-plugin-transform-es2015-object-super": "^6.8.0",
    "babel-plugin-transform-es2015-parameters": "^6.11.4",
    "babel-plugin-transform-es2015-shorthand-properties": "^6.8.0",
    "babel-plugin-transform-es2015-spread": "^6.8.0",
    "babel-plugin-transform-es2015-sticky-regex": "^6.8.0",
    "babel-plugin-transform-es2015-template-literals": "^6.8.0",
    "babel-plugin-transform-es2015-typeof-symbol": "^6.8.0",
    "babel-plugin-transform-es2015-unicode-regex": "^6.11.0",
    "babel-plugin-transform-proto-to-assign": "^6.9.0",
    "babel-preset-es2015": "*"
}

Jefferson估计你在这里直接npm install多半要挂,会报一个npm-cache文件夹下关于tar.gz包的.lock文件的错(wtf....这是什么鬼)。
看了一下各个plugin的依赖,发现很多plugin都依赖同一个lodash4.16.5版本的包,估计是解压时候读文件碰到死锁了。
解决方案嘛,一共20个左右的plugins,3个一组npm install装一下,然后再添3个,再继续npm install,亲测有效。

经过性能测试发现,PaBabelProcessor在初始化时会等待10s左右,然后单个读文件并转化会比较快(平均0.05s一个,应该还算是达到预期了)。node命令行下单独用babel-core转化多个文件,效果雷同。这样如果碰到一个30+个action(100个mvc文件左右)的项目,BabelProcessor耗时大约会在半分钟,占总耗时的40%-50%。

值得一提的是,在发现babel6对export default的支持不佳时,我试图将坑归结在er3的window.require上。
由于框架的er3是用window.require的导致得到的Action不再是类而是{default: 类},于是我试图用一段代码改er3来解决(其实也可以work,但改框架源码毕竟不好是吧):

:::javascript
else if (typeof SpecificAction['default'] === 'function') {
    // es6模块build时是babel6,而webServer下是babel5就ok
    SpecificAction = SpecificAction['default'];
}

呵呵,如果er3的代码有更新的es6版本,那估计就不会有这个“歧途”了,也不会有上面的,对babel5 babel6编译出来的结果所进行的差异化研究了。

附录

这次兼容es6升级所提交的cooder issue

好书1:EcmaScript 6入门
好书2:Babel 入门教程

任何问题和建议,Jefferson Deng waiting for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant