本文来自 网易云社区 。javascript
但愿在生产环境中使用es6/7,babel应该是最广泛的选择。这是babel官网中,它对本身的定义:html
Babel 自带了一组 ES2015 语法转化器。这些转化器能让你如今就使用最新的 JavaScript 语法,而不用等待浏览器提供支持。java
babel就像一个javascript文件预处理器,你能够自由使用es6/7语法,不用小心兼容性问题,由于浏览器中运行是babel为你处理妥帖的代码。为了方便使用,它提供了许多使用方法:webpack、gulp、browserify、grunt......webpack
经过哪一种方式来在当前技术栈(nej+regular+stateman)中使用babel,是一个值得深思熟虑问题,而没有通过深思熟虑就试图使用webpack的我,一度掉进了一个坑中:es6
webpack应该是目前最流行的构建工具,关于它和babel的使用方法,网上的资料汗牛充栋,前人对各类可能发生的问题(好比ie8的兼容)基本都有了解决方法,换句话说就是这条路的坑比较少。web
可是对咱们而言,webpack+babel的方式存在如下几个问题:npm
//webpack.config.js ... alias: { pro: 'a/b/c' } ... //test.js define(['pro/d']) //define(['a/b/c/d']) define(['pro/e/{mode}/f']) //error
这个问题能够经过编写babel插件,根据nej的模块语法定制修改:babel-plugin-transform-nej-modulegulp
除此以外,还遇到一个比较特殊的问题,webpack没法识别咱们历史代码中的一个文件。api
综上所述,若是有着丰富的webpack使用经验,可以承受改变打包方式带来的风险,能够考虑使用webpack来引入babel。数组
从webpack的踩坑过程当中,找到了作es6/7改造的两个原则:
根据上述的两个原则,,gulp无疑是个很好的选择:
所以,利用gulp来引入es6/7,过程应该是这样的:
虚线方框里是须要咱们来作的工做:
经过babel的插件能够完成这两个任务:
babel自己会将import和export的语法转化为commonjs格式:
转化前 | 转化后 | 目标 |
---|---|---|
import a from 'A' |
var a = require('A') |
define('A', function(a){...} |
export {b} |
exports.b = b; |
{... return b} |
这个转化只是个简写,详细的转化后代码能够在这里看,代码解析能够参考这篇文章
目前没有前人的工做能够直接实现的咱们的目标,因此必须本身编写一个,babel插件的编写能够参考这篇文章。简单的说,babel会把javascript代码解析为一棵语法树,经过修改这棵树来方便、准确的修改javascript代码。
插件babel-plugin-transform-es2015-modules-nej将es6的模块语法转换为nej的模块语法,主要流程为:
判断是否为nej模块->解析import,生成路径数组、文件名数组->解析export,生成return语句->将除了import和export的其它代码生成内容数组,并将return语句放入->利用这三个数组构建amd格式的模块语法
babel经过.baberc
来配置
//.babelrc { preset: '...', plugins: '...' }
能够看到,babel的配置由preset
和plugins
构成,preset
是插件的集合,选择预设的插件集合配合一些解决比较特殊问题的插件,来完成babel的配置。
在npm中搜索babel-preset
和babel-plugin
可以得到3000+的结果,在预设的插件集合中,babel-preset-env
是很是省心的选择,它是一个动态的插件集合,经过指定你想要兼容的浏览器,它会帮你引入须要的插件。加上上一步中编写的transform-es2015-modules-nej
,配置就完成了。
babel转化后的代码会默认使用严格模式,若是历史代码中存在严格模式下报错的问题,记得在插件中加上transform-remove-strict-mode
{ "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "IE 8-10"] } }] ], "plugins": [ "transform-es2015-modules-nej" ] }
最后一个问题是:babel只转换语法,不转换api,因此须要polyfill来保证generate
、async
这些喜闻乐见的api的正常使用。polyfill.min.js
文件体积不算小:102kb,须要在每一个页面中都引用,固然,加载一次事后,缓存能够帮助节省大部分时间。
最优解是在打包的时候,根据每一个页面使用的api来引入对应的polyfill
。babel的官方插件babel-plugin-transform-runtime
,能作到只引入文件用到的api的polyfill,可是它的引入方式为commonjs。实际上,即便是amd方式,也难以和nej的模块语法完美融合。并且,针对文件来引用polyfill
仍然使得同一个页面引用多个相同polyfill
,加载重复数据。
所以,对于API的转化,由以下三种解决方案
polyfill.min.js
;polyfill
;(修改插件babel-plugin-transform-runtime
)polyfill
;(利用打包来polyfill)
graph TD A(gulp)-->|监视|B[raw/xxx/a.js] B-->|发生改变|C(babel) C-->|babel.rc|D(src/xxx/a.js) A-->|sourcemap|E(src/xxx/a.js.map)
gulp检测文件的变更,经过babel转化es6代码,转化过程当中,gulp生成对应文件的sourcemap:a.js.map
。
分为四步:
安装gulp和babel
npm install --save-dev gulp; npm install --save-dev gulp-babel
配置gulpfile.js:
const gulp = require('gulp'); const babel = require('gulp-babel'); gulp.task('babel', () => gulp.src('./raw/**/*.js') .pipe(babel()) .pipe(gulp.dest('./src')) ); gulp.task('watch:babel', () => { gulp.watch('./raw/**/*.js', ['babel']); });
{ "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "IE 8-10"] } }] ], "plugins": [ "transform-remove-strict-mode","transform-es2015-modules-nej" ] }
提醒不熟悉babel的小伙伴一句,这些插件和预设须要安装,babel包中并不提供:
npm install --save-dev babel-preset-env; npm install --save-dev babel-plugin-transform-remove-strict-mode; npm install --save-dev babel-plugin-transform-es2015-modules-nej;
修改gulpfile.js以下:
const gulp = require('gulp'); const babel = require('gulp-babel'); const sourcemaps = require('gulp-sourcemaps'); gulp.task('babel', () => gulp.src('./raw/**/*.js') .pipe(sourcemaps.init()) .pipe(babel()) .pipe(sourcemaps.write('.',{sourceRoot: 'raw'})) .pipe(gulp.dest('./src')) ); gulp.task('watch:babel', () => { gulp.watch('./raw/**/*.js', ['babel']); });
生成sourcemap后,能够在浏览器中运行转换后代码,调试转换前代码。
<script src="/res/vendorjs/core/polyfill.min.js"></script>
目前测试的状况,ie9及其以上环境有效,理论上支持ie8。
es6最大的优势是给码农带来的快乐,一个不是很明显的快乐对好比下:
原来这样写:
NEJ.define([ 'text!./app.html', 'pro/cache/indexCache', 'pro/util/userUtil', 'pro/module/module', 'pro/util/util' ],function(template, IndexCache, userUtil, Module, util ){ var App = Module.extend({ template: template, config: function(){ this.supr(); this.cache =new IndexCache(); util.extend(this.data, { columns: [], columnConfig: [{ isShowIntroPic: false, isVertical: false },{ isShowIntroPic: true, isVertical: true },{ isShowIntroPic: true, isVertical: true },{ isShowIntroPic: false, isVertical: false }], courseUrlPrefix: userUtil.isUserLogin() ? this.$urlPrefix.termDetailPrefix : this.$urlPrefix.courseDetailPrefix }); this.data.courseUrl = "/path/courses/"; }, init: function(){ this.supr(); this.getInitData(); }, enter: function(){ this.supr(); }, getInitData: function() { this.cache.courseColumn( this.onGetCourseColumn._$bind(this)); }, onGetCourseColumn: function(data) { for(var i=0; i<data.length; i++ ) { var columnData = data[i]; var columnConfig = this.data.columnConfig[i]; var column = { title: columnData.sectionName, isShowIntroPic: columnConfig.isShowIntroPic, isVertical: columnConfig.isVertical, introPicSrc: columnData.photoUrl, courseCards: [] }; var coursesData = columnData.termCardVos; for(var j=0; j<coursesData.length; j++) { var courseData = coursesData[j]; column.courseCards.push({ title: courseData.courseName, url: this.data.courseUrlPrefix.replace(':termid', courseData.termId), src: courseData.bigPhoto, price: courseData.price == 0 ? '免费' : courseData.price + '元' }) } this.data.columns.push(column); } this.$update(); }, leave: function(){ this.supr(); } }); return App; });
如今能够这样写:
import template from './app.html'; import IndexCache from 'pro/cache/indexCache'; import userUtil from 'pro/util/userUtil'; import Module from 'pro/module/module'; import util from 'pro/util/util'; const App = Module.extend({ template: template, config: function () { this.supr(); this.cache = new IndexCache(); Object.assign(this.data, { columns: [], columnConfig: [{ isShowIntroPic: false, isVertical: false }, { isShowIntroPic: true, isVertical: true }, { isShowIntroPic: true, isVertical: true }, { isShowIntroPic: false, isVertical: false }], courseUrlPrefix: userUtil.isUserLogin() ? this.$urlPrefix.termDetailPrefix : this.$urlPrefix.courseDetailPrefix }); this.data.courseUrl = '/path/courses/'; }, init: function () { this.supr(); this.getInitData(); }, enter: function () { this.supr(); }, getInitData: async function () { const data = await this.cache.courseColumn(); for(let [columnIdx, columnData] of data.entries()) { let columnConfig = this.data.columnConfig[columnIdx], column = { title: columnData.sectionName, isShowIntroPic: columnConfig.isShowIntroPic, isVertical: columnConfig.isVertical, introPicSrc: columnData.photoUrl, courseCards: [] }, coursesData = columnData.termCardVos; for(let courseData of coursesData) { column.courseCards.push({ title: courseData.courseName, url: this.data.courseUrlPrefix.replace(':termid', courseData.termId), src: courseData.bigPhoto, price: courseData.price == 0 ? '免费' : `${courseData.price}元` }); } this.data.columns.push(column); } this.$update(); }, leave: function () { this.supr(); } }); export { App };
本文来自网易云社区,经做者曹阳受权发布。
更多网易研发、产品、运营经验分享请访问网易云社区。