面試官讓我解釋什麼是前端模塊化

2022-01-03 前端技術優選

前兩篇文章中,有小夥伴給我留言說怎麼沒有模塊化相關的知識點,模塊化在面試中被問到的概率非常大,但因為前幾篇文章篇幅實在太長了些,所以模塊化知識點單獨這篇文章給大家聊聊。

先說說什麼是模塊化,就是將獨立的功能代碼封裝成一個獨立的文件,其他模塊需要使用,在進行引用。

模塊化有利於代碼的拆分和架構上的解耦,模塊化在服務端領域已經早已成熟,nodejs 也已經支持模塊化。

而在瀏覽器上,js 腳本是異步載入的,腳本按照編碼順序依次執行,依賴關係只能按照編碼順序來控制。因此前端早早就有了模塊化技術,可每天醒來前端就多一個名詞多一個框架的,發展實在迅猛,就前端模塊化這些年的積累就有好幾種,我們依次來看看。

commonjs

先看伴隨 nodejs 而誕生的 commonjs 規範。commonjs 規範應用於 nodejs 應用中,在 nodejs 應用中每個文件就是一個模塊,擁有自己的作用域,文件中的變量、函數都是私有的,與其他文件相隔離。

CommonJS規範規定,每個模塊內部, module 變量代表當前模塊。這個變量是一個對象,它的 exports 屬性(即 module.exports )是對外的接口。加載某個模塊,其實是加載該模塊的 module.exports 屬性。(引用阮一峰老師的描述)

舉個慄子看看模塊化後的文件該怎麼寫

let name = 'now';let age = 18;
let fun = () => { console.log('into fun'); name = 'change'}
module.exports = { name, fun}console.log(module)
var { name, fun } = require('./util/index.js')

上面這個文件有兩個變量,一個函數,通過 module.exports 暴露變量 name 和函數 fun ,age 這個變量就是私有的,外部無法直接訪問,如果想讓 age 變量全局都可以訪問,那麼可以改成 global.age = 18 ,但這樣子會汙染全局作用域,會導致意想不到的驚喜(嚇)。

我們看看 util\index.js 列印出來的 module

commonjs

module 中有這些屬性

module.id 模塊的識別符,通常是帶有絕對路徑的模塊文件名。module.filename 模塊的文件名,帶有絕對路徑。module.loaded 返回一個布爾值,表示模塊是否已經完成加載。module.parent 返回一個module對象,表示調用該模塊的模塊,如果改該模塊沒有被引用,那麼 parent 就是 null module.children 返回一個module數組,表示該模塊要用到的其他模塊。module.exports 表示模塊對外輸出的值。module.paths 這個用於 require 查找該文件的位置。

在開發中我們常使用的就是 module.exports , 通過 module.exports 輸出的對象就是引用方 require 出來的值

require

既然有 module.exports 導出,那麼就有與之相對應的 require 導入,如下

var { name, fun, object } = require('./util/index.js') // 不用解構,直接導出對象也可以使用 require 我們最關心的就是文件路徑,這裡還是引用阮一峰老師的解釋

根據參數的不同格式,require命令去不同路徑尋找模塊文件。

如果參數字符串以「/」開頭,則表示加載的是一個位於絕對路徑的模塊文件。比如,require('/home/marco/foo.js')將加載/home/marco/foo.js。

如果參數字符串以「./」開頭,則表示加載的是一個位於相對路徑(跟當前執行腳本的位置相比)的模塊文件。比如,require('./circle')將加載當前腳本同一目錄的circle.js。

如果參數字符串不以「./「或」/「開頭,則表示加載的是一個默認提供的核心模塊(位於Node的系統安裝目錄中),或者一個位於各級node_modules目錄的已安裝模塊(全局安裝或局部安裝)。大家還記得 module.paths 吧,這裡就派上用場了。舉例來說,腳本/home/user/projects/foo.js執行了require('bar.js')命令,Node會依據 module.paths 路徑加上文件名稱,依次搜索。這樣設計的目的是,使得不同的模塊可以將所依賴的模塊本地化。

