JavaScript 工程化:npm、Vite 与代码检查

AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。

Node.js 22 LTS · Vite 6

写得出功能,和能交付给别人跑起来,中间隔着一层工程化:依赖怎么装、脚本怎么跑、代码怎么查、产物怎么出。这一篇把最小可用的工具链搭起来。

环境准备

Node.js 22 LTS 自带 npm 10。示例用 Vite 6 起项目,npm create vite@latest 交互式生成,也可以手动建目录。

mkdir my-app && cd my-app
npm init -y
npm install -D vite

package.json 关键字段

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "lint": "eslint .",
    "format": "prettier --write ."
  },
  "dependencies": {
    "date-fns": "^4.1.0"
  },
  "devDependencies": {
    "vite": "^6.0.0"
  },
  "engines": {
    "node": ">=22.12"
  }
}
  • private: true:防止误 npm publish。
  • type: module:.js 按 ESM 解析,别和 CJS 混。
  • dependencies vs devDependencies:运行时需要的进前者,构建/检查工具进后者。打包器会把 devDependencies 排除在产物外(服务端项目更要分清)。
  • engines 只是提示,真正强制要配 .npmrc 的 engine-strict=true 或用 CI 检查。

语义化版本与锁文件

^4.1.0 允许升级到 <5.0.0;~4.1.0 只允许 4.1.x;写死 4.1.0 完全不动。

  • package.json 记录范围,package-lock.json 记录确切版本 + 完整依赖树。
  • 锁文件必须提交到仓库,否则不同机器装出的版本可能不同。
  • 安装用 npm install(会更新锁文件);CI 用 npm ci(严格按锁文件装,锁文件与 package.json 不一致直接失败)。
npm install          # 本地加依赖
npm install -D vite  # 加开发依赖
npm ci               # CI / 部署
npm outdated         # 看哪些包落后
npm ls react         # 查某个包是谁引入的

npm scripts

npm run <name> 执行脚本;不带 run 的快捷命令只有 start、test、stop、restart。

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "check": "npm run lint && tsc --noEmit"
  }
}
  • 脚本里能直接用到 node_modules/.bin 的命令,不用写路径。
  • npm run a -- --flag 里的 -- 用来把参数传给脚本,而不是 npm。
  • 需要跨平台(Windows/macOS 都能跑)时避免 rm -rf、&& 之外的 shell 语法,或用 rimraf、cross-env 这类小工具。
  • 脚本之间用 && 串联,前一个失败会中断;需要都跑完用 ;(仅 POSIX)或写 Node 脚本。

npx 用于一次性执行包里的命令,不装到全局:

npx vite --version
npx prettier --check .

Vite:开发与构建

npm run dev      # 启动开发服务器(默认 5173)
npm run build    # 产物输出到 dist/
npm run preview  # 本地预览 dist/

vite.config.js 常用配置:

import { defineConfig } from "vite";

export default defineConfig({
  base: "/",                       // 部署到子路径时改成 "/blog/"
  build: {
    outDir: "dist",
    sourcemap: true,
    target: "es2022",
  },
  server: {
    port: 5173,
    proxy: {
      "/api": {
        target: "http://localhost:3000",
        changeOrigin: true,
      },
    },
  },
});
  • 开发时 vite 按需编译,几乎秒开;import 的裸模块会被预构建。
  • 构建时 用 Rollup 打包,产出带哈希的静态文件,需要部署 dist/ 而不是源码。
  • base 配错会导致线上资源全 404,部署到子目录时最容易踩。
  • 代理只在开发服务器生效,线上仍需真实的反向代理配置。

import.meta.env 提供环境变量:

console.log(import.meta.env.MODE);       // development / production
console.log(import.meta.env.BASE_URL);   // "/"
console.log(import.meta.env.VITE_API);   // 只有 VITE_ 前缀的会注入

只有 VITE_ 前缀的变量会进产物,而且进产物就等于公开——密钥、token 绝不能放在前端环境变量里。

ESLint 与 Prettier 的分工

  • ESLint:找问题(未使用变量、错误的 hook 用法、可疑的相等比较)。
  • Prettier:只管格式(缩进、引号、换行),不判断对错。

两者都装时,用 eslint-config-prettier 关掉 ESLint 里与格式冲突的规则,避免互相打架。

npm install -D eslint @eslint/js prettier eslint-config-prettier

eslint.config.js(扁平配置,ESLint 9 起的默认形式):

import js from "@eslint/js";
import prettier from "eslint-config-prettier";

export default [
  js.configs.recommended,
  prettier,
  {
    files: ["**/*.js"],
    languageOptions: {
      ecmaVersion: 2024,
      sourceType: "module",
      globals: { window: "readonly", document: "readonly" },
    },
    rules: {
      "no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
      "no-console": "warn",
      eqeqeq: ["error", "smart"],
    },
  },
  { ignores: ["dist/", "node_modules/"] },
];

编辑器里开「保存时格式化」+「保存时修复 ESLint」,问题不必等到提交才发现。

目录与产物约定

my-app/
  index.html
  src/
    main.js
    styles.css
  public/           # 原样复制的静态资源
  dist/             # 构建产物,不进仓库
  package.json
  vite.config.js

.gitignore 至少包含:

node_modules/
dist/
.env
.env.local

构建完别只看命令成功,打开 dist/ 检查:入口 HTML 是否引用了带哈希的资源、base 路径对不对、有没有把 .env 打进去。

依赖治理

  • 装之前先问:这个包值不值得引入?小工具自己写十几行往往更稳。
  • 用 npm ls <pkg> 查重复版本;同一库出现两份实例会导致状态不共享。
  • 定期 npm audit,但别盲目 --force 升级;先看漏洞是否影响实际用法。
  • 大版本升级先读 changelog,跑一遍 npm run check && npm run build。

常见错误与注意事项

1. 用 npm install 部署

版本漂移的常见原因。CI/部署一律 npm ci。

2. 提交 node_modules

仓库爆炸、平台相关二进制冲突。靠锁文件而不是目录来保证一致。

3. 把锁文件加进 .gitignore

团队里每个人装出不同版本,bug 无法复现。

4. 脚本依赖全局安装的工具

换台机器就报 command not found。一律装成 devDependencies,用 npm run 调。

5. base 与路由不匹配

部署到 https://example.com/app/ 却配 base: "/",JS/CSS 全 404。先看 DevTools 的 Network。

6. 把密钥写进 VITE_ 变量

构建后就是明文,任何访问者都能看到。密钥只能留在服务端。

7. 只跑 lint 不跑 build

类型/导入错误常常只在构建时暴露。两条命令都要进 CI。

小结

  • package.json 管声明,锁文件管确定性,npm ci 管复现。
  • 脚本写进 npm run,依赖装进 devDependencies,别依赖全局环境。
  • Vite 开发按需编译、构建输出 dist/;base 配错是最常见的上线故障。
  • ESLint 找问题,Prettier 管格式,两者不冲突。
  • 前端环境变量等于公开信息,密钥不进产物。

下一步