React 项目 在开发模式下 点击页面元素 唤起 IDE

320 阅读7分钟

背景: 现在已经有 babel-plugin-jsxFileAttribut 这个babel 插件,此插件只能在展示的 DOM 上展示组件的位置信息,但是开发者还需要手动将文件地址 复制下来 去 vscode 粘贴进来才能找到文件,比较麻烦。现在提供点击页面元素直接唤起 ide功能,方便开发者快速定位源码位置信息。 部分插件完成工作大概在一年半前,因为离职换工作,现在开源出来。有好的坑,内推我😊

需要使用到的 npm包有3个,分别是 babel-plugin-jsxFileAttribut launch-ide-loader webpack-plugin-forceinsertscripttag

(其中 launch-ide-loaderwebpack-plugin-forceinsertscripttag 两者选一个即可,两者在不同的场景使用拥有不同的选择 )

效果:

录屏2024-06-23-17.08.101111_1.gif

  1. 开启成功在控制台有如下图的提示

image.png

  1. babel-plugin-jsxFileAttribut 包的功能是: 在 babel 编译文件阶段找到被编译的文件,并且把 文件位置信息注入到 元素标签上, 之前写过一篇文章记录这个包的用法仓库地址

  2. webpack-plugin-forceinsertscripttag 包的功能是: 在 webpack 打包阶段 调用 html-webpack-pluginafterTemplateExecution 钩子去插入一段 script,被插入的 script 就是唤起 IDE 的script。 (webpack-plugin-forceinsertscripttag 可在 编译阶段 插入任意一段 script, 欢迎大家在业务中使用) 仓库地址之前写过一篇文章记录这个包的用法。 此插件基于html-webpack-plugin 实现的,所以你的项目如果没有使用 html-webpack-plugin ,接入此插件不会按照预期工作,最近我会在写一个插件弥补这部分工作,(已经完成了, 新的webpack插件: webpack-plugin-addscriptforhtmlplugin, 此插件不依赖任何webpack插件,功能和 webpack-plugin-forceinsertscripttag一致 )

  3. launch-ide-loader 包的功能是: 在 webpack 打包阶段 插入一段唤起 IDE 的script。本质上和 webpack-plugin-forceinsertscripttag 没有区别。但是launch-ide-loader 支持文件改变 (这个文件是唤起不同的 IDE 名字),重新再次编译,相当于支持了热更新。仓库地址

如何配置: 场景1 -> 这里的配置 适合整个团队统一使用同一个ide的情况

  1. 如何使用
  2. 首先安装 babel-plugin-jsxFileAttribut 、webpack-plugin-forceinsertscripttag
npm i babel-plugin-jsxFileAttribut webpack-plugin-forceinsertscripttag -D
  1. babel.config.js 配置(只在dev 开启这个插件即可)
// babel.config.js
const basePlugins = [...] // 原来配置的 babel plugins

// isDev 根据各自的环境变量来判断
const babelPlugins = isDev
  ? [
      ...basePlugins,
      [
        "babel-plugin-jsxfileattribute",
        {
          showCompleteFilePath: true,
          isShowAwakeIdeMsg: false,
        },
      ],
    ]
  : basePlugins;

module.exports = {
  presets: ["react-app"],
  plugins: babelPlugins,
};
  1. webpack.config.js 配置(只在dev 开启这个插件即可)
// webpack.config.js
module.exports = {
    ...,
    plugins: [
        ...,
        // isDev 根据各自的环境变量来判断
        isDev && new webpackPluginForceinsertscriptTag({
          isShift: false,
          isInsertBody: true,
          jsDeferLoad: false,
          jsAsyncLoad: false,
          isLaunchIdeJs: true,
          // ideName: 'vscode' -> 这里默认 唤起的是 vscode ide
        }),
        ....
    ].filter(Boolean),
    ....
}
  1. 面对多样化的开发环境,团队成员可能采用不同的集成开发环境(IDE),这要求我们的构建配置具备灵活性以适应每位开发者的个性化需求。具体而言,通过在webpackPluginForceInsertScriptTag插件中设定ideName参数来指定目标IDE,确实能够精准适配各成员的编辑器偏好。然而,这一配置调整因其直接作用于核心的webpack.config.js文件,不可避免地引入了一定程度的侵入性,特别是在维护大型团队项目时,这种针对特定IDE的定制化配置不易统一管理,可能导致版本控制上的困扰——每次个性化的调整都意味着此配置文件不宜直接提交至共享代码库,这无疑增加了协作的复杂度和不便,影响了团队的整体协同效率。因此,寻找一种既能够满足个性化IDE配置需求,又能保持webpack.config.js无侵入性改动的解决方案,成为了提升开发体验的关键所在。(这段话使用GPT美化了一下, 翻译一下: 团队有人用 vscode 有人用webstorm,那么 这个 webpackPluginForceinsertscriptTagideName 配置项 就需要定制化改动 , 那么改动的人还不能提交 webpack.config.js 因为会影响别人使用)

如何配置: 场景2 -> 这里的配置 适合整个团队没有强制性要求统一使用同一个ide的情况, 拥有千人千面的ide配置。但是千人千面的 ide 配置没有热更新的功能,(即改了配置文件,需要重启进行编译)

  1. 如何使用配置千人千面配置开发
  2. 首先安装 babel-plugin-jsxFileAttribut 、webpack-plugin-forceinsertscripttag
