日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Rollup-構(gòu)建原理及簡(jiǎn)易實(shí)現(xiàn)

一、Rollup 概述

官網(wǎng)地址:https://rollupjs.org/guide/en/

Rollup 是什么

我們先看看 Rollup 的作者 Rich Harris 是怎么講的?Rollup 是一個(gè)模塊化的打包工具。本質(zhì)上,它會(huì)合并 JavaScript 文件。而且你不需要去手動(dòng)指定它們的順序,或者去擔(dān)心文件之間的變量名沖突。它的內(nèi)部實(shí)現(xiàn)會(huì)比說(shuō)的復(fù)雜一點(diǎn),但是它就是這么做的 —— 合并。

對(duì)比 Webpack

webpack 對(duì)前端來(lái)說(shuō)是再熟悉不過(guò)的工具了,它提供了強(qiáng)大的功能來(lái)構(gòu)建前端的資源,包括 html/js/ts/css/less/scss ... 等語(yǔ)言腳本,也包括 images/fonts ... 等二進(jìn)制文件。正是因?yàn)?webpack 擁有如此強(qiáng)大的功能,所以 webpack 在進(jìn)行資源打包的時(shí)候,就會(huì)產(chǎn)生很多冗余的代碼(如果你有查看過(guò) webpack 的 bundle 文件,便會(huì)發(fā)現(xiàn))。

而對(duì)于一些項(xiàng)目(特別是類庫(kù))只有 js,而沒(méi)有其他的靜態(tài)資源文件,使用 webpack 就有點(diǎn)大才小用了,因?yàn)?webpack bundle 文件的體積略大,運(yùn)行略慢,可讀性略低。這個(gè)時(shí)候就可以選擇 Rollup

Rollup是一個(gè)模塊打包器,支持 ES6 模塊,支持 Tree-shaking,但不支持 webpack 的 code-splitting、模塊熱更新等,這意味著它更適合用來(lái)做類庫(kù)項(xiàng)目的打包器而不是應(yīng)用程序項(xiàng)目的打包器。

簡(jiǎn)單總結(jié)

對(duì)于應(yīng)用適用于 webpack,對(duì)于類庫(kù)更適用于 Rollup,react/vue/anngular 都在用Rollup作為打包工具

二、Rollup 前置知識(shí)

在闡述 Rollup 的構(gòu)建原理之前,我們需要了解一些前置知識(shí)

magic-string

magic-string 是 Rollup 作者寫的一個(gè)關(guān)于字符串操作的庫(kù),這個(gè)庫(kù)主要是對(duì)字符串一些常用方法進(jìn)行了封裝

 
 
 
 
  1. var MagicString = require('magic-string')
  2. var magicString = new MagicString('export var name = "zhangsan"')
  3. // 以下所有操作都是基于原生字符串
  4. // 類似于截取字符串
  5. console.log(magicString.snip(0, 6).toString()) // export
  6. // 從開始到結(jié)束刪除
  7. console.log(magicString.remove(0, 7).toString()) //  var name = "zhangsan"
  8. // 多個(gè)模塊,把他們打包在一個(gè)文件里,需要把很多文件的源代碼合并在一起
  9. let bundleString = new MagicString.Bundle();
  10. bundleString.addSource({
  11.     content: 'console.log(hello)',
  12.     separator: '\n'
  13. })
  14. bundleString.addSource({
  15.     content: 'console.log(world)',
  16.     separator: '\n'
  17. })
  18. // // 原理類似
  19. // let str = ''
  20. // str += 'console.log(hello);\n'
  21. // str += 'console.log(world);\n'
  22. console.log(bundleString.toString()) 
  23. // hello
  24. // world

AST

通過(guò) javascript parse 可以把代碼轉(zhuǎn)化為一顆抽象語(yǔ)法樹 AST,這顆樹定義了代碼的結(jié)構(gòu),通過(guò)操縱這個(gè)樹,我們可以精確的定位到聲明語(yǔ)句、賦值語(yǔ)句、運(yùn)算符語(yǔ)句等等,實(shí)現(xiàn)對(duì)代碼的分析、優(yōu)化、變更等操作 源代碼:main.js

 
 
 
 
  1. // main.js
  2. import { a } from './a'
  3. console.log(a)

轉(zhuǎn)化為 AST 是長(zhǎng)這樣子的,如下:

 
 
 
 
  1. {
  2.   "type": "Program", // 這個(gè) AST 類型為 Program,表明是一個(gè)程序
  3.   "start": 0,
  4.   "end": 40,
  5.   "body": [ // body 是一個(gè)數(shù)組,每一條語(yǔ)句都對(duì)應(yīng) body 下的一個(gè)語(yǔ)句
  6.     {
  7.       "type": "ImportDeclaration", // 導(dǎo)入聲明類型
  8.       "start": 0,
  9.       "end": 23,
  10.       "specifiers": [
  11.         {
  12.           "type": "ImportSpecifier",
  13.           "start": 9,
  14.           "end": 10,
  15.           "imported": {
  16.             "type": "Identifier",
  17.             "start": 9,
  18.             "end": 10,
  19.             "name": "a" // 導(dǎo)入模塊命名 name 'a'
  20.           },
  21.           "local": {
  22.             "type": "Identifier",
  23.             "start": 9,
  24.             "end": 10,
  25.             "name": "a" // 本地模塊命名,同 imported.name
  26.           }
  27.         }
  28.       ],
  29.       "source": {
  30.         "type": "Literal",
  31.         "start": 18,
  32.         "end": 23,
  33.         "value": "./a", // 導(dǎo)入路徑 './a'
  34.         "raw": "'./a'"
  35.       }
  36.     },
  37.     {
  38.       "type": "ExpressionStatement", // 表達(dá)式類型
  39.       "start": 24,
  40.       "end": 38,
  41.       "expression": {
  42.         "type": "CallExpression", // 調(diào)用表達(dá)式類型
  43.         "start": 24,
  44.         "end": 38,
  45.         "callee": {
  46.           "type": "MemberExpression",
  47.           "start": 24,
  48.           "end": 35,
  49.           "object": {
  50.             "type": "Identifier",
  51.             "start": 24,
  52.             "end": 31,
  53.             "name": "console"
  54.           },
  55.           "property": {
  56.             "type": "Identifier",
  57.             "start": 32,
  58.             "end": 35,
  59.             "name": "log"
  60.           },
  61.           "computed": false,
  62.           "optional": false
  63.         },
  64.         "arguments": [
  65.           {
  66.             "type": "Identifier",
  67.             "start": 36,
  68.             "end": 37,
  69.             "name": "a"
  70.           }
  71.         ],
  72.         "optional": false
  73.       }
  74.     }
  75.   ],
  76.   "sourceType": "module"
  77. }

AST 工作流

Parse(解析)將代碼轉(zhuǎn)化成抽象語(yǔ)法樹,樹上有很多的 estree 節(jié)點(diǎn) Transform(轉(zhuǎn)換) 對(duì)抽象語(yǔ)法樹進(jìn)行轉(zhuǎn)換 Generate(代碼生成) 將上一步經(jīng)過(guò)轉(zhuǎn)換過(guò)的抽象語(yǔ)法樹生成新的代碼

acorn

acorn 是一個(gè) JavaScript 語(yǔ)法解析器,它將 JavaScript 字符串解析成語(yǔ)法抽象樹 AST 如果想了解 AST 語(yǔ)法樹可以點(diǎn)下這個(gè)網(wǎng)址https://astexplorer.net/

作用域/作用域鏈

在 js 中,作用域是用來(lái)規(guī)定變量訪問(wèn)范圍的規(guī)則, 作用域鏈?zhǔn)怯僧?dāng)前執(zhí)行環(huán)境和上層執(zhí)行環(huán)境的一系列變量對(duì)象組成的,它保證了當(dāng)前執(zhí)行環(huán)境對(duì)符合訪問(wèn)權(quán)限的變量和函數(shù)的有序訪問(wèn)

三、Rollup

Rollup 是怎樣工作的呢?

你給它一個(gè)入口文件 —— 通常是 index.js。Rollup 將使用 Acorn 讀取解析文件 —— 將返回給我們一種叫抽象語(yǔ)法樹(AST)的東西。一旦有了 AST ,你就可以發(fā)現(xiàn)許多關(guān)于代碼的東西,比如它包含哪些 import 聲明。

假設(shè) index.js 文件頭部有這樣一行:

 
 
 
 
  1. import foo from './foo.js';

這就意味著 Rollup 需要去加載,解析,分析在 index.js 中引入的 ./foo.js。重復(fù)解析直到?jīng)]有更多的模塊被加載進(jìn)來(lái)。更重要的是,所有的這些操作都是可插拔的,所以您可以從 node_modules 中導(dǎo)入或者使用 sourcemap-aware 的方式將 ES2015 編譯成 ES5 代碼。

在 Rollup 中,一個(gè)文件就是一個(gè)模塊,每個(gè)模塊都會(huì)根據(jù)文件的代碼生成一個(gè) AST 抽象語(yǔ)法樹。

分析 AST 節(jié)點(diǎn),就是看這個(gè)節(jié)點(diǎn)有沒(méi)有調(diào)用函數(shù)方法,有沒(méi)有讀到變量,有,就查看是否在當(dāng)前作用域,如果不在就往上找,直到找到模塊頂級(jí)作用域?yàn)橹?。如果本模塊都沒(méi)找到,說(shuō)明這個(gè)函數(shù)、方法依賴于其他模塊,需要從其他模塊引入。如果發(fā)現(xiàn)其他模塊中有方法依賴其他模塊,就會(huì)遞歸讀取其他模塊,如此循環(huán)直到?jīng)]有依賴的模塊為止 找到這些變量或著方法是在哪里定義的,把定義語(yǔ)句包含進(jìn)來(lái)即可 其他無(wú)關(guān)代碼一律不要

看如下代碼,我們先實(shí)際操作一下:

 
 
 
 
  1. // index.js
  2. import { foo } from "./foo";
  3. foo()
  4. var city = 'hangzhou'
  5. function test() {
  6.     console.log('test')
  7. }
  8. console.log(test())
 
 
 
 
  1. // foo.js
  2. import { bar } from "./bar";
  3. export function foo() {
  4.     console.log('foo')
  5. }
 
 
 
 
  1. // bar.js
  2. export function bar() {
  3.     console.log('bar')
  4. }
 
 
 
 
  1. // rollup.config.js
  2. export default {
  3.     input: './src/index.js',
  4.     output: {
  5.         file: './dist/bundle.js', // 打包后的存放文件
  6.         format: 'cjs', //輸出格式 amd es6 life umd cjs
  7.         name: 'bundleName', //如果輸出格式 life,umd 需要指定一個(gè)全局變量
  8.     }
  9. };

執(zhí)行 npm run build,會(huì)得到如下結(jié)果:

 
 
 
 
  1. 'use strict';
  2. function foo() {
  3.     console.log('foo');
  4. }
  5. foo();
  6. function test() {
  7.     console.log('test');
  8. }
  9. console.log(test());

以上,我們可以看到Rollup 只是會(huì)合并你的代碼 —— 沒(méi)有任何浪費(fèi)。所產(chǎn)生的包也可以更好的縮小。有人稱之為 “作用域提升(scope hoisting)”。其次,它把你導(dǎo)入的模塊中的未使用代碼移除。這被稱為“(搖樹優(yōu)化)treeshaking”。總之,Rollup 就是一個(gè)模塊化的打包工具。

接下來(lái)我們進(jìn)入源碼,具體分析下 Rollup 的構(gòu)建流程

Rollup 構(gòu)建流程分析

Rollup 源碼結(jié)構(gòu)

 
 
 
 
  1. │  bundle.js // Bundle 打包器,在打包過(guò)程中會(huì)生成一個(gè) bundle 實(shí)例,用于收集其他模塊的代碼,最后再將收集的代碼打包到一起。
  2. │  external-module.js // ExternalModule 外部模塊,例如引入了 'path' 模塊,就會(huì)生成一個(gè) ExternalModule 實(shí)例。
  3. │  module.js // Module 模塊,module 實(shí)例。
  4. │  rollup.js // rollup 函數(shù),一切的開始,調(diào)用它進(jìn)行打包。
  5. ├─ast // ast 目錄,包含了和 AST 相關(guān)的類和函數(shù)
  6. │      analyse.js // 主要用于分析 AST 節(jié)點(diǎn)的作用域和依賴項(xiàng)。
  7. │      Scope.js // 在分析 AST 節(jié)點(diǎn)時(shí)為每一個(gè)節(jié)點(diǎn)生成對(duì)應(yīng)的 Scope 實(shí)例,主要是記錄每個(gè) AST 節(jié)點(diǎn)對(duì)應(yīng)的作用域。
  8. │      walk.js // walk 就是遞歸調(diào)用 AST 節(jié)點(diǎn)進(jìn)行分析。
  9. ├─finalisers
  10. │      cjs.js
  11. │      index.js
  12. └─utils // 一些幫助函數(shù)
  13.         map-helpers.js
  14.         object.js
  15.         promise.js
  16.         replaceIdentifiers.js

Rollup 構(gòu)建流程

我們以 index.js 入口文件,index 依賴了 foo.js,foo 依賴了 bar.js

 
 
 
 
  1. // index.js
  2. import { foo } from "./foo";
  3. foo()
  4. var city = 'hangzhou'
  5. function test() {
  6.     console.log('test')
  7. }
  8. console.log(test())
 
 
 
 
  1. // foo.js
  2. import { bar } from "./bar";
  3. export function foo() {
  4.     console.log('foo')
  5. }
 
 
 
 
  1. // bar.js
  2. export function bar() {
  3.     console.log('bar')
  4. }

debug 起來(lái)!!!

 
 
 
 
  1. // debug.js
  2. const path = require('path')
  3. const rollup = require('./lib/rollup')
  4. // 入口文件的絕對(duì)路徑
  5. let entry = path.resolve(__dirname, 'src/main.js')
  6. // 和源碼有所不同,這里使用的是同步,增加可讀性
  7. rollup(entry, 'bundle.js')

1.new Bundle(), build()

首先生成一個(gè) Bundle 實(shí)例,也就是打包器。然后執(zhí)行 build 打包編譯

 
 
 
 
  1. // rollup.js
  2. let Bundle = require('./bundle')
  3. function rollup(entry, outputFileName) {
  4.     // Bundle 代表打包對(duì)象,里面包含所有的模塊信息
  5.     const bundle = new Bundle({ entry })
  6.     // 調(diào)用 build 方法開始進(jìn)行編譯
  7.     bundle.build(outputFileName)
  8. }
  9. module.exports = rollup

lib/bundle.js根據(jù)入口路徑出發(fā)(在 bundle 中,我們會(huì)首先統(tǒng)一處理下入口文件的后綴),去找到他的模塊定義,在 fetchModule 中,會(huì)生成一個(gè) module 實(shí)例

我們關(guān)注紅框中的代碼,會(huì)發(fā)現(xiàn)返回了一個(gè) module

2.new Module()

每個(gè)文件都是一個(gè)模塊,每個(gè)模塊都會(huì)有一個(gè) Module 實(shí)例。在 Module 實(shí)例中,會(huì)調(diào)用 acorn 庫(kù)的 parse() 方法將代碼解析成 AST。

對(duì)生成的 AST 進(jìn)行分析analyse我們先看一下入口文件 index.js 生成的 AST

