狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

[轉帖]【推薦】前端調試神器,支持 Vue、React 等多框架!!!

liguoquan
2024年3月1日 16:14 本文熱度 740
:【推薦】前端調試神器,支持 Vue、React 等多框架!!!


背景及相關信息

不知道你是否遇到過產品或者測試給你一個頁面讓你改一點東西,你卻找不到頁面源代碼在哪里的場景?對于一些大型項目,文件數量多、文件層級深、代碼行數多,查找一個頁面上組件對應的源代碼位置,往往需要花費大量時間。

為了解決這個問題,我開發了 code-inspector-plugin 插件,只需要點擊頁面上的元素,就能夠自動打開 vscode 定位到源代碼。已經在快手內部30+項目中接入了使用,取得了不錯的反響。效果如下圖所示:

點擊下述的 demo,也可以快速在線體驗效果:

  • vue online demo[1]
  • react online demo[2]
  • preact online demo[3]
  • solid online demo[4]
  • 接入:想要使用的小伙伴,可以參考code-inspector-plugin接入文檔[5]接入使用
  • github 源碼:覺得插件好用的可以辛苦動下小手幫作者 github 點個 star:code-inspector[6]

code-inspector-plugin 的優點

其實 code-inspector-plugin 是之前我看到過一篇 react 點擊頁面元素定位源代碼的文章,受到啟發后實現的。但是相比而言, code-inspector-plugin 在支持場景的豐富性以及接入的便捷程度上,都得到了巨大的提升,具備以下優勢:

  • 支持的打包器更加廣泛:支持 webpack/vite/rspack 以及 umi 等一切基于上述三個打包器實現的打包工具
  • 支持的框架及場景更加廣泛:支持 vue2/vue3/react/preact/solid 框架以及 next/nuxt 等SSR場景(以及一切以 vue2/vue3/react/preact/solid 框架為基礎封裝的 SSR 場景),支持在微前端中使用。
  • 支持多種系統及 IDE:支持 Mac、Windows 和 Linux 系統,支持 vscode、webstorm、atom、hbuilderX、IDEA、phpsotrm 等多種 IDE,也支持自定義 IDE 的支持
  • 接入更加簡便,對代碼無侵入:無論是在什么項目中,只需要在 webpack/vite/rspack 的配置中添加 code-inspector-plugin 插件即可,不需要修改任何源代碼或者其他的配置
  • 自動識別環境:插件內部會針對 webpack/vite/rspack 開發環境下的一些內置信息,自動識別環境,僅在開發環境下生效,不會影響生產環境

code-inspector-plugin 實現原理

下面我們重點解析一下 code-inspector-plugin 的實現原理,插件的整體功能可以簡單拆解為以下幾部分:

  • 參與源碼編譯:打包工具(webpack/vite/rspack)編譯時,code-inspector-plugin 插件會參與編譯過程,對于 vue/jsx 語法會進行 ast 解析,獲取到 dom 部分的源代碼所在的 文件路徑、行、列 信息,并將這些信息作為 dom 上的 attribute 額外添加進去。
  • 運行時交互代碼:編譯完成后,插件會向網頁中注入監聽按鍵定位源代碼的交互邏輯,當用戶點擊定位 dom 時,能夠獲取 dom 的 attribute 上的 文件路徑、行、列 信息,將信息發送一個 http 請求給后臺
  • 啟動一個 node server 服務:在后臺啟動一個 node server 服務,用于接收上一步發送過來的 http 請求
  • 識別并打開 IDE:node server 收到請求后,根據請求帶過來的 文件路徑、行、列 信息,使用 node 的 spawn 或者 exec 子進程打開 IDE,并將鼠標定位到 IDE 對應的位置

編譯 vue/jsx 源代碼

要參與源代碼的編譯過程,對于 vite 項目,我們可以通過 vite 插件的 transform 函數入口中實現;對于 webpack/rspack 項目,可以實現一個 loader 實現。不同的打包工具只是對應的入口不同,而對于 vue/jsx 語法的編譯和解析過程都是公用的。

編譯 vue 語法

對于 vue 語法的編譯,我們可以使用 vue 內置的包 @vue/compiler-dom 實現,以及通過 magic-string 包來向 ast 注入額外的信息,簡化的代碼如下:

import { parse, transform } from '@vue/compiler-dom';
import MagicString from 'magic-string';

// content 是由 vite transform 函數或者 webpack/rspack loader 傳過來的源代碼
const s = new MagicString(content);

// vue/react 部分內置元素添加 attrs 可能報錯,不處理
const escapeTags = [
  'style',
  'script',
  'template',
  'transition',
  'keepalive',
  'keep-alive',
  'component',
  'slot',
  'teleport',
  'transition-group',
  'transitiongroup',
  'suspense',
  "fragment"
];

