前言
從事前端工作已經兩年多了,技術上從最開始的jq,到後來的angular,然後react,vue,都一一學習過來並且應用到了實踐中。自從有了es6,node,有了腳手架,感覺寫程式碼體驗如飛。直到有一天用原生開發,出現了這樣一個問題。我需要在原生的頁面上引入另一個檔案,我習慣性的import 了那個檔案,可是沒有效果...
Uncaught SyntaxError: Unexpected string
瞬間懵逼。我明明用的js啊,我在Vue,React,Angular中都這麼用的啊,為什麼寫到原生就不行了呢?我知道babel,可是沒想到import也需要被babel轉譯。瞬間感覺離開babel就好像不會寫es6。可見我的認知被以前很方便的框架給侷限了,而且es6中的import/export我也是理解很久,因為它們相互交錯較難理解。下面我來歸納我所理解的import/export的幾種常見用法,因為這也是屬於es6的基礎。
export
export 就是對外暴露方法、類或者資料供另外的js使用。
export const A = 1; // 對外暴露模組內變數A
export const Fn = ()=>{}; // 對外暴露模組內方法Fn.其中Fn可以使用模組內的方法。
// 上面的export方式,等價於
const A= 1;
const Fn = () =>{};
export {A,Fn};
// 其中可以對匯出的內容起別名
export {A as B ,Fn as Fm};
// 預設匯出.一個檔案中只能有一個預設匯出
export default class extends react.Components{}
複製程式碼
import
我們在原生js中要Import看來是難以實現了,或者說,現在瀏覽器對js直接使用es6語法的支援還不夠。但是在擁有babel的環境下(比如使用框架)使用import 還是很必須的。
import './config.js'
// 這裡的import, 表示引入了整個config.js檔案,並且執行了這個js檔案除了export以外的所有程式碼。
import vue from 'vue'
import aa from '../config.js'
// from顧名思義就是來自某個檔案。其中沒有相對路徑的(比如Vue)就是取自node_modules內的對應資料夾內的index.js。
// import 後面則是來自檔案內的預設匯出的模組,引入後等同於在引入檔案中新增了對應的模組。預設匯入可以用as 起別名
import {Fn,A} from 'a.js'; // 引入js檔案a中的匯出的方法Fn和常量A
複製程式碼
import/export的es5實現
es6的相關語法,其實也就是es5的相關的語法糖。我通過babel還原了一下使用import/export的場景。首先是export的部分程式碼。
// index.js
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var Fn = exports.Fn = function Fn() {
};
exports.default = {
a: 1
};
var A = exports.A = 1;
複製程式碼
首先就是程式碼頭部宣告嚴格模式,這很重要。然後我們看到匯出部分程式碼是通過 exports 物件來輸出模組內的引用,這類似於CommonJs的模式,即有要輸出的物件統統掛載在 module.exports 上,然後暴露給外界。但是相比較於CommonJs,Babel解析出來的程式碼多了一個export.default,即預設匯出。同時對export物件新增屬性"exports.__esModul =true",這是為什麼呢?讓我們再看一下匯入解析出來的程式碼。
// import.js
'use strict';
var _index = require('./index');
var _index2 = _interopRequireDefault(_index);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
(0, _index.Fn)();
console.log(_index.A);
console.log(_index2.default);
複製程式碼
這裡看到匯入檔案是使用的require(),那麼可以確定babel對模組是參照CommonJs進行解析的。然後可以看到匯入檔案中有一個_interopRequireDefault函式,這裡就解釋了"exports.__esModul =true"的用法,原來是方便檢測匯入模組是否通過了babel解析,這樣 Babel 就可以放心地從載入的模組中呼叫"exports.default" 這個匯出的物件了。如果載入的模組沒有檢測到,則可能是第三方的複合CommonJs的模組,可能不存在default匯出,此時就將default匯出指向模組本身。繼續看程式碼發現"(0, _index.Fn)();",我從事前端兩年了也沒有見過這種寫法,有點類似立即執行函式。查閱資料才知道,如果執行 ("(0, _index.Fn)();",這個逗號表示式等價於執行 _index.Fn(),但是執行時的上下文環境會被繫結到全域性物件身上,所以實際上真正等價於執行 foo.bar.call(GLOBAL_OBJECT)。類的匯入匯出和函式類似,解析出的程式碼有點長這裡就不多做概述了。
結語
模組化對前端的意義是巨大的,es6的出現對前端的影響也是空前絕後。 在當今前端界三大框架盛行的情況下,我們寫著業務程式碼的時候,不知是否有思考這個方法的原理?es6以前,js模組化需要用到複雜的commonjs、seajs、requirejs中的一個或者幾個,這也是需要對基礎有較高的要求。框架方便開發,但是基礎才是一個人真正實力的體現。以後前端的局勢可能會變動,但是基礎知識的牢固是突破未來的利劍。我以前就是太依賴於框架,認為會了框架就會了前端,看山都是山。以至於脫離框架,就開始看山不識山。只有將基礎打磨好,才可以一覽縱山小。
作者簡介: 張栓,人和未來大資料前端工程師,專注於html/css/js的學習與開發。