如果參數字符串不以「./「或」/「開頭,而且是一個路徑,比如require('example-module/path/to/file'),則將先找到example-module的位置,然後再以它為參數,找到後續路徑。

如果指定的模塊文件沒有發現,Node會嘗試為文件名添加.js、.json、.node後,再去搜索。.js件會以文本格式的JavaScript腳本文件解析,.json文件會以JSON格式的文本文件解析,.node文件會以編譯後的二進位文件解析。所以文件名的後綴可以省略。

如果想得到require命令加載的確切文件名,使用require.resolve()方法。

module.exports 和 exports

我們還可以導出 exports 直接使用,但需要注意一點,exports 是已經定義的常量,在導出的時候不能在給它定義,如下

let exports = module.exports // 錯誤 #region exports  Identifier 'exports' has already been declaredexports = module.exports; // 正確的

使用 exports 我們可以這麼導出對象,但需要注意一點,在導出對象前不能修改 exports 的指向,若修改 exports 就與 module.exports 不是一個東西了,當然你可以在導出對象後隨意修改,這時候就不會影響導出。

exports = module.exports// exports = ()=>{} 不能修改exports.fun = () => {    console.log('into fun');    name = 'change'}exports.name = 'now';// exports = ()=>{} 隨你改

單獨使用 exports 和 module.exports 其實沒啥區別,個人建議還是使用 module.exports ,畢竟這才是常規穩妥的寫法。

隔離性

commonjs 規範是在運行時加載的,在運行時導出對象,導出的對象與原本模塊中的對象是隔離的,簡單的說就是克隆了一份。看下面這個慄子

// util\index.jslet object = {    age: 10}let fun = function() {    console.log('modules obj', object);    object = { age: 99 }}module.exports = {    fun,    object}
// index.jsvar { name, fun, object } = require('./util/index.js')console.log('before fun', object)fun()console.log('end fun', object)

執行 node index.js 看看列印

before fun { age: 10 }modules obj { age: 10 }end fun { age: 10 }

引用方調用了導出的 fun 方法,fun 方法改變了模塊中的 object 對象,可是在 index.js 中導出的 object 對象並沒有發生改變,所以可見 commonjs 規範下模塊的導出是深克隆的。

在瀏覽器中使用 commonjs 規範 browserify

因為瀏覽器中缺少 module exports require global 這個四個變量,所以在瀏覽器中沒法直接使用 commonjs 規範,非要使用就需要做個轉換,使用 browserify ,它是常用的 commonjs 轉換工具,可以搭配 gulp webpack 一起使用。看下經過 browserify 處理後的代碼,就截取了些關鍵部分。

broswervify

browserify1

我把核心代碼複製出來,大致的結構如下,browserify 給每一個模塊都設置了一個唯一 id ,通過模塊路徑來映射模塊id,以此來找到各個模塊。、

原本模塊中的代碼被有 require module exports 這三個參數的函數所包裹,其中 require 用來加載其他模塊,exports 用來導出對象。

!function e(t, n, r) {    function s(o, u) {        if (!n[o]) {            if (!t[o]) {                var a = "function" == typeof require && require;                if (!u && a)                    return a(o, !0);                if (i)                    return i(o, !0);                var f = new Error("Cannot find module '" + o + "'");                throw f.code = "MODULE_NOT_FOUND",                f            }            var l = n[o] = {                exports: {}            };            t[o][0].call(l.exports, function(e) {                var n = t[o][1][e];                return s(n || e)            }, l, l.exports, e, t, n, r)        }        return n[o].exports    }    for (var i = "function" == typeof require && require, o = 0; o < r.length; o++)        s(r[o]);    return s}({    1:[function(require, module, exports) {        "use strict"    },{"babel-runtime/helpers/classCallCheck": 2},[3,4]},    2: [function(require, module, exports) {        "use strict";        exports.__esModule = !0,        exports["default"] = function(instance, Constructor) {            if (!(instance instanceof Constructor))                throw new TypeError("Cannot call a class as a function")        }    }    , {}]},{},[])

ES6 模塊化

ECMA推出了官方標準的模塊化解決方案,使用 export 導出,import 導入,編碼簡潔,從語義上更加通俗易懂。