if (fileType === 'vue') {
  // vue template 處理
  const ast = parse(content, {
    comments: true,
  });

  transform(ast, {
    nodeTransforms: [
      ((node: TemplateChildNode) => {
        // node.type === 1 說明是元素(排除掉 text、comment 等)
        if (
          !node.loc.source.includes('data-insp-path') &&
          node.type === 1 &&
          escapeTags.indexOf(node.tag.toLowerCase()) === -1
        
) {
          // 向 dom 上添加一個帶有 filepath/row/column 的屬性
          const insertPosition =
            node.loc.start.offset + node.tag.length + 1;
          const { line, column } = node.loc.start;
          // filePath 也是 vite transform 函數或者 webpack/rspack loader 傳過來的
          const addition = ` data-insp-path="${filePath}:${line}:${column}:${
            node.tag
          }"${node.props.length ? ' ' : ''}`;

          s.prependLeft(insertPosition, addition);
        }
      }
as NodeTransform,
    ],
  });

  return s.toString();
}

編譯 tsx 代碼

對于 tsx 語法的編譯和解析使用 babel 實現,并且需要引入一些 babel 相關的包,完成對于 ts、vueJsx 等場景的兼容,簡化的代碼如下:

import MagicString from 'magic-string';
import type { TemplateChildNode, NodeTransform } from '@vue/compiler-dom';
import vueJsxPlugin from '@vue/babel-plugin-jsx';
import { parse as babelParse, traverse as babelTraverse } from '@babel/core';
import tsPlugin from '@babel/plugin-transform-typescript';
import importMetaPlugin from '@babel/plugin-syntax-import-meta';
import proposalDecorators from '@babel/plugin-proposal-decorators';

// content 是由 vite transform 函數或者 webpack/rspack loader 傳過來的源代碼
const s = new MagicString(content);

// vue/react 部分內置元素添加 attrs 可能報錯,不處理
const escapeTags = [
  'style',
  'script',
  'template',
  'transition',
  'keepalive',
  'keep-alive',
  'component',
  'slot',
  'teleport',
  'transition-group',
  'transitiongroup',
  'suspense',
  "fragment"
];

if (fileType === 'jsx') {
  // jsx 處理
  const ast = babelParse(content, {
    babelrc: false,
    comments: true,
    configFile: false,
    plugins: [
      importMetaPlugin,
      [vueJsxPlugin, {}],
      [tsPlugin, { isTSX: true, allowExtensions: true }],
      [proposalDecorators, { legacy: true }],
    ],
  });

  babelTraverse(ast, {
    enter({ node }: any) {
      if (
        node.type === 'JSXElement' &&
        escapeTags.indexOf(
          (node?.openingElement?.name?.name || '').toLowerCase()
        ) === -1 &&
        node?.openingElement?.name?.name
      ) {
        if (
          node.openingElement.attributes.some(
            (attr: any) =>
              attr.type !== 'JSXSpreadAttribute' &&
              attr.name.name === 'data-insp-path'
          )
        ) {
          return;
        }

        // 向 dom 上添加一個帶有 filepath/row/column 的屬性
        const insertPosition =
          node.openingElement.end -
          (node.openingElement.selfClosing ? 2 : 1);
        const { line, column } = node.loc.start;
        // filePath 也是 vite transform 函數或者 webpack/rspack loader 傳過來的
        const addition = ` data-insp-path="${filePath}:${line}:${column + 1}:${
          node.openingElement.name.name
        }
"${node.openingElement.attributes.length ? ' ' : ''}`
;

        s.prependLeft(insertPosition, addition);
      }
    },
  });
  return s.toString();
}

上面 vue/jsx 編譯完成后,其實相當于在源代碼基礎上為每個 dom 注入了一個 data-insp-path 屬性,最終元素到頁面上,對應的 dom 就會添加一個這樣的屬性,如下圖所示:

運行時交互注入

code-inspector-plugin 插件的交互功能主要包含監聽兩部分:

  • 監聽組合鍵按住時,鼠標在 dom 上移動時會出現 DOM 遮罩層信息
  • 點擊遮罩層會獲取 DOM attribute 上的源代碼信息,向后臺發送一個請求

這部分功能的實現上難度不大,就是基礎的 html+js+css,為了保證 js 邏輯和 css 樣式不會影響到宿主頁面,我采用了 web component 組件的方式來封裝了這部分邏輯(基于 lit 實現的 web component)。具體的實現細節將不多講了,源碼位于 packages/core/src/client/index.ts[7] 文件中。

為了簡化用戶的使用,不需要用戶手動向頁面中添加交互邏輯的組件,我通過 webpack/vite/rspack 插件,在 development 環境下將 web component 組件注入到頁面中。

本地的 Node Server 服務

Node Server 同樣是插件在 webpack/vite/rspack 開始編譯的時候啟動的,用于監聽用戶發送 http 請求。

我們設置了一個默認的端口 6666,為了防止端口沖突,我們需要使用 portFinder 繼續向下尋找一個可用的接口去啟動服務:

import http from 'http';
import portFinder from 'portfinder';
import path from 'path';
import launchEditor from './launch-editor';

const DefaultPort = 6666;

export function startServer(callback: (port: number) => any, editor?: Editor{
  const server = http.createServer((req: any, res: any) => {
    // 收到請求喚醒vscode
    const params = new URLSearchParams(req.url.slice(1));
    const file = params.get('file'as string;
    const line = Number(params.get('line'));
    const column = Number(params.get('column'));
    res.writeHead(200, {
      'Access-Control-Allow-Origin''*',
      'Access-Control-Allow-Methods''*',
      'Access-Control-Allow-Headers''*',
      'Access-Control-Allow-Private-Network''true',
    });
    res.end('ok');
    launchEditor(file, line, column, editor);
  });

  // 尋找可用接口
  portFinder.getPort({ port: DefaultPort }, (err: Error, port: number) => {
    if (err) {
      throw err;
    }
    server.listen(port, () => {
      callback(port);
    });
  });
}

識別并打開 IDE

Node Server 接收到了請求后,需要打開用戶的 IDE 并定位到源代碼,這一步是如何實現的呢?

市面上大多數的 IDE,多支持通過 {IDE路徑} -g {path}:{line}:{column} 的終端命令,打開 IDE 并將鼠標光標定位到指定的位置,部分 IDE 還支持在全局安裝命令行工具簡化使用。以 vscode 為例,有兩種方式:

  1. 在終端通過 vscode 應用路徑直接打開應用
  2. 通過安裝 vscode 提供的命令行工具,在終端通過 code 指令喚醒,launching-from-the-command-line[8]

這里我們采用了第二種方式,通過 node 的 spwan 或者 exec 啟動一個子進程,執行 code -g 文件路徑:行:列 就能打開 vscode 并定位到對應的文件路徑、行、列位置,簡化代碼如下:

function launchEditor(
  fileName: string,
  lineNumber: unknown,
  colNumber: unknown,
  _editor?: Editor
{
  // others code....

  let [editor, ...args] = guessEditor(_editor);

  // others code....

  _childProcess = child_process.spawn(editor, args, { stdio: 'inherit' });
}

除了如何打開 IDE 的問題,另一個要解決的問題是,如果用戶設備上安裝了多種 IDE,我們要打開哪個 IDE?

這個功能我們是基于 react-devtools[9] 的源碼實現了,它會去匹配用戶當前設備上正在運行的進程,在 IDE 列表中匹配打開。在此基礎上我們優化并豐富了這部分的功能,支持了以下特性:

  • 優化了 IDE 的匹配順序:因為對于 web 項目,大多數開發者使用的 IDE 是 vscode 或者 webstorm,所以我們會優先匹配這兩個 IDE
  • 支持用戶指定 IDE:支持用戶通過在 .env.local 文件中指定聲明要打開的 IDE,除了內置支持識別的 IDE 外,用戶也可以用 IDE 可執行路徑方式指定(意味著支持所有 IDE)

代碼架構設計

上面的實現原理中,我們講述了 code-inspector-plugin 插件的核心內容,除了這部分之外,還想分享下我們在代碼可維護性和用戶使用體驗方面所做的努力。

分包提升可維護性

上述核心內容的實現,絕大部分是與 vite/webpack/rspack 等打包器無關的,打包器插件只是作為代碼編譯和交互代碼注入的入口承載。所以我們采用 monorepo 架構,將核心代碼都提取到了 core 中,monorepo 的包如下:

📦packages
┣ 📂code-inspector-plugin --------------------------   入口包
┣ 📂core ----------------------------------------  核心代碼處理
┣ 📂vite-plugin ---------------------------------  vite 插件
┗ 📂webpack-plugin ---------------------------   webpack 插件

其中,vite-plugin 和 webpack-plugin 分別作為 vite 和 webpack 的入口。(rspack 由于在插件系統的設計上完全支持了 webpack,所以可以直接使用 webpack-plugin 作為 rspack 的入口,如果后面二者出現差異,會考慮再分一個 rspack-plugin 的包)。

同時為了降低用戶在多種打包器中的接入心智,我們使用 code-inspector-plugin 將 vite/webpack/rspack 等不同項目的插件進行了整合作為唯一入口,用戶只需要通過 bundler 參數指定項目的打包器即可,其他配置完全一致。

降低用戶接入本

在降低用戶成本方面,我們主要做了兩件事情:

  1. 為了讓用戶不需要修改任何的源代碼,我們對于頁面交互的代碼,直接通過插件注入,不需要用戶手動引入任何的組件,對用戶代碼無任何侵入。
  2. 對于 webpack 和 rspack 的項目,像啟動 node 服務這種邏輯是在插件中實現的,而參與源代碼的編譯需要在 loader 中實現。雖然讓用戶同時接入一個 plugin 和一個 loader 成本也沒有那么高,但是為了最大程度降低用戶接入成本,我們插件會在 webpack/rspack 編譯前,自動將 loader 添加到 module.rules 中,用戶只需要接入一個 plugin 即可,免去了 loader 的接入成本。

原文地址:https://juejin.cn/post/7326002010084311079



該文章在 2024/3/1 16:14:54 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved