本文來自 網易雲社區 。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 };
本文來自網易雲社區,經做者曹陽受權發佈。
更多網易研發、產品、運營經驗分享請訪問網易雲社區。