nej+regular环境使用es6的低成本方案
本文來自?網(wǎng)易云社區(qū)?。
?
希望在生產(chǎn)環(huán)境中使用es6/7,babel應(yīng)該是最普遍的選擇。這是babel官網(wǎng)中,它對自己的定義:
Babel 自帶了一組 ES2015 語法轉(zhuǎn)化器。這些轉(zhuǎn)化器能讓你現(xiàn)在就使用最新的 JavaScript 語法,而不用等待瀏覽器提供支持。
babel就像一個javascript文件預(yù)處理器,你可以自由使用es6/7語法,不用當(dāng)心兼容性問題,因為瀏覽器中運(yùn)行是babel為你處理妥帖的代碼。為了方便使用,它提供了許多使用方法:webpack、gulp、browserify、grunt......
通過哪種方式來在當(dāng)前技術(shù)棧(nej+regular+stateman)中使用babel,是一個值得深思熟慮問題,而沒有經(jīng)過深思熟慮就試圖使用webpack的我,一度掉進(jìn)了一個坑中:
?
webpack + babel 的踩坑過程
webpack應(yīng)該是目前最流行的構(gòu)建工具,關(guān)于它和babel的使用方法,網(wǎng)上的資料汗牛充棟,前人對各種可能發(fā)生的問題(比如ie8的兼容)基本都有了解決方法,換句話說就是這條路的坑比較少。
但是對我們而言,webpack+babel的方式存在以下幾個問題:
這個問題可以通過編寫babel插件,根據(jù)nej的模塊語法定制修改:babel-plugin-transform-nej-module
除此之外,還遇到一個比較特殊的問題,webpack無法識別我們歷史代碼中的一個文件。
綜上所述,如果有著豐富的webpack使用經(jīng)驗,能夠承受改變打包方式帶來的風(fēng)險,可以考慮使用webpack來引入babel。
從webpack的踩坑過程中,找到了做es6/7改造的兩個原則:
?
gulp + babel
根據(jù)上述的兩個原則,,gulp無疑是個很好的選擇:
因此,利用gulp來引入es6/7,過程應(yīng)該是這樣的:
虛線方框里是需要我們來做的工作:
通過babel的插件可以完成這兩個任務(wù):
?
import/export轉(zhuǎn)化為nej的模塊語法
babel本身會將import和export的語法轉(zhuǎn)化為commonjs格式:
| import a from 'A' | var a = require('A') | define('A', function(a){...} |
| export {b} | exports.b = b; | {... return b} |
這個轉(zhuǎn)化只是個簡寫,詳細(xì)的轉(zhuǎn)化后代碼可以在這里看,代碼解析可以參考這篇文章
目前沒有前人的工作可以直接實現(xiàn)的我們的目標(biāo),所以必須自己編寫一個,babel插件的編寫可以參考這篇文章。簡單的說,babel會把javascript代碼解析為一棵語法樹,通過修改這棵樹來方便、準(zhǔn)確的修改javascript代碼。
插件babel-plugin-transform-es2015-modules-nej將es6的模塊語法轉(zhuǎn)換為nej的模塊語法,主要流程為:
判斷是否為nej模塊->解析import,生成路徑數(shù)組、文件名數(shù)組->解析export,生成return語句->將除了import和export的其它代碼生成內(nèi)容數(shù)組,并將return語句放入->利用這三個數(shù)組構(gòu)建amd格式的模塊語法
?
根據(jù)兼容需求,轉(zhuǎn)化es6/7語法
babel通過.baberc來配置
//.babelrc {preset: '...',plugins: '...' }可以看到,babel的配置由preset和plugins構(gòu)成,preset是插件的集合,選擇預(yù)設(shè)的插件集合配合一些解決比較特殊問題的插件,來完成babel的配置。
在npm中搜索babel-preset和babel-plugin能夠獲得3000+的結(jié)果,在預(yù)設(shè)的插件集合中,babel-preset-env是非常省心的選擇,它是一個動態(tài)的插件集合,通過指定你想要兼容的瀏覽器,它會幫你引入需要的插件。加上上一步中編寫的transform-es2015-modules-nej,配置就完成了。
babel轉(zhuǎn)化后的代碼會默認(rèn)使用嚴(yán)格模式,如果歷史代碼中存在嚴(yán)格模式下報錯的問題,記得在插件中加上transform-remove-strict-mode
{"presets": [["env", {"targets": {"browsers": ["last 2 versions", "IE 8-10"]}}]],"plugins": ["transform-es2015-modules-nej"] }API的轉(zhuǎn)化
最后一個問題是:babel只轉(zhuǎn)換語法,不轉(zhuǎn)換api,所以需要polyfill來保證generate、async這些喜聞樂見的api的正常使用。polyfill.min.js文件體積不算小:102kb,需要在每個頁面中都引用,當(dāng)然,加載一次過后,緩存可以幫助節(jié)省大部分時間。
最優(yōu)解是在打包的時候,根據(jù)每個頁面使用的api來引入對應(yīng)的polyfill。babel的官方插件babel-plugin-transform-runtime,能做到只引入文件用到的api的polyfill,但是它的引入方式為commonjs。實際上,即使是amd方式,也難以和nej的模塊語法完美融合。而且,針對文件來引用polyfill仍然使得同一個頁面引用多個相同polyfill,加載重復(fù)數(shù)據(jù)。
因此,對于API的轉(zhuǎn)化,由如下三種解決方案
- 每個頁面引入polyfill.min.js;
- 每個文件引入對應(yīng)polyfill;(修改插件babel-plugin-transform-runtime)
- 打包時引入頁面所需polyfill;(利用打包來polyfill)
??
最終方案
graph TDA(gulp)-->|監(jiān)視|B[raw/xxx/a.js]B-->|發(fā)生改變|C(babel)C-->|babel.rc|D(src/xxx/a.js)A-->|sourcemap|E(src/xxx/a.js.map)gulp檢測文件的變動,通過babel轉(zhuǎn)化es6代碼,轉(zhuǎn)化過程中,gulp生成對應(yīng)文件的sourcemap:a.js.map。
分為四步:
?
1. 配置gulp
安裝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']); });2.配置babelrc
{"presets": [["env", {"targets": {"browsers": ["last 2 versions", "IE 8-10"]}}]],"plugins": ["transform-remove-strict-mode","transform-es2015-modules-nej"] }提醒不熟悉babel的小伙伴一句,這些插件和預(yù)設(shè)需要安裝,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;3.配置gulp+babel生成sourcemap
修改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后,可以在瀏覽器中運(yùn)行轉(zhuǎn)換后代碼,調(diào)試轉(zhuǎn)換前代碼。
4. polyfill
<script src="/res/vendorjs/core/polyfill.min.js"></script>總結(jié)及效果
目前測試的情況,ie9及其以上環(huán)境有效,理論上支持ie8。
es6最大的優(yōu)點(diǎn)是給碼農(nóng)帶來的快樂,一個不是很明顯的快樂對比如下:
原來這樣寫:
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 ? '免費(fèi)' : courseData.price + '元'})}this.data.columns.push(column);}this.$update();},leave: function(){this.supr();}});return App; });現(xiàn)在可以這樣寫:
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 ? '免費(fèi)' : `${courseData.price}元`});}this.data.columns.push(column);}this.$update();},leave: function () {this.supr();} });export {App };本文來自網(wǎng)易云社區(qū),經(jīng)作者曹陽授權(quán)發(fā)布。
原文地址:nej+regular環(huán)境使用es6的低成本方案
更多網(wǎng)易研發(fā)、產(chǎn)品、運(yùn)營經(jīng)驗分享請訪問網(wǎng)易云社區(qū)。
總結(jié)
以上是生活随笔為你收集整理的nej+regular环境使用es6的低成本方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vim基础-一般模式
- 下一篇: 人工智能听了很多遍,都应用在哪些领域了你