《The Road to React》 -- 读后笔记
React 基础知识
介绍
Single-page applications (SPA) 单页面应用是现在比较流行的框架,从 Angular 出现到现在,传统的js+jquery被淘汰,由 facebook 开发的
React 框架,摒弃了以前单体式web应用,也叫做服务器渲染,将前端代码和文件放在服务器里,用户每请求一个页面会将html页面,js和css文件传给浏览器,但是现在改为了在前端负责渲染,在第一次请求网站时传js文件,后续请求都是使用第一次的js文件,而且现在组件化的概念也支持,拥有丰富的社区和生态。
需要工具
开发工具:推荐 VS Code,它集成了终端,并且提供了很多的解决方案,在web前端里是最流行的IDE工具;CodeSanBox ,在线编辑网站,是一个不用安装的替代方案。
运行环境:Node 和 NPM 包管理器,几乎是所有前端必需安装的;yarn 和 pnpm 是一个替代方案。
建立项目
使用 Vite 工具建立并初始化项目,Vite 提供了很多在线工程模版,可以直接以这些模版创建工程,里面提供了很多初始配置,就不用自己配置一些初始配置项;选择一个自己想要建立工程的目录下,打开命令行,输入如下 npm 命令建立工程;
//使用 Vite 创建My-React-app目录工程,使用 react 的模版
npm create vite@latest My-React-app -- --template react
cd My-React-app
//安装所需要的依赖
npm install
//运行
npm run dev
项目结构
package.json :第三方依赖包安装列表,以及node/npm重要的配置项。
package-lock.json :这是指示 npm 如何去解决node包版本和第三方包依赖,不需要动里面的内容。
node_modules :存放了所有我们安装的第三方依赖包文件,不需要动。
.gitignore :git 上传代码的忽略文件。
vite.config.js :Vite 配置文件。
index.html :网站html页面文件。
npm 脚本命令可以在 package.json 里面看到。
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
//上面这部分在 package.json 里面,下面则是命令行输入命令
npm run dev
npm run build
npm run lint
npm run preview //运行这个命令前,必须运行 npm run build 命令
React 组件化
通过Vite 模版创建的项目,在源码src目录下有个初始的App.jsx文件,这个是模版提供的初始组件,只是没有将分离成多个文件结构,代码如下。
import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
function App() {
const [count, setCount] = useState(0);
return (<>
<div>
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank" rel="noreferrer">
<img src={reactLogo} className="logo react" alt="React logo"/>
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>count is {count}</button>
<p>Edit <code>src/App.jsx</code> and save to test HMR</p>
</div>
<p className="read-the-docs">Click on the Vite and React logos to learn more</p>
Fundamentals of React 20</>
);
}
export default App;
虽然代码在一个文件里面比较容易理解,但是分离代码能保持整体结构清晰明了,更灵活;组件文件需要遵守 PascalCase 命令,开头首字母大写,不然React不会将其视为一个组件;其次在构造函数(签名函数)里不需要参数,组件之间传递参数需要使用 props;返回的内容是使用的 JSX 语法,能更好的将 js 和页面结合。
签名函数里可以定义处理数据和其它细节,它们会在每次渲染组件的时候都执行一遍,当组件有改变重渲染时也会执行。
当我们不想每次渲染组件时都重新定义一次变量,我们可以将变量定义在签名函数外面,定义在签名函数上方,这样就只会第一次执行。
React JSX(JavaScript XML)
在页面里使用变量或者js代码时,使用 { } 符号括起来;当我们改变变量时,保存后页面会自动变化,这个是依赖于react的热加载和开发服务的热模块替换技术,会检测编译后的文件是否发生变化,发生了则替换文件,jsx 里注解使用 /* */ 符号。
const title = 'React';
function App() {
return (
<div><h1>Hello {title}</h1></div>
);
}
export default App;
JSX 重写了 HTML 内部属性处理逻辑或者新增了某些属性,这些属性都遵守驼峰命名法,例如 htmlFor 属性。
JSX 使用了 html ,react 会在内部将html属性转为js表达,但是在页面渲染过程中依然会保留某些属性字段,所以 react 添加了某些属性去替代原来的属性,然而在实际中,当第一次渲染完成后,这些属性会转为原生属性名。

