Webpack 优化细节详述

Webpack 优化细节详述

本地开发优化

开启模块热替换(HMR)使用 Source Maps 优化编译速度

模块热替换(HMR)配置

HMR 允许在运行时更新模块,无需完整刷新页面,大大提升开发效率。

javascript 复制代码
// webpack.dev.js
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  devServer: {
    hot: true, // 启用 HMR
    port: 3000,
    open: true,
    compress: true,
    historyApiFallback: true,
    static: {
      directory: path.join(__dirname, 'public'),
    },
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],
  // 快速的 source map,适合开发环境
  devtool: 'eval-cheap-module-source-map',
};
Source Maps 最佳配置

不同的 source map 类型对编译速度影响很大:

javascript 复制代码
// 开发环境 source map 配置选择
const sourceMapConfig = {
  // 最快,但质量较低
  fastest: 'eval',
  
  // 平衡速度和质量
  balanced: 'eval-cheap-module-source-map',
  
  // 高质量,但较慢
  quality: 'eval-source-map',
  
  // 生产环境
  production: 'source-map'
};

module.exports = {
  devtool: process.env.NODE_ENV === 'production' 
    ? sourceMapConfig.production 
    : sourceMapConfig.balanced,
};
HMR 工作流程
graph TD A[文件变更] --> B[Webpack 检测变化] B --> C[重新编译变更模块] C --> D[生成 Hot Update] D --> E[推送到浏览器] E --> F[HMR Runtime 接收] F --> G{模块支持 HMR?} G -->|是| H[模块热更新] G -->|否| I[页面刷新] H --> J[UI 更新完成] I --> J

使用持久化缓存

Webpack 5 引入的持久化缓存功能可以显著提升构建速度:

javascript 复制代码
// webpack.config.js
module.exports = {
  cache: {
    type: 'filesystem', // 使用文件系统缓存
    buildDependencies: {
      config: [__filename], // 当配置文件改变时,缓存失效
    },
    cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
    store: 'pack', // 缓存存储方式
    version: '1.0', // 缓存版本
  },
  
  // 优化缓存命中率
  optimization: {
    moduleIds: 'deterministic',
    chunkIds: 'deterministic',
  },
};
缓存配置详解
javascript 复制代码
// 高级缓存配置
const cacheConfig = {
  cache: {
    type: 'filesystem',
    
    // 缓存版本管理
    version: createHash('md5')
      .update(JSON.stringify({
        nodeVersion: process.version,
        webpackVersion: require('webpack/package.json').version,
        configHash: getConfigHash(),
      }))
      .digest('hex'),
    
    // 缓存依赖项
    buildDependencies: {
      config: [
        __filename,
        path.resolve(__dirname, 'babel.config.js'),
        path.resolve(__dirname, 'postcss.config.js'),
      ],
    },
    
    // 自定义缓存目录
    cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack'),
    
    // 缓存策略
    managedPaths: [
      path.resolve(__dirname, 'node_modules'),
    ],
    
    // 缓存压缩
    compression: 'gzip',
  },
};

更快的增量编译

优化模块解析
javascript 复制代码
module.exports = {
  resolve: {
    // 减少文件扩展名解析
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
    
    // 优化模块查找路径
    modules: [
      path.resolve(__dirname, 'src'),
      'node_modules',
    ],
    
    // 设置别名减少查找时间
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
    },
    
    // 优化解析器缓存
    unsafeCache: true,
    
    // 符合链接优化
    symlinks: false,
  },
  
  // 优化模块构建
  module: {
    // 明确不解析的模块
    noParse: /jquery|lodash/,
    
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        include: path.resolve(__dirname, 'src'),
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true, // 启用 Babel 缓存
            cacheCompression: false,
          },
        },
      },
    ],
  },
};
多进程构建
javascript 复制代码
const TerserPlugin = require('terser-webpack-plugin');
const HappyPack = require('happypack');
const os = require('os');

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'happypack/loader?id=babel',
        exclude: /node_modules/,
      },
    ],
  },
  
  plugins: [
    new HappyPack({
      id: 'babel',
      threads: os.cpus().length,
      loaders: ['babel-loader?cacheDirectory=true'],
    }),
  ],
  
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: true, // 启用多进程压缩
        terserOptions: {
          compress: {
            drop_console: true,
          },
        },
      }),
    ],
  },
};

线上产物构建优化

使用生产模式

javascript 复制代码
// webpack.prod.js
module.exports = {
  mode: 'production', // 自动启用多项优化
  
  // 生产环境优化配置
  optimization: {
    minimize: true,
    sideEffects: false, // 启用 Tree Shaking
    usedExports: true,
    
    // 代码分割配置
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
        },
        common: {
          name: 'common',
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
        },
      },
    },
    
    // 运行时代码单独打包
    runtimeChunk: {
      name: 'runtime',
    },
  },
  
  // 生产环境 source map
  devtool: 'source-map',
};

代码分割

动态导入实现代码分割
javascript 复制代码
// 路由级别的代码分割
const routes = [
  {
    path: '/home',
    component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue'),
  },
  {
    path: '/about',
    component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'),
  },
];

// 组件级别的代码分割
const LazyComponent = lazy(() => 
  import(/* webpackChunkName: "lazy-component" */ './LazyComponent')
);

// 工具库按需加载
async function loadUtility() {
  const { debounce } = await import(
    /* webpackChunkName: "lodash-debounce" */ 
    'lodash/debounce'
  );
  return debounce;
}
高级代码分割配置
javascript 复制代码
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 20000,
      maxSize: 250000,
      
      cacheGroups: {
        // 第三方库分割
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
          chunks: 'all',
        },
        
        // React 相关库单独分割
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          priority: 20,
          chunks: 'all',
        },
        
        // 工具库分割
        utils: {
          test: /[\\/]node_modules[\\/](lodash|moment|axios)[\\/]/,
          name: 'utils',
          priority: 15,
          chunks: 'all',
        },
        
        // 公共代码分割
        common: {
          name: 'common',
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
代码分割流程
graph TD A[应用入口] --> B[分析依赖图] B --> C[识别分割点] C --> D[动态导入] C --> E[路由分割] C --> F[第三方库分割] D --> G[生成 Chunk] E --> G F --> G G --> H[文件命名] H --> I[输出文件] I --> J{需要时加载} J -->|是| K[异步加载 Chunk] J -->|否| L[保持空闲]

压缩 JavaScript

TerserPlugin 配置
javascript 复制代码
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: true, // 多进程压缩
        extractComments: false, // 不提取注释到单独文件
        
        terserOptions: {
          compress: {
            drop_console: true, // 移除 console
            drop_debugger: true, // 移除 debugger
            pure_funcs: ['console.log'], // 移除指定函数调用
            unused: true, // 移除未使用的代码
          },
          
          mangle: {
            safari10: true, // 兼容 Safari 10
          },
          
          format: {
            comments: false, // 移除注释
          },
        },
      }),
    ],
  },
};
高级 JavaScript 压缩
javascript 复制代码
// 自定义压缩配置
const compressionConfig = {
  // 生产环境压缩配置
  production: {
    compress: {
      arguments: false,
      dead_code: true,
      drop_console: true,
      drop_debugger: true,
      evaluate: true,
      hoist_funs: true,
      hoist_vars: false,
      if_return: true,
      join_vars: true,
      keep_fargs: false,
      loops: true,
      passes: 2, // 多次压缩
      pure_funcs: [
        'console.log',
        'console.info',
        'console.debug',
        'console.warn',
      ],
      reduce_vars: true,
      sequences: true,
      side_effects: false,
      typeofs: false,
      unused: true,
    },
    
    mangle: {
      properties: {
        regex: /^_/, // 混淆以 _ 开头的属性
      },
    },
  },
};

压缩 CSS

CSS 压缩配置
javascript 复制代码
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  ['autoprefixer'],
                  ['cssnano', { preset: 'default' }],
                ],
              },
            },
          },
        ],
      },
    ],
  },
  
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css',
    }),
  ],
  
  optimization: {
    minimizer: [
      new CssMinimizerPlugin({
        parallel: true,
        minimizerOptions: {
          preset: [
            'default',
            {
              discardComments: { removeAll: true },
              normalizeWhitespace: true,
              colormin: true,
              convertValues: true,
              discardDuplicates: true,
              discardEmpty: true,
              mergeRules: true,
              minifyFontValues: true,
              minifySelectors: true,
            },
          ],
        },
      }),
    ],
  },
};

启用 Tree Shaking 持久化缓存

Tree Shaking 配置
javascript 复制代码
module.exports = {
  mode: 'production',
  
  optimization: {
    usedExports: true, // 标记使用的导出
    sideEffects: false, // 标记无副作用
  },
  
  // 在 package.json 中配置
  // {
  //   "sideEffects": [
  //     "*.css",
  //     "*.scss",
  //     "./src/polyfills.js"
  //   ]
  // }
};
ES6 模块最佳实践
javascript 复制代码
// 推荐:命名导出,支持 Tree Shaking
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;

// 避免:默认导出整个对象
// export default { add, subtract, multiply };

// 使用时只导入需要的函数
import { add, subtract } from './math-utils';

// Lodash Tree Shaking 示例
import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle';
// 而不是:import { debounce, throttle } from 'lodash';

Bundle 分析

Bundle 分析工具配置
javascript 复制代码
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: process.env.ANALYZE ? 'server' : 'disabled',
      analyzerHost: '127.0.0.1',
      analyzerPort: 8888,
      reportFilename: 'bundle-report.html',
      defaultSizes: 'parsed',
      openAnalyzer: true,
      generateStatsFile: true,
      statsFilename: 'bundle-stats.json',
    }),
  ],
};

// package.json scripts
// {
//   "analyze": "ANALYZE=true npm run build",
//   "build:stats": "webpack --profile --json > stats.json"
// }
性能监控配置
javascript 复制代码
module.exports = {
  performance: {
    hints: 'warning',
    maxEntrypointSize: 250000, // 入口文件大小限制
    maxAssetSize: 200000, // 单个资源大小限制
    
    assetFilter: function(assetFilename) {
      // 只监控 JS 和 CSS 文件
      return assetFilename.endsWith('.js') || assetFilename.endsWith('.css');
    },
  },
  
  // 输出构建信息
  stats: {
    assets: true,
    chunks: true,
    modules: false,
    reasons: false,
    usedExports: true,
    providedExports: true,
  },
};

懒加载和按需加载压缩图片

图片优化配置
javascript 复制代码
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024, // 8KB 以下转为 base64
          },
        },
        generator: {
          filename: 'images/[name].[hash:8][ext]',
        },
      },
    ],
  },
  
  plugins: [
    new ImageMinimizerPlugin({
      minimizer: {
        implementation: ImageMinimizerPlugin.imageminMinify,
        options: {
          plugins: [
            ['imagemin-pngquant', { quality: [0.6, 0.8] }],
            ['imagemin-mozjpeg', { quality: 80 }],
            ['imagemin-svgo', {
              plugins: [
                { name: 'removeViewBox', active: false },
                { name: 'removeDimensions', active: true },
              ],
            }],
          ],
        },
      },
      
      generator: [
        {
          type: 'asset',
          preset: 'webp-custom-name',
          implementation: ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: ['imagemin-webp'],
          },
        },
      ],
    }),
  ],
};
响应式图片加载
javascript 复制代码
// 图片懒加载组件
import { useState, useEffect, useRef } from 'react';

const LazyImage = ({ src, alt, className, placeholder }) => {
  const [imageSrc, setImageSrc] = useState(placeholder);
  const [imageRef, setImageRef] = useState();

  useEffect(() => {
    let observer;
    
    if (imageRef && imageSrc === placeholder) {
      observer = new IntersectionObserver(
        entries => {
          entries.forEach(entry => {
            if (entry.isIntersecting) {
              setImageSrc(src);
              observer.unobserve(imageRef);
            }
          });
        },
        { threshold: 0.1 }
      );
      observer.observe(imageRef);
    }
    
    return () => {
      if (observer && observer.unobserve) {
        observer.unobserve(imageRef);
      }
    };
  }, [imageRef, imageSrc, placeholder, src]);

  return (
    <img
      ref={setImageRef}
      src={imageSrc}
      alt={alt}
      className={className}
      loading="lazy"
    />
  );
};

// WebP 支持检测
const supportsWebP = () => {
  return new Promise(resolve => {
    const webP = new Image();
    webP.onload = webP.onerror = () => {
      resolve(webP.height === 2);
    };
    webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
  });
};

内联 CSS 和 JavaScript

关键资源内联
javascript 复制代码
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
    }),
    
    // 内联运行时代码
    new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
  ],
  
  optimization: {
    runtimeChunk: {
      name: 'runtime',
    },
  },
};
关键 CSS 内联
javascript 复制代码
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlCriticalWebpackPlugin = require('html-critical-webpack-plugin');

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
    
    new HtmlCriticalWebpackPlugin({
      base: path.resolve(__dirname, 'dist'),
      src: 'index.html',
      dest: 'index.html',
      inline: true,
      minify: true,
      extract: true,
      width: 375,
      height: 565,
      penthouse: {
        blockJSRequests: false,
      },
    }),
  ],
};

完整示例配置

开发环境配置

javascript 复制代码
// webpack.dev.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  
  entry: {
    main: './src/index.js',
  },
  
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.js',
    chunkFilename: '[name].chunk.js',
    publicPath: '/',
  },
  
  devtool: 'eval-cheap-module-source-map',
  
  devServer: {
    hot: true,
    port: 3000,
    open: true,
    compress: true,
    historyApiFallback: true,
    static: {
      directory: path.join(__dirname, 'public'),
    },
  },
  
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename],
    },
  },
  
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
    },
    modules: [
      path.resolve(__dirname, 'src'),
      'node_modules',
    ],
  },
  
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: ['@babel/plugin-syntax-dynamic-import'],
          },
        },
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024,
          },
        },
      },
    ],
  },
  
  plugins: [
    new CleanWebpackPlugin(),
    
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      inject: true,
    }),
    
    new webpack.HotModuleReplacementPlugin(),
    
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('development'),
    }),
  ],
  
  optimization: {
    moduleIds: 'named',
    chunkIds: 'named',
  },
};

生产环境配置

javascript 复制代码
// webpack.prod.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  mode: 'production',
  
  entry: {
    main: './src/index.js',
  },
  
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    publicPath: '/',
    clean: true,
  },
  
  devtool: 'source-map',
  
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename],
    },
    version: '1.0',
  },
  
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
    },
  },
  
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            presets: [
              ['@babel/preset-env', {
                modules: false,
                useBuiltIns: 'usage',
                corejs: 3,
              }],
              '@babel/preset-react',
            ],
            plugins: ['@babel/plugin-syntax-dynamic-import'],
          },
        },
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
        ],
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024,
          },
        },
        generator: {
          filename: 'images/[name].[hash:8][ext]',
        },
      },
    ],
  },
  
  plugins: [
    new CleanWebpackPlugin(),
    
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
    }),
    
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css',
    }),
    
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production'),
    }),
    
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 8192,
      minRatio: 0.8,
    }),
    
    process.env.ANALYZE && new BundleAnalyzerPlugin(),
  ].filter(Boolean),
  
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
        extractComments: false,
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true,
            pure_funcs: ['console.log'],
          },
          mangle: {
            safari10: true,
          },
          format: {
            comments: false,
          },
        },
      }),
      
      new CssMinimizerPlugin({
        parallel: true,
      }),
    ],
    
    moduleIds: 'deterministic',
    chunkIds: 'deterministic',
    
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
        },
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          priority: 20,
        },
        common: {
          name: 'common',
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
        },
      },
    },
    
    runtimeChunk: {
      name: 'runtime',
    },
    
    usedExports: true,
    sideEffects: false,
  },
  
  performance: {
    hints: 'warning',
    maxEntrypointSize: 250000,
    maxAssetSize: 200000,
  },
};
构建优化流程总览
graph TD A[开始构建] --> B{环境判断} B -->|开发环境| C[启用 HMR] B -->|生产环境| D[启用压缩] C --> E[快速 Source Map] C --> F[持久化缓存] D --> G[代码分割] D --> H[Tree Shaking] D --> I[资源压缩] E --> J[增量编译] F --> J G --> K[Bundle 分析] H --> K I --> K J --> L[开发服务器] K --> M[生产构建] L --> N[热更新] M --> O[部署优化] N --> P[开发完成] O --> Q[上线部署]

通过以上配置和优化策略,可以显著提升 Webpack 项目的构建性能和运行效率。开发环境注重快速编译和调试体验,生产环境则专注于代码质量和加载性能的优化。根据项目实际需求,可以灵活调整相关配置参数。

相关推荐
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax