<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <atom:link href="https://niunaiclub.online/tips/feed.xml" rel="self" type="application/rss+xml" />
    <title>锦囊妙计 - 墨渊书肆</title>
    <link>https://niunaiclub.online/tips</link>
    <description>墨渊书肆开发者问答库，涵盖 CSS、JavaScript、TypeScript、React、Vue、Node.js 等技术领域的实战问答，一问一答解决真实生产问题。</description>
    <language>zh-CN</language>
    <lastBuildDate>Thu, 04 Jun 2026 09:21:44 GMT</lastBuildDate>
    <dc:creator>仓乃文</dc:creator>
    <generator>墨渊书肆 RSS</generator>
    
    <item>
      <title><![CDATA[前端项目从 Webpack 迁移到 Vite 怎么做？有哪些常见坑？]]></title>
      <link>https://niunaiclub.online/tips/engineering/migrate-from-webpack-to-vite-common-pitfalls</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/migrate-from-webpack-to-vite-common-pitfalls</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Webpack 迁移 Vite 不是简单的配置替换，而是开发范式的转换。最常见的坑是环境变量前缀变化、CommonJS 模块兼容、index.html 位置变化，以及插件生态不兼容。建议渐进式迁移，不要一步到位。

迁移步骤：
```bash
# 1. 安装 Vite 和框架插件
pnpm add -D vite @vitejs/plugin-vue

# 2. 将 index.html 从 public/ 移到项目根目录
```

index.html 修改：
```html


  
  

```

vite.config.ts 核心配置：
```typescript
import { defineConfig } from &quot;vite&quot;;
import vue from &quot;@vitejs/plugin-vue&quot;;
import { resolve } from &quot;path&quot;;

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      &quot;@&quot;: resolve(__dirn]]></description>
    </item>
    <item>
      <title><![CDATA[Git 提交规范怎么落地？husky + commitlint 配置后不生效怎么排查？]]></title>
      <link>https://niunaiclub.online/tips/engineering/git-commit-convention-husky-commitlint-not-working</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/git-commit-convention-husky-commitlint-not-working</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[团队约定了 Git 提交规范（feat/fix/docs...），但总有人不遵守。配置了 husky + commitlint 后发现不生效，提交不规范信息照样能提交。

配置步骤：
```bash
# 1. 安装依赖
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional

# 2. 初始化 husky
npx husky init

# 3. 添加 commit-msg 钩子
echo &quot;npx --no -- commitlint --edit \$1&quot; &gt; .husky/commit-msg

# 4. 配置 commitlint
# commitlint.config.js
module.exports = {
  extends: [&quot;@commitlint/config-conventional&quot;],
};

# 5. 配置 pre-commit 钩子
echo &quot;npx lint-staged&quot; &gt; .husky/pre-commit
```

提交规范格式：
```
()]]></description>
    </item>
    <item>
      <title><![CDATA[ESLint 和 Prettier 格式冲突怎么解决？]]></title>
      <link>https://niunaiclub.online/tips/engineering/eslint-prettier-conflict-resolution</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/eslint-prettier-conflict-resolution</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[ESLint 和 Prettier 同时使用时，格式规则经常冲突：ESLint 要求单引号，Prettier 格式化成双引号；保存文件时两个工具来回拉扯，代码永远有红色波浪线。

冲突原因：
```
ESLint 有两类规则：
1. 代码质量规则：no-unused-vars, no-console（必须保留）
2. 格式规则：quotes, semi, indent（和 Prettier 冲突的部分）

Prettier 只管格式，不管代码质量。
两者在格式规则上各执一词，就冲突了。
```

解决方案：
```bash
# 1. 安装兼容包
npm install --save-dev eslint-config-prettier eslint-plugin-prettier
# eslint-config-prettier：关闭 ESLint 中和 Prettier 冲突的规则
# eslint-plugin-prettier：让 ESLint 用 Prettier 的格式规则

# 2. 配置 .eslintrc.js
module.exports = {
  extends:]]></description>
    </item>
    <item>
      <title><![CDATA[CI/CD 流水线本地能跑但构建失败怎么排查？]]></title>
      <link>https://niunaiclub.online/tips/engineering/cicd-pipeline-local-works-but-build-fails-troubleshoot</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/cicd-pipeline-local-works-but-build-fails-troubleshoot</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[&quot;本地没问题，CI 挂了&quot;是最让人崩溃的工程化问题。90% 的原因是环境不一致：Node 版本、依赖版本、环境变量、文件大小写。

常见原因与修复：
```bash
# 原因 1：Node 版本不一致
# 修复：在 CI 配置中明确指定 Node 版本
# GitHub Actions
- uses: actions/setup-node@v3
  with:
    node-version: &quot;20&quot;
# 或创建 .nvmrc
echo &quot;20&quot; &gt; .nvmrc

# 原因 2：依赖版本不一致
# 修复：CI 中用 npm ci 而不是 npm install
- run: npm ci  # 不是 npm install！

# 原因 3：环境变量缺失
# 修复：在 CI 配置中注入环境变量
- run: npm run build
  env:
    VITE_API_URL: ${{ secrets.API_URL }}

# 原因 4：文件大小写问题
# macOS 不区分大小写，Linux 区分
# import Header from &quot;./header&quot; 但文件名]]></description>
    </item>
    <item>
      <title><![CDATA[前端构建产物体积太大怎么分析和优化？webpack-bundle-analyzer 怎么用？]]></title>
      <link>https://niunaiclub.online/tips/engineering/frontend-bundle-size-analysis-webpack-bundle-analyzer-optimization</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/frontend-bundle-size-analysis-webpack-bundle-analyzer-optimization</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[构建产物体积大通常不是单一原因，而是重复依赖、未 tree-shake、未压缩、大库未拆分等多个问题叠加。先用分析工具定位问题，再针对性优化，不要盲目猜测。

安装和使用分析工具：
```bash
# Webpack 项目
pnpm add -D webpack-bundle-analyzer

# Vite 项目
pnpm add -D rollup-plugin-visualizer
```

Webpack 配置：
```javascript
const BundleAnalyzerPlugin = require(&quot;webpack-bundle-analyzer&quot;).BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: &quot;server&quot;,
      analyzerPort: 8888,
      openAnalyzer: true,
    }),
  ],
};
```

Vite 配置：
```typescri]]></description>
    </item>
    <item>
      <title><![CDATA[npm 私有包怎么搭建？Verdaccio 怎么部署和使用？]]></title>
      <link>https://niunaiclub.online/tips/engineering/npm-private-registry-verdaccio-deployment</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/npm-private-registry-verdaccio-deployment</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[企业内部共享组件库和工具包需要私有 npm 仓库。Verdaccio 是最轻量的私有 npm 方案，5 分钟即可部署，支持代理上游 npm、权限控制和包缓存。比 Git submodule 和文件引用优雅得多。

部署 Verdaccio：
```bash
# 方式 1：Docker 部署（推荐）
docker run -d \
  --name verdaccio \
  -p 4873:4873 \
  -v /data/verdaccio:/verdaccio/storage \
  verdaccio/verdaccio

# 方式 2：全局安装
npm install -g verdaccio
verdaccio
# 默认运行在 http://localhost:4873
```

配置文件：
```yaml
# ~/.config/verdaccio/config.yaml
storage: /verdaccio/storage

auth:
  htpasswd:
    file: /verdaccio/htpasswd
    max_users: 100

upli]]></description>
    </item>
    <item>
      <title><![CDATA[Vite 依赖预构建失败或卡住怎么排查和修复？]]></title>
      <link>https://niunaiclub.online/tips/engineering/vite-dependency-pre-bundling-failed-stuck-troubleshoot</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/vite-dependency-pre-bundling-failed-stuck-troubleshoot</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Vite 启动时会自动预构建依赖，但有时预构建失败或卡住，导致开发服务器启动不了。

预构建的作用：
```
1. CommonJS → ESM 转换
2. 减少模块数量（lodash 600+ 模块合并成一个文件）
3. 缓存到 node_modules/.vite/
```

常见问题与修复：
```bash
# 问题 1：预构建失败
# 修复：重新安装依赖
pnpm install

# 问题 2：预构建卡住
# 修复：排除问题包
# vite.config.ts
export default defineConfig({
  optimizeDeps: {
    exclude: [&quot;problematic-package&quot;],
  },
});

# 问题 3：CJS 包报 require is not defined
# 修复：强制包含
export default defineConfig({
  optimizeDeps: {
    include: [&quot;cjs-package&quot;],
  },
});

# 问题 4：依赖更新后还是旧的预构建结果
# 修复：清除缓存]]></description>
    </item>
    <item>
      <title><![CDATA[EditorConfig、Prettier、ESLint 配置优先级冲突怎么统一？]]></title>
      <link>https://niunaiclub.online/tips/engineering/editorconfig-prettier-eslint-priority-conflict-resolution</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/editorconfig-prettier-eslint-priority-conflict-resolution</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[EditorConfig、Prettier、ESLint 三个工具都能管代码格式，配置冲突时互相打架。必须明确各自职责和优先级。

三者的职责划分：
```
| 工具         | 职责                    | 管什么                  |
|-------------|------------------------|------------------------|
| EditorConfig | 编辑器基础格式           | 缩进、换行符、字符集      |
| Prettier     | 代码格式化              | 行宽、引号、分号、逗号     |
| ESLint       | 代码质量+格式检查        | 未使用变量、最佳实践      |

原则：格式问题统一由 Prettier 决定，ESLint 只管代码质量
```

配置统一方案：
```ini
# .editorconfig
[*]
charset = utf-8
end_of_line = lf
indent_style = sp]]></description>
    </item>
    <item>
      <title><![CDATA[前端项目怎么配置一键回滚？上线出问题 5 分钟内恢复？]]></title>
      <link>https://niunaiclub.online/tips/engineering/frontend-one-click-rollback-5min-recovery</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/frontend-one-click-rollback-5min-recovery</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[上线出问题是常态，关键是怎么快速恢复。一键回滚是生产环境的基本要求。

推荐方案：版本号切换（30 秒回滚）：
```bash
# 目录结构
/var/www/
├── releases/
│   ├── 20240509-abc123/    # 每次部署一个目录
│   ├── 20240508-def456/
│   └── 20240507-ghi789/
├── current -&gt; releases/20240509-abc123/  # 软链接
└── rollback.sh

# 部署脚本
VERSION=$(date +%Y%m%d)-$(git rev-parse --short HEAD)
mkdir -p /var/www/releases/$VERSION
cp -r dist/* /var/www/releases/$VERSION/
ln -sfn /var/www/releases/$VERSION /var/www/current

# 回滚脚本（30 秒完成）
CURRENT=$(readlink /var/www/current)
PREVIOUS]]></description>
    </item>
    <item>
      <title><![CDATA[pnpm workspace 中 --filter 怎么用？只构建变更的包？]]></title>
      <link>https://niunaiclub.online/tips/engineering/pnpm-workspace-filter-build-changed-packages</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/pnpm-workspace-filter-build-changed-packages</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Monorepo 中全量构建所有包太慢，pnpm 的 --filter 可以精准地只构建变更的包及其依赖者。

基础用法：
```bash
pnpm --filter @acme/app dev           # 只启动指定包
pnpm --filter &quot;./packages/*&quot; build     # 按目录模式过滤
pnpm --filter &quot;!@acme/docs&quot; build      # 排除某个包
pnpm -r --parallel dev                 # 并行执行所有包
```

依赖感知过滤：
```bash
# 构建 @acme/app 及其所有依赖
pnpm --filter @acme/app... build

# 构建 @acme/ui 及其所有依赖者
pnpm --filter ...@acme/ui build

# 只构建变更的包（CI 中最常用）
pnpm --filter &quot;[origin/main]&quot; build
```

补充说明：
- ... 在包名后面 = 包含该包的所有依赖
- ... 在包名前面 = 包含]]></description>
    </item>
    <item>
      <title><![CDATA[Git 合并冲突频繁怎么减少？分支策略怎么选？]]></title>
      <link>https://niunaiclub.online/tips/engineering/git-merge-conflict-reduce-branch-strategy-selection</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/git-merge-conflict-reduce-branch-strategy-selection</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[团队开发中合并冲突频繁，严重影响效率。减少冲突需要从分支策略、代码组织、自动化工具三方面入手。

三种分支策略对比：
```
| 策略         | 适用场景           | 复杂度 |
|-------------|-------------------|-------|
| Git Flow    | 大型项目、版本发布 | 高    |
| GitHub Flow | 持续部署、敏捷开发 | 低    |
| GitLab Flow | 多环境部署        | 中    |

推荐：5人以下 GitHub Flow，5-20人 GitLab Flow，20人以上 Git Flow
```

减少冲突的方法：
```bash
# 1. 缩短分支存活时间（1-2 天合并一次）
# 2. 频繁同步主分支
git fetch origin &amp;&amp; git rebase origin/main
# 3. 模块化代码组织，减少多人改同一文件
# 4. 用 rebase 代替 merge
git config --global pull.rebase true
# 5. li]]></description>
    </item>
    <item>
      <title><![CDATA[前端部署后用户还是看到旧页面？缓存策略怎么设计？]]></title>
      <link>https://niunaiclub.online/tips/engineering/frontend-deployment-cache-strategy-users-see-old-page</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/frontend-deployment-cache-strategy-users-see-old-page</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[部署了新版本，用户刷新还是看到旧页面。根本原因是缓存策略设计不合理，需要在&quot;加载速度&quot;和&quot;及时更新&quot;之间找到平衡。

缓存策略核心原则：
```
HTML 文件：不缓存或短缓存（必须每次拿最新的）
JS/CSS/图片：长期缓存（文件名带 hash，内容变了 hash 就变）
API 请求：不缓存（每次都拿最新数据）
```

Nginx 缓存配置：
```nginx
server {
  location / {
    # HTML 不缓存
    if ($request_filename ~* \.html$) {
      add_header Cache-Control &quot;no-cache, no-store, must-revalidate&quot;;
      add_header Pragma &quot;no-cache&quot;;
      add_header Expires 0;
    }
  }

  location /assets/ {
    # 带 hash 的静态资源，长期缓存
    expires 1y;
    add_header Cache-Control ]]></description>
    </item>
    <item>
      <title><![CDATA[npm 和 pnpm 有什么区别？为什么推荐 pnpm？]]></title>
      <link>https://niunaiclub.online/tips/engineering/npm-vs-pnpm-difference-why-recommend-pnpm</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/npm-vs-pnpm-difference-why-recommend-pnpm</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[npm 和 pnpm 都是 Node.js 包管理器，但依赖结构和安装机制完全不同。pnpm 凭借严格依赖隔离和磁盘共享，已经成为前端工程化的推荐选择。

核心区别：
```
| 维度         | npm v3+              | pnpm                  |
|-------------|----------------------|------------------------|
| 依赖结构     | 扁平化               | 严格隔离（软链接+硬链接） |
| 幽灵依赖     | 有                   | 无                     |
| 磁盘占用     | 每个项目独立存储      | 全局 store 硬链接共享    |
| 安装速度     | 慢                   | 快                     |
| Monorepo    | workspaces（基础）    | workspace（原生+过滤）  |
| 依赖一致性   | 不保]]></description>
    </item>
    <item>
      <title><![CDATA[前端项目怎么接入 Sentry 错误监控？Source Map 怎么配置？]]></title>
      <link>https://niunaiclub.online/tips/engineering/frontend-sentry-error-monitoring-source-map-configuration</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/frontend-sentry-error-monitoring-source-map-configuration</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Sentry 接入本身很简单，但 Source Map 配置是最大的坑。Source Map 没配好，线上报错看到的都是压缩后的代码，完全无法定位问题。关键原则：Source Map 必须上传到 Sentry，绝不能部署到生产环境。

接入步骤：
```bash
# 1. 安装 Sentry SDK
pnpm add @sentry/vue @sentry/tracing

# 2. 安装 Source Map 上传插件
pnpm add -D @sentry/vite-plugin
```

初始化配置：
```typescript
import * as Sentry from &quot;@sentry/vue&quot;;
import { BrowserTracing } from &quot;@sentry/tracing&quot;;

Sentry.init({
  dsn: &quot;https://xxx@xxx.ingest.sentry.io/xxx&quot;,
  integrations: [new BrowserTracing()],
  tracesSampleRate: 0.1,
  environment]]></description>
    </item>
    <item>
      <title><![CDATA[Vite HMR 热更新失效或不触发怎么排查？]]></title>
      <link>https://niunaiclub.online/tips/engineering/vite-hmr-hot-reload-not-working-troubleshooting</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/vite-hmr-hot-reload-not-working-troubleshooting</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Vite 的 HMR 失效通常不是 Vite 本身的 Bug，而是文件不在模块图内、插件拦截了更新、或浏览器 WebSocket 连接断开。按以下步骤排查，基本都能定位原因。

排查清单：
```bash
# 1. 检查浏览器控制台是否有 WebSocket 连接错误
# 常见报错：WebSocket connection to &quot;ws://localhost:5173/&quot; failed
# → 检查是否有代理或防火墙拦截
# → 检查 server.hmr.port 是否被占用

# 2. 检查文件是否在 Vite 的模块图内
# 直接在浏览器访问该文件，如果 404 说明路径配置有问题

# 3. 检查 Docker/WSL 环境下的文件监听
# Docker 和 WSL 的文件系统事件默认不传递到宿主机
```

WSL/Docker 环境修复：
```typescript
// vite.config.ts
export default defineConfig({
  server: {
    watch: {
      usePolling: true,
      i]]></description>
    </item>
    <item>
      <title><![CDATA[前端组件库怎么搭建和发布到 npm？package.json 需要配置哪些字段？]]></title>
      <link>https://niunaiclub.online/tips/engineering/build-and-publish-component-library-to-npm-package-json</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/build-and-publish-component-library-to-npm-package-json</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[组件库发布到 npm 最大的坑不是代码本身，而是 package.json 的字段配置错误导致消费者无法正确引入。main/module/exports/types 这四个字段必须配齐，否则要么引入报错，要么类型丢失。

package.json 关键字段：
```json
{
  &quot;name&quot;: &quot;my-ui-lib&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;private&quot;: false,
  &quot;main&quot;: &quot;dist/index.cjs.js&quot;,
  &quot;module&quot;: &quot;dist/index.esm.js&quot;,
  &quot;types&quot;: &quot;dist/index.d.ts&quot;,
  &quot;exports&quot;: {
    &quot;.&quot;: {
      &quot;import&quot;: &quot;./dist/index.esm.js&quot;,
      &quot;require&quot;: &quot;./dist/index.cjs.js&quot;,
      &quot;types&quot;: &quot;./dist/index.d.ts&quot;
    },
    &quot;./style.css&quot;: &quot;./dist/style.css&quot;
  },
  &quot;files&quot;: ]]></description>
    </item>
    <item>
      <title><![CDATA[package.json 中 ~ 和 ^ 版本号有什么区别？为什么建议锁版本？]]></title>
      <link>https://niunaiclub.online/tips/engineering/package-json-tilde-caret-version-difference-lock-version</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/package-json-tilde-caret-version-difference-lock-version</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[package.json 中的版本号前缀决定了依赖更新的范围，用错了可能导致&quot;昨天还能跑，今天就挂了&quot;。

版本号格式：
```
^1.2.3  → 允许 &gt;=1.2.3 =1.2.3 &lt;1.3.0（补丁更新，只允许 patch）
1.2.3   → 精确版本（不允许任何更新）

示例：
^1.2.3 → 1.2.3, 1.2.4, 1.3.0, 1.9.9  ✓
         2.0.0                       ✗
~1.2.3 → 1.2.3, 1.2.4, 1.2.9        ✓
         1.3.0                       ✗
1.2.3  → 1.2.3                       ✓
         1.2.4                       ✗
```

为什么 ^ 可能出问题：
```bash
# 你的 package.json
&quot;lodash&quot;: &quot;^4.17.0&quot;
# 今天安装：4.17.21
# 明天 lodash 发布 4.18.0（可能有 breaking change）
]]></description>
    </item>
    <item>
      <title><![CDATA[Webpack tree-shaking 不生效怎么排查？sideEffects 怎么配置？]]></title>
      <link>https://niunaiclub.online/tips/engineering/webpack-tree-shaking-not-working-sideEffects-configuration</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/webpack-tree-shaking-not-working-sideEffects-configuration</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Tree-shaking 不生效最常见的三个原因：mode 不是 production、模块有副作用、ES Module 导出方式不对。排查时按顺序检查这三项，90% 的问题都能解决。

排查步骤：
```bash
# 第 1 步：确认 mode 为 production
npx webpack --mode=production

# 第 2 步：确认使用 ES Module 导出
# 错误写法（CommonJS，无法 tree-shake）：
module.exports = { foo, bar }
# 正确写法（ES Module）：
export function foo() {}
export function bar() {}

# 第 3 步：检查 package.json 的 sideEffects
```

sideEffects 配置：
```json
{
  &quot;sideEffects&quot;: false
}
// 告诉 Webpack：这个包的所有模块都没有副作用，可以安全地 tree-shake

// 如果有副作用的文件，用数组列出：
{
  &quot;sideEffe]]></description>
    </item>
    <item>
      <title><![CDATA[微前端方案怎么选？qiankun vs Module Federation vs single-spa 各自适用场景？]]></title>
      <link>https://niunaiclub.online/tips/engineering/micro-frontend-comparison-qiankun-module-federation-single-spa</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/micro-frontend-comparison-qiankun-module-federation-single-spa</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[三种主流微前端方案的本质区别在于隔离粒度和加载时机。选错方案会导致后期维护成本翻倍，特别是样式污染和通信复杂度这两个坑，每种方案的处理方式完全不同。

方案对比：
```
| 维度           | qiankun          | Module Federation | single-spa       |
|---------------|------------------|-------------------|------------------|
| 隔离性         | 强（JS沙箱+样式隔离）| 弱（共享运行时）    | 中（需自行配置）   |
| 加载时机       | 运行时            | 构建时+运行时       | 运行时            |
| 技术栈要求     | 无限制            | 需 Webpack 5      | 无限制            |
| 通信方式       | props/initGlobalState | 模块直接导入      | Custom Events     |
|]]></description>
    </item>
    <item>
      <title><![CDATA[Webpack Module Federation 微前端怎么配置？远程模块加载失败怎么排查？]]></title>
      <link>https://niunaiclub.online/tips/engineering/webpack-module-federation-micro-frontend-remote-loading-failed</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/webpack-module-federation-micro-frontend-remote-loading-failed</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Module Federation 是 Webpack 5 原生的微前端方案，允许运行时动态加载远程模块。配置不当最常见的报错是 Loading script failed 和 Shared module is not available，90% 是 publicPath 或 shared 版本不匹配导致的。

基础配置：
```javascript
// 远程应用（Remote）webpack.config.js
const ModuleFederationPlugin = require(&quot;webpack/lib/container/ModuleFederationPlugin&quot;);

module.exports = {
  output: {
    publicPath: &quot;auto&quot;,  // 关键：让 Webpack 自动推断公共路径
  },
  plugins: [
    new ModuleFederationPlugin({
      name: &quot;remoteApp&quot;,
      filename: &quot;remoteEntry.js&quot;,
      expo]]></description>
    </item>
    <item>
      <title><![CDATA[前端项目 Docker 构建镜像太大怎么优化？]]></title>
      <link>https://niunaiclub.online/tips/engineering/frontend-docker-image-too-large-optimization</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/frontend-docker-image-too-large-optimization</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[前端项目 Docker 镜像动辄 1-2GB，因为包含了 node_modules、源代码等不需要的东西。优化后可以降到 50MB 以下。

优化方案：
```dockerfile
# 错误示范（镜像 1.5GB）
FROM node:18
COPY . .
RUN npm install  &quot;]
```

优化要点：
```
1. 多阶段构建：构建阶段用 node 镜像，运行阶段用 nginx
2. alpine 基础镜像：node:18-alpine 比 node:18 小 800MB
3. .dockerignore：排除不需要的文件
4. 利用缓存：先 COPY package.json 再 COPY 源代码
```

.dockerignore：
```
node_modules
dist
.git
.env*
*.md
.vscode
.idea
```

补充说明：
- 规则：前端 Docker 镜像只需要 nginx + dist 产物
- 多阶段构建是关键，最终镜像不包含 node_modules
- alpine 镜像比标准镜像小 80%
- COPY packag]]></description>
    </item>
    <item>
      <title><![CDATA[Monorepo 中包之间互相依赖怎么联调？workspace:* 怎么用？]]></title>
      <link>https://niunaiclub.online/tips/engineering/monorepo-workspace-protocol-package-linking</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/monorepo-workspace-protocol-package-linking</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Monorepo 中多个包互相依赖时，联调是核心痛点。用 npm link 有双实例问题，pnpm workspace:* 协议完美解决了这个问题。

workspace 协议：
```json
// packages/ui/package.json
{ &quot;name&quot;: &quot;@acme/ui&quot;, &quot;version&quot;: &quot;1.0.0&quot; }

// packages/app/package.json
{
  &quot;dependencies&quot;: {
    &quot;@acme/ui&quot;: &quot;workspace:*&quot;  // 指向本地 workspace
  }
}
```

workspace:* 的行为：
```
开发环境：workspace:* → 软链接到 packages/ui
  修改 ui 的代码，app 立即生效（HMR 正常）

发布时：workspace:* → 替换为实际版本号
  &quot;@acme/ui&quot;: &quot;workspace:*&quot; → &quot;@acme/ui&quot;: &quot;1.0.0&quot;
```

联调常见问题：
```bash
# 问题 1：修改了共享包但 app 没有更新
# 原因：共享包需要]]></description>
    </item>
    <item>
      <title><![CDATA[Vite 开发环境代理正常但生产环境跨域怎么办？]]></title>
      <link>https://niunaiclub.online/tips/engineering/vite-dev-proxy-works-but-production-cors-issue</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/vite-dev-proxy-works-but-production-cors-issue</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Vite 的 server.proxy 只在开发环境生效，生产环境没有 Vite Dev Server，代理自然失效。这是初学者最容易翻车的认知盲区。

问题本质：
```
开发环境：浏览器 → Vite Dev Server → 代理到 API（同源，无跨域）
生产环境：浏览器 → API 服务器（跨域！）

Vite 的 proxy 是开发工具，不是生产方案！
```

生产环境解决方案：
```nginx
# 方案 1：Nginx 反向代理（推荐）
server {
  location /api/ {
    proxy_pass http://api-server:3000/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}
# 浏览器请求 /api/users → Nginx 转发，无跨域

# 方案 2：CORS（后端配置）
Access-Control-Allow-Origin: https://your-frontend.com
Access-Con]]></description>
    </item>
    <item>
      <title><![CDATA[Webpack 的 hash/chunkhash/contenthash 有什么区别？用错导致缓存失效？]]></title>
      <link>https://niunaiclub.online/tips/engineering/webpack-hash-chunkhash-contenthash-difference-cache</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/webpack-hash-chunkhash-contenthash-difference-cache</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Webpack 提供了三种 hash 类型，用错了会导致缓存频繁失效或缓存不更新。

三种 hash 对比：
```
| 类型        | 计算范围              | 变化时机               | 推荐度 |
|------------|---------------------|----------------------|-------|
| hash       | 整个构建             | 任何文件变化，所有hash都变 | 不推荐 |
| chunkhash  | 同一 chunk          | chunk 内任何文件变化     | 多入口 |
| contenthash| 文件内容             | 只有内容变了hash才变     | 推荐   |
```

问题演示：
```javascript
// 用 hash：修改任何一个文件，所有 JS 的 hash 都变
// 用户要重新下载所有 JS

// 用 contenthash（推荐）：只有文件内容真正变化，hash 才变
output: {
  file]]></description>
    </item>
    <item>
      <title><![CDATA[前端环境变量 .env 怎么管理？VITE_ 前缀和 NODE_ENV 有什么坑？]]></title>
      <link>https://niunaiclub.online/tips/engineering/frontend-env-file-management-vite-prefix-node-env</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/frontend-env-file-management-vite-prefix-node-env</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[环境变量管理不好会导致：API 地址写死在代码里、密钥泄露到前端、多环境切换混乱。

Vite 环境变量规则：
```bash
# .env 文件加载顺序（后者覆盖前者）
.env → .env.local → .env.development → .env.development.local
.env → .env.local → .env.production → .env.production.local

# 关键规则：只有 VITE_ 前缀的变量才会暴露给客户端！
VITE_API_URL=https://api.example.com    # 代码中可访问
SECRET_KEY=abc123                        # 代码中访问不到！安全！

# 代码中使用
const apiUrl = import.meta.env.VITE_API_URL;  // 正确
const secret = import.meta.env.SECRET_KEY;     // undefined！
```

NODE_ENV 的坑：
```bash
# 坑 1：不要]]></description>
    </item>
    <item>
      <title><![CDATA[TypeScript 项目 tsc 编译慢怎么优化？]]></title>
      <link>https://niunaiclub.online/tips/engineering/typescript-tsc-compile-slow-optimization</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/typescript-tsc-compile-slow-optimization</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[TypeScript 项目随着代码量增长，tsc 编译越来越慢，大项目全量类型检查可能要 1-2 分钟。

核心优化方案：
```json
// 1. 开启增量编译（性价比最高）
{ &quot;compilerOptions&quot;: { &quot;incremental&quot;: true } }
// 生成 .tsbuildinfo 缓存，二次编译只检查变化的文件

// 2. 跳过库的类型检查
{ &quot;compilerOptions&quot;: { &quot;skipLibCheck&quot;: true } }
// 不检查 node_modules 的类型，编译速度提升 30-50%

// 3. 项目引用（大型项目）
{
  &quot;references&quot;: [
    { &quot;path&quot;: &quot;./packages/shared&quot; },
    { &quot;path&quot;: &quot;./packages/app&quot; }
  ]
}
// 每个子项目独立编译

// 4. 排除不需要检查的目录
{ &quot;exclude&quot;: [&quot;node_modules&quot;, &quot;dist&quot;, &quot;**/*.spec.ts&quot;] }
```

开发环境优化：
```json
// 开]]></description>
    </item>
    <item>
      <title><![CDATA[Webpack 构建太慢怎么优化？从 10 分钟降到 1 分钟的方案？]]></title>
      <link>https://niunaiclub.online/tips/engineering/webpack-build-slow-optimization-10min-to-1min</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/webpack-build-slow-optimization-10min-to-1min</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Webpack 构建慢是前端工程化的经典痛点，大项目构建动辄 5-10 分钟。优化需要从缓存、并行、范围缩小三个方向同时发力。