npm i babel-plugin-jsxFileAttribut webpack-plugin-forceinsertscripttag -D
  1. babel.config.js 配置 同上面
  2. webpack.config.js 配置略有不同
// webpack.config.js

/******  定制化配置 (默认 ide 是 vscode, 这里千人千面 可以变更为 自己使用的 ide),会被 webpack-plugin-forceinsertscripttag 合并 ******/
let ignoreConfigPath = path.resolve(__dirname, "./ignore.config.js");
const isExitIgnoreConfigFile = fs.existsSync(ignoreConfigPath);
const ignoreConfig = isExitIgnoreConfigFile ? require(ignoreConfigPath) : {};
/******  定制化配置 (默认 ide 是 vscode, 这里千人千面 可以变更为 自己使用的 ide),会被 webpack-plugin-forceinsertscripttag 合并 *****/

module.exports = {
    ...,
    plugins: [
        ...,
        // isDev 根据各自的环境变量来判断
         isEnvDevelopment &&
        new webpackPluginForceinsertscriptTag({
          isShift: false,
          isInsertBody: true,
          jsDeferLoad: false,
          jsAsyncLoad: false,
          isLaunchIdeJs: true,
          ...ignoreConfig, // 这里的配置 有差异
        }),
        ....
    ].filter(Boolean),
    ....
}
  1. 上面的核心配置就是支持了千人千面的 IDE 配置,维护一个 ignore.config.js, 里面存放唤起的 IDE 配置,这个ignore.config.js 放到 .gitignore 文件里面去。(ideName: 用户的ide名字, userGetUrl: 自定义跳转逻辑,函数的入参数代表 文件的绝对路径, 函数的出参数代表 被 ide 唤起的协议 + 路径 + 行列)
// ignore.config.js
module.exports = {
  ideName: "vscode",

  // 这里的函数是自定义跳转逻辑,函数的入参数代表 文件的绝对路径, 函数的出参数代表 被 ide 唤起的协议 + 路径 + 行列
  userGetUrl(completeFilepath) {
    return "vscode://file" + completeFilepath;
  },
};
  1. 核心: 通过维护 ignore.config.js里面的 ideName 字段配置,达到千人千面开发配置

  2. 此配置有个核心问题: ignore.config.js 没办法做到 热加载,即 改了 ignore.config.js 没办法立即生效,需要重新启动编译一次才可以

如何配置: 场景3 -> 这里的配置 适合整个团队没有强制性要求统一使用同一个ide的情况, 拥有千人千面的ide配置。但是千人千面的 ide 配置有热更新的功能,(即改了配置文件,不需要重启进行编译)

  1. 如何使用 配置千人千面配置开发 且热更新配置文件
  2. 首先安装 babel-plugin-jsxFileAttribut 、webpack-plugin-forceinsertscripttag
npm i babel-plugin-jsxFileAttribut webpack-plugin-forceinsertscripttag -D
  1. babel.config.js 配置 同上面
  2. webpack.config.js 配置略有不同
// webpack.config.js

module.exports = {
    ...,
    module: {
        rules: [
            ...,
             {
                  test: require.resolve("../xxxx.js"), // 这里 随便找个项目里面的 js 文件,相当于编译此文件出发 该 loader
                  use: {
                    loader: "launch-ide-loader",
                    options: {
                      filename: path.resolve(__dirname, "./ignore.config.js"),
                    },
                  },
            },
            ...,
        ]
    }
    ....
}
  1. 上面的核心配置就是支持了千人千面的 IDE 配置,维护一个 ignore.config.js, 里面存放唤起的 IDE 配置,这个ignore.config.js 放到 .gitignore 文件里面去, 且 ignore.config.js 支持了热更新( 在 launch-ide-loader 对传入的 filename 进行了热更新处理)
  2. 热更新原理看仓库代码,上面提供了仓库地址

next接入:

  1. 启用 babel-plugin-jsxFileAttribut 插件,此插件将会在 DOM 上携带组件的位置信息
  2. layout.tsx 里面维护如下代码: (layout.tsx 只会在 ssr 阶段执行, 下面的 条件 require 没问题)
  3. 长久规划,此方案废弃,改为 基于 swc 插件方式实现
// layout.tsx
let launchIDEConfig = () => '';
const isDev = process.env.NODE_ENV === 'development';
if (isDev) {
  launchIDEConfig = require('cus-utils').launchIDEConfig;
}
  
export default async function RootLayout(props) {
  return (
    <html lang={locale}>
      <body className="light">
        {isDev ? (
          <script
            dangerouslySetInnerHTML={{
              __html: launchIDEConfig(),
            }}
          />
        ) : (
          ''
        )}
      </body>
    </html>
  );
}

总结:

  1. 提供3种接入方式
  2. 第一种适合团队统一 ide 的情况
  3. 第二种适合团队定制化,千人千面配置的情况。但是配置文件变更不会触发热更新
  4. 第三种适合合团队定制化,千人千面配置的情况。配置文件变更变会触发热更新