可以看到 ast.body 是一個(gè)數(shù)組,分別對(duì)應(yīng) index.js 的五條語(yǔ)句 展開這個(gè) ast 樹如下:

 
 
 
 
  1. {
  2.   "type": "Program",
  3.   "start": 0,
  4.   "end": 128,
  5.   "body": [
  6.     {
  7.       "type": "ImportDeclaration", // 導(dǎo)入聲明
  8.       "start": 0,
  9.       "end": 31,
  10.       "specifiers": [
  11.         {
  12.           "type": "ImportSpecifier",
  13.           "start": 9,
  14.           "end": 12,
  15.           "imported": {
  16.             "type": "Identifier",
  17.             "start": 9,
  18.             "end": 12,
  19.             "name": "foo"
  20.           },
  21.           "local": {
  22.             "type": "Identifier",
  23.             "start": 9,
  24.             "end": 12,
  25.             "name": "foo"
  26.           }
  27.         }
  28.       ],
  29.       "source": {
  30.         "type": "Literal",
  31.         "start": 20,
  32.         "end": 30,
  33.         "value": "./foo.js",
  34.         "raw": "\"./foo.js\""
  35.       }
  36.     },
  37.     {
  38.       "type": "ExpressionStatement",
  39.       "start": 32,
  40.       "end": 37,
  41.       "expression": {
  42.         "type": "CallExpression",
  43.         "start": 32,
  44.         "end": 37,
  45.         "callee": {
  46.           "type": "Identifier",
  47.           "start": 32,
  48.           "end": 35,
  49.           "name": "foo"
  50.         },
  51.         "arguments": [],
  52.         "optional": false
  53.       }
  54.     },
  55.     {
  56.       "type": "VariableDeclaration",
  57.       "start": 38,
  58.       "end": 59,
  59.       "declarations": [
  60.         {
  61.           "type": "VariableDeclarator",
  62.           "start": 42,
  63.           "end": 59,
  64.           "id": {
  65.             "type": "Identifier",
  66.             "start": 42,
  67.             "end": 46,
  68.             "name": "city"
  69.           },
  70.           "init": {
  71.             "type": "Literal",
  72.             "start": 49,
  73.             "end": 59,
  74.             "value": "hangzhou",
  75.             "raw": "'hangzhou'"
  76.           }
  77.         }
  78.       ],
  79.       "kind": "var"
  80.     },
  81.     {
  82.       "type": "FunctionDeclaration",
  83.       "start": 61,
  84.       "end": 104,
  85.       "id": {
  86.         "type": "Identifier",
  87.         "start": 70,
  88.         "end": 74,
  89.         "name": "test"
  90.       },
  91.       "expression": false,
  92.       "generator": false,
  93.       "async": false,
  94.       "params": [],
  95.       "body": {
  96.         "type": "BlockStatement",
  97.         "start": 77,
  98.         "end": 104,
  99.         "body": [
  100.           {
  101.             "type": "ExpressionStatement",
  102.             "start": 83,
  103.             "end": 102,
  104.             "expression": {
  105.               "type": "CallExpression",
  106.               "start": 83,
  107.               "end": 102,
  108.               "callee": {
  109.                 "type": "MemberExpression",
  110.                 "start": 83,
  111.                 "end": 94,
  112.                 "object": {
  113.                   "type": "Identifier",
  114.                   "start": 83,
  115.                   "end": 90,
  116.                   "name": "console"
  117.                 },
  118.                 "property": {
  119.                   "type": "Identifier",
  120.                   "start": 91,
  121.                   "end": 94,
  122.                   "name": "log"
  123.                 },
  124.                 "computed": false,
  125.                 "optional": false
  126.               },
  127.               "arguments": [
  128.                 {
  129.                   "type": "Literal",
  130.                   "start": 95,
  131.                   "end": 101,
  132.                   "value": "test",
  133.                   "raw": "'test'"
  134.                 }
  135.               ],
  136.               "optional": false
  137.             }
  138.           }
  139.         ]
  140.       }
  141.     },
  142.     {
  143.       "type": "ExpressionStatement",
  144.       "start": 106,
  145.       "end": 125,
  146.       "expression": {
  147.         "type": "CallExpression",
  148.         "start": 106,
  149.         "end": 125,
  150.         "callee": {
  151.           "type": "MemberExpression",
  152.           "start": 106,
  153.           "end": 117,
  154.           "object": {
  155.             "type": "Identifier",
  156.             "start": 106,
  157.             "end": 113,
  158.             "name": "console"
  159.           },
  160.           "property": {
  161.             "type": "Identifier",
  162.             "start": 114,
  163.             "end": 117,
  164.             "name": "log"
  165.           },
  166.           "computed": false,
  167.           "optional": false
  168.         },
  169.         "arguments": [
  170.           {
  171.             "type": "CallExpression",
  172.             "start": 118,
  173.             "end": 124,
  174.             "callee": {
  175.               "type": "Identifier",
  176.               "start": 118,
  177.               "end": 122,
  178.               "name": "test"
  179.             },
  180.             "arguments": [],
  181.             "optional": false
  182.           }
  183.         ],
  184.         "optional": false
  185.       }
  186.     }
  187.   ],
  188.   "sourceType": "module"
  189. }

我們通過(guò)這個(gè) AST 樹,分析 **analyse **具體做了什么???

第一步:分析當(dāng)前模塊導(dǎo)入【import】和導(dǎo)出【exports】模塊,將引入的模塊和導(dǎo)出的模塊存儲(chǔ)起來(lái)this.imports = {};//存放著當(dāng)前模塊所有的導(dǎo)入