诊断构建瓶颈：
```bash
# 1. 分析构建耗时
npm install --save-dev speed-measure-webpack-plugin
# 会显示每个 loader 和 plugin 的耗时

# 2. 分析打包体积
npm install --save-dev webpack-bundle-analyzer
# 可视化展示每个模块的大小
```

核心优化方案：
```javascript
// 1. 开启持久化缓存（Webpack 5，性价比最高）
module.exports = {
  cache: {
    type: &quot;filesystem&quot;,  // 文件系统缓存
    buildDependencies: {
      config: [__filename],
    },
  },
  // 二次构建从 10 分钟降到 30 秒
};

// 2. 缩小构建范围
module.exports = {
  mo]]></description>
    </item>
    <item>
      <title><![CDATA[Vite 生产环境打包后静态资源 404 或页面空白怎么排查？]]></title>
      <link>https://niunaiclub.online/tips/engineering/vite-production-build-static-assets-404-blank-page</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/vite-production-build-static-assets-404-blank-page</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[Vite 开发环境一切正常，打包部署后页面空白、静态资源 404，这是 Vite 初学者最常遇到的坑。90% 的原因是 base 路径配置不对或 Nginx 配置缺失。

问题分类与修复：
```bash
# 问题 1：所有资源 404
# 原因：base 路径配置错误
# 部署在子路径下必须配置 base
# vite.config.ts
export default defineConfig({
  base: &quot;/admin/&quot;,  // 部署在子路径
  // 或用相对路径（通用方案）
  base: &quot;./&quot;,
})

