Skip to content

局部安装方法与react工程化项目目录结构分析(Day4 1/4) ​

本页对应 React + TypeScript + 医通项目,共 6 段视频,约 34 分钟。

学习目标 ​

  • 能够区分第三方样式库、重置样式、全局通用样式和组件内样式的放置位置与引入方式,并在 React 工程中配置 link 或 import 后验证样式生效。
  • 能够使用 npm 局部安装指定版本第三方样式库,并在 src/index.js 中通过 import 导入其 CSS 文件,验证样式正常应用。
  • 能够将重置样式、全局通用样式、组件样式分别放入对应文件,并通过 index.js 或 App.js 正确导入。
  • 能够将 CSS 文件命名为 *.module.css,通过 import 生成 JS 变量,并使用 className={styles.xxx} 解决组件间类名冲突。
  • 能够分别处理网络图片和本地图片,使用 import 或 require 导入本地图片,并通过变量控制 require 路径验证图片动态切换。
  • 能够定位 ESLint 警告对应规则,在配置中将规则级别设置为 0、1 或 2,重启项目后验证警告消失或出现。

知识点 ​

本节要解决的问题:为什么用“局部安装” ​

本节笔记讲的是用局部安装的方式使用 create-react-app 创建 React 项目。为什么需要:如果直接把脚手架装到全局,多个项目可能共用同一个全局版本,后续升级、切换版本时容易互相影响;局部安装则把依赖放进当前项目的 node_modules,版本跟项目走,项目更独立。怎么想:先准备一个普通 npm 项目,让目录里有 package.json;再把 create-react-app 装进当前项目;最后用 npx 调用项目内的局部命令创建 React 项目。怎么做:按笔记五步走。易错点:目录不要用中文;不要跳过 npm init -y;装完后不要误以为可以直接在任意位置执行 create-react-app,本节要用 npx 调局部命令。

准备目录并打开终端 ​

为什么需要:脚手架和 npm 都会读写路径,目录名、路径层级不对时,容易出现命令找不到、文件解析失败等问题。笔记明确要求“找一个目录【不能是中文】”。怎么想:先选定一个纯英文、无空格的项目父目录,再在这个目录里打开终端,后续所有命令都在这里执行。怎么做:在文件管理器中进入目标目录,右键打开终端,或用终端 cd 到该目录。易错点:路径含中文、空格或特殊符号;终端当前目录不是你以为的目录。排错时先看当前路径,再执行下一步。

初始化 npm 项目:npm init -y ​

为什么需要:局部安装需要当前目录是一个 npm 项目,package.json 用来记录项目信息和依赖。怎么想:npm init -y 会用默认值快速生成 package.json,避免一路回车。怎么做:

shell
npm init -y

执行后当前目录应出现 package.json。易错点:在错误目录执行,导致 package.json 生成到别处;目录已有项目配置时先确认是否要覆盖或复用。转写稿里反复出现“工程化环境”“npm 安装依赖”“node_modules”等思路,这里可以迁移理解为:先把当前目录变成一个可管理依赖的工程。

局部安装 create-react-app ​

为什么需要:把脚手架装到当前项目里,而不是装到全局,项目依赖更清晰。怎么想:用 npm i 安装,npm 会把包放进 node_modules,并在 package.json 中记录依赖。怎么做:

shell
npm i create-react-app

安装完成后,可以检查当前目录是否出现 node_modules。易错点:安装时网络或 registry 异常要先看终端报错;不要以为安装完就直接有了全局命令。局部安装的可执行文件通常在 node_modules/.bin 下,直接敲 create-react-app 可能找不到命令,所以下一步要用 npx。

用 npx 调用局部命令创建项目 ​

为什么需要:npx 会优先查找当前项目 node_modules/.bin 里的命令,适合调用局部安装的脚手架。怎么想:既然上一步已经把 create-react-app 装到当前项目,就用 npx create-react-app 项目名 让 npx 去找本地命令。怎么做:

shell
npx create-react-app 项目名

项目名按笔记不要用中文;实际使用中建议用小写英文、数字和连字符,避免空格和特殊符号。易错点:在错误目录执行,项目会生成到别处;项目名不合规导致创建失败;如果本地没有安装,npx 可能临时下载包,这就不完全是本节“先局部安装再调用”的流程了。

进入项目目录并启动 ​

为什么需要:npm start 依赖项目根目录的 package.json 中的脚本配置,必须进入刚创建的项目目录再启动。怎么想:创建完成后,项目名就是新目录名,先 cd 进去,再启动开发服务器。怎么做:

shell
cd 项目名
npm start

易错点:还在父目录就执行 npm start,会找不到 start 脚本;终端报错时先读第一条错误,确认当前目录、package.json 和依赖是否正常。转写稿中“路径写错导致解析失败”“改配置后要重启”的排错思路在这里同样适用:命令找不到先查当前目录和 node_modules/.bin,启动失败先回到项目根目录重试。

说明:提供的转写稿主要内容是 CSS 第三方样式库、CSS 模块化、图片处理和 ESLint 配置,与“局部安装 create-react-app”不是同一主题;本节只采用其中可迁移的 npm 安装、node_modules、路径与重启排错经验,不把无关 CSS 细节硬套进本节。

先看整体目录:先分清 public 与 src ​

为什么需要:React 工程化项目不是只写一个 App.js,而是把静态资源、开发代码、依赖配置分开管理。先看懂目录,后面才知道样式、图片、入口文件该放哪里。怎么想:把根目录分成三类——node_modules 是依赖,public 是不参与模块化编译的静态资源,src 是程序员主要开发目录;package.json 和 package-lock.json 管依赖与版本。怎么做:本节笔记里的目录结构如下,先整体认识:

shell
02-react-scaffold                          项目根目录
    |- node_modules                        npm包目录
    |— public                              静态资源目录
    |    |- favicon.ico                    站点图标
    |    |- index.html                     网站的入口html文件
    |    |- logo192.png                    移动端图片
    |    |- logo512.png                    移动端图片
    |    |- manifest.json                  移动端图标的配置文件
    |    |- robots.txt                     爬虫文件
    |- src                                 程序员开发目录
    |   |- App.css                         根组件的样式文件
    |   |- App.js                          根组件App
    |   |- App.test.js                     测试文件
    |   |- index.js                        js的入口文件
    |   |- index.css                       入口文件的样式文件
    |   |- logo.svg                        旋转的菊花图标
    |   |- reportWebVitals.js              google的报告文件
    |   |- setupTests.js                   测试启动文件
    |- package-lock.json                   包版本锁文件
    |- package.json                        包配置文件

易错点:不要把 public 当成普通源码目录,也不要把需要 import 的本地图片随便放到 src 外面;index.html 是网站入口 HTML,index.js 是 JS 入口文件,两者位置和作用不同。

public 目录:第三方样式和纯静态资源放这里 ​

为什么需要:像 Bootstrap 这种成熟第三方 CSS 库,如果不经过 npm 模块化引入,可以直接作为静态资源交给浏览器加载。怎么想:转写稿里先恢复干净项目,再把 Bootstrap 的 CSS 放到 public/css/bootstrap.css,然后在 public/index.html 用 link 引入。怎么做:路径可用 %PUBLIC_URL%/css/bootstrap.css,也可理解为网站根目录下的 /css/bootstrap.css。调试时在 App.js 写一个 button,按钮样式生效,说明引入成功。易错点:路径前面不要随手加点,写成 ./css/bootstrap.css 在工程打包或二级路由下可能出问题;转写稿特别强调“斜杠前面没有点”,它表示网站根目录。

src 目录:入口、组件与样式按类型放置 ​

为什么需要:src 里的文件会参与 React 工程编译,样式也分类型:第三方库、重置样式、全局通用样式、组件内样式。怎么想:第三方库可走 public/index.html 的 link,也可走 npm 安装后在 src/index.js 里 import;重置样式一般放 src/index.css,在 src/index.js 中 import;全局通用样式可放 App.css,在 App.js 中 import;组件样式放组件目录中,在组件文件里 import。怎么做:方式二是先执行 npm i bootstrap@3,再在 src/index.js 中写:

js
import 'bootstrap/dist/css/bootstrap.min.css';

转写稿调试时注释掉 public/index.html 的 link,再用 import 引入,按钮样式仍然生效。易错点:npm 包样式路径要看清 dist/css 目录;import 样式时不要漏写包名后的具体 CSS 文件。

CSS 模块化:解决组件同类名冲突 ​

为什么需要:A、B 两个组件可能由不同人开发,都写了 .box,后导入的样式会覆盖前一个。转写稿里 A 的 box 是红色,B 的 box 是绿色,结果绿色生效,说明普通 CSS 会全局冲突。怎么想:把 CSS 文件变成“模块”,让类名在编译后变成独特名字,组件只读取自己导入的对象。怎么做:文件名改成 index.module.css;在组件里 import styles from './index.module.css';使用时写 className={styles.box}。调试时打印 styles,会看到它是一个对象,里面有 box 属性;页面选中元素可看到类名被处理成独特名字。易错点:文件名后缀必须是 .module.css;import 后要存成变量;className 不能直接写字符串 "box",要写 {styles.box}。

图片处理:网络图片与本地图片分开 ​

为什么需要:React 里图片分网络图片和本地图片,本地图片还涉及工程编译路径。怎么想:网络图片直接填 URL;本地图片可 import 成 JS 变量,也可用 require。怎么做:网络图片写 <img src="网络地址" />;本地图片若用 import,图片必须放在 src 目录中,例如 import image from './assets/images/1.jpeg',再 <img src={image} />。转写稿中把图片放到 public 下再用 import,控制台报 “outside of the project src directory”,说明 import 不支持 src 之外路径;移到 src 后正常。require 写法可让路径成为变量,例如 require(./assets/images/${index}.jpeg),通过状态切换图片。易错点:import 路径写死;require 路径可用模板字符串,但要注意路径和文件名真实存在。

package 文件与 ESLint 警告:先理解再处理 ​

为什么需要:package.json 记录项目依赖和脚本,package-lock.json 锁定版本,保证团队安装一致。ESLint 负责语法和风格检查,学习阶段出现黄色 warning 不一定是业务错误。怎么想:转写稿中警告来自 ESLint 规则,不是代码逻辑错误;如果不想看,可在 ESLint 配置的 rules 中把对应规则设为 0。怎么做:在配置里加入规则并设 0 表示忽略,设 1 是 warning,设 2 是 error;改完配置要重启项目。易错点:不要把 ESLint 警告和依赖兼容性警告混为一谈;改规则后不重启可能仍看到旧警告。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例一:局部安装 create-react-app + 在 public/index.html 中引入第三方样式库 ​

shell
# 1. 找一个目录(路径不能包含中文),打开终端
mkdir 02-react-scaffold && cd 02-react-scaffold

# 2. 初始化 package.json
npm init -y

# 3. 局部安装 create-react-app(装到当前目录的 node_modules 里)
npm i create-react-app

# 4. 通过局部命令创建项目(npx 优先使用本地 node_modules 中的命令)
npx create-react-app react-demo

# 5. 进入项目目录并启动项目
cd react-demo
npm start

把 bootstrap 的 css 放到 public/css/bootstrap.css 后,在 public/index.html 中引入:

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>React App</title>

    <!-- 第三方成熟样式库:放在 public 下,用 link 标签引入 -->
    <link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css" />
    <!-- 等价写法:<link rel="stylesheet" href="/css/bootstrap.css" /> -->
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>
jsx
// src/App.js —— 此时可以直接使用 bootstrap 的类名
export default function App() {
  return (
    <div>
      <button className="btn btn-primary">按钮</button>
    </div>
  );
}

要点解释与易错点

  • npm i create-react-app 是局部安装,命令只落在当前目录的 node_modules/.bin 下,所以必须用 npx create-react-app 项目名 调用;直接敲 create-react-app 在没配全局时会提示找不到命令。
  • 项目目录不能是中文路径,否则安装/启动阶段容易报错。
  • 第三方样式库的定位是“不需要参与打包计算”的静态资源,因此放 public 下用 <link> 引入;%PUBLIC_URL% 是 CRA 提供的变量,代表打包后的网站根目录。
  • 最大易错点:href="%PUBLIC_URL%/css/bootstrap.css" 或 href="/css/bootstrap.css" 前面不能加点写成 ./css/bootstrap.css。/ 开头表示网站根目录(绝对路径),带点的相对路径在二级路由下会 404,样式失效。
  • 另一种方式是工程化导入:先 npm i bootstrap@3 安装,再在 src/index.js 中 import 'bootstrap/dist/css/bootstrap.min.css',由 webpack 打包进 bundle。

示例二:CSS 模块化解决组件同类名样式冲突 ​

src/components/A/index.module.css:

css
.box {
  width: 200px;
  height: 100px;
  background-color: red;
}

src/components/A/index.jsx:

jsx
import styles from './index.module.css';

export default function A() {
  return <div className={styles.box}>A 组件</div>;
}

src/components/B/index.module.css:

css
.box {
  width: 200px;
  height: 100px;
  background-color: green;
}

src/components/B/index.jsx:

jsx
import styles from './index.module.css';

export default function B() {
  return <div className={styles.box}>B 组件</div>;
}

src/App.js:

jsx
import A from './components/A';
import B from './components/B';

export default function App() {
  return (
    <div>
      <A />
      <B />
    </div>
  );
}

要点解释与易错点

  • 不模块化时,两个人分别写 index.css 都定义 .box,最后谁生效只取决于 import 顺序:后导入的覆盖前面的(转写稿里后导入的 B 让红色被绿色覆盖),本质是全局样式污染。
  • 模块化三步:① CSS 文件名必须改成 xxx.module.css 后缀;② import styles from './index.module.css' 把样式导入成 JS 变量(打印出来是一个对象,{ box: "A_box__xxxx" });③ 用插值表达式 className={styles.box} 取值,而不是写死字符串 className="box"。
  • 易错点:导入时漏写 ./ 会报 Can't resolve './index.module.css',相对路径的点斜杠不能省。
  • 易错点:改了文件名后缀却忘了同步改 import 路径,或还写 className="box",样式就完全不生效。
  • 原理是 CRA 会给类名生成唯一哈希(如 A_box__1a2b3),A、B 各自独立互不干扰。
  • 顺带一提:控制台里的 eslint 黄色警告可以在 package.json 的 eslintConfig.rules 中把对应规则设为 0(1 是警告、2 是报错)关掉,改完需重启 npm start 才生效。

示例三:本地图片处理(import 与 require 两种方式) ​

jsx
import { Component } from 'react';
// 方式一:import 导入成 JS 变量(路径写死)
import img1 from './assets/images/1.jpeg';

export default class App extends Component {
  state = { index: 1 };

  render() {
    const { index } = this.state;
    return (
      <div>
        {/* 网络图片:直接填网络地址即可 */}
        <img src="https://example.com/xxx.jpeg" alt="网络图片" width="200" />

        {/* 方式一:import 变量,编译时路径就固定了 */}
        <img src={img1} alt="本地图片-import" width="200" />

        {/* 方式二:require 导入,路径可用模板字符串拼接变量 */}
        <img
          src={require(`./assets/images/${index}.jpeg`)}
          alt="本地图片-require"
          width="200"
        />

        <button onClick={() => this.setState({ index: 2 })}>切换到第二张</button>
      </div>
    );
  }
}

要点解释与易错点

  • 图片分两类:网络图片直接写 URL;本地图片必须交给构建工具处理。
  • 最大易错点:本地图片必须放在 src 目录中。放在 public/assets/images 下再用 import 导入会直接报错:... is outside of the project src/ directory. Imports outside of src/ are not supported. 这是 CRA 的硬性限制。
  • 图片放在 src 后,import 路径用当前目录的相对路径 ./assets/images/1.jpeg,不需要再往上层回退。
  • import 与 require 的差别:import 是静态导入,路径在编译期写死,没法用变量;require 的路径可以拼成模板字符串,配合 state 就能实现“图片随数据变化”(如 index 为 1 显示第一张、为 2 显示第二张)。
  • 易错点:模板字符串里不要多写一层花括号(写成 require({./assets/${index}.jpeg})),否则拿到的是对象而不是字符串路径,图片加载失败。
  • 图片文件名和扩展名要与 require 里拼出的路径完全一致(.jpeg 和 .jpg 不能混)。

常见错误 ​

  1. 现象:执行 create-react-app 项目名 提示命令找不到;原因:只做了局部安装,命令不在全局 PATH 中;修法:改用 npx create-react-app 项目名,并确认在目标目录执行。
  2. 现象:npm init -y 或创建项目时报路径/编码异常;原因:项目目录含中文或特殊字符;修法:换到纯英文、无空格目录重新执行。
  3. 现象:public/css/bootstrap.css 在首页正常,二级路由样式丢失;原因:写成 ./css/bootstrap.css 这类相对路径;修法:在 public/index.html 用 /css/bootstrap.css 或 %PUBLIC_URL%/css/bootstrap.css。
  4. 现象:两个组件都写 className="box",后导入组件覆盖前者;原因:普通 CSS 全局作用域,同名类冲突;修法:改为 xxx.module.css,import styles from './xxx.module.css',使用 className={styles.box}。
  5. 现象:从 public 目录 import 图片报 “outside of the project src directory”;原因:CRA 不允许 src 外文件被 import;修法:把本地图片移到 src 内,再按相对路径导入。
  6. 现象:ESLint 规则设为 0 后警告仍在;原因:修改配置后未重启开发服务;修法:停止并重新 npm start 验证。
  7. 现象:在项目根目录外执行 npm start 启动失败;原因:未进入脚手架项目目录;修法:先 cd 项目名,再 npm start。

练习 ​

  1. 在纯英文目录中,用局部安装方式创建并启动一个 React 项目,写出完整命令顺序,并说明为什么用 npx。
  2. 分别用 public/index.html 的 link 和 src/index.js 的 import 接入 Bootstrap,切换两种方式验证按钮样式,记录差异。
  3. 创建 A、B 两个组件,各自使用同名 .box 样式,先复现覆盖问题,再改为 CSS Modules 实现互不干扰。
  4. 在 src 内放两张本地图片:一张用 import 固定展示,一张用 require 配合状态切换路径,验证动态换图。
  5. 在 ESLint 配置中将某条警告规则设为 0,重启后确认警告消失,并说明 0/1/2 的含义。

原视频对照 ​

#视频时长
1css-第三方样式库处理06:27
2css-第三方样式出导入方式二03:17
3css其他样式处理位置及导入方式01:33
4css模块化10:32
5图片处理10:10
6eslint语法检查规则配置02:52

小结 ​

  1. 局部安装脚手架后,创建项目优先使用 npx,不要假设全局命令可用。
  2. 项目目录命名要避免中文和特殊字符,减少初始化与构建异常。
  3. public 下的资源按站点根目录思路引用,避免二级路由下相对路径失效。
  4. 第三方样式库可走 public + link,也可走 npm + import,团队内应统一一种主方案。
  5. CSS Modules 的核心价值是解决组件间同名类名冲突,使用时文件名、导入变量、className 写法三者必须配套。
  6. 本地图片要放在 src 内;固定图片用 import,需要变量控制路径时用 require。
  7. ESLint 规则级别 0/1/2 分别对应关闭、警告、错误,改配置后要重启验证。
  8. 工程化项目要先分清 public、src、node_modules、package.json 的职责,再决定资源放哪、怎么引。