以前如果要使用 JSX 语法,需要使用 .jsx 文件扩展名,但是现在都支持识别 js 文件中的 jsx 语法;浏览器并不能直接渲染 jsx 语法,需要其它工具将 jsx 转为 js 语法,例如 Babel 。
const title = 'React';
// JSX 语法
const myElement = <h1>Hello {title}</h1>;
// 转为 JavaScript 语法
const myElement = React.createElement('h1', null, `Hello ${title}`);
// 被 React 渲染后
<h1>Hello React</h1>
React 不强制要求使用 JSX,可以使用 js 的方法,但是更多开发者觉得 JSX 能更直观清晰表示,JSX 不完全依赖 React ,但大多数开发者都会结合使用。
React 列表的使用
React 里遍历数组,可以使用 js 的数组内置方法 map() ,js 不止这个方法,还有 filter ,reduce 等,可以自行了解。
const numbers = [1, 2, 3, 4];
const exponentialNumbers = numbers.map(function (number) {return number * number;});
console.log(exponentialNumbers);
// [1, 4, 9, 16]
我们也可以在 jsx 语法里使用该方法,根据该列表渲染相应的 html 元素,下面是返回的最主要的 jsx 片段。
//list是一个对象列表,里面每个对象有title和objectID属性
<ul>
{list.map(function (item) {
return <li>{item.title}</li>;
})}
</ul>
//但是上面这样的写法会在浏览器的控制台打印警告,因为 react 列表里的每个元素都需要一个唯一值标识,类似于数据库里的主键
//Vue 里也是有相同的要求,所以需要在每个html元素里添加 key,将上面的返回替换为下面的
return <li key={item.objectID}>{item.title}</li>;
为什么要用 key ,那是因为 react 重渲染列表时,会检测哪个元素改变了,然后将旧元素替换成新的元素,这样更有效率,不用key虽然不报错,但每次有变化重新渲染时,都会把列表所有元素都替换一遍。

如果列表里的对象没有任何的唯一标识,当保证列表的顺序不会发生错误时,可以使用 index 列表每项的索引当 key ,但不推荐这样,因为这很容易出现渲染性能问题。
构建多组件
当我们项目越来越复杂时,我们单个组件里的内容也会越来越多,所以为了保持内容清晰明了,我们可以将单组件分成多组件。
//提取代码,定义为新方法
function List() {
return (<div>hello {item.name}</div>);
}
//然后在签名函数里可以作为 HTML 内联元素使用
function App(){
return (<List/>);
}
当我们不断地创建新组件时,就构成了一个组件树,而根节点就只有一个,例子中就是 App 组件,没有其他需要渲染的组件就是叶子节点,我们不仅可以提取成一个组件方法,也能将其提取成一个组件文件,组件和 js 里的类概念相似,都是定义好了,就可以创建多个实例。
React DOM
在我们第一次访问时,渲染页面之前,根节点组件必须要实例化,否则后续的整个组件树都不会被渲染,我们可以打开 src/main.jsx 文件,可以在里面看到如何实例化根组件的。
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
上面我们可以看到,我们导入了 react 和 react-dom 库,里面的 ReactDOM.createRoot 函数是具体实例化根组件的,我们使用 js 的 getElementById 函数去找到对应 html 元素节点,获取该元素的 dom ,可以在 index.html 找到,然后通过根组件渲染我们的组件树到 html 页面里。
在 React 项目里我们只调用了一次 createRoot 函数,但是在其他不是 React 的项目里,我们可以看到多次调用,因为那是他们只需要部分区域使用 React 技术。
StrictMode 是 react 提供的检查工具,能识别并警告潜在问题,而且只在开发环境运行,生产环境中不会执行。
React 组件定义
之前使用函数组件去定义一个组件,现在用箭头函数去替代函数定义,这样能让结构更清晰,而且不止是函数组件定义,所有的普通函数定义都可以替换为箭头函数。
// 普通函数定义
function App() { ... }
// 箭头函数表达式
const App = () => { ... }
// 如果箭头函数里不做任何其他操作,直接返回数据可以再次省略优化
const App = () => { return a+b; }
// 简约优化
const App = () => a+b;
JSX 事件处理
在 HTML 原生事件里,可以通过获取 DOM 元素,然后添加事件函数 addEventListener() ,但是在 React 里,我们可以定义一个事件处理器函数,然后通过赋值给 JSX 提供的 html 标签里的 onChange 属性完成,在 react 里将这样的流程叫做合成事件,封装了浏览器的原生事件。
const Search = () => {
const handleChange = (event) => {console.log(event.target.value);};
return (
<div>
<input id="search" type="text" onChange={handleChange} />
</div>
);
};
React 会阻止原生浏览器事件行为,因为我们不仅是要触发事件,可能还需要关心后续发生了什么,所以会阻止原生事件,如果真要使用原生事件,可以使用 event.nativeEvent 函数去实现。
事件函数里不允许返回值,除非你返回的是另一个函数;我们可以将函数定义简写在属性里,如果我们需要访问事件的 event 对象,只需要传递 event 参数给函数就可以,例如 handleClick(event) ;event.preventDefault() 函数会阻止原生事件行为;e.stopPropagation() 会阻止事件的冒泡传递,不管是向上还是向下传递,都会阻止。
React 的 Props
当我们分离组件到不同的文件里时,我们通常需要传递数据给其他组件,当我们从父组件传递给子组件时,我们使用 props 方法,需要在子组件的签名函数里添加 props 对象参数,然后通过访问 props 对象里父组件定义的属性访问数据,父组件则是需要在使用组件标记里添加该属性,并且赋值;传递的数据是不允许被更改的,只允许访问。
const App = () => {
const stories = [ ... ];
return (
<div>
...
<List list={stories} /> //定义了 list 属性
</div>
);
};
const List = (props) => (
<ul>
{props.list.map((item) => (
<li key={item.objectID}>
...
</li>
))}
</ul>
);
如果使用了 ESLint 的话,可能会报错,提示 list 缺少参数校验,这个是因为保证代码的健壮性,不让错误类型的数据传递过去,所以强制要求添加类型校验,解决方法,一 使用 TypeScript 语法,定义相同的类型;二 关闭 ESLint 配置里的 props 校验。
React 的 State
为了解决 props 在子组件无法更改数据的问题,引入了 state 的使用,它提供了可变的数据结构,让子组件更改数据并同步给其他组件和父组件,如果组件没有被改变的 state 影响到,那么这些组件不会被重渲染。

import * as React from 'react';
const Search = () => {
const [searchTerm, setSearchTerm] = React.useState('');
const handleChange = (event) => {
setSearchTerm(event.target.value);
};
...
};
useState 里的参数是初始状态,返回一个数组,第一个是状态实体,第二个是更新状态实体的函数;这个函数也被叫做 react 的钩子函数,这只是众多钩子函数的其中一个;state 将信息存储在内存中,读取只会读最新的数据,直到 js 的垃圾收集将其回收。
只要调用了更新函数,那么组件一定会重渲染,不管是否值变化了;而且调用了更新函数,不会立马重渲染,react 会批量更新状态,并且使用异步的方式已提高性能。
JSX 回调处理器
state 只是解决了 props 无法修改数据的问题,但是没有解决子组件传递参数给父组件,而 callback handler 就解决该问题,让子组件和父组件交流;和 props 的过程一样,只是我们将数据换成了函数传递给子组件,然后让子组件调用该函数传递数据。
const App = () => {
const stories = [ ... ];
const handleSearch = (event) => {
console.log(event.target.value);
};
return (
<Search onSearch={handleSearch} />
);
};
const Search = (props) => {
const [searchTerm, setSearchTerm] = React.useState('');
const handleChange = (event) => {
setSearchTerm(event.target.value);
props.onSearch(event);
};
return ( ... );
};
上面例子可以看到,我们将 handleSearch 函数作为 props 传递给子组件,子组件调用函数,但是实际调用函数的位置是发生在父组件,而且子组件可以接受来自同一个父组件的多个回调函数。
React 的状态提升
这不是一个技术,只是一种设计的思想,也就是将子组件定义的 state 放在父组件里去,这样就可以利用回调函数,让子组件和父组件都可以改变 state 。
arrays 内置函数 filter() ,能遍历并且过滤条件为假的项目,保留条件为真的项目并返回新的数组。
解构,扩展,剩余
当我们取对象的属性非常多时,我们会写很多冗余代码,js 提供了解构对象的特性,一句就可以提取对象的所需的全部属性;扩展运算符和剩余运算符是使用同样的写法,但是使用场景不同,‘…’ 三个点作为操作符号,如果符号后跟着已有的变量,则会将对变量内的属性或者对象扩展分开,如果跟着是没有创建的变量,则是剩余符,将对象内的剩余属性全部取出,具体使用方式如下,看了示例就知道如何使用。
const {name,gender,age} = props; //从 props 对象里提取三个属性,可以直接使用
//剩余运算符
const {name,...profile} = user; //从 user 对象里单独取出name属性,然后剩下的所有属性都取出放入了profile对象里
console.log(profile.age);
//扩展运算符
<Item key="{object.id}" {...object} /> //会将object对象里所有的属性都创建单独的 attribute ,然后在Item子组件里接收使用
//箭头函数优化
const Item = ({name,age,gender})=>{...};
副作用
这个副作用的意思不是缺点的意思,在网页应用里,通常指在浏览器环境中执行代码时,除了返回值之外对程序状态或外部环境产生的持久性影响,例如全局存储,定时器,异步请求,浏览器api调用等。
localStorage.setItem('search', event.target.value); //浏览器本地存储
//小知识,在 js 里判断对象是否存在,存在则返回对象,否则返回自定义内容可以用或操作
const search = localStorage.getItem('search') || 'React';
//useEffect 钩子函数,第一个参数是执行函数,第二个参数是需要检测的变量数组,作用是当变量数组里的变量改变时,执行第一个参数函数
//如果有第二个参数,那么函数只会在第一次渲染和每次变量更新时执行;如果没有第二个参数,则会每次渲染都执行;
//如果有第二个参数,但是数组里面没有变量,那么函数只会第一次渲染执行
React.useEffect(() => {
localStorage.setItem('search', searchTerm);
}, [searchTerm]);
自定义钩子函数
和定义普通的方法一样,但是有几个约束条件:1. 必须以 use 为前缀命令方法;2. 方法里面必须使用其他钩子函数;3. 返回结果必须是数组结果。
React 片段元素
当我们使用 JSX 返回 html 内容时,需要在最外面加上一个顶层容器元素,例如 div 元素等,如果有需求是我们不需要顶层容器,就让每个元素并列返回,有两种方式可以实现,例子如下。
//方法1
const Search =()=>[<label key=1"></label>,<label key="2"></label>,];
//方法2
const Search =()=>(<React.Fragment><label></label><label></label></React.Fragment>);
const Search =()=>(<><label></label><label></label></>);
方法1 需要遵守两个规则,为每个同级元素添加 key 属性以及返回为元素数组,这样写麻烦,破坏结构以及冗余;方法2 只需要在添加顶级层去包括这些元素,下面那个是缩写,虽然也添加了顶层,但是在浏览器里是不会渲染DOM节点的。
高可用组件
经过上面的知识,我们可以将某些重复的页面内容抽取出来,定义成一个通用组件,这样我们就可以提升高可用,减少代码量。
//type 属性当父组件没有给这个属性时,默认 text ,如果有则是父组件传递的数据
const InputWithLabel = ({id,label,value,type = 'text',onInputChange,}) => (
<>
<label htmlFor={id}>{label}</label>
<input id={id} type={type} value={value} onChange={onInputChange}/>
</>
);
React 组件组成
如何让 react 组件像其他 html 标签一样,内部设置子元素dom树,渲染的时候也将其渲染出来,可以通过 props 里的 children 属性使用,这个属性不需要显式的赋予,只需要添加子元素就自动的会添加,在组件里直接使用就可以了;如果有多个子元素,那么就需要在实例化时,显式的赋予属性,然后通过 props 访问。
const InputWithLabel = ({id,children,}) => (
<>
<label htmlFor={id}>{children}</label>
</>
);
<MyComponent slotOne={<em>1</em>} slotTwo={<em>2</em>} />
命令式编程
声明式改为命令式,官方说明就是让关注怎样去做,变为关注做的目的,其实就是将我们平时正常写的代码细节隐藏,然后通过一个属性命令去控制逻辑,例如:我们写了一大段代码让组件的背景颜色自定义改变,但我们通过封装,然后让用户只需要在标签里添加red,blue,green 等属性就可以控制颜色改变。
useRef 函数是钩子函数,目的是创建一个可变变量,而且保证渲染过程中的值一致,这个和 useState 不同就是,这个不会引起页面的重渲染。
内联事件处理
我们之前学过怎么将事件处理函数通过回调的方式传递给子组件,但是我们使用的是普通写法,就是将方法创建在外面,然后通过一个方法变量传递,内联的方式则是直接在标签里创建方法传递;两种方式,1. js 的 bind 函数;2. 箭头函数。
条件性渲染
在某些时候我们需要不同条件下渲染不同的样式,例如,我们添加一个新的状态,根据这个状态来判断是否添加加载动画;根据我们上面学到的知识,很容易就想到在签名函数里使用条件判断语句,根据不同条件来返回不同页面结果;我们也可以在 JSX 里面添加判断语句,例如三元表达式,且或表达式。
React 多状态管理
如果想管理多状态的变量,使用 useReducer 钩子函数,和 useState 类似,返回一个当前状态和一个更新函数,传入一个状态处理函数和初始状态作为参数。
//多状态处理函数
const storiesReducer = (state, action) => {
if (action.type === 'SET_STORIES') {
return action.payload;
} else {
throw new Error();
}
};
//创建状态变量
const [stories, dispatchStories] = React.useReducer(storiesReducer,[]);
//使用更新方法,传入的参数就是状态处理函数里的 action 对象
dispatchStories({type: 'SET_STORIES',payload: newStories,});
未知状态
如果使用了多个 useState ,很容易就引起未知的状态出现,导致程序出现 bug,所以书中提到将同组的状态合并到一个多状态的变量里去 useReducer ,这样比较方便管理以及对未知状态的处理。
数据请求
使用第三方请求工具 Hacker News API ,下面示例则是使用方法,字符串里使用了 js 的模板方法,then 是后继执行动作,catch 则是捕获异常函数。
const API_ENDPOINT = 'https://hn.algolia.com/api/v1/search?query=';
fetch(`${API_ENDPOINT}react`)
.then((response) => response.json())
.then((result) => {dispatchStories({type: 'STORIES_FETCH_SUCCESS',payload: result.hits,});})
.catch(() => dispatchStories({ type: 'STORIES_FETCH_FAILURE' }));
记忆函数
使用 react 的 useCallback 钩子函数,返回一个回调函数,传入参数是缓存函数和依赖数组;功能就是性能优化的,当依赖数组里的值发生变化时,缓存函数就会被重新创建,如果没有发生变化则传递原来的函数引用;如果依赖数组是空的,那么函数只会被初始化创建一次并且在生命周期内不会再创建。
其他第三方库
axios 也是非常好用的一个数据请求的库,而且支持旧浏览器版本环境,而且不需要我们显式的将结果转为 json 格式,自动就返回的 json 格式结果,返回的 promise ,支持 restful api 风格。
axios.get(url).then((result) => {...}).catch(() =>{...});
async/await
如果要发起异步请求,可以使用 async/await 来实现,await 关键词需要加在返回 promise 的函数前,而 async 则需要添加在调用 await 的上一层函数前,这个就相当于 .then() 调用链的简介版;Promise.all() 可以满足多个异步请求全部完成后处理结果。
const handleFetchStories = React.useCallback(async () => {
dispatchStories({ type: 'STORIES_FETCH_INIT' });
try {
const result = await axios.get(url);
dispatchStories({type: 'STORIES_FETCH_SUCCESS',payload: result.data.hits,});
} catch {
dispatchStories({ type: 'STORIES_FETCH_FAILURE' });
}
}, [url]);
React 的表单
JSX 使用方式和 HTML 的相差不大,在表单元素外加上 form 标签元素,然后给 onSubmit 属性设置提交处理事件,为了防止原生 html 表单事件行为,可以在处理事件函数里添加 event.preventDefault() 函数去阻止;如果想获取单独某个控件的内容,可以通过在控件上添加name属性,然后用 event.target.name 去访问。
<form onSubmit={handleSearchSubmit}>
...
</form>
const handleSearchSubmit = (event) => {
console.log("submit data!");
event.preventDefault();
};
React 样式
对于 UI 界面的设计有很多种方式,但是在 React 里比较突出的就是下图中的几种。
css in css 指的是在 css 文件里面使用 css 样式,是一种技术思路,不是特指某个技术;css in js 就是在 js 文件里写 css 样式,不需要单独写 css 文件;js in css 就是在 css 文件里写 js 代码。

react 里的 css
和普通的页面写法一样,建立 css 文件,写上样式类,然后在组件里面导入文件,最后在 JSX 里面使用样式就可以了,使用方式就是在元素里添加 className 属性即可;如果不想建立文件,只是一个简单的样式,可以使用内联样式,style ={{ xxx: xxx }} ,给元素添加 style 属性,并且写样式代码即可,但这样的方式尽量少用,而且 className 可以作为 prop 传递。
react 的 css 模块
将 css 扩展名改为 xxx.module.css ,然后在组件里导入后,可以像 js 对象一样使用,优点是支持 css 嵌入特性,如果使用不存在的样式类名会抛错误;就是使用多个样式时会比较麻烦,但是可以使用第三方包简化,就是引入 clsx 来使用,非常方便,而且还能使用条件判断语句。
import styles from './App.module.css';
<button className={styles.button} />
//下面这个是使用字符串模板方法添加多个样式
<button className={`${styles.button} ${styles.buttonMargin}`} />
//如果不想上面这么麻烦可以使用 clsx 第三方包
import clsx from 'clsx';
<button className={clsx(styles.button, styles.buttonMargin)} />
//前面参数是样式类名,后面是判断条件,只有后面参数为真时才会添加
<button className={clsx(styles.button, { [styles.buttonMargin]: isLarge }) />
Styled Components
Styled Components 是css in js里最流行的一种,需要安装依赖,下面介绍简单的用法,其他用法例如选择器等需要自己学习。
npm install styled-components
import styled from 'styled-components';
//创建的这个对象就是一个组件,可以直接在 jsx 里使用
const StyledContainer = styled.div`
height: 100vw;
padding: 20px;
background: #83a4d4;
background: linear-gradient(to left, #b6fbff, #83a4d4);
color: #171212;
`;
const App = () => {
...
return (
<StyledContainer>....</StyledContainer>
);
};
//创建新的组件,这个组件会继承传入的组件所有基础样式
const StyledContainerSmall = styled(StyledContainer)`
padding: 5px;
`;
SVGs
在vite 里使用矢量图需要导入插件,然后配置,最后像建立一个矢量图文件,然后引入文件,在 jsx 里像使用组件一样。
维护项目
在项目上线后,维护项目都是重中之重的任务,书中会提到性能优化,安全检测,测试和工程结构内容。
性能优化
react 的严格模式 strict mode 会帮助开发者发现潜藏的问题,但是会导致 useEffect 钩子函数在初始化渲染的时候执行两次,这是为了解决等幂问题,不过该模式只会在开发环境有用,生成环境会自动去掉。
对于某些耗时的请求,使用 useEffect 会在第一次渲染的时候也请求,而 react 没有只在更新的时候调用的操作,所以我们可以采用 useRef 创建变量,然后在 useEffect 里判断该变量状态,就可以变相的实现只在更新时调用函数的功能。
react 里父组件如果重渲染了,那么子组件也会跟着渲染,这是默认的行为,这是由于子组件渲染一般都很快,但是如果遇到渲染慢的,这样就会影响性能,所以我们可以使用 memo 和 useCallback 函数让组件只在 props 改变的时候才重渲染。
当遇到在组件签名方法里出现复杂计算或者耗时计算时,组件每次重渲染都会重新创建并执行计算,将方法提到外面可以避免重复创建,使用 useMemo 函数调用该方法可以实现在参数改变时才重新计算。
使用 typescript
使用静态语言可以带来很多好处,能让项目代码更健壮,能让很多运行时才报错的问题在编译时发现,能让代码可读性更好等,由于能定义变量的类型,可以让项目类型安全问题得到改善。
测试
项目上线前,测试非常重要,可以使用 testing pyramid 来测试,或者 Vitest 和 React Testing Library 测试工具。

项目结构
将组件根据功能分割成多个文件,例如样式文件,js文件等,然后分离出代码后,通过 import 和 export 导入导出组件,其他没啥可说的,每个项目都有独属于自己的结构,考虑结构清晰,容易查找等因素建立。
真实项目经验
对于排序,或者其他方法,可以引入 lodash 包,里面提供了很多方便使用,常用的函数。
分页功能使用 the Hacker News API 可以实现,书中介绍了两种分页方式,一种是平常见到的普通表格式,下面标注数字的页数;另一种就是瀑布流式,没有数字标识,采用触发式加载下一页,例如花瓣网的往下滚动加载下一页内容。
书中也就讲解了这两个内容,并没有深入的给出更多开发代码,也就开拓视野的效果,看与不看影响不大,反正我是看了。
项目部署
构建
也叫打包,说法多样,bunding,build,packaging 等,但是做法都一致,都是使用工具完成,对打包工具配置好后,然后在命令行运行命令 npm run build 即可,一般输出文件夹都叫 dist 名。
在服务器部署
服务器上面部署,步骤一般就是配置服务器的端口,防火墙等配置,然后安装 node 环境,最后执行运行命令就结束了。
但是如果不想租界云服务器的,可以使用内网穿透去让内网和外网进行映射,这样本地部署的应用外网也可以访问,例如花生壳,firebase 等,书中介绍了后面一种,但是这个要使用 google 账号,不推荐。
参考链接
网站和网页应用:https://www.robinwieruch.de/web-applications/
React文档:https://bit.ly/3xrvxkI
React的js基础:https://www.robinwieruch.de/javascript-fundamentals-react-requirements/
怎样学js框架:https://www.robinwieruch.de/how-to-learn-framework/
怎样学React:https://www.robinwieruch.de/learn-react-js/
JSX 的 html 属性 :https://bit.ly/2Z42zcK
更多推荐


所有评论(0)