ES6 支持異步加載模塊 的模塊不是對象,而是在編譯的時候就完成模塊的引用,所以是編譯時才加載的。

個人認為,ES6模塊化是以後的主流。

還是上面的慄子,用ES6模塊化改寫,改動上並不大,幾個關鍵字做下修改即可

// util/index.jslet name = 'now';
let fun = () => { name = 'change'}
export { name, fun}// app.jsimport { name, fun } from "../util";console.log('before fun', object)fun()console.log('end fun', object)

瀏覽器中使用

但是ES6模塊化在瀏覽器上的支持並不是很好,大部分瀏覽器還是不支持,所以需要做轉換

不使用 webpack ,使用 gulp 等構建流工具,那麼我們需要使用babel將 es6 轉成 es5 語法

使用 babel 轉換,在babel 配置文件 .babelrc 寫上

在使用 browserify 對模塊規範進行轉換。

若使用 webpack ,webpack 是支持 es6 模塊化的,所以就只要引用 babel-loader ,對 es6 的語法做處理即可模塊的導出是對象的引用

ES6模塊化下的導出是對象的引用,我們看下面這個慄子

// util/index.jslet name = 'now';
let fun = () => { name = 'change';}let getName = function() { console.log('module:',name)}
export { name, fun, getName}// app.jsimport { name, fun, getName } from "../util";console.log("before fun:", name);fun();console.log("after fun:", name);name = "change again";getName();

我們看看輸出

before fun: nowafter fun: changemodule: change

可見,模塊內部函數改變了模塊內的對象,外部導出使用的對象也跟著發生了變化,這一點是和 commonjs 規範區別最大的地方,這個特性可用於狀態提升。

ES6 模塊規範和 commonjs 規範 運行機制的區別

CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口

運行時加載: CommonJS 模塊就是對象;即在輸入時是先加載整個模塊,生成一個對象,然後再從這個對象上面讀取方法,這種加載稱為「運行時加載」。

編譯時加載: ES6 模塊不是對象,而是通過 export 命令顯式指定輸出的代碼,import時採用靜態命令的形式。即在import時可以指定加載某個輸出值,而不是加載整個模塊,這種加載稱為「編譯時加載」。

CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完才會生成。而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。

AMD-require.js 和 CMD-sea.js

聊到 AMD 和 CMD 這兩個規範都離不開 require.js 和 sea.js,這是早些年,為了解決瀏覽器異步加載模塊而誕生的方案。隨著打包工具的發展,commonjs和es6都可以在瀏覽器上運行了,所以 AMD、CMD 將逐漸被替代。

AMD規範的模塊化:用 require.config()指定引用路徑等,用define()定義模塊,用require()加載模塊。

CMD規範的模塊化:用define()定義模塊, seajs.use 引用模塊。

模塊兼容處理

我們開發插件時可能需要對各種模塊做支持,我們可以這麼處理

const appJsBridge = function(){};if ("function" === typeof define) {        define(function() {        return appJsBridge;    })} else if ("undefined" != typeof exports) {        module.exports = appJsBridge;} else {        window.appJsBridge = appJsBridge;}

小結

歡迎小夥伴們分享點讚收藏三連~

因這個號沒有留言功能,小夥伴們可以在公號對話框留言,或者添加我微信,與大夥共同學習,向高級前端邁進~

參考連結

http://javascript.ruanyifeng.com/nodejs/module.html#toc2 http://www.ruanyifeng.com/blog/2015/05/commonjs-in-browser.html https://juejin.im/post/5aaa37c8f265da23945f365c#heading-5

相關焦點

  • 為什麼文職面試是模塊化面試?模塊化面試與結構化面試有什麼不同
    中國解放軍陸軍步兵學院教授、紅師教育創始人範進忠首次提出模塊化面試的理論,並將軍隊文職面試屬於模塊化面試。並將其定義概括為:模塊化面試是根據招聘崗位知識和技能需求,將面試標準拆分為多個模塊,建立題庫,制定標準和規範。面試時隨機抽取相關模塊題目進行組合,考生在規定時間內作答。
  • 2018web前端面試總結
    關於流程一般的實習&校招招聘大體有這麼一個流程:(這裡我個人都是走的官網投遞的渠道,沒有經歷過宣講會的環節,這一部分我沒啥經驗)筆試筆試作為招聘的第一個環節,一般考察的都是專業基礎知識,這裡會著重考察你在學校的計算機相關基礎,也就是很多人會發現,我明明考的是前端的卷子為啥前端的知識點考的這麼少,而且有時候也會發現,前端的卷子考的沒有前端的知識點
  • 前端模塊化
    前端模塊化模塊化也就是將一個系統細分為多個小單元,這些小單元是抽象的、可擴充的、可復用的邏輯代碼。模塊化是工程化的基礎,只有能將代碼模塊化,拆分組織為合理的單元結構,才能使其具備調度整合的能力。模塊化發展歷程模擬時期JavaScript 偶然的成功以及初期設計不完善的原因,在其成為客戶端主流腳本時,缺少了很多成熟語言具有的特性。
  • 我的前端面試心法(已拿字節offer)
    0、 我的秋招經歷題主基本情況,南方普通985普通學生,接觸前端十個月左右,今年 8 月後開始正式準備秋招。說起我的秋招,一個詞,就是「逆襲」。一開始,華為筆試沒過,三七互娛筆試沒過被撈起來後一面又掛,信心受挫。
  • 模塊化面試-9丨模塊化面試&結構化面試的優點及缺陷
    中國解放軍陸軍步兵學院教授、紅師教育創始人範進忠首次提出模塊化面試的理論,並將軍隊文職面試屬於模塊化面試。並將其定義概括為:模塊化面試是根據招聘崗位知識和技能需求,將面試標準拆分為多個模塊,建立題庫,制定標準和規範。面試時隨機抽取相關模塊題目進行組合,考生在規定時間內作答。
  • 我做前端技術面試官的一些體會
    比較重要的是,我希望能看到項目連結、個人博客地址。博客希望有原創的內容。看項目連結是看用了哪些技術,代碼怎麼寫的,結構如何劃分,綜合看一下,就算不完全是他自己寫的也沒關係,最後面試會根據他的項目,問他自己的理解。列出的技能樹,只能做一點小參考,面試的時候,會問我會的技術,我不會的不問。
  • 學姐來襲:前端崗位的面試經驗
    以下文章分享自北郵人論壇:    開始學習前端差不多是今年4月份,那會大家也都開始了實習的面試準備,心急如焚,不知道自己定位。考慮在實驗室的確是整個人都不行不行的。考慮自己是女生,結合性格特徵,並且要儘快入門。最終在運營,測試,前端,產品中選擇了前端。之後也一直斷斷續續在面試學習。下面我來分享一下自己半年來的面試過程。
  • 模塊化面試&結構化面試的優點及缺陷
    中國解放軍陸軍步兵學院教授、紅師教育創始人範進忠首次提出模塊化面試的理論,並將軍隊文職面試屬於模塊化面試。並將其定義概括為:模塊化面試是根據招聘崗位知識和技能需求,將面試標準拆分為多個模塊,建立題庫,制定標準和規範。面試時隨機抽取相關模塊題目進行組合,考生在規定時間內作答。
  • 12~18k的前端面試會問個啥?
    是我的一個朋友的公司,他們招前端的薪資就是12~18k,大家可以看看他們的要求是什麼吧名詞解釋程度副詞對於不同程度的詞,我的理解如下:精通了解背後的原理和規範動手重寫過實現代碼可以以為此為基礎做二次開發能讀懂源碼並做出修改掌握曾在項目中大量使用過此技術可以自如的使用這項技術進行開發
  • 面試分享:一年經驗初探阿里巴巴前端社招
    簡歷發過去之後,boss就給我打了電話,讓我簡單的介紹一下自己,我就噼裡啪啦說了一些,還說了一些題外話。然後boss就開始問我問題。由於面了四輪,所以最開始的面試記憶有點模糊了,細細回想,又感覺記憶猶新。(web前端學習交流群:328058344 禁止閒聊,非喜勿進!)1.電話初探1.說一下你了解CSS盒模型。
  • 前端面試題每日1練_253
    點擊上方 web前端知識點 ,選擇 星標 公眾號
  • 360子公司,華閱文化前端面試 面試題,技術問題,邏輯題詳解
    有什麼想問我的嗎?第一次勉強過了一面。二面前端負責人。高高瘦瘦的,很有禮貌。問項目,學校經歷。三方登錄,微信支付,冒泡處理,ajax提交表單,sku,事物,Linux,動畫,閉包,緩存,memcached,nosql和mysql區別(樓主不知道)。
  • web前端開發工程師面試題大全
    最近看到web群裡的人,特別關心面試web前端開發工程師時,面試官都會問那些問題,今天我整理了一份,web前端開發工程師崗位面試題的大全,大家可看看
  • 前端面試真題分享
    以下是技術面試環節問題整理(技術面試是由大連總公司的技術進行電話面試):面試官:首先面試官介紹了下公司業務方向, 主要是給華為做外包, 會有加班, 能不能接受我: 影響不大, 能接受面試官:(基礎問題) 兩個div, 在不加任何樣式修飾的情況下
  • 我背透了這些前端八股文
    HTTP 各版本的區別 通常並不會幹巴巴的讓你回答,在我遇到的所有情況下,通常都是在我答到緩存的時候,面試官會讓我停下來解釋緩存,這時候回答到一部分 HTTP 各版本中的區別;另外,一部分會在我回答性能優化的時候,答到 HTTP2 的時候會回答。
  • 一場感覺身體被掏空的前端面試
    一般來說,想要拿到公司的現場面試(onsite)邀請,候選人要經過兩到三輪的選拔,包括電話面試(phone interview),網上做題(online coding)等等。只有通過了這幾輪測試得到了公司招聘部門的青睞,才有機會拿到「免費旅遊」的機會。所以,天下真的有免費的午餐,但這種機會大多屬於有準備的人。來自領英獵頭的勾搭當然,機會偶爾也會光顧沒有什麼準備的人,比如說我。
  • 【IPO】從PAMiD看射頻前端模塊化的演進與未來;
    模塊化的演進眾所周知,射頻前端作為所有通信設備的核心,包括射頻功放、濾波器(雙工器)、射頻開關、射頻低噪放、天線調諧、包絡跟蹤等,決定了通信質量、信號功率、信號帶寬、網絡連接速度等。縱觀智慧型手機中射頻前端的發展歷程,其集成度和價值量也成倍增加。
  • 什麼是模塊化?如何使用模塊化?
    主要內容什麼是模塊化為什麼使用模塊化模塊嵌套Requirejs使用>學習目標第一節 什麼是模塊化1.1 模塊化產生模塊化發展歷程js一開始並沒有模塊化的概念,直到ajax被提出,前端能夠像後端請求數據,前端邏輯越來越複雜,就出現了許多問題
  • 前端模擬面試【一面】
    我個人認為面試有時候有點像應試教育【雖然不想承認,但現實就是如此】,就類似我們的高考,我們得刷題等。那竟然有《五年高考三年模擬》,那模擬面試我覺得也是一個很好的準備過程。優點的話,我覺得有以下幾點:可以通過模擬面試找到自己的不足之處【建議先複習一段時間再進行模擬面試,要不意義不大】,達到查漏補缺的效果不用浪費面試官和自己的時間,畢竟有時候面試跑來跑去挺累的還有一個優點,就是有別人能夠指點你一下,所以建議模擬面試官最好也是一名前端,這樣其實遇到一些問題,還能夠相互討論一下。
  • 年末的大廠前端面試總結(20屆雙非二本)-終入字節
    講真,面試官很真誠,說我的表現是超預期,但是在年限和經驗上有所欠缺,hr那邊可能會影響定級。我表示理解,畢竟確實年限短。印象最深的是算法部分,面試官出了題,我直接說了思路,好像還巴拉巴拉說了一下其他的。面試官說:哎呀,我只是想讓你做個題...(撒嬌語氣??) 好的,做題。