¥
立即购买

项目构建配置

444 浏览
43 试用
11 购买
Nov 18, 2025更新

指导在特定项目类型中配置构建工具,实现目标效果如优化打包和部署流程,并确保配置可扩展、可维护,提升项目开发效率与质量。

下面给出一套可直接落地的 Webpack 5 配置方案,面向 TypeScript + React 的单页应用(SPA),满足以下目标:

  • 代码分包与长期缓存(contenthash、SplitChunks、runtime 分离、deterministic ids)
  • 区分开发/生产配置(webpack-merge)
  • 启用 Tree Shaking 与 SourceMap
  • 自动注入环境变量(限定前缀,避免泄露)
  • 生成 CI 可用构建产物(stats、manifest、可选压缩)
  • 可扩展与可维护(分层配置、约定目录、路径别名)

一、目录结构建议

  • package.json
  • tsconfig.json
  • babel.config.js
  • postcss.config.js
  • .browserslistrc
  • .env.example
  • public/
    • index.html
    • favicon.ico
  • src/
    • index.tsx
    • App.tsx
    • pages/...
    • styles/...
  • webpack/
    • utils/
      • env.js
    • webpack.common.js
    • webpack.dev.js
    • webpack.prod.js

二、依赖(精简清单)

  • webpack 核心:webpack webpack-cli webpack-dev-server webpack-merge
  • 加载器与语言:babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript typescript
  • React HMR:react-refresh @pmmmwh/react-refresh-webpack-plugin
  • 样式与 PostCSS:style-loader css-loader postcss-loader postcss postcss-preset-env autoprefixer mini-css-extract-plugin css-minimizer-webpack-plugin
  • 资源与优化:html-webpack-plugin terser-webpack-plugin webpack-manifest-plugin
  • 工具:dotenv dotenv-expand cross-env fork-ts-checker-webpack-plugin tsconfig-paths-webpack-plugin
  • 可选:compression-webpack-plugin(CI 产物压缩)

三、package.json(摘取关键) { "scripts": { "start": "cross-env NODE_ENV=development webpack serve --config webpack/webpack.dev.js", "build": "cross-env NODE_ENV=production webpack --config webpack/webpack.prod.js", "build:stats": "cross-env NODE_ENV=production webpack --config webpack/webpack.prod.js --profile --json > build/stats.json", "typecheck": "tsc -p tsconfig.json --noEmit", "analyze": "cross-env ANALYZE=true npm run build" }, "sideEffects": [".css", ".scss"], "browserslist": [ "defaults", "not IE 11", "maintained node versions" ] }

说明:

  • sideEffects 声明使 JS 支持 Tree Shaking,同时保留对样式资源的副作用声明。
  • 使用 cross-env 确保跨平台 NODE_ENV 一致。
  • build:stats 生成 CI 可分析的 stats.json。

四、tsconfig.json(确保支持 Tree Shaking 与路径别名) { "compilerOptions": { "target": "ES2018", "lib": ["ES2018", "DOM"], "module": "ESNext", "moduleResolution": "Bundler", "jsx": "react-jsx", "strict": true, "noEmit": true, "baseUrl": ".", "paths": { "@/": ["src/"] }, "resolveJsonModule": true, "isolatedModules": true, "skipLibCheck": true }, "include": ["src"] }

五、babel.config.js(React + TS + 按需 polyfill) module.exports = function (api) { const isProd = api.env('production'); const isTest = api.env('test');

return { presets: [ ["@babel/preset-env", { targets: ">0.2%, not dead, not op_mini all", useBuiltIns: "usage", corejs: 3 }], ["@babel/preset-react", { runtime: "automatic", development: !isProd }], ["@babel/preset-typescript", { allowDeclareFields: true }] ], plugins: [ !isProd && "react-refresh/babel" ].filter(Boolean), assumptions: { setPublicClassFields: true } }; };

六、postcss.config.js module.exports = { plugins: [ require("postcss-preset-env")({ stage: 3 }), require("autoprefixer") ] };

七、.browserslistrc(可与 package.json 中 browserslist 二选一)

0.2% not dead not op_mini all

八、环境变量约定

  • 仅注入以 PUBLIC_ 前缀开头的变量,避免敏感信息进入前端包。
  • .env.example 示例: PUBLIC_API_BASE=https://api.example.com PUBLIC_SENTRY_DSN=

九、webpack/utils/env.js(统一加载并筛选环境变量) const fs = require("fs"); const path = require("path"); const dotenv = require("dotenv"); const dotenvExpand = require("dotenv-expand");

function loadEnv(mode) { const base = path.resolve(process.cwd(), ".env"); const modeFile = ${base}.${mode}; const files = [base, modeFile].filter(fs.existsSync);

let env = {}; files.forEach((file) => { const result = dotenv.config({ path: file }); dotenvExpand.expand(result); Object.assign(env, result.parsed || {}); });

// 从 process.env 合并,CI 中常用 Object.keys(process.env).forEach((k) => { if (!(k in env)) env[k] = process.env[k]; });

// 仅暴露 PUBLIC_ 开头的键 const raw = Object.keys(env) .filter((k) => /^PUBLIC_/i.test(k)) .reduce((acc, k) => ((acc[k] = env[k]), acc), {});

// 供 DefinePlugin 使用 const stringified = Object.keys(raw).reduce((acc, k) => { acc[process.env.${k}] = JSON.stringify(raw[k]); return acc; }, { "process.env.NODE_ENV": JSON.stringify(mode) });

return { raw, stringified }; }

module.exports = { loadEnv };

十、webpack/webpack.common.js(基础配置) const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { DefinePlugin } = require("webpack"); const { loadEnv } = require("./utils/env"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); const { WebpackManifestPlugin } = require("webpack-manifest-plugin");

