日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

面条html5,使用 babel 全家桶模块化古老的面条代码

發布時間:2025/3/12 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面条html5,使用 babel 全家桶模块化古老的面条代码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在最近的工作中,接手了一個古老的項目,其中的 JS 代碼是一整坨的面條代碼,約 3000 行的代碼全寫在一個文件里,維護起來著實讓人頭疼。

想不通為啥之前維護項目的同學能夠忍受這么難以維護的代碼……既然現在這個鍋被我拿下了,怎么著也不能容忍如此丑陋的代碼繼續存在著,必須把它優化一下。

橫豎看了半天,由于邏輯都揉在了一個文件里,看都看得眼花繚亂,當務之急便是把它進行模塊化拆分,把這一大坨面條狀代碼拆分成一個個模塊并抽離成文件,這樣才方便后續的持續優化。

一、結構分析

說干就干,既然要拆分成模塊,首先就要分析源碼的結構。雖然源碼內容很長很復雜,但萬幸的是它還是有一個清晰的結構,簡化一下,就是下面這種形式:

很容易看出,這是一種 ES5 時代的經典代碼組織方式,在一個 IIFE 里面放一個構造函數,在構造函數的 protorype 上掛載不同的方法,以實現不同的功能。既然代碼結構是清晰的,那么我們要做模塊化的思路也很清晰,就是想辦法把所有綁定在構造函數的 prototype 上的方法抽離出來,以模塊文件的形式放置,而源碼則使用 ES6 的 import 語句把模塊引入進來,完成代碼的模塊化:

為了完成這個效果,我們可以借助 @babel 全家桶來構造我們的轉化腳本。

二、借助 AST 分析代碼

關于 AST 的相關資料一搜一大堆,在這里就不贅述了。在本文中,我們會借助 AST 去分析源碼,挑選源碼中需要被抽離、改造的部分,因此 AST 可以說是本文的核心。在 https://astexplorer.net/ 這個網站,我們可以貼入示例代碼,在線查看它的 AST 長什么樣:

從右側的 AST 樹中可以很清晰地看到,Demo.prototype.func = function () {} 屬于 AssignmentExpression 節點,即為“賦值語句”,擁有左右兩個不同的節點(left,right)。

由于一段 JS 代碼里可能存在多種賦值語句,而我們只想處理形如 Demo.prototype.func = function () {} 的情況,所以我們需要繼續對其左右兩側的節點進行深入分析。

首先看左側的節點,它屬于一個“MemberExpression”,其特征如下圖箭頭所示:

對于左側的節點,只要它的 object.property.name 的值為 prototype 即可,那么對應的函數名就是該節點的 property.name。

接著看右側的節點,它屬于一個“FunctionExpression”:

我們要做的,就是把它提取出來作為一個獨立的文件。

分析完了 AST 以后,我們已經知道需要被處理的代碼都有一些什么樣的特征,接下來就是針對這些特征進行操作了,這時候就需要我們的 @babel 全家桶出場了!

三、處理代碼

首先我們需要安裝四個工具,它們分別是:@babel/parser:用于把 JS 源碼轉化成 AST;

@babel/traverse:用于遍歷 AST 樹,獲取當中的節點內容;

@babel/generator:把 AST 節點轉化成對應的 JS 代碼;

@babel/types:新建 AST 節點。

接下來新建一個 index.js 文件,引入上面四個工具,并設法加載我們的源碼(源碼為 demo/es5code.js):const fs = require('fs')

const { resolve } = require('path')

const parser = require('@babel/parser')

const traverse = require('@babel/traverse').default

const generator = require('@babel/generator').default

const t = require('@babel/types')

const INPUT_CODE = resolve(__dirname, '../demo/es5code.js')

const code = fs.readFileSync(`${INPUT_CODE}`, 'utf-8')

接著使用 @babel/parser 獲取源碼的 AST:const ast = parser.parse(code)

拿到 AST 以后,就可以使用 @babel/traverse 來遍歷它的節點。從上一節的 AST 分析可以知道,我們只需要關注“AssignmentExpression”節點即可:traverse(ast, {

AssignmentExpression ({ node }) {

/* ... */

}

})

當前節點即為參數 node,我們需要分析它左右兩側的節點。只有當左側節點的類型為“MemberExpression”且右側節點的類型為“FunctionExpression”才需要進入下一步分析(因為形如 a = 1 之類的節點也屬于 AssignmentExpression 類型,不在我們的處理范圍內)。

由于 JS 中可能存在不同的 MemberExpression 節點,如 a.b.c = function () {},但我們現在只需要處理 a.prototype.func 的情況,意味著要盯著關鍵字 prototype。通過分析 AST 節點,我們知道這個關鍵字位于左側節點的 object.property.name 屬性中:

同時對應的函數名則藏在左側節點的 property.name 屬性中:

因此便可以很方便地提取出方法名:traverse(ast, {

AssignmentExpression ({ node }) {

const { left, right } = node

if (left.type === 'MemberExpression' && right.type === 'FunctionExpression') {

const { object, property } = left

if (object.property.name === 'prototype') {

const funcName = property.name // 提取出方法名

console.log(funcName)

}

}

}

})

可以很方便地把方法名打印出來檢查:

現在我們已經分析完左側節點的代碼,提取出了方法名。接下來則是處理右側節點。由于右側代碼直接就是一個 FunctionExpression 節點,因此我們要做的就是通過 @babel/generator 把該節點轉化成 JS 代碼,并寫入文件。

此外,我們也要把原來的代碼從 Demo.prototype.func = function () {} 轉化成 Demo.prototype.func = func 的形式,因此右側的節點需要從“FuncitionExpression”類型轉化成“Identifier”類型,我們可以借助 @babel/types 來處理。

還有一個事情別忘了,就是我們已經把右側節點的代碼抽離成了 JS 文件,那么我們也應該在最終改造完的源文件里把它們給引入進來,形如 import func1 from './func1'這種形式,因此可以繼續使用 @babel/types 的 importDeclaration() 函數來生成對應的代碼。這個函數參數比較復雜,可以封裝成一個函數:function createImportDeclaration (funcName) {

return t.importDeclaration([t.importDefaultSpecifier(t.identifier(funcName))], t.stringLiteral(`./${funcName}`))

}

只需要傳入一個 funcName,就可以生成一段 import funcName from './funcName' 代碼。

最終整體代碼如下:const fs = require('fs')

const { resolve } = require('path')

const parser = require('@babel/parser')

const traverse = require('@babel/traverse').default

const generator = require('@babel/generator').default

const t = require('@babel/types')

const INPUT_CODE = resolve(__dirname, '../demo/es5code.js')

const OUTPUT_FOLDER = resolve(__dirname, '../output')

const code = fs.readFileSync(`${INPUT_CODE}`, 'utf-8')

const ast = parser.parse(code)

function createFile (filename, code) {

fs.writeFileSync(`${OUTPUT_FOLDER}/${filename}.js`, code, 'utf-8')

}

function createImportDeclaration (funcName) {

return t.importDeclaration([t.importDefaultSpecifier(t.identifier(funcName))], t.stringLiteral(`./${funcName}`))

}

traverse(ast, {

AssignmentExpression ({ node }) {

const { left, right } = node

if (left.type === 'MemberExpression' && right.type === 'FunctionExpression') {

const { object, property } = left

if (object.property.name === 'prototype') {

// 獲取左側節點的方法名

const funcName = property.name

// 獲取右側節點對應的 JS 代碼

const { code: funcCode } = generator(right)

// 右側節點改為 Identifier

const replacedNode = t.identifier(funcName)

node.right = replacedNode

// 借助 `fs.writeFileSync()` 把右側節點的 JS 代碼寫入外部文件

createFile(funcName, 'export default ' + funcCode)

// 在文件頭部引入抽離的文件

ast.program.body.unshift(createImportDeclaration(funcName))

}

}

}

})

// 輸出新的文件

createFile('es6code', generate(ast).code)

四、運行腳本

在我們的項目目錄中,其結構如下:.

├── demo

│ └── es5code.js

├── output

├── package.json

└── src

└── index.js

運行腳本,demo/es5code.js 的代碼將會被處理,然后輸出到 output 目錄:.

├── demo

│ └── es5code.js

├── output

│ ├── es6code.js

│ ├── func1.js

│ ├── func2.js

│ └── func3.js

├── package.json

└── src

└── index.js

看看我們的代碼:

大功告成!把腳本運用到我們的項目中,甚至可以發現原來的約 3000 行代碼,已經被整理成了 300 多行:

放到真實環境去跑一遍這段代碼,原有功能不受影響!

小結

剛剛接手這個項目,我的內心是一萬頭神獸奔騰而過,內心是非常崩潰的。但是既然接手了,就值得好好對待它。借助 AST 和 @babel 全家桶,我們就有了充分改造源碼的手段。花半個小時個腳本,把丑陋的面條代碼整理成清晰的模塊化代碼,內心的陰霾一掃而空,對這個古老的項目更是充滿了期待——會不會有更多的地方可以被改造被優化呢?值得拭目以待!

關于找一找教程網

本站文章僅代表作者觀點,不代表本站立場,所有文章非營利性免費分享。

本站提供了軟件編程、網站開發技術、服務器運維、人工智能等等IT技術文章,希望廣大程序員努力學習,讓我們用科技改變世界。

[使用 babel 全家桶模塊化古老的面條代碼]http://www.zyiz.net/tech/detail-147399.html

總結

以上是生活随笔為你收集整理的面条html5,使用 babel 全家桶模块化古老的面条代码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。