主頁 > 企業開發 > AST抽象語法樹 Javascript版

AST抽象語法樹 Javascript版

2020-10-12 00:50:13 企業開發

在javascript世界中,你可以認為抽象語法樹(AST)是最底層, 再往下,就是關于轉換和編譯的“黑魔法”領域了,

現在,我們拆解一個簡單的add函式

function add(a, b) {
    return a + b
}

首先,我們拿到的這個語法塊,是一個FunctionDeclaration(函式定義)物件,

用力拆開,它成了三塊:

  • 一個id,就是它的名字,即add
  • 兩個params,就是它的引數,即[a, b]
  • 一塊body,也就是大括號內的一堆東西

add沒辦法繼續拆下去了,它是一個最基礎Identifier(標志)物件,用來作為函式的唯一標志,

{
    name: 'add'
    type: 'identifier'
    ...
}

params繼續拆下去,其實是兩個Identifier組成的陣列,之后也沒辦法拆下去了,

[
    {
        name: 'a'
        type: 'identifier'
        ...
    },
    {
        name: 'b'
        type: 'identifier'
        ...
    }
]

接下來,我們繼續拆開body
我們發現,body其實是一個BlockStatement(塊狀域)物件,用來表示是{return a + b}

打開Blockstatement,里面藏著一個ReturnStatement(Return域)物件,用來表示return a + b

繼續打開ReturnStatement,里面是一個BinaryExpression(二項式)物件,用來表示a + b

繼續打開BinaryExpression,它成了三部分,leftoperatorright

  • operator+
  • left 里面裝的,是Identifier物件 a
  • right 里面裝的,是Identifer物件 b

就這樣,我們把一個簡單的add函式拆解完畢,

抽象語法樹(Abstract Syntax Tree),的確是一種標準的樹結構,

那么,上面我們提到的Identifier、Blockstatement、ReturnStatement、BinaryExpression, 這一個個小部件的說明書去哪查?

請查看 AST物件檔案

recast

輸入命令:npm i recast -S

你即可獲得一把操縱語法樹的螺絲刀

接下來,你可以在任意js檔案下操縱這把螺絲刀,我們新建一個parse.js示意:

創建parse.js檔案

// 給你一把"螺絲刀"——recast
const recast = require("recast");

// 你的"機器"——一段代碼
// 我們使用了很奇怪格式的代碼,想測驗是否能維持代碼結構
const code =
  `
  function add(a, b) {
    return a +
      // 有什么奇怪的東西混進來了
      b
  }
  `
// 用螺絲刀決議機器
const ast = recast.parse(code);

// ast可以處理很巨大的代碼檔案
// 但我們現在只需要代碼塊的第一個body,即add函式
const add  = ast.program.body[0]

console.log(add)

輸入node parse.js你可以查看到add函式的結構,與之前所述一致,通過AST物件檔案可查到它的具體屬性:

FunctionDeclaration{
    type: 'FunctionDeclaration',
    id: ...
    params: ...
    body: ...
}

recast.types.builders 制作模具

recast.types.builders里面提供了不少“模具”,讓你可以輕松地拼接成新的機器,

最簡單的例子,我們想把之前的function add(a, b){...}宣告,改成匿名函式式宣告const add = function(a ,b){...}

如何改裝?

第一步,我們創建一個VariableDeclaration變數宣告物件,宣告頭為const, 內容為一個即將創建的VariableDeclarator物件,

第二步,創建一個VariableDeclarator,放置add.id在左邊, 右邊是將創建的FunctionDeclaration物件

第三步,我們創建一個FunctionDeclaration,如前所述的三個組件,id params body中,因為是匿名函式id設為空,params使用add.params,body使用add.body,

這樣,就創建好了const add = function(){}的AST物件,

在之前的parse.js代碼之后,加入以下代碼

// 引入變數宣告,變數符號,函式宣告三種“模具”
const {variableDeclaration, variableDeclarator, functionExpression} = recast.types.builders

// 將準備好的組件置入模具,并組裝回原來的ast物件,
ast.program.body[0] = variableDeclaration("const", [
  variableDeclarator(add.id, functionExpression(
    null, // 匿名化函式運算式.
    add.params,
    add.body
  ))
]);

//將AST物件重新轉回可以閱讀的代碼
//這一行其實是recast.parse的逆向程序,具體公式為
//recast.print(recast.parse(source)).code === source
const output = recast.print(ast).code;

console.log(output)

列印出來還保留著“原裝”的函式內容,連注釋都沒有變,

我們其實也可以列印出美化格式的代碼段:

const output = recast.prettyPrint(ast, { tabWidth: 2 }).code

//輸出為
const add = function(a, b) {
  return a + b;
};

實戰進階:命令列修改js檔案

除了parse/print/builder以外,Recast的三項主要功能:

  • run: 通過命令列讀取js檔案,并轉化成ast以供處理,
  • tnt: 通過assert()和check(),可以驗證ast物件的型別,
  • visit: 遍歷ast樹,獲取有效的AST物件并進行更改,

通過一個系列小務來學習全部的recast工具庫:

demo.js

function add(a, b) {
  return a + b
}

function sub(a, b) {
  return a - b
}

function commonDivision(a, b) {
  while (b !== 0) {
    if (a > b) {
      a = sub(a, b)
    } else {
      b = sub(b, a)
    }
  }
  return a
}

recast.run 命令列檔案讀取

新建一個名為read.js的檔案,寫入

read.js

recast.run( function(ast, printSource){
    printSource(ast)
})

命令列輸入

node read demo.js

我們查以看到js檔案內容列印在了控制臺上,

我們可以知道,node read可以讀取demo.js檔案,并將demo.js內容轉化為ast物件,

同時它還提供了一個printSource函式,隨時可以將ast的內容轉換回原始碼,以方便除錯,

recast.visit AST節點遍歷

read.js

#!/usr/bin/env node
const recast  = require('recast')

recast.run(function(ast, printSource) {
  recast.visit(ast, {
      visitExpressionStatement: function({node}) {
        console.log(node)
        return false
      }
    });
});

recast.visit將AST物件內的節點進行逐個遍歷,

注意

  • 你想操作函式宣告,就使用visitFunctionDelaration遍歷,想操作賦值運算式,就使用visitExpressionStatement, 只要在 AST物件檔案中定義的物件,在前面加visit,即可遍歷,
  • 通過node可以取到AST物件
  • 每個遍歷函式后必須加上return false,或者選擇以下寫法,否則報錯:
#!/usr/bin/env node
const recast  = require('recast')

recast.run(function(ast, printSource) {
  recast.visit(ast, {
      visitExpressionStatement: function(path) {
        const node = path.node
        printSource(node)
        this.traverse(path)
      }
    })
});

除錯時,如果你想輸出AST物件,可以console.log(node)

如果你想輸出AST物件對應的原始碼,可以printSource(node)

命令列輸入 node read demo.js進行測驗,

#!/usr/bin/env node 在所有使用recast.run()的檔案頂部都需要加入這一行,它的意義我們最后再討論,

TNT 判斷AST物件型別

TNT,即recast.types.namedTypes,它用來判斷AST物件是否為指定的型別,

TNT.Node.assert(),就像在機器里埋好的摧毀器,當機器不能完好運轉時(型別不匹配),就摧毀機器(報錯退出)

TNT.Node.check(),則可以判斷型別是否一致,并輸出False和True

上述Node可以替換成任意AST物件

例如:TNT.ExpressionStatement.check(),TNT.FunctionDeclaration.assert()

read.js
#!/usr/bin/env node
const recast = require("recast");
const TNT = recast.types.namedTypes

recast.run(function(ast, printSource) {
  recast.visit(ast, {
      visitExpressionStatement: function(path) {
        const node = path.value
        // 判斷是否為ExpressionStatement,正確則輸出一行字,
        if(TNT.ExpressionStatement.check(node)){
          console.log('這是一個ExpressionStatement')
        }
        this.traverse(path);
      }
    });
});

read.js

#!/usr/bin/env node
const recast = require("recast");
const TNT = recast.types.namedTypes

recast.run(function(ast, printSource) {
  recast.visit(ast, {
      visitExpressionStatement: function(path) {
        const node = path.node
        // 判斷是否為ExpressionStatement,正確不輸出,錯誤則全域報錯
        TNT.ExpressionStatement.assert(node)
        this.traverse(path);
      }
    });
});

實戰:用AST修改原始碼,匯出全部方法

exportific.js

現在,我們想讓這個檔案中的函式改寫成能夠全部匯出的形式,例如

function add (a, b) {
    return a + b
}

想改變為

exports.add = (a, b) => {
  return a + b
}

首先,我們先用builders憑空實作一個鍵頭函式

exportific.js
#!/usr/bin/env node
const recast = require("recast");
const {
  identifier:id,
  expressionStatement,
  memberExpression,
  assignmentExpression,
  arrowFunctionExpression,
  blockStatement
} = recast.types.builders

recast.run(function(ast, printSource) {
  // 一個塊級域 {}
  console.log('\n\nstep1:')
  printSource(blockStatement([]))

  // 一個鍵頭函式 ()=>{}
  console.log('\n\nstep2:')
  printSource(arrowFunctionExpression([],blockStatement([])))

  // add賦值為鍵頭函式  add = ()=>{}
  console.log('\n\nstep3:')
  printSource(assignmentExpression('=',id('add'),arrowFunctionExpression([],blockStatement([]))))

  // exports.add賦值為鍵頭函式  exports.add = ()=>{}
  console.log('\n\nstep4:')
  printSource(expressionStatement(assignmentExpression('=',memberExpression(id('exports'),id('add')),
    arrowFunctionExpression([],blockStatement([])))))
});

上面寫了我們一步一步推斷出exports.add = ()=>{}的程序,從而得到具體的AST結構體,

使用node exportific demo.js運行可查看結果,

接下來,只需要在獲得的最終的運算式中,把id('add')替換成遍歷得到的函式名,把引數替換成遍歷得到的函式引數,把blockStatement([])替換為遍歷得到的函式塊級作用域,就成功地改寫了所有函式!

另外,我們需要注意,在commonDivision函式內,參考了sub函式,應改寫成exports.sub

exportific.js
#!/usr/bin/env node
const recast = require("recast");
const {
  identifier: id,
  expressionStatement,
  memberExpression,
  assignmentExpression,
  arrowFunctionExpression
} = recast.types.builders

recast.run(function (ast, printSource) {
  // 用來保存遍歷到的全部函式名
  let funcIds = []
  recast.types.visit(ast, {
    // 遍歷所有的函式定義
    visitFunctionDeclaration(path) {
      //獲取遍歷到的函式名、引數、塊級域
      const node = path.node
      const funcName = node.id
      const params = node.params
      const body = node.body

      // 保存函式名
      funcIds.push(funcName.name)
      // 這是上一步推匯出來的ast結構體
      const rep = expressionStatement(assignmentExpression('=', memberExpression(id('exports'), funcName),
        arrowFunctionExpression(params, body)))
      // 將原來函式的ast結構體,替換成推導ast結構體
      path.replace(rep)
      // 停止遍歷
      return false
    }
  })


  recast.types.visit(ast, {
    // 遍歷所有的函式呼叫
    visitCallExpression(path){
      const node = path.node;
      // 如果函式呼叫出現在函式定義中,則修改ast結構
      if (funcIds.includes(node.callee.name)) {
        node.callee = memberExpression(id('exports'), node.callee)
      }
      // 停止遍歷
      return false
    }
  })
  // 列印修改后的ast原始碼
  printSource(ast)
})

一步到位,發一個最簡單的exportific前端工具

以下代碼添加作了兩個小改動

  1. 添加說明書--help,以及添加了--rewrite模式,可以直接覆寫檔案或默認為匯出*.export.js檔案,
  2. 將之前代碼最后的 printSource(ast)替換成 writeASTFile(ast,filename,rewriteMode)

exportific.js

#!/usr/bin/env node
const recast = require("recast");
const {
  identifier: id,
  expressionStatement,
  memberExpression,
  assignmentExpression,
  arrowFunctionExpression
} = recast.types.builders

const fs = require('fs')
const path = require('path')
// 截取引數
const options = process.argv.slice(2)

//如果沒有引數,或提供了-h 或--help選項,則列印幫助
if(options.length===0 || options.includes('-h') || options.includes('--help')){
  console.log(`
    采用commonjs規則,將.js檔案內所有函式修改為匯出形式,

    選項: -r  或 --rewrite 可直接覆寫原有檔案
    `)
  process.exit(0)
}

// 只要有-r 或--rewrite引數,則rewriteMode為true
let rewriteMode = options.includes('-r') || options.includes('--rewrite')

// 獲取檔案名
const clearFileArg = options.filter((item)=>{
  return !['-r','--rewrite','-h','--help'].includes(item)
})

// 只處理一個檔案
let filename = clearFileArg[0]

const writeASTFile = function(ast, filename, rewriteMode){
  const newCode = recast.print(ast).code
  if(!rewriteMode){
    // 非覆寫模式下,將新檔案寫入*.export.js下
    filename = filename.split('.').slice(0,-1).concat(['export','js']).join('.')
  }
  // 將新代碼寫入檔案
  fs.writeFileSync(path.join(process.cwd(),filename),newCode)
}


recast.run(function (ast, printSource) {
  let funcIds = []
  recast.types.visit(ast, {
    visitFunctionDeclaration(path) {
      //獲取遍歷到的函式名、引數、塊級域
      const node = path.node
      const funcName = node.id
      const params = node.params
      const body = node.body

      funcIds.push(funcName.name)
      const rep = expressionStatement(assignmentExpression('=', memberExpression(id('exports'), funcName),
        arrowFunctionExpression(params, body)))
      path.replace(rep)
      return false
    }
  })


  recast.types.visit(ast, {
    visitCallExpression(path){
      const node = path.node;
      if (funcIds.includes(node.callee.name)) {
        node.callee = memberExpression(id('exports'), node.callee)
      }
      return false
    }
  })

  writeASTFile(ast,filename,rewriteMode)
})

現在嘗試一下

node exportific demo.js

已經可以在當前目錄下找到原始碼變更后的demo.export.js檔案了,

npm發包

編輯一下package.json檔案

{
  "name": "exportific",
  "version": "0.0.1",
  "description": "改寫原始碼中的函式為可exports.XXX形式",
  "main": "exportific.js",
  "bin": {
    "exportific": "./exportific.js"
  },
  "keywords": [],
  "author": "wanthering",
  "license": "ISC",
  "dependencies": {
    "recast": "^0.15.3"
  }
}

注意bin選項,它的意思是將全域命令exportific指向當前目錄下的exportific.js

這時,輸入npm link 就在本地生成了一個exportific命令,

之后,只要哪個js檔案想匯出來使用,就exportific XXX.js一下,

一定要注意exportific.js檔案頭有

#!/usr/bin/env node

接下來,正式發布npm包!

如果你已經有了npm 帳號,請使用npm login登錄

如果你還沒有npm帳號 https://www.npmjs.com/signup 非常簡單就可以注冊npm

然后,輸入
npm publish

沒有任何繁瑣步驟,絲毫審核都沒有,你就發布了一個實用的前端小工具exportific ,任何人都可以通過

npm i exportific -g

全域安裝這一個插件,

提示:在試驗教程時,請不要和我的包重名,修改一下發包名稱,

#!/usr/bin/env node

不同用戶或者不同的腳本解釋器有可能安裝在不同的目錄下,系統如何知道要去哪里找你的解釋程式呢? /usr/bin/env就是告訴系統可以在PATH目錄中查找, 所以配置#!/usr/bin/env node, 就是解決了不同的用戶node路徑不同的問題,可以讓系統動態的去查找node來執行你的腳本檔案,

如果出現No such file or directory的錯誤?因為你的node安裝路徑沒有添加到系統的PATH中,所以去進行node環境變數配置就可以了,

要是你只是想簡單的測驗一下,那么你可以通過which node命令來找到你本地的node安裝路徑,將/usr/bin/env改為你查找到的node路徑即可,


參考文章:https://segmentfault.com/a/1190000016231512?utm_source=tag-newest#comment-area

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/168253.html

標籤:JavaScript

上一篇:json.stringify()與json.parse()

下一篇:JS基礎語法---函式---介紹、定義、函式引數、回傳值

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more