本文提供通用解决方案,适用于任何使用 Rsbuild 的前端项目。
技术栈
- 构建工具: Rsbuild (基于 Rspack)
- 监控平台: Sentry (支持云版和自部署版本)
- 包管理工具: npm/pnpm/yarn
- 核心依赖:
@sentry/browser: Sentry 浏览器端 SDK@sentry/webpack-plugin: Sentry SourceMap 上传插件@rspack/core: Rspack 核心(Rsbuild 内置)
开始集成
第一步:安装依赖
npm install @sentry/browser
npm install -D @sentry/webpack-plugin
-
@sentry/browser:Sentry 浏览器端 SDK,用于在浏览器端捕获错误并上报至 Sentry 服务器。 -
@sentry/webpack-plugin:Sentry SourceMap 上传插件,用于在构建时将 SourceMap 上传到 Sentry 服务器。
第二步:创建 Sentry 初始化文件
创建 src/sentry.js,用于初始化 Sentry。
其中:
YOUR_SENTRY_DSN_HERE 是 Sentry 控制台,创建完项目后获取的 DSN。
__PACKAGE_VERSION__ 是项目版本号,用于在 Sentry 中显示版本号。
import * as Sentry from '@sentry/browser'
Sentry.init({
dsn: 'YOUR_SENTRY_DSN_HERE', // 从 Sentry 控制台获取
environment: process.env.NODE_ENV,
release: __PACKAGE_VERSION__,
tracesSampleRate: 1.0,
})
export default Sentry
第三部:在应用入口引入
修改 src/index.tsx:
import './sentry' // ⚠️ 必须在最前面
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')!).render(<App />)
必须保证 Sentry 初始化文件在应用入口之前引入,才能保证捕获所有错误。
除了在 index.tsx中引入,还可以在rsbuild.config.ts的entry中引入,代码如下:
export default defineConfig({
source: {
entry: {
index: {
import: [
'./src/sentry.js', // ⚠️ 必须在最前面
'./src/index.tsx'
],
},
},
},
})
第四步:配置 Rsbuild
修改 rsbuild.config.ts:
添加 DefinePlugin 插件,注入版本号,这样在 src/sentry.js 中才可以使用 __PACKAGE_VERSION__ 获取版本号。
version 一般为项目根目录下的 package.json 中的 version 字段,可以通过 import 导入。
import { defineConfig } from '@rsbuild/core'
import { DefinePlugin } from '@rspack/core'
import { version } from './package.json'
export default defineConfig({
tools: {
rspack: {
plugins: [
new DefinePlugin({
__PACKAGE_VERSION__: JSON.stringify(version), // 注入版本号
}),
],
},
},
})
第五步:创建 Rspack 插件配置(可选)
创建 config/sentry.plugins.js 文件,用于配置 Sentry 相关插件:
import { sentryWebpackPlugin } from '@sentry/webpack-plugin'
import { DefinePlugin } from '@rspack/core'
import { execSync } from 'child_process'
import fs from 'fs'
import path from 'path'
/**
* 获取 Git 信息
*/
function getGitInfo() {
try {
const commitHash = execSync('git rev-parse --short HEAD').toString().trim()
const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim()
return { commitHash, branch }
} catch (error) {
console.warn('Failed to get git info:', error)
return { commitHash: 'unknown', branch: 'unknown' }
}
}
/**
* Sentry Webpack Plugin - 上传 SourceMap
*/
export function getSentryPlugin(options = {}) {
const {
projectName,
org,
authToken,
release,
dist,
outputPath,
url = 'https://sentry.io', // Sentry 云版地址
} = options
if (!authToken) {
console.warn('⚠️ Sentry authToken not provided, skipping SourceMap upload')
return null
}
const { commitHash, branch } = getGitInfo()
return sentryWebpackPlugin({
org,
project: projectName,
url,
authToken,
telemetry: false,
release: {
name: release,
// 可选:添加部署信息
dist: dist || `${commitHash}-${branch}`,
},
sourcemaps: {
// 指定要上传的文件路径
assets: `${outputPath}/**/*.{js,map}`,
// 上传后删除本地 SourceMap 文件
filesToDeleteAfterUpload: `${outputPath}/**/*.map`,
},
})
}
/**
* 清理 SourceMap 插件 - 确保不部署 .map 文件
*/
export function getClearSourceMapPlugin(outputPath) {
return {
name: 'ClearSourceMapPlugin',
apply(compiler) {
compiler.hooks.done.tap('ClearSourceMapPlugin', () => {
try {
const files = fs.readdirSync(outputPath, { recursive: true })
files.forEach(file => {
const fullPath = path.join(outputPath, file)
if (fullPath.endsWith('.map') && fs.existsSync(fullPath)) {
fs.unlinkSync(fullPath)
console.log(`✓ Deleted: ${file}`)
}
})
} catch (error) {
console.warn('Failed to delete source maps:', error.message)
}
})
}
}
}
在 rsbuild.config.ts 中,开启生成环境的 sourceMap,并配置 Sentry 相关插件,完成最终的集成配置,最终集成代码如下:
import { defineConfig } from '@rsbuild/core'
import { pluginReact } from '@rsbuild/plugin-react'
import { getDefinePlugin, getSentryPlugin, getClearSourceMapPlugin } from './config/sentry.plugins.js'
import { version } from './package.json'
const isProd = process.env.NODE_ENV === 'production'
export default defineConfig({
// 源码配置
source: {
entry: {
index: {
import: [
'./src/sentry.js', // Sentry 初始化文件必须在最前面
'./src/index.tsx'
],
},
},
},
// 输出配置
output: {
// 生产环境生成 SourceMap,这里一定要是完整的 source-map,不要使用 cheap-source-map 或 eval-source-map
sourceMap: isProd ? {
js: 'source-map',
} : false,
},
// 插件配置
plugins: [pluginReact()],
// 工具配置
tools: {
rspack: {
// 需要将自定义的插件配置在这里
// 因为 rsbuild 本质上是基于 Rspack 的
plugins: [
// 注入版本号
getDefinePlugin(version),
// 生产环境启用 Sentry
isProd && getSentryPlugin({
projectName: 'your-project-name',
org: 'your-org-name',
authToken: process.env.SENTRY_AUTH_TOKEN,
release: version,
outputPath: 'dist',
// 如果使用自部署版本,修改 url
// url: 'https://sentry.yourcompany.com',
}),
// 清理 SourceMap
isProd && getClearSourceMapPlugin('dist'),
].filter(Boolean),
},
},
})