<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Fork me on GitHub

      import、require、export、module.exports 混合使用詳解

      自從使用了 es6 的模塊系統(tǒng)后,各種地方愉快地使用 import 、 export default,但也會在老項目中看到使用commonjs規(guī)范的 requiremodule.exports。甚至有時候也會常??吹絻烧呋ビ玫膱鼍啊J褂脹]有問題,但其中的關(guān)聯(lián)與區(qū)別不得其解,使用起來也糊里糊涂。比如:

      1. 為何有的地方使用 require 去引用一個模塊時需要加上 default? require('xx').default
      2. 經(jīng)常在各大UI組件引用的文檔上會看到說明import { button } from 'xx-ui'這樣會引入所有組件內(nèi)容,需要添加額外的 babel 配置,比如 babel-plugin-component?
      3. 為什么可以使用 es6 的 import 去引用 commonjs 規(guī)范定義的模塊,或者反過來也可以又是為什么?
      4. 我們在瀏覽一些 npm 下載下來的 UI 組件模塊時(比如說 element-ui 的 lib 文件下),看到的都是 webpack 編譯好的 js 文件,可以使用 importrequire 再去引用。但是我們平時編譯好的 js 是無法再被其他模塊 import 的,這是為什么?
      5. babel 在模塊化的場景中充當了什么角色?以及 webpack ?哪個啟到了關(guān)鍵作用?
      6. 聽說 es6 還有 tree-shaking 功能,怎么才能使用這個功能?

      如果你對這些問題都了然于心,那么可以關(guān)掉本文了,如果有疑問,這篇文章就是為你準備的!

      webpack 與 babel 在模塊化中的作用

      webpack 模塊化的原理

      webpack 本身維護了一套模塊系統(tǒng),這套模塊系統(tǒng)兼容了所有前端歷史進程下的模塊規(guī)范,包括 amd commonjs es6 等,本文主要針對 commonjs es6 規(guī)范進行說明。模塊化的實現(xiàn)其實就在最后編譯的文件內(nèi)。
      我編寫了一個 demo 更好的展示效果。

      // webpack
      
      const path = require('path');
      
      module.exports = {
        entry: './a.js',
        output: {
          path: path.resolve(__dirname, 'dist'),
          filename: 'bundle.js',
        }
      };
      
      
      
      
      // a.js
      import a from './c';
      
      export default 'a.js';
      console.log(a);
      
      
      
      
      
      // c.js
      
      export default 333;
      
      
      
      (function(modules) {
      
        
        function __webpack_require__(moduleId) {
          var module =  {
            i: moduleId,
            l: false,
            exports: {}
          };
          modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
          return module.exports;
        }
      
        return __webpack_require__(0);
      })([
        (function (module, __webpack_exports__, __webpack_require__) {
      
          // 引用 模塊 1
          "use strict";
          Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
          /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(1);
      
      /* harmony default export */ __webpack_exports__["default"] = ('a.js');
      console.log(__WEBPACK_IMPORTED_MODULE_0__c__["a" /* default */]);
      
        }),
        (function (module, __webpack_exports__, __webpack_require__) {
      
          // 輸出本模塊的數(shù)據(jù)
          "use strict";
          /* harmony default export */ __webpack_exports__["a"] = (333);
        })
      ]);
      
      
      
      

      上面這段 js 就是使用 webpack 編譯后的代碼(經(jīng)過精簡),其中就包含了 webpack的運行時代碼,其中就是關(guān)于模塊的實現(xiàn)。

      我們再精簡下代碼,會發(fā)現(xiàn)這是個自執(zhí)行函數(shù)。

      
      (function(modules) {
      
      })([]);
      
      
      

      自執(zhí)行函數(shù)的入?yún)⑹莻€數(shù)組,這個數(shù)組包含了所有的模塊,包裹在函數(shù)中。
      自執(zhí)行函數(shù)體里的邏輯就是處理模塊的邏輯。關(guān)鍵在于 __webpack_require__ 函數(shù),這個函數(shù)就是 require 或者是 import 的替代,我們可以看到在函數(shù)體內(nèi)先定義了這個函數(shù),然后調(diào)用了他。這里會傳入一個 moduleId,這個例子中是0,也就是我們的入口模塊 a.js 的內(nèi)容。
      我們再看 __webpack_require__ 內(nèi)執(zhí)行了

      即從入?yún)⒌?modules 數(shù)組中取第一個函數(shù)進行調(diào)用,并入?yún)?/p>

      • module
      • module.exports
      • webpack_require

      我們再看第一個函數(shù)(即入口模塊)的邏輯(精簡):

      function (module, __webpack_exports__, __webpack_require__) {
      
      /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(1);
      
          /* harmony default export */ __webpack_exports__["default"] = ('a.js');
          console.log(__WEBPACK_IMPORTED_MODULE_0__c__["a" /* default */]);
      
        }
      
      

      我們可以看到入口模塊又調(diào)用了 __webpack_require__(1) 去引用入?yún)?shù)組里的第2個函數(shù)。
      然后會將入?yún)⒌?__webpack_exports__ 對象添加 default 屬性,并賦值。
      這里我們就能看到模塊化的實現(xiàn)原理,這里的 __webpack_exports__ 就是這個模塊的 module.exports 通過對象的引用傳參,間接的給 module.exports 添加屬性。
      最后會將 module.exports return 出來。就完成了 __webpack_require__ 函數(shù)的使命。
      比如在入口模塊中又調(diào)用了 __webpack_require__(1),就會得到這個模塊返回的 module.exports。
      **但在這個自執(zhí)行函數(shù)的底部,webpack 會將入口模塊的輸出也進行返回 **

      return __webpack_require__(0);
      

      目前這種編譯后的js,將入口模塊的輸出(即 module.exports) 進行輸出沒有任何作用,只會作用于當前作用域。這個js并不能被其他模塊繼續(xù)以 requireimport 的方式引用。

      babel 的作用

      按理說 webpack 的模塊化方案已經(jīng)很好的將es6 模塊化轉(zhuǎn)換成 webpack 的模塊化,但是其余的 es6 語法還需要做兼容性處理。babel 專門用于處理 es6 轉(zhuǎn)換 es5。當然這也包括 es6 的模塊語法的轉(zhuǎn)換。

      其實兩者的轉(zhuǎn)換思路差不多,區(qū)別在于 webpack 的原生轉(zhuǎn)換 可以多做一步靜態(tài)分析,使用tree-shaking 技術(shù)(下面會講到)

      babel 能提前將 es6 的 import 等模塊關(guān)鍵字轉(zhuǎn)換成 commonjs 的規(guī)范。這樣 webpack 就無需再做處理,直接使用 webpack 運行時定義的 __webpack_require__ 處理。

      這里就解釋了 問題5。

      babel 在模塊化的場景中充當了什么角色?以及 webpack ?哪個啟到了關(guān)鍵作用?

      那么 babel 是如何轉(zhuǎn)換 es6 的模塊語法呢?

      #導出模塊

      es6 的導出模塊寫法有

      export default 123;
      
      export const a = 123;
      
      const b = 3;
      const c = 4;
      export { b, c };
      
      

      babel 會將這些統(tǒng)統(tǒng)轉(zhuǎn)換成 commonjs 的 exports。

      exports.default = 123;
      exports.a = 123;
      exports.b = 3;
      exports.c = 4;
      exports.__esModule = true;
      
      

      babel 轉(zhuǎn)換 es6 的模塊輸出邏輯非常簡單,即將所有輸出都賦值給 exports,并帶上一個標志 __esModule 表明這是個由 es6 轉(zhuǎn)換來的 commonjs 輸出。
      babel將模塊的導出轉(zhuǎn)換為commonjs規(guī)范后,也會將引入 import 也轉(zhuǎn)換為 commonjs 規(guī)范。即采用 require 去引用模塊,再加以一定的處理,符合es6的使用意圖。

      引入 default

      對于最常見的

      import a from './a.js';
      

      在es6中 import a from './a.js' 的本意是想去引入一個 es6 模塊中的 default 輸出。
      通過babel轉(zhuǎn)換后得到 var a = require(./a.js) 得到的對象卻是整個對象,肯定不是 es6 語句的本意,所以需要對 a 做些改變。
      我們在導出提到,default 輸出會賦值給導出對象的default屬性。

      exports.default = 123;
      
      

      所以 babel 加了個 help _interopRequireDefault 函數(shù)。

      function _interopRequireDefault(obj) {
          return obj && obj.__esModule
              ? obj
              : { 'default': obj };
      }
      
      var _a = require('assert');
      var _a2 = _interopRequireDefault(_a);
      
      var a = _a2['default'];
      

      所以這里最后的 a 變量就是 require 的值的 default 屬性。如果原先就是commonjs規(guī)范的模塊,那么就是那個模塊的導出對象。

      引入 * 通配符

      我們使用 import * as a from './a.js' es6語法的本意是想將 es6 模塊的所有命名輸出以及defalut輸出打包成一個對象賦值給a變量。

      已知以 commonjs 規(guī)范導出:

      exports.default = 123;
      exports.a = 123;
      exports.b = 3;
      exports.__esModule = true;
      
      

      那么對于 es6 轉(zhuǎn)換來的輸出通過 var a = require('./a.js') 導入這個對象就已經(jīng)符合意圖。

      所以直接返回這個對象。

      if (obj && obj.__esModule) {
         return obj;
      }
      
      

      如果本來就是 commonjs 規(guī)范的模塊,導出時沒有default屬性,需要添加一個default屬性,并把整個模塊對象再次賦值給default屬性。

      function _interopRequireWildcard(obj) {
          if (obj && obj.__esModule) {
              return obj;
          }
          else {
              var newObj = {}; // (A)
              if (obj != null) {
                  for (var key in obj) {
                      if (Object.prototype.hasOwnProperty.call(obj, key))
                          newObj[key] = obj[key];
                  }
              }
              newObj.default = obj;
              return newObj;
          }
      }
      
      
      import { a } from './a.js'
      

      直接轉(zhuǎn)換成 require('./a.js').a 即可。

      總結(jié)
      經(jīng)過上面的轉(zhuǎn)換分析,我們得知即使我們使用了 es6 的模塊系統(tǒng),如果借助 babel 的轉(zhuǎn)換,es6 的模塊系統(tǒng)最終還是會轉(zhuǎn)換成 commonjs 的規(guī)范。所以我們?nèi)绻鞘褂?babel 轉(zhuǎn)換 es6 模塊,混合使用 es6 的模塊和 commonjs 的規(guī)范是沒有問題的,因為最終都會轉(zhuǎn)換成 commonjs。
      這里解釋了問題3

      為什么可以使用 es6 的 import 去引用 commonjs 規(guī)范定義的模塊,或者反過來也可以又是為什么?

      babel5 & babel6

      我們在上文 babel 對導出模塊的轉(zhuǎn)換提到,es6 的 export default 都會被轉(zhuǎn)換成 exports.default,即使這個模塊只有這一個輸出。

      這也解釋了問題1

      為何有的地方使用 require 去引用一個模塊時需要加上 default?require('xx').default

      我們經(jīng)常會使用 es6 的 export default 來輸出模塊,而且這個輸出是這個模塊的唯一輸出,我們會誤以為這種寫法輸出的是模塊的默認輸出。

      // a.js
      
      export default 123;
      
      // b.js 錯誤
      
      var foo = require('./a.js')
      

      在使用 require 進行引用時,我們也會誤以為引入的是a文件的默認輸出。
      結(jié)果這里需要改成 var foo = require('./a.js').default
      這個場景在寫 webpack 代碼分割邏輯時經(jīng)常會遇到。

      require.ensure([], (require) => {
         callback(null, [
           require('./src/pages/profitList').default,
         ]);
       });
      

      這是 babel6 的變更,在 babel5 的時候可不是這樣的。

      在 babel5 時代,大部分人在用 require 去引用 es6 輸出的 default,只是把 default 輸出看作是一個模塊的默認輸出,所以 babel5 對這個邏輯做了 hack,如果一個 es6 模塊只有一個 default 輸出,那么在轉(zhuǎn)換成 commonjs 的時候也一起賦值給 module.exports,即整個導出對象被賦值了 default 所對應的值。
      這樣就不需要加 default,require('./a.js') 的值就是想要的 default值。
      但這樣做是不符合 es6 的定義的,在es6 的定義里,default 只是個名字,沒有任何意義。

      export default = 123;
      export const a = 123;
      
      

      這兩者含義是一樣的,分別為輸出名為 default 和 a 的變量。

      還有一個很重要的問題,一旦 a.js 文件里又添加了一個具名的輸出,那么引入方就會出麻煩。

      // a.js
      
      export default 123;
      
      export const a = 123; // 新增
      
      // b.js 
      
      var foo = require('./a.js');
      
      // 由之前的 輸出 123
      // 變成 { default: 123, a: 123 }
      

      所以 babel6 去掉了這個hack,這是個正確的決定,升級 babel6 后產(chǎn)生的不兼容問題 可以通過引入 babel-plugin-add-module-exports 解決。

      webpack 編譯后的js,如何再被其他模塊引用

      通過 webpack 模塊化原理章節(jié)給出的 webpack 配置編譯后的 js 是無法被其他模塊引用的,webpack 提供了 output.libraryTarget 配置指定構(gòu)建完的 js 的用途。

      默認 var

      如果指定了 output.library = 'test'
      入口模塊返回的 module.exports 暴露給全局 var test = returned_module_exports

      commonjs

      如果library: 'spon-ui' 入口模塊返回的 module.exports 賦值給 exports['spon-ui']

      commonjs2

      入口模塊返回的 module.exports 賦值給 module.exports
      所以 element-ui 的構(gòu)建方式采用 commonjs2 ,導出的組件的js 最后都會賦值給 module.exports,供其他模塊引用。

      這里解釋了問題4

      我們在瀏覽一些 npm 下載下來的 UI 組件模塊時(比如說 element-ui 的 lib 文件下),看到的都是 webpack 編譯好的 js 文件,可以使用 import 或 require 再去引用。但是我們平時編譯好的 js 是無法再被其他模塊 import 的,這是為什么?

      模塊依賴的優(yōu)化

      我們在使用各大 UI 組件庫時都會被介紹到為了避免引入全部文件,請使用 babel-plugin-component 等babel 插件。

      import { Button, Select } from 'element-ui'
      

      由前文可知 import 會先轉(zhuǎn)換為 commonjs, 即

      var a = require('element-ui');
      var Button = a.Button;
      var Select = a.Select;
      

      var a = require('element-ui'); 這個過程就會將所有組件都引入進來了。
      所以 babel-plugin-component就做了一件事,將 import { Button, Select } from 'element-ui' 轉(zhuǎn)換成了

      import Button from 'element-ui/lib/button'
      import Select from 'element-ui/lib/select'
      

      即使轉(zhuǎn)換成了 commonjs 規(guī)范,也只是引入自己這個組件的js,將引入量減少到最低。
      所以我們會看到幾乎所有的UI組件庫的目錄形式都是

      |-lib
      ||--component1
      ||--component2
      ||--component3
      |-index.common.js
      

      index.common.jsimport element from 'element-ui' 這種形式調(diào)用全部組件。
      lib 下的各組件用于按需引用。

      這里解釋了問題2

      經(jīng)常在各大UI組件引用的文檔上會看到說明 import { button } from 'xx-ui' 這樣會引入所有組件內(nèi)容,需要添加額外的 babel 配置,比如 babel-plugin-component

      tree-shaking

      webpack2 開始引入 tree-shaking 技術(shù),通過靜態(tài)分析 es6 的語法,可以刪除沒有被使用的模塊。他只對 es6 的模塊有效,所以一旦 babel 將 es6 的模塊轉(zhuǎn)換成 commonjs,webpack2 將無法使用這項優(yōu)化。所以要使用這項技術(shù),我們只能使用 webpack 的模塊處理,加上 babel 的es6轉(zhuǎn)換能力(需要關(guān)閉模塊轉(zhuǎn)換)。
      最方便的使用方法為修改babel的配置。

      use: {
           loader: 'babel-loader',
           options: {
             presets: [['babel-preset-es2015', {modules: false}]],
           }
         }
      
      

      修改最開始demo

      // webpack
      
      const path = require('path');
      
      module.exports = {
        entry: './a.js',
        output: {
          path: path.resolve(__dirname, 'dist'),
          filename: 'bundle.js',
        },
        module: {
          rules: [
            {
              test: /\.js$/,
              exclude: /(node_modules|bower_components)/,
              use: {
                loader: 'babel-loader',
                options: {
                  presets: [['babel-preset-es2015', {modules: false}]],
                }
              }
            }
          ]
        }
      };
      
      
      // a.js
      import a from './c';
      
      export default 'a.js';
      console.log(a);
      
      
      
      // c.js
      
      export default 333;
      
      const foo = 123;
      export { foo };
      
      

      修改的點在于增加了babel,并關(guān)閉其modules功能。然后在 c.js 中增加一個輸出 export { foo },但是 a.js 中并不引用它。

      最后在編譯出的 js 中,c.js 模塊如下:

      
      "use strict";
      /* unused harmony export foo */
      /* harmony default export */ __webpack_exports__["a"] = (333);
      
      var foo = 123;
      
      

      foo 變量被標記為沒有使用,在最后壓縮時這段會被刪除。
      需要說明的是,即使在 引入模塊時使用了 es6 ,但是引入的那個模塊卻是使用 commonjs 進行輸出,這也無法使用tree-shaking。
      而第三方庫大多是遵循 commonjs 規(guī)范的,這也造成了引入第三方庫無法減少不必要的引入。
      所以對于未來來說第三方庫要同時發(fā)布 commonjs 格式和 es6 格式的模塊。es6 模塊的入口由 package.json 的字段 module 指定。而 commonjs 則還是在 main 字段指定。

      這里解釋了問題6

      聽說 es6 還有 tree-shaking 功能,怎么才能使用這個功能?

      原文鏈接: https://juejin.cn/post/6844903520865386510#heading-2

      posted @ 2022-03-10 10:56  較瘦  閱讀(1746)  評論(0)    收藏  舉報
      知識點文章整理
      主站蜘蛛池模板: 97精品国产91久久久久久久| 亚洲成A人片在线观看无码不卡| 蜜芽久久人人超碰爱香蕉| 动漫AV纯肉无码AV电影网| 国内精品伊人久久久久影院对白| 激情97综合亚洲色婷婷五| 午夜精品福利亚洲国产| 开心五月激情综合久久爱| 亚洲精品av一二三区无码| 国产精品午夜福利小视频| 情欲少妇人妻100篇| 青草成人精品视频在线看| 日韩乱码人妻无码中文字幕视频 | 比如县| 国产一区一一区高清不卡| 中文字幕永久精品国产| 日本久久99成人网站| 亚洲天堂av免费在线看| 天天做天天爱夜夜爽女人爽| 国产又色又爽又黄的网站免费| 四虎永久在线精品免费播放| 中文字幕久区久久中文字幕| 国产suv精品一区二区四| 丝袜高潮流白浆潮喷在线播放| 国产女人18毛片水真多1| 欧美在线人视频在线观看| 日本无人区一区二区三区| 成人一区二区三区在线午夜| 在线观看美女网站大全免费| 麻豆国产va免费精品高清在线| 久久国产精品第一区二区| 无码专区 人妻系列 在线| 亚洲AV永久无码精品秋霞电影影院 | 欧洲中文字幕一区二区| 久久99精品久久久久久青青| 老司机久久99久久精品播放免费| av天堂亚洲天堂亚洲天堂| 俺也来俺也去俺也射| 国产精品国产三级国产试看| 亚洲国产天堂久久综合226114| 亚洲人成网网址在线看|