module.exports = (env, argv) => { const mode = argv.mode || process.env.NODE_ENV || "development"; const isProd = mode === "production"; const { stringified } = loadEnv(mode);

return { mode, entry: path.resolve(__dirname, "../src/index.tsx"), output: { path: path.resolve(__dirname, "../dist"), filename: isProd ? "js/[name].[contenthash:8].js" : "js/[name].js", chunkFilename: isProd ? "js/[name].[contenthash:8].chunk.js" : "js/[name].chunk.js", assetModuleFilename: "assets/[name].[contenthash:8][ext]", publicPath: "/", clean: true }, resolve: { extensions: [".tsx", ".ts", ".jsx", ".js", ".json"], plugins: [new TsconfigPathsPlugin()] }, cache: { type: "filesystem", buildDependencies: { config: [__filename] } }, module: { rules: [ { test: /.[jt]sx?$/, include: path.resolve(dirname, "../src"), use: [ { loader: "babel-loader", options: { cacheDirectory: true, cacheCompression: false } } ] }, // CSS Modules { test: /.module.css$/, use: [ isProd ? require("mini-css-extract-plugin").loader : "style-loader", { loader: "css-loader", options: { modules: { localIdentName: isProd ? "[hash:base64:8]" : "[path][name][local]" }, importLoaders: 1 } }, "postcss-loader" ] }, // 全局 CSS { test: /.css$/, exclude: /.module.css$/, use: [ isProd ? require("mini-css-extract-plugin").loader : "style-loader", "css-loader", "postcss-loader" ] }, // 资源模块 { test: /.(png|jpe?g|gif|svg|webp|avif)$/i, type: "asset", parser: { dataUrlCondition: { maxSize: 8 * 1024 } } }, { test: /.(woff2?|ttf|eot|otf)$/i, type: "asset/resource" } ] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "../public/index.html"), favicon: path.resolve(__dirname, "../public/favicon.ico"), inject: "body" }), new DefinePlugin(stringified), new ForkTsCheckerWebpackPlugin({ async: !isProd }), new WebpackManifestPlugin({ fileName: "asset-manifest.json", publicPath: "/" }) ] }; };

十一、webpack/webpack.dev.js(开发配置) const { merge } = require("webpack-merge"); const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); const common = require("./webpack.common");

module.exports = (env, argv) => merge(common(env, argv), { mode: "development", devtool: "cheap-module-source-map", devServer: { port: 3000, open: true, hot: true, historyApiFallback: true, compress: true, client: { overlay: { errors: true, warnings: false } }, static: { directory: "public", watch: true } }, plugins: [new ReactRefreshWebpackPlugin()], optimization: { runtimeChunk: "single" } });

十二、webpack/webpack.prod.js(生产配置:长期缓存、分包、压缩与 SourceMap) const { merge } = require("webpack-merge"); const common = require("./webpack.common"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); // 可选 gzip/brotli 压缩 // const CompressionPlugin = require("compression-webpack-plugin");

const isAnalyze = process.env.ANALYZE === "true";

module.exports = (env, argv) => merge(common(env, argv), { mode: "production", devtool: process.env.SOURCE_MAP === "hidden" ? "hidden-source-map" : "source-map", output: { filename: "js/[name].[contenthash:8].js", chunkFilename: "js/[name].[contenthash:8].chunk.js" }, plugins: [ new MiniCssExtractPlugin({ filename: "css/[name].[contenthash:8].css", chunkFilename: "css/[name].[contenthash:8].chunk.css" }) // new CompressionPlugin({ algorithm: "brotliCompress", filename: "[path][base].br" }), // new CompressionPlugin({ algorithm: "gzip", filename: "[path][base].gz" }) ], optimization: { minimize: true, minimizer: [ new TerserPlugin({ extractComments: false, terserOptions: { safari10: true, compress: { passes: 2, pure_getters: true, drop_console: !!process.env.DROP_CONSOLE } } }), new CssMinimizerPlugin() ], moduleIds: "deterministic", chunkIds: "deterministic", runtimeChunk: "single", splitChunks: { chunks: "all", minSize: 20_000, cacheGroups: { // 将第三方依赖抽到 vendor vendor: { test: /[\/]node_modules[\/]/, name: "vendors", priority: -10, reuseExistingChunk: true }, // 可选:单独拆出 React 生态,保持更稳定的缓存 react: { test: /[\/]node_modules\/[\/]/, name: "react-vendor", priority: 10, enforce: true } } } }, performance: { hints: false } });

说明:

  • runtimeChunk: single + deterministic ids + contenthash,确保长期缓存稳定。
  • splitChunks 将第三方依赖与应用代码分开,React 生态单独抽取可提升缓存命中。
  • SourceMap 默认 source-map,若不想对外暴露映射,可用 SOURCE_MAP=hidden 切换为 hidden-source-map。

十三、public/index.html(模板要有根节点)

App

十四、src/index.tsx(示例:动态 import 触发代码分包) import React, { Suspense } from "react"; import { createRoot } from "react-dom/client";

const App = React.lazy(() => import("./App"));

createRoot(document.getElementById("root")!).render( <Suspense fallback={

Loading...
}> );

十五、关键点解释与落地建议

  • Tree Shaking
    • 使用 "module": "ESNext" 以保留 ESM 供 Webpack 分析。
    • 在 package.json 声明 sideEffects,避免误删样式副作用。
    • 生产模式会自动启用 usedExports、minimize 与 DCE。
  • SourceMap
    • 开发:cheap-module-source-map(与 HMR 兼容、速度快)。
    • 生产:source-map(CI/定位问题);若不想暴露,切换为 hidden-source-map。
  • 环境变量
    • 仅注入以 PUBLIC_ 开头变量,避免泄露服务端敏感信息。
    • 可在 CI 中通过环境注入(如 GitHub Actions 的 env)直接覆盖。
  • 长期缓存
    • contenthash 文件名、deterministic ids、runtime 分离、稳定的 vendor/react-vendor 拆分。
    • 升级依赖时尽量锁版本,避免 vendor 内容频繁变化。
  • 构建性能
    • filesystem cache + babel-loader cacheDirectory。
    • TypeScript 类型检查通过 ForkTsChecker 并行执行,不阻塞打包。
  • CI 产物与可观测性
    • dist/ 为可部署产物;asset-manifest.json 用于服务端注入或调试;build:stats 生成 stats.json 供分析。
    • 可选启用 gzip/brotli 生成预压缩包(Nginx/CDN 可直接回源使用)。
  • 可扩展与可维护
    • 按 common/dev/prod 分层,env 工具模块化,后续扩展(如 SASS、SVG ReactComponent、Module Federation)只需在 common 中增加 loader/插件或新增特定配置。
    • 使用 TsconfigPathsPlugin 与路径别名提升可维护性。

十六、常见拓展(可选)

  • SCSS:在 CSS 规则中加上 sass-loader,并在 sideEffects 中包含 *.scss。
  • Bundle 分析:ANALYZE=true 时接入 webpack-bundle-analyzer。
  • Sentry:使用 @sentry/webpack-plugin 上传 source maps;配合 hidden-source-map。
  • PWA:结合 workbox-webpack-plugin 生成 service worker。

至此,该配置即满足:

  • 分包与长期缓存(contenthash、splitChunks、runtime 分离、deterministic ids)
  • 开发/生产区分(merge 与独立文件)
  • Tree Shaking 与 SourceMap
  • 环境变量安全注入(PUBLIC_ 前缀)
  • CI 可用产物(dist、asset-manifest.json、stats.json、可选压缩)
  • 结构清晰,便于扩展维护。

下面给出一套在 Gradle Kotlin DSL 下可复用的工程化配置思路与示例,覆盖:多模块构建、版本对齐与依赖约束、统一格式化与静态检查、按环境打包、可插拔插件与缓存加速,并兼顾扩展性与可维护性。示例以 Java 21 为主、Gradle 8.10+、Kotlin DSL(.kts)为基准,可适配纯 Java 或 Spring Boot 服务。

一、推荐目录结构

  • settings.gradle.kts
  • build.gradle.kts(尽量精简到最少逻辑)
  • gradle.properties
  • gradle/libs.versions.toml(版本目录 Version Catalog)
  • build-logic/(Included Build,放约定式插件/共用逻辑)
    • settings.gradle.kts
    • build.gradle.kts
    • src/main/kotlin/
      • my.company.java-library-conventions.gradle.kts
      • my.company.java-application-conventions.gradle.kts
      • my.company.quality-conventions.gradle.kts
      • my.company.publishing-conventions.gradle.kts
      • my.company.spring-boot-application-conventions.gradle.kts(可选)
  • platform/(内部平台BOM/约束模块)
    • build.gradle.kts
  • config/
    • checkstyle/checkstyle.xml
    • pmd/pmd.xml(可选)
    • spotless/.editorconfig 或 google-style config
  • common/core、common/web、services/user-service、services/order-service …(子模块)
    • build.gradle.kts
    • src/main/java …
    • src/dev/resources、src/stg/resources、src/prod/resources(可选资源覆盖)

二、settings.gradle.kts(集中式管理与加速)

  • 内容要点:
    • 使用 included build 提供约定式插件(build-logic)
    • 打开构建缓存、配置缓存、并行构建
    • 统一仓库与版本目录
    • 打开依赖锁定(可选)
    • 引入多模块

示例(要点): file: settings.gradle.kts pluginManagement { repositories { gradlePluginPortal() mavenCentral() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { mavenCentral() } versionCatalogs { create("libs") { from(files("gradle/libs.versions.toml")) } } } includeBuild("build-logic") rootProject.name = "my-company-services"

enableFeaturePreview("STABLE_CONFIGURATION_CACHE") // 如使用的Gradle需要 buildCache { local { isEnabled = true } // 可选远程缓存(含鉴权): // remote(HttpBuildCache::class) { // url = uri(System.getenv("GRADLE_REMOTE_CACHE_URL") ?: "") // isPush = (System.getenv("CI") == "true") // credentials { username = System.getenv("CACHE_USER"); password = System.getenv("CACHE_PASS") } // } }

// 开启 typesafe project accessors(Gradle 8+ 中已稳定,可不写) enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

// 依赖锁定(可选,建议在CI稳定后启用) dependencyResolutionManagement { components { // 也可在这里做元数据规则、漏洞拒绝等 } }

include( "platform", "common:core", "common:web", "services:user-service", "services:order-service" )

三、gradle.properties(全局默认) file: gradle.properties org.gradle.jvmargs=-Xmx2g -XX:ReservedCodeCacheSize=512m -Dfile.encoding=UTF-8 org.gradle.caching=true org.gradle.parallel=true org.gradle.configuration-cache=true org.gradle.daemon=true org.gradle.warning.mode=all

默认环境(可被 -Penv=prod 覆盖)

env=dev

统一Java Toolchain版本

javaVersion=21

远程缓存/仓库鉴权可以放到环境变量或CI的secret中

四、版本目录 gradle/libs.versions.toml(版本对齐与集中管理) 要点:

  • 将第三方库、插件版本集中在此
  • 使用 bundle 统一引入一组依赖
  • 尽量用 BOM 对齐生态内部版本(如 Jackson、Spring、Log4j)

示例(片段): file: gradle/libs.versions.toml [versions] java = "21" junit = "5.10.2" slf4j = "2.0.13" logback = "1.5.6" jackson = "2.17.1" guava = "33.2.0-jre" spotless = "6.25.0" errorpronePlugin = "3.1.0" errorprone = "2.27.2" nullaway = "0.10.20" spotbugsPlugin = "6.0.17" versionsPlugin = "0.51.0" shadow = "8.1.1" jib = "3.4.2"

[libraries] junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" } jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" } jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson" } guava = { module = "com.google.guava:guava", version.ref = "guava" }

[bundles] logging = ["slf4j-api", "logback-classic"] jackson = ["jackson-databind", "jackson-annotations", "jackson-core"]

[plugins] spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } errorprone = { id = "net.ltgt.errorprone", version.ref = "errorpronePlugin" } spotbugs = { id = "com.github.spotbugs", version.ref = "spotbugsPlugin" } versions = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" } shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } jib = { id = "com.google.cloud.tools.jib", version.ref = "jib" }

五、约定式插件(build-logic,提升可维护性、可插拔)

  • 将通用配置做成 precompiled convention plugins,子模块只需应用简单的插件ID
  • 可按模块类型拆分:java-library、java-application、quality、publishing、spring-boot-application 等
  1. build-logic/settings.gradle.kts pluginManagement { repositories { gradlePluginPortal(); mavenCentral() } } rootProject.name = "build-logic"

  2. build-logic/build.gradle.kts plugins { kotlin-dsl } repositories { mavenCentral() } dependencies { // 访问根工程的 version catalog implementation(files(locateOrCreate("gradle/libs.versions.toml"))) } fun locateOrCreate(path: String) = path

  3. Java库约定插件(工具链、测试、基本依赖、缓存友好) file: build-logic/src/main/kotlin/my.company.java-library-conventions.gradle.kts plugins { java-library jacoco } val libs = extensions.getByType().named("libs") java { toolchain { languageVersion.set(JavaLanguageVersion.of(providers.gradleProperty("javaVersion").get().toInt())) } withJavadocJar() withSourcesJar() } tasks.withType().configureEach { options.encoding = "UTF-8" options.release.set(providers.gradleProperty("javaVersion").map(String::toInt)) } tasks.withType().configureEach { useJUnitPlatform() testLogging { events("passed", "skipped", "failed") } systemProperty("junit.jupiter.displayname.generator.default", "org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores") } tasks.withType().configureEach { isPreserveFileTimestamps = false isReproducibleFileOrder = true } dependencies { testImplementation(libs.findLibrary("junit-jupiter").get()) }

  4. Java应用约定插件(应用打包、环境资源、Shadow/Jib可插拔) file: build-logic/src/main/kotlin/my.company.java-application-conventions.gradle.kts plugins { application alias(libs.plugins.shadow) apply false alias(libs.plugins.jib) apply false } val env = providers.gradleProperty("env").orElse("dev").get() application { mainClass.set(providers.gradleProperty("appMainClass")) } java { toolchain { languageVersion.set(JavaLanguageVersion.of(providers.gradleProperty("javaVersion").get().toInt())) } } sourceSets { named("main") { resources.srcDir("src/$env/resources") } } tasks.processResources { // 可按需过滤占位符,如 ${env} inputs.property("env", env) } tasks.withType().configureEach { isPreserveFileTimestamps = false isReproducibleFileOrder = true }

  5. 质量约定插件(Spotless、Checkstyle/PMD可选、Error Prone/NullAway、SpotBugs) file: build-logic/src/main/kotlin/my.company.quality-conventions.gradle.kts plugins { alias(libs.plugins.spotless) alias(libs.plugins.errorprone) // alias(libs.plugins.spotbugs) // 可选 } spotless { java { target("**/*.java") googleJavaFormat() // 或者 palantirFormatter()/eclipse().editorConfigFile("$rootDir/config/spotless/.editorconfig") // 可统一licenseHeaderFile } } tasks.withType().configureEach { options.errorprone { isEnabled.set(true) disable("MissingSummary") // 示例:定制规则 // 引入 NullAway option("Xep:NullAway:ERROR") option("XepOpt:NullAway:AnnotatedPackages=my.company") } } dependencies { "errorprone"(provider { "com.google.errorprone:error_prone_core:${libs.findVersion("errorprone").get().requiredVersion}" }) "annotationProcessor"(provider { "com.uber.nullaway:nullaway:${libs.findVersion("nullaway").get().requiredVersion}" }) } // SpotBugs 如需: // spotbugs { toolVersion.set(libs.findVersion("spotbugs").get().requiredVersion) }

  6. 发布约定插件(可选:统一 maven-publish、签名、仓库) file: build-logic/src/main/kotlin/my.company.publishing-conventions.gradle.kts plugins { maven-publish } publishing { repositories { // 配置企业Nexus/Artifactory // maven { url = uri("..."); credentials { ... } } } }

  7. Spring Boot 应用约定插件(可选) file: build-logic/src/main/kotlin/my.company.spring-boot-application-conventions.gradle.kts plugins { id("org.springframework.boot") version "3.3.5" apply true id("io.spring.dependency-management") version "1.1.6" } java { toolchain { languageVersion.set(JavaLanguageVersion.of(providers.gradleProperty("javaVersion").get().toInt())) } } tasks.named<org.springframework.boot.gradle.tasks.bundling.BootJar>("bootJar") { isReproducibleFileOrder = true isPreserveFileTimestamps = false }

六、平台模块(platform)做统一依赖约束与版本对齐

  • 用 java-platform 提供公司内部BOM,所有子模块通过 implementation(platform(project(":platform"))) 对齐版本
  • 可组合第三方生态BOM(如 Jackson、Log4j),并加入内部库约束

file: platform/build.gradle.kts plugins { java-platform maven-publish } javaPlatform { allowDependencies() } dependencies { constraints { api("com.fasterxml.jackson.core:jackson-databind:${libs.versions.jackson.get()}") api("com.fasterxml.jackson.core:jackson-core:${libs.versions.jackson.get()}") api("com.fasterxml.jackson.core:jackson-annotations:${libs.versions.jackson.get()}") api("org.slf4j:slf4j-api:${libs.versions.slf4j.get()}") api("ch.qos.logback:logback-classic:${libs.versions.logback.get()}") api("com.google.guava:guava:${libs.versions.guava.get()}") // 可引入第三方BOM以强制对齐: api(platform("org.apache.logging.log4j:log4j-bom:2.23.1")) // Spring Boot(可选):api(platform("org.springframework.boot:spring-boot-dependencies:3.3.5")) } }

七、根 build.gradle.kts(尽量精简) file: build.gradle.kts plugins { alias(libs.plugins.versions) apply true // 依赖更新检查 } allprojects { group = "my.company" version = "1.0.0" repositories { mavenCentral() } } subprojects { // 只在需要的模块应用约定插件,保持可插拔 // 例如:所有模块都应用质量检查 apply(plugin = "my.company.quality-conventions") }

tasks.register("checkAll") { dependsOn(gradle.includedBuild("build-logic")) dependsOn(subprojects.map { it.tasks.matching { t -> t.name == "check" } }) }

八、子模块示例(库与应用)

  1. 库模块(common/core) file: common/core/build.gradle.kts plugins { id("my.company.java-library-conventions") } dependencies { api(platform(project(":platform"))) api(libs.slf4j.api) implementation(libs.guava) testImplementation(libs.junit.jupiter) }

  2. 应用模块(services/user-service) file: services/user-service/gradle.properties appMainClass=my.company.user.Main

file: services/user-service/build.gradle.kts plugins { id("my.company.java-application-conventions") // 可按需选择 Shadow/Jib/Spring Boot // alias(libs.plugins.shadow) // alias(libs.plugins.jib) } dependencies { implementation(platform(project(":platform"))) implementation(projects.common.core) implementation(projects.common.web) implementation(libs.bundles.logging) implementation(libs.bundles.jackson) testImplementation(libs.junit.jupiter) }

// 如使用 Shadow 打包 fat jar: /* tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") { archiveClassifier.set("all-${providers.gradleProperty("env").orElse("dev").get()}") mergeServiceFiles() } tasks.named("build") { dependsOn("shadowJar") } */

// 如使用 Jib 生成镜像(推荐无需Docker守护进程): /* plugins { alias(libs.plugins.jib) } jib { from { image = "eclipse-temurin:21-jre" } to { image = "registry.example.com/my-company/user-service" tags = setOf(providers.gradleProperty("env").orElse("dev").get()) // auth from env/CI secret } container { jvmFlags = listOf("-Xms256m","-Xmx512m") ports = listOf("8080") environment = mapOf("APP_ENV" to providers.gradleProperty("env").orElse("dev").get()) } } */

九、统一代码格式化与静态检查

  • Spotless:统一 Java 格式(可用 google-java-format 或 Eclipse formatter)
  • Error Prone + NullAway:编译期捕捉常见 Bug 和空指针问题
  • 可选:Checkstyle/PMD/SpotBugs,根据团队偏好
  • 将规则、配置文件集中放在 config/,由 quality-conventions 插件统一引用
  • 在 CI 中执行 ./gradlew spotlessCheck check,pre-commit 可挂接 ./gradlew spotlessApply

十、按环境打包与配置

  • 环境选择:-Penv=dev|stg|prod
  • 资源覆盖:在 java-application-conventions 中按 env 增加资源目录 src/$env/resources
  • 变量注入:processResources 可基于 env 做过滤或占位符替换
  • 产物区分:
    • JAR:可使用 classifier 包含环境名(shadowJar 示例)
    • 容器镜像:Jib 根据 env 追加 tag 或注入环境变量
  • Spring Boot 项目可改用 spring-boot-conventions,并使用 bootJar/bootBuildImage

十一、构建性能与缓存加速

  • settings.gradle.kts 中开启 local/remote build cache
  • gradle.properties 开启并行与配置缓存
  • 任务缓存友好:
    • 避免在任务中使用非声明式 I/O
    • Jar/BootJar 设为 reproducible(已在约定插件中设置)
    • 测试使用 JUnit5,确保输入输出稳定可缓存
  • 可选:接入 Gradle Enterprise/Develocity 获得 build scan 与远程缓存

十二、可插拔插件策略

  • 所有第三方插件版本放 libs.versions.toml
  • 通过约定插件按需 apply(质量、发布、应用打包、Boot/Jib/Shadow 各自拆分)
  • 子模块最小化配置,只显式声明模块类型与依赖
  • 新增模块只需:创建模块目录 + 选择合适约定插件 + 声明依赖

十三、版本锁定与升级策略

  • 依赖锁定:在稳定后开启 gradle dependency locking(生成 gradle.lockfile),确保可重复构建
  • 更新检查:使用 com.github.ben-manes.versions 插件
    • 运行 ./gradlew dependencyUpdates
  • 漏洞治理:可在 settings 里加入组件规则拒绝已知漏洞版本,或在平台模块中用 strictly/reject 约束

十四、常见扩展点

  • 多语言混合:增加 my.company.kotlin-library-conventions(ktlint/Detekt)
  • 聚合测试覆盖率:根项目创建 Jacoco aggregation(使用 jacoco-report-aggregation 插件)
  • 发布与签名:publishing-conventions 统一配置 maven-publish、signing
  • 私有 BOM:在 platform 模块中持续沉淀公司内部库与选型

十五、常用命令与 CI 建议

  • 本地全量构建与检查:./gradlew clean build
  • 仅格式化:./gradlew spotlessApply
  • 依赖更新报告:./gradlew dependencyUpdates
  • 指定环境打包:
    • JAR(shadow):./gradlew -Penv=prod :services:user-service:shadowJar
    • 镜像(jib):./gradlew -Penv=stg :services:user-service:jib
  • CI 建议:
    • 缓存 Gradle 缓存目录(~/.gradle/caches)与 wrapper
    • 步骤:spotlessCheck -> build -> 测试 -> 发布/镜像
    • 在 CI 环境变量中注入 env、远程缓存与私有仓库凭证

总结

  • 用 Version Catalog + platform 模块(java-platform)实现版本对齐与约束
  • 用 Included Build 的约定式插件集中共用配置,实现可插拔与低耦合
  • 统一格式化与静态检查,内置在 quality-conventions 中强制执行
  • 按环境打包通过 env 属性 + 资源覆盖 + Shadow/Jib 实现
  • 全面启用缓存、并行与配置缓存,配合 reproducible jar 确保高性能与稳定
  • 最终子模块配置最小化、结构清晰,便于扩展与维护。

下面给出一套“现代 CMake”做法与可直接落地的示例,覆盖跨平台库与示例程序的构建、Debug/Release 配置、FetchContent 拉依赖、按平台编译选项、启用 CTest 与覆盖率、安装与打包目标,并兼顾可扩展与可维护性。

一、项目结构建议

  • CMakeLists.txt 顶层
  • src/ 库源码
  • include//... 公共头文件
  • examples/ 示例程序
  • tests/ 单元测试
  • cmake/ 自定义 CMake 模块
    • CodeCoverage.cmake
    • CompilerWarnings.cmake
    • Sanitizers.cmake
  • CMakePresets.json 跨平台一致的配置/构建/测试预设
  • LICENSE, README, etc.

二、顶层 CMakeLists.txt(可直接使用的最小可扩展示例) 以下示例涵盖:多配置生成器兼容、FetchContent、平台编译选项、CTest/覆盖率、安装导出与CPack打包。可按需裁剪。

cmake_minimum_required(VERSION 3.22)

推荐开启新策略行为(保持未来可升级)

if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) # FetchContent 下载结果采用最近 mtime endif()

project(MyProj VERSION 1.2.0 DESCRIPTION "A cross-platform C++ library with examples" LANGUAGES CXX)

选项开关(保持扩展性)

option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(BUILD_EXAMPLES "Build examples" ON) option(ENABLE_COVERAGE "Enable code coverage flags" OFF) option(ENABLE_SANITIZERS "Enable sanitizers in Debug builds (where supported)" OFF) option(USE_SYSTEM_FMT "Use system fmt package instead of FetchContent" OFF)

设定默认构建类型(单配置生成器才有效,如 Ninja/Makefiles)

if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel") endif()

C++ 标准与特性(目标导向)

set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF)

include(GNUInstallDirs) include(CMakePackageConfigHelpers) include(FetchContent) include(CTest) # 定义 BUILD_TESTING 选项并启用 ctest

通用选项/警告/平台编译器设置:用 INTERFACE 目标承载,避免全局污染

add_library(project_options INTERFACE) add_library(project_warnings INTERFACE)

统一的编译特性与属性

target_compile_features(project_options INTERFACE cxx_std_20)

所有库(静态/共享)默认使用 PIC 提高可移植性

set_property(TARGET project_options PROPERTY POSITION_INDEPENDENT_CODE ON)

常见平台与编译器差异处理(警告、编码、定义等)

if(MSVC) target_compile_options(project_warnings INTERFACE /W4 /permissive- /EHsc /Zc:__cplusplus /MP /utf-8)

你也可以在 Debug 时开启 /WX 视为错误:

target_compile_options(project_warnings INTERFACE $<$CONFIG:Debug:/WX>)

target_compile_definitions(project_options INTERFACE WIN32_LEAN_AND_MEAN NOMINMAX _CRT_SECURE_NO_WARNINGS) else() target_compile_options(project_warnings INTERFACE -Wall -Wextra -Wpedantic $<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wconversion -Wshadow -Wnon-virtual-dtor>)

可选:默认隐藏符号,导出靠宏

target_compile_options(project_options INTERFACE -fvisibility=hidden)

可选:Linux Debug 开启 libstdc++ 断言

if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_definitions(project_options INTERFACE $<$CONFIG:Debug:_GLIBCXX_ASSERTIONS>) endif() endif()

可选:Sanitizers(推荐 Debug)

if(ENABLE_SANITIZERS AND NOT MSVC) target_compile_options(project_options INTERFACE $<$CONFIG:Debug:-fsanitize=address,undefined>) target_link_options(project_options INTERFACE $<$CONFIG:Debug:-fsanitize=address,undefined>) endif()

依赖管理:优先 find_package,可选回退 FetchContent(可测试/可缓存/可锁版本)

以 fmt 为例

if(USE_SYSTEM_FMT) find_package(fmt 11 CONFIG REQUIRED) else() FetchContent_Declare( fmt GIT_REPOSITORY https://github.com/fmtlib/fmt.git GIT_TAG 11.0.2 GIT_SHALLOW ON ) FetchContent_MakeAvailable(fmt) endif()

常用系统库

find_package(Threads REQUIRED)

库目标

add_library(mylib src/mylib.cpp )

对外头文件路径(build/install)

target_include_directories(mylib PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include> # 导出宏头文件会生成到这 $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) target_link_libraries(mylib PUBLIC project_options project_warnings fmt::fmt Threads::Threads )

可选:导出符号宏(跨平台 DLL 与隐藏符号)

include(GenerateExportHeader) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/mylib) generate_export_header(mylib EXPORT_FILE_NAME ${PROJECT_BINARY_DIR}/include/mylib/mylib_export.hpp ) target_compile_definitions(mylib PUBLIC $<TARGET_PROPERTY:mylib,DEFINE_SYMBOL>)

注意:你的 public 头里应包含 <mylib/mylib_export.hpp> 并在符号上使用 MYLIB_EXPORT

示例程序

if(BUILD_EXAMPLES) add_executable(example_hello examples/hello.cpp) target_link_libraries(example_hello PRIVATE mylib) endif()

测试(启用 CTest)

if(BUILD_TESTING)

以 GoogleTest 为例(可换为 Catch2/doctest)

FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.14.0 GIT_SHALLOW ON )

避免在 MSVC 下安装 GTest

set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest)

add_executable(mylib_tests tests/mylib_tests.cpp) target_link_libraries(mylib_tests PRIVATE mylib GTest::gtest_main) add_test(NAME mylib.tests COMMAND mylib_tests) endif()

覆盖率支持(GCC/Clang)

if(ENABLE_COVERAGE AND NOT MSVC) include(cmake/CodeCoverage.cmake) # 见下文 enable_coverage_for_target(mylib) if(TARGET mylib_tests) enable_coverage_for_target(mylib_tests) endif() add_coverage_target(NAME coverage EXECUTABLE ctest -j ${PROCESSOR_COUNT} --output-on-failure) endif()

安装与导出(供 find_package 使用)

1) 安装目标与头文件

install(TARGETS mylib EXPORT MyProjTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

2) 安装与导出 targets

install(EXPORT MyProjTargets NAMESPACE MyProj:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProj )

3) 生成并安装 *Config.cmake 与 *ConfigVersion.cmake

write_basic_package_version_file( ${PROJECT_BINARY_DIR}/MyProjConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) configure_package_config_file( ${PROJECT_SOURCE_DIR}/cmake/MyProjConfig.cmake.in ${PROJECT_BINARY_DIR}/MyProjConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProj ) install(FILES ${PROJECT_BINARY_DIR}/MyProjConfig.cmake ${PROJECT_BINARY_DIR}/MyProjConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProj )

允许从构建目录被 find_package(开发期很方便)

export(EXPORT MyProjTargets NAMESPACE MyProj:: FILE ${PROJECT_BINARY_DIR}/MyProjTargets.cmake )

RPATH 建议(macOS/Linux)

set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

CPack 打包(zip/tgz 等)

set(CPACK_PACKAGE_NAME "MyProj") set(CPACK_PACKAGE_VENDOR "Your Org") set(CPACK_PACKAGE_CONTACT "you@example.com") set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")

生成器可按平台区分

if(WIN32) set(CPACK_GENERATOR "ZIP") else() set(CPACK_GENERATOR "TGZ") endif() include(CPack)

三、cmake/CodeCoverage.cmake(最小化,基于 gcovr 或 llvm-cov) 说明:ENABLE_COVERAGE=ON 时使用;仅对 GCC/Clang。要求系统安装 gcovr 或使用 llvm-cov + llvm-profdata。推荐 Debug 配置执行测试。

function(enable_coverage_for_target tgt) if(MSVC) message(WARNING "Coverage is not supported on MSVC by this helper.") return() endif() if(NOT TARGET ${tgt}) message(FATAL_ERROR "Target ${tgt} not found for coverage.") endif() target_compile_options(${tgt} PRIVATE $<$CONFIG:Debug:--coverage -O0 -g>) target_link_options(${tgt} PRIVATE $<$CONFIG:Debug:--coverage>) endfunction()

function(add_coverage_target) set(options) set(oneValueArgs NAME EXECUTABLE) set(multiValueArgs) cmake_parse_arguments(COV "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT COV_NAME) set(COV_NAME coverage) endif()

find_program(GCOVR_BIN gcovr) if(GCOVR_BIN) add_custom_target(${COV_NAME} COMMAND ${COV_EXECUTABLE} COMMAND ${GCOVR_BIN} --root ${PROJECT_SOURCE_DIR} --filter ${PROJECT_SOURCE_DIR}/src --xml --output ${PROJECT_BINARY_DIR}/coverage.xml --html --html-details --output ${PROJECT_BINARY_DIR}/coverage.html WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMENT "Run tests and generate coverage (gcovr)" USES_TERMINAL ) else() # llvm-cov 回退(要求 clang/llvm-profdata 可用) find_program(LLVM_PROFDATA llvm-profdata) find_program(LLVM_COV llvm-cov) if(LLVM_COV AND LLVM_PROFDATA) add_custom_target(${COV_NAME} COMMAND ${COV_EXECUTABLE} # 这里根据项目二进制与 .profraw/.profdata 的生成策略补充命令 COMMENT "Run tests and generate coverage via llvm-cov (customize as needed)" USES_TERMINAL ) else() message(WARNING "No gcovr or llvm-cov found; coverage target will be dummy.") add_custom_target(${COV_NAME} COMMENT "Coverage tools not found") endif() endif() endfunction()

四、cmake/CompilerWarnings.cmake(可选:若需要更细粒度警告集)

  • 把警告组织为一组 INTERFACE 目标,按编译器区分,后续统一 link 到项目目标即可。
  • 上文已在顶层实现简版,若项目更复杂可独立成模块。

五、cmake/Sanitizers.cmake(可选)

  • 用同样思路做成函数 add_sanitizers(project_options Address;Undefined;Leak...),便于控制平台差异与冲突检测。

六、包配置模板 cmake/MyProjConfig.cmake.in @PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/MyProjTargets.cmake")

check_required_components(MyProj)

七、示例源码提示

  • include/mylib/mylib.hpp

    • 对外 API 使用 MYLIB_EXPORT 宏标注(来自 generate_export_header 生成的 mylib_export.hpp)
    • 例如: #include <mylib/mylib_export.hpp> struct MYLIB_EXPORT Foo { void hello(); };
  • examples/hello.cpp

    • 简单链接 mylib,调用 API。
  • tests/mylib_tests.cpp

    • 使用 TEST(TestSuite, Case) { ... },链接 GTest。

八、CMakePresets.json(建议)

  • 统一 CI、本地与不同平台的配置/构建/测试命令,减少文档与脚本分歧。
  • 示例(精简): { "version": 5, "cmakeMinimumRequired": { "major": 3, "minor": 22, "patch": 0 }, "configurePresets": [ { "name": "ninja-debug", "generator": "Ninja", "binaryDir": "out/build/ninja-debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "ENABLE_COVERAGE": "OFF", "BUILD_EXAMPLES": "ON" } }, { "name": "msvc-2022-release", "generator": "Visual Studio 17 2022", "binaryDir": "out/build/msvc-2022-release", "cacheVariables": { "CMAKE_CONFIGURATION_TYPES": "Debug;Release;RelWithDebInfo;MinSizeRel" } } ], "buildPresets": [ { "name": "build-debug", "configurePreset": "ninja-debug" }, { "name": "build-msvc", "configurePreset": "msvc-2022-release", "configuration": "Release" } ], "testPresets": [ { "name": "test-debug", "configurePreset": "ninja-debug", "output": { "outputOnFailure": true } } ] }

九、关键实践与可维护性要点

  • 目标导向(Modern CMake):使用 target_xxx API(compile_options/definitions/include_directories/link_libraries),不要用全局变量污染。
  • 版本可重复:FetchContent 固定 GIT_TAG,必要时开启 GIT_SHALLOW;发布时写死 URL_HASH。必要时提供 USE_SYSTEM_xxx 开关优先用包管理器。
  • 多配置与单配置兼容:对配置相关的选项使用生成器表达式 $<$CONFIG:Debug:...>;不要直接硬写 CMAKE_CXX_FLAGS_DEBUG 等全局变量。
  • 可选能力模块化:Sanitizers、Coverage、Warnings 放到 cmake/ 模块;顶层只开关和包含。
  • 导出与安装:始终安装导出目标和 *Config.cmake,给下游项目 find_package 使用;为命名空间选择稳定、唯一的前缀(如 MyProj::)。
  • 跨平台注意:
    • Windows 动态库导出用生成导出头(GenerateExportHeader),避免 CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 的不确定性。
    • 非 Windows 默认隐藏符号并通过导出宏控制外部可见性。
    • RPATH:Linux/macOS 设置 CMAKE_INSTALL_RPATH_USE_LINK_PATH,必要时精细化处理。
  • CI 与本地一致性:用 CMakePresets 驱动;CI 中直接 cmake --preset + cmake --build --preset + ctest --preset。
  • 构建类型默认值:仅针对单配置生成器设置 CMAKE_BUILD_TYPE;多配置(VS/Xcode)使用 CMAKE_CONFIGURATION_TYPES 或在 build 阶段传 configuration。
  • 打包:CPack 即插即用;复杂场景(DEB/RPM/NSIS)再按平台细化 CPACK_* 变量。
  • 警告即错误:可在 Debug/CI 才启用,以免影响用户编译体验。

这套配置可在小项目“即插即用”,又能平滑扩展到中大型工程(多库、多示例、多测试、更多依赖与平台)。将通用策略沉淀为 cmake/ 模块,保持 CMakeLists.txt 简洁清晰,便于维护与团队协作。

示例详情

该提示词已被收录:
“程序员必备:提升开发效率的专业AI提示词合集”
让 AI 成为你的第二双手,从代码生成到测试文档全部搞定,节省 80% 开发时间
√ 立即可用 · 零学习成本
√ 参数化批量生成
√ 专业提示词工程师打磨

解决的问题

帮助用户快速掌握用于项目构建工具配置的最佳实践,通过提供清晰、可操作的指导,解决项目配置的复杂性问题,同时提升配置的可扩展性和可维护性,最终实现高效开发与项目管理。

适用用户

初级开发者

帮助初级开发者快速上手复杂的构建工具配置,实现项目构建所需的基础目标,减少因经验不足带来的配置错误。

高级工程师

为高级工程师提供高效的配置优化指导,帮助他们在复杂项目中实现更优构建性能并确保良好扩展性。

项目经理

提供规范化和结构化的构建流程建议,帮助项目经理提升团队开发效率,确保持续交付质量。

特征总结

为开发者提供清晰的构建工具配置指导,快速搭建适合项目类型的构建环境。
智能匹配项目需求,生成符合多语言和多项目类型的最佳实践配置方案。
帮助开发者优化构建工具配置,确保项目能够高效运行并满足特定目标效果。
支持扩展性与可维护性,从长远角度提供具备前瞻性的配置建议。
减少复杂的配置时间,通过结构化的提示快速开始并高效推进。
适配不同场景,满足开发、测试、部署等全生命周期的构建需求。
提供灵活的模板化指导,便于开发者定制配置,满足个性化项目需求。
助力提升团队研发效率,规范化构建流程,有效降低配置错误风险。

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥20.00元
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 66 tokens
- 3 个可调节参数
{ 构建工具 } { 项目类型 } { 目标效果 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
使用提示词兑换券,低至 ¥ 9.9
了解兑换券 →
限时半价

不要错过!

半价获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59