this.exports = {};//存放著當(dāng)前模塊所有的導(dǎo)出

 
 
 
 
  1. this.imports = {};//存放著當(dāng)前模塊所有的導(dǎo)入
  2.   this.exports = {};//存放著當(dāng)前模塊所有的導(dǎo)出
  3.   this.ast.body.forEach(node => {
  4.     if (node.type === 'ImportDeclaration') {// 說(shuō)明這是一個(gè) import 語(yǔ)句
  5.       let source = node.source.value; // 從哪個(gè)模塊導(dǎo)入的
  6.       let specifiers = node.specifiers; // 導(dǎo)入標(biāo)識(shí)符
  7.       specifiers.forEach(specifier => {
  8.         const name = specifier.imported.name; //name
  9.         const localName = specifier.local.name; //name
  10.         //本地的哪個(gè)變量,是從哪個(gè)模塊的的哪個(gè)變量導(dǎo)出的
  11.         this.imports[localName] = { name, localName, source }
  12.       });
  13.       //}else if(/^Export/.test(node.type)){ // 導(dǎo)出方法有很多
  14.     } else if (node.type === 'ExportNamedDeclaration') { // 說(shuō)明這是一個(gè) exports 語(yǔ)句
  15.       let declaration = node.declaration;//VariableDeclaration
  16.       if (declaration.type === 'VariableDeclaration') {
  17.         let name = declaration.declarations[0].id.name;
  18.         this.exports[name] = {
  19.           node, localName: name, expression: declaration
  20.         }
  21.       }
  22.     }
  23.   });
  24.   analyse(this.ast, this.code, this);//找到了_defines 和 _dependsOn

打斷點(diǎn)可以看到,foo 已經(jīng)被存入 imports =》** import { foo } from "./foo"; ** exports:{} 表示沒(méi)有導(dǎo)出語(yǔ)句

第二步:analyse(this.ast, this.code, this); //找到_defines 和 _dependsOn

找出當(dāng)前模塊使用到了哪些變量 標(biāo)記哪些變量時(shí)當(dāng)前模塊聲明的,哪些變量是導(dǎo)入別的模塊的變量 我們定義以下字段用來(lái)存放:_defines: { value: {} },//存放當(dāng)前模塊定義的所有的全局變量

_dependsOn: { value: {} },//當(dāng)前模塊沒(méi)有定義但是使用到的變量,也就是依賴的外部變量_included: { value: false, writable: true },//此語(yǔ)句是否已經(jīng)被包含到打包結(jié)果中,防止重復(fù)打包_source: { value: magicString.snip(statement.start, statement.end) } //magicString.snip 返回的還是 magicString 實(shí)例 clone

分析每個(gè) AST 節(jié)點(diǎn)之間的作用域,構(gòu)建 scope tree,

 
 
 
 
  1. function analyse(ast, magicString, module) {
  2.     let scope = new Scope();//先創(chuàng)建一個(gè)模塊內(nèi)的全局作用域
  3.     //遍歷當(dāng)前的所有的語(yǔ)法樹的所有的頂級(jí)節(jié)點(diǎn)
  4.     ast.body.forEach(statement => {
  5.     
  6.         //給作用域添加變量 var function const let 變量聲明
  7.         function addToScope(declaration) {
  8.             var name = declaration.id.name;//獲得這個(gè)聲明的變量
  9.             scope.add(name);
  10.             if (!scope.parent) {//如果當(dāng)前是全局作用域的話
  11.                 statement._defines[name] = true;
  12.             }
  13.         }
  14.         Object.defineProperties(statement, {
  15.             _defines: { value: {} },//存放當(dāng)前模塊定義的所有的全局變量
  16.             _dependsOn: { value: {} },//當(dāng)前模塊沒(méi)有定義但是使用到的變量,也就是依賴的外部變量
  17.             _included: { value: false, writable: true },//此語(yǔ)句是否已經(jīng) 被包含到打包結(jié)果中了
  18.             //start 指的是此節(jié)點(diǎn)在源代碼中的起始索引,end 就是結(jié)束索引
  19.             //magicString.snip 返回的還是 magicString 實(shí)例 clone
  20.             _source: { value: magicString.snip(statement.start, statement.end) }
  21.         });
  22.         
  23.         //這一步在構(gòu)建我們的作用域鏈
  24.         walk(statement, {
  25.             enter(node) {
  26.                 let newScope;
  27.   &nb
    網(wǎng)頁(yè)名稱:Rollup-構(gòu)建原理及簡(jiǎn)易實(shí)現(xiàn)
    網(wǎng)頁(yè)鏈接:http://m.5511xx.com/article/cccegcg.html