# 问题 2：首页正常，路由跳转后 404
# 原因：SPA 路由需要 Nginx 配置 fallback 到 index.html
# Nginx 配置
server {
  location / {
    try_files $uri $uri/ /index.html;  # 关键！
  }
}

# 问题 3：图片/字体 404
# 检查构建产物
ls -la dist/assets/

# 问题 4：跨平台路径问题（Windows 开发 + Lin]]></description>
    </item>
    <item>
      <title><![CDATA[pnpm 幽灵依赖是什么？从 npm 迁移到 pnpm 后报 Cannot find module 怎么办？]]></title>
      <link>https://niunaiclub.online/tips/engineering/pnpm-phantom-dependency-npm-migration-cannot-find-module</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/pnpm-phantom-dependency-npm-migration-cannot-find-module</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[从 npm 迁移到 pnpm 后最常见的报错就是 Cannot find module，代码明明没改，本地 npm 能跑，pnpm 就炸了。这不是 pnpm 的 Bug，而是 npm 掩盖了你的 Bug——幽灵依赖。

什么是幽灵依赖：
```
你的 package.json 只声明了 express，但 express 依赖了 cookie。

npm 的扁平化安装：
node_modules/
├── express/       ← 你声明的
├── cookie/        ← 被提升上来了！你能直接 require(&quot;cookie&quot;)
└── body-parser/   ← 也被提升了

pnpm 的严格隔离：
node_modules/
├── .pnpm/         ← 所有依赖的真实存储
│   ├── express@4.18.0/
│   │   └── node_modules/
│   │       ├── express/
│   │       └── cookie/   ← 只 express 能访问
│   └── cookie@1.0.0]]></description>
    </item>
    <item>
      <title><![CDATA[前端静态资源 CDN 部署后跨域或字体加载失败怎么解决？]]></title>
      <link>https://niunaiclub.online/tips/engineering/frontend-cdn-cors-font-loading-failed-fix</link>
      <guid isPermaLink="true">https://niunaiclub.online/tips/engineering/frontend-cdn-cors-font-loading-failed-fix</guid>
      <pubDate>Sat, 09 May 2026 05:52:07 GMT</pubDate>
      <author>仓乃文</author>
      <category><![CDATA[工程化]]></category>
      <description><![CDATA[静态资源上 CDN 后，字体文件加载失败、图片跨域报错是常见问题。根本原因是 CDN 域名和页面域名不同，需要正确配置 CORS。

常见错误：
```
Access to font at &quot;https://cdn.example.com/fonts/icon.woff2&quot; from origin
&quot;https://www.example.com&quot; has been blocked by CORS policy
```

解决方案：
```nginx
# CDN 服务器 Nginx 配置
location ~* \.(woff2?|ttf|otf|eot)$ {
  add_header Access-Control-Allow-Origin &quot;https://www.example.com&quot;;
  # 或允许所有域名
  add_header Access-Control-Allow-Origin &quot;*&quot;;
  add_header Access-Control-Allow-Methods &quot;GET&quot;;
}

# 图片跨域
location ~* \.(png|jpg|gif|svg]]></description>
    </item>
  </channel>
</rss>