前端---【react阶段缩略版】_react缩略图依赖-程序员宅基地

技术标签: react.js  前端框架  html5  前端  html  javascript  

## react 介绍
原生 DOM 操作
```js
let div = document.querySelector('#box');
div.style.background = 'black'
```

React 操作  (数据驱动视图)
```js
this.setState({
  bg: 'black'
})

setBg('black');

setArr(['X', 'Y', 'Z'])
```



## react 的报错

![image-20230504151047212](./assets/image-20230504151047212.png)



## 大括号中能够渲染的数据

* 字符串
* 数字
* 虚拟 DOM 对象
* 数组



## 高阶函数

高阶函数: 如果一个函数返回一个新函数, 或者接收函数类型的参数, 我们就将该函数称之为『高阶函数』

* 返回新函数: 闭包, 防抖节流函数, bind
* 接收函数参数: Promise, then, catch, 定时器, 数组方法

## create-react-app

是 react 的脚手架开发工具, 借助于这个工具创建 react 的(脚手架)项目

## 检测 npm 配置淘宝镜像的方法

```
npm config list
```

如果 registry 的配置项值为 `https://registry.npmmirror.com/` 即表明已经安装成功

## CSS 模块化

1. css 样式命名时使用 `名字.module.css`
2. 组件中导入 CSS 文件

```js
import css from './名字.module.css';
```

3. 为元素设置类名

```html
<p className={css.text}></p>
```

## state

state 是类式组件实例对象身上的一个『特殊』属性, 值是一个对象,里面设置一些属性
当 state 中的数据发生变化时, render 方法会自动执行, 重新渲染组件 => 『数据驱动视图』

```js
class A{
  state = {
    a: 100,
    b: 200
  }
}
```

### 状态的更新

```js
this.state.a = 300; //X
this.setState({
  a: 300
}) // √  set 设置  state 状态
```


## vscode 注释的快捷键

`ctrl + /`

## 组件, 组件标签, 组件实例

```
class A extends React.Component{}
```

* 组件        A
* 组件标签    `<A></A>`   React.createElement(A, null)
* 组件实例    



## React VSCode 插件

![image-20230506090747692](./assets/image-20230506090747692.png)

* rcc   类式组件代码片段
* rfc   函数式组件的代码片段

## new 构造函数
1. 开辟新空间                        创建一个空对象
2. 确定 this 指向                    将函数运行时 this 指向空对象
3. 执行代码                          执行代码
4. 返回地址                          返回创建的对象

```js
function Person(name, age){
  this.name = name;
  this.age = age;
}
let p = new Person('张三', 28);


class Person{
  constructor(name, age){
    this.name = name;
    this.age = age;
  }
}
new Person()
/**
  创建一个空对象
  空对象调用 constructor
  返回空对象
*/
```

## props
全名叫『properties』属性. 是类式组件实例对象的一个属性.
作用: 接收组件外传入的数据, 实现组件的『解耦与复用』



## refs

reference 也是组件实例对象身上的一个属性, 可以帮我们快速的获取『真实 DOM 对象』


## 复习
```
<Person age="18" name="康熙">xxx</Person>
```

## 生命周期
生命周期方法, 生命周期回调. 本质就是类式组件『实例对象中的一些方法』
特别之处在于, 会在组件的不同阶段『自动执行』, 且这些方法都是由实例对象自动调用的
* componentDidMount   component 组件  did 做  mount 挂载
  * 当组件挂载完成的时候, 由虚拟 DOM 转为真实 DOM 之后, 插入到文档之后自动执行
  * 常见的操作, 组件的初始化操作
    * 启动定时器
    * 发送 AJAX 请求
    * 事件绑定 例如为 window 元素绑定事件

* componentWillUnmount  will 将要   unmount 卸载
  * 当组件将要销毁的时候, 自动执行
  * 完成一些组件的收尾工作
    * 取消定时器
    * 取消事件绑定
  
* componentDidUpdate   update 更新
  * 当组件完成更新之后, 自动执行
  * 例如: 操作真实 DOM

## forceUpdate


## todoList 状态
* input 的输入值   =>  受控组件  √
* 选中与不选中  =>   X
* 全选与不全选  =>   √  => X
* 已完成与全部  =>   √  => X
* 任务列表     =>    √

使用数组存放任务的列表数据
```js
[
  {title: 'xxx', done: true},
  {title: '----', done: true},
]
```

> 状态设置原则:  最小化原则

## axios 使用形式
* axios()
  * axios({})
  * axios('http://api.xiaohigh.com')
* axios.get();
  * get  delete         get(url, config)
  * post put  patch     post(url, data, config)
    * data
      * JSON            axios.post(url, {a: 100, b: 200})      =>   {"a": 100, "b": 200}
      * querystring     axios.post(url, 'c=300&d=400')         =>   c=300&d=400

## 配置对象
* method  请求方法
* url     请求的 URL
* baseURL 基础 URL
* params  URL 查询字符串
* headers 请求头
* data    设置请求体
* cancelToken 
* timeout 超时时间

## 创建实例发送请求
```js
let instance = axios.create({
  method: 'get',
  baseURL: 'http://api.xiaohigh.com'
});

instance();
instance.get();

instance.interceptors.request.use()
instance.interceptors.response.use()
```

## 拦截器
* 请求拦截器
* 响应拦截器

## node.js 创建 HTTP 服务的方式
1. express 框架
2. 原生 http 模块
3. json-server

## 搭建 HTTP 的流程
1. 创建 server/db.json
2. 输入基本数据
```json
{
  "todo": [
    {"id": 1, "title": "买洗衣粉", "done": false},
    {"id": 2, "title": "买洗衣机", "done": true}
  ]
}
```
3. 以 db.json 『所在目录作为命令行工作目录』运行命令启动服务
```
json-server --watch db.json --port 3001
```

4. 浏览器访问 `http://localhost:3001/todo`

## 解决跨域问题的方案
1. jsonp
2. cors
3. 设置代理, 只能在开发阶段有效. 项目上线之后, 还是要求后端响应结果时, 添加 CORS 响应头
增加 package.json 中的配置
```json
"proxy": "http://localhost:3001"
```

## 配置代理实现跨域的操作步骤
1. 启动服务时, 关闭 cors 响应
```sh
json-server --watch db.json --port 3001 --nc
```

2. 访问 todoList 网页.
  * 如果不能正常访问, 表明『成功』
  * 如果能访问, 表明『失败』, 此时是浏览器缓存了跨域的结果, 使用无痕窗口进行访问

3. 修改 package.json 配置文件, 增加 package.json 中的配置
```json
"proxy": "http://localhost:3001"
```

4. 修改 request.js 将 baseURL 的值设置为 ''
```js
let instance = axios.create({
  baseURL: ''
});
```

## 订阅与发布
关注账号  
`冯提莫`

三个方法:
* subscribe 订阅
* publish   发布
* unsubscribe 取消订阅

## 
将数据渲染到网页中, 有两种方式:
1. 操作真实 DOM
2. 状态 数据驱动视图

## SPA
Single Page Application  单页应用
* 效果高, 减少资源的重复加载(请求)
* 用户体验更好, 可以添加 loading 效果, 减少用户的负面情绪
* 方便后续打包成 APP

## 服务端路由
```js
//  http://127.0.0.1/home?a=100&b=200
app.get('/home', (req, res) => {
  res.send('首页');
})

app.get('/admin', (req, res) => {
  res.send('后台');
})
```

## 前端路由
key => value 映射
* key     就是页面 URL 的『路径』, 与查询字符串没有关系
* value   是一个组件
```js
// 路由示例   当网页 URL 的路径为 /home 的时候, Home 组件就会渲染显示
<Route path='/home'  element={<Home />}  />
```


## 路由操作流程
1. 安装工具包

   ```sh
   npm i react-router-dom
   ```
2. App.js 导入路由相关的组件

   ```js
   import {BrowserRouter, Routes, Route, Link, NavLink} from 'react-router-dom';
   ```
3. 创建路由规则

   ```js
   export default function App() {
     return (
       <>
       <BrowserRouter>
         <NavLink to="/hero">英雄列表</NavLink>&nbsp;&nbsp;
         <NavLink to="/goods">商品图片</NavLink>&nbsp;&nbsp;
         <NavLink to="/clock">电子时钟</NavLink>
         <hr />
         <Routes>
           <Route path='/hero' element={<HeroList />} />
           <Route path='/goods' element={<GoodsImage />} />
           <Route path='/clock' element={<EleClock />} />
         </Routes>
       </BrowserRouter>;
     </>
     )
   }
   ```
4. 浏览器访问 `http://localhost:3000/hero`

## 路由实现的思路
* 观察或者分析 URL 变化的过程中, 哪块儿区域的内容在变化
* 将变化的内容, 封装成不同的组件
* 可以通过 <Route> 组件进行路由设置

## 脚手架的特点
如果请求在服务端能够找到对应的资源, 则返回该文件的内容
如果找不到对应的资源, 就会 public/index.html 文件打包后的文件的内容



## 路由体验的操作步骤

1. 复制 index.html 的 HTML 结构至 App.js

2. 将 bootstrap.css 文件复制到 public/css/bootstrap.css

3. public/index.html 使用 link 引入 bootstrap.css

4. 拆分 App.js 中变化内容至不同的组件

   1. Home.jsx
   2. About.jsx

5. 使用 Route 进行路由配置规则

   ```jsx
   export default function App() {
     return (
       <BrowserRouter>
       <div>
         <div class="row">
           <div class="col-xs-offset-2 col-xs-8">
             <div class="page-header">
               <h2>React Router Demo</h2>
             </div>
           </div>
         </div>
         <div class="row">
           <div class="col-xs-2 col-xs-offset-2">
             <div class="list-group">
               <NavLink className="list-group-item" to="/about">About</NavLink>
               <NavLink className="list-group-item" to="/home">Home</NavLink>
             </div>
           </div>
           <div class="col-xs-6">
               <Routes>
                 <Route path='/home' element={<Home></Home>} />  
                 <Route path='/about' element={<About></About>} />  
               </Routes>          
           </div>
         </div>
       </div>
       </BrowserRouter>
     );
   }
   ```

   

## 二级路由的操作步骤

1. 找到 home.html, 将二级路由的结构复制到 Home.jsx 中

2. 拆分组件, 将变化的内容拆分至不同的组件

   * News.jsx
   * Message.jsx

3. 配置路由规则

   ```jsx
   <div className="col-xs-6">
       <Routes>
           <Route path='/home' element={<Home></Home>}>  
               <Route path="/home/news" element={<News />} />
               <Route path="/home/message" element={<Message />} />
           </Route>
           <Route path='/about' element={<About></About>} />  
       </Routes>          
   </div>
   ```

4. 在 Home.jsx 中, 设置 Outlet 组件. 设置时要将 Outlet 放置在『内容切换显示的区域』

5. 设置 NavLink 组件, 替换链接



## 路由组件传参方式一

1. 修改 routes/index.js

   ```js
   {
       path: '/heros',
           element: <HeroList />
   },
      
       {
           path: '/hero/:id',
               element: <HeroDetail />
       }
   ```

2. 修改 HeroList.jsx

   ```jsx
   <div className="hero-list">
       {
           !isLoading && heros.map(item => {
               return <div className="hero-item" key={item.id}>
                   <Link to={`/hero/${item.id}`}><img width="100%" src={'http://cdn.xiaohigh.com' + item.image} alt="" /></Link>
                   <p>{item.name}</p>
               </div>
           })
       }
   </div>
   ```

3. 修改 HeroDetail.jsx

   ```jsx
   //获取页面 URL 中的 id 值   
   let {id} = useParams();// 别忘了导入 useParams
   ```

   

## 路由组件间传递参数的第二种方式

1. 修改 routes/index.js

   ```js
   {
       path: '/heros',
       element: <HeroList />
     },
     // app.get('/goods/:id.html', (req, res) => {})
     {
       path: '/hero',
       element: <HeroDetail />
     }
   ```

2. 修改 HeroList.jsx, 修改 Link 组件的 to 属性

   ```jsx
   <div className="hero-list">
       {
           !isLoading && heros.map(item => {
               return <div className="hero-item" key={item.id}>
                   <Link to={`/hero?id=${item.id}`}><img width="100%" src={'http://cdn.xiaohigh.com' + item.image} alt="" /></Link>
                   <p>{item.name}</p>
               </div>
           })
       }
   </div>
   ```

3. 修改 HeroDetail.jsx

   ```js
   //获取页面 URL 中的 id 值     针对 query 参数的处理方式
   let [search] = useSearchParams();
   
   //获取 URL 的查询字符串
   let id = search.get('id');
   ```

## 路由组件间传递参数的第三种方式 state - 本质是通过 history 对象

1. 修改 routes/index.js

   ```js
   {
       path: '/heros',
           element: <HeroList />
   },
      
       {
           path: '/hero/:id',
               element: <HeroDetail />
       }
   ```

2. 修改 HeroList.jsx

   ```jsx
   <div className="hero-list">
           {
             !isLoading && heros.map(item => {
               return <div className="hero-item" key={item.id}>
                       <Link to={`/hero`} state={
   {id: item.id}} ><img width="100%" src={'http://cdn.xiaohigh.com' + item.image} alt="" /></Link>
                       <p>{item.name}</p>
                     </div>
             })
           }
         </div>
   ```

3. 修改 HeroDetail.jsx

   ```jsx
   let {state: {id}} = useLocation();
   ```

   



## Link 与 navigate 比较

* Link  使用简单, 但是只能通过点击跳转
* navigate 使用相对麻烦一些, 但是调用场景非常灵活.  单击, 鼠标移入, 键盘事件



## navigate 的使用步骤

在组件中如果要使用 navigate 函数, 操作步骤如下

1. 在组件中导入 useNavigate

   ```js
   import { useNavigate } from "react-router-dom";
   ```

2. 在函数式组件中, 调用 useNavigate

   ```js
   function Com(){
   	//获得 navigate 函数
   	 let navigate = useNavigate();
   
   }
   ```

3. 在组件的回调函数中调用该函数

   ```jsx
   <img onClick={() => { navigate('/hero/1') }} />
   ```

   

## 路由组件间传递数据的方式

* params 参数

  * 路由设置   `<Route path="/hero/:id" element={<HeroDetail />}  />`

  * 跳转时

    * `<Link to="/hero/10">跳转</Link>`
    * `navigate('/hero/10')`

  * 组件获取参数的方式

    ```
    let {id} = useParams();
    ```

* query 参数

  * 路由设置  `<Route path="/hero" element={<HeroDetail />}  />`

  * 跳转时

    * `<Link to="/hero?id=20">跳转</Link>`
    * `navigate('/hero?id=20')`

  * 组件获取参数的方式

    ```
    let [search] = useSearchParams();
    let id = search.get('id');
    ```

* state  参数

  * 路由设置  `<Route path="/hero" element={<HeroDetail />}  />`

  * 跳转时

    * `<Link to="/hero" state={
   {id: 20}}>跳转</Link>`
    * `navigate('/hero', {state: {id: 20}})`

  * 组件获取参数的方式

    ```
    let {state: {id}} = useLocation();
    ```

    

## Routes

实现路由的单一匹配

<Route path="/home" element={<Home />}>

<Route path="/home" element={<Home2 />}>

public/home.html

app.get('/home.html', (req, res) => {})



useRoutes([

​	{

​		path: '/home',

​		element: <Home/>,

​      children: [

​      ]

   }

])



## redux 状态影响 react 组件更新

1. 创建新的组件,  显示状态, 按钮更新状态
2. store.js 暴露 store 与 action creator
3. 组件中导入 store 与 action creator, 使用 store 进行状态的展示与更新
   * 这里操作完会出现一个问题, 就是点击无法更新页面的内容
4. src/index.js 中增加 store.subscribe() 操作 



## react 脚手架项目的特点

当编辑某个文件以后, 会自动将页面中当前组件的真实 DOM 卸载, 重新挂载当前组件



## redux 异步更新状态的操作步骤

1. 安装 redux-thunk 

   ```
   npm i redux-thunk
   ```

2. store.js 导入相关内容

   ```
   import {createStore, applyMiddleware} from 'redux';
   import thunk from 'redux-thunk';
   ```

3. 修改 createStore 的参数

   ```js
   let store = createStore((state, action) => {  
     ...
   }, 100, applyMiddleware(thunk));// 添加第三个参数
   ```

4. dispatch 函数类型的值

   ```js
   store.dispatch(() => {
     setTimeout(() => {
       store.dispatch({type: 'add', payload: 1});
     }, 1000);
   });
   ```

   



## 同步与异步的 action

* 同步 action     {type: 'add', payload: 10}
* 异步 action     () => {}



## 点击异步更新状态的步骤

1. 修改 store.js 封装异步的 action creator

   ```jsx
   export let asyncAddAction = (payload) => {
     return () => {
       setTimeout(() => {
         store.dispatch({type: 'add', payload: payload});
       }, 1000);
     }
   }
   ```

2. Button.jsx 中添加结构与绑定事件

   ```jsx
   //导入结构
   import { addAction, subAction, asyncAddAction } from '../../redux/store';
   
   //绑定事件
   let addTen = () => {
       store.dispatch(asyncAddAction(10));
     }
   
   //增加结构
   <button onClick={addTen}>1秒后状态加 10</button>
   ```

   

## store.js 代码拆分步骤

1. 创建 reducer 文件 (redux/reducer/countReducer.js)

2. 将 store.js 中的 action creator 函数剪切到 countReducer 中

   ```js
   //创建 action creator 创造者
   export function addAction(payload){
     return {
       type: 'add',
       payload: payload
     }
   }
   
   //减法的 action creator
   export let subAction = payload => ({type: 'sub', payload});
   
   //封装函数返回回调函数 
   // 异步的 action creator
   export let asyncAddAction = (payload) => {
     return dispatch => {
       setTimeout(() => {
         dispatch({type: 'add', payload: payload});
       }, 1000);
     }
   }
   
   ```

3. 将 store.js 中 createStore 中的 reducer 函数剪切到 countReducer 中, 并暴露

4. countReducer 中将状态初始值设置在 state 形参中.

   ```js
   let reducer = (state=100, action) => {  
     console.log(action);
     //对 action type 属性进行判断
     switch(action.type){
       case 'add':
         return state + action.payload;
       case 'sub':
         return state - action.payload;
       //该回调函数会同步调用一次, 通过返回值获得内部的状态初始值, 该行代码一定要写
       default: 
         return state;
     }
   };
   
   export default reducer;
   ```

5. store.js 中导入 countReducer 函数, 并设置在 createStore 的第一个参数中

   ```js
   import countReducer from './reducers/countReducer';
   let store = createStore(countReducer, applyMiddleware(thunk));
   ```

6. Button.jsx 中调整导入的路径

   ```jsx
   import { addAction, subAction, asyncAddAction } from '../../redux/reducers/countReducer';
   ```



## 新增 redux 状态的操作步骤

1. 创建 reducer 文件. 文件中声明状态对应的 reducer 函数与 action creator

2. store.js 中导入新的 reducer 文件, 使用 combineReducers 函数进行合并

   ```js
   import {createStore, applyMiddleware, combineReducers} from 'redux';
   
   //合并 reducer. 合并 reducer 之后, redux 内部保存的状态值就会称为一个『对象』
   let reducer = combineReducers({
     count: countReducer,
     zan: zanReducer
   });
   ```

3. 在组件中使用, `合并 reducer 之后, redux 内部保存的状态值就会称为一个『对象』`

   ```
   store.getState(); //值就是一个对象
   ```

   

## redux 开发者工具的使用步骤

1. 谷歌应用商店安装 redux-devtools

2. react 脚手架项目中安装依赖

   ```
   npm i redux-devtools-extension -D
   ```

3. store.js 中导入并配置 createStore

   ```js
   import {composeWithDevTools} from 'redux-devtools-extension'
   
   const store = createStore(counter, composeWithDevTools(applyMiddleware(thunk)))
   ```

   

## redux 实现 todoList  - 1

1. 将静态组件结构复制到 components 下

2. App.jsx 中导入 TodoList, 并返回组件标签

   ```jsx
   import TodoList from './components/16_Redux-TodoList/TodoList';
   
   export default function App() {
     //获得路由返回的虚拟 DOM 对象
     return <TodoList />;
   }
   ```

3. 创建 redux/reducers/todoReducer.js

   ```js
   //创建 reducer 函数. 将任务的列表存入到 redux 中
   let todoReducer = (state=[], action) => {
     //获取 type 和 payload 值
     let {type, payload} = action;
   
     switch(type){
   
       default: 
         return state;
     }
   }
   
   //暴露
   export default todoReducer;
   ```

4. store.js 中进行合并

   ```js
   import countReducer from './reducers/countReducer';
   import zanReducer from './reducers/zanReducer';
   import todoReducer from './reducers/todoReducer';// 导入新的 reducer 
   
   
   let reducer = combineReducers({
     count: countReducer,
     zan: zanReducer,
     todo: todoReducer  //配置合并 reducer
   });
   ```

   

## redux-todolist 初始化的数据获取与显示

1. todoReducer.js 中创建异步的 action creator

   ```js
   //发送请求获取所有的任务   异步的 action creator
   export let asyncGetTodos = () => {
     return async dispatch => {
       //发送请求获取所有的任务
       let result = await request.get('/todo');
       //更新 redux 中任务状态
       dispatch({type: 'init', payload: result});
     }
   }
   ```

2. store.js 中测试该 action creator

   ```js
   //测试 asyncGetTodos
   store.dispatch(asyncGetTodos());
   ```

3. TodoList 组件挂载完毕之后, 分发该任务

   ```js
   import store from "../../redux/store";
   import { asyncGetTodos } from "../../redux/reducers/todoReducer";
   
   //组件挂载完毕之后, 发送请求获取初始化的任务数据
     useEffect(() => {
       store.dispatch(asyncGetTodos());
     }, []);
   ```

4. TodoMain.jsx 中进行列表渲染

   ```jsx
   <ul className="todo-main">
       {
           store.getState().todo.map(item => {
               return <li key={item.id}>
                   <label>
                       <input type="checkbox" checked={item.done} />
                       <span className={item.done ? 'done': undefined}>{item.title}</span>
                   </label>
                   <button className="btn btn-danger">删除</button>
               </li>
           })
       }
   </ul>
   ```

## vscode 的快捷操作
ctrl + p 快速搜索并打开文件



## todoList 新增任务的操作流程

1. 修改 TodoHeader.jsx 调整 input 为受控组件

2. 为 input 绑定 keyup 事件, 并判断按下的按键是否为回车

   ```jsx
   export default function TodoHeader() {
     let [v, setV] = useState('');
     //声明 keyup 的事件回调
     let keyup = (e) => {
       //判断是否按下了回车键
       if(e.code === 'Enter'){
         //发送请求 增加任务的状态
         store.dispatch(asyncAddTodo(v));
       }
     }
     return (
       <div className="todo-header">
         <input type="text" onKeyUp={keyup} onChange={(e) => {setV(e.target.value)}} value={v} placeholder="请输入你的任务名称,按回车键确认" />
       </div>
     );
   }
   ```

3. todoReducer.js 中增加异步的 action creator

   ```js
   export let asyncAddTodo = (title) => {
     return async dispatch => {
       //执行异步任务了
       let result = await request.post('/todo', {title: title, done: false});
       //更新 redux 内部的状态
       dispatch({type: 'addTodo', payload: result})
     }
   }
   ```

4. 在 TodoHeader.jsx 中导入声明好的 action creator

5. 增加 todoReducer 文件中的 reducer 功能

   ```jsx
   let todoReducer = (state=[], action) => {
     let {type, payload} = action;
   
     switch(type){
       case 'init':
         return payload;
       //新增任务状态的操作
       case 'addTodo':
         return [...state, payload];
       default: 
         return state;
     }
   }
   ```




## vscode 分支操作

1. 如果只是想查看某个版本的内容, 使用 `checkout`
2. 如果想在某个版本的基础上继续写代码 `create branch`



## TypeScript 初体验

1. 安装包

   ```
   npm i -g typescript
   ```

2. 测试包是否安装成功

   ```
   tsc -v
   ```

3. 创建空目录

4. 以空目录作为工作目录启动命令行

   ```
   tsc --init
   ```

5. 创建 ts 目录并在目录下创建 hello.ts, 代码如下

   ```tsx
   let hello:string = 'iloveyou';
   console.log(hello);
   ```

6. 在刚才打开的命令行中运行命令

   ```
   tsc -w
   ```

7. 在空目录下创建 index.html , 引入编译后的 JS

   ```html
   <!DOCTYPE html>
   <html lang="en">
   <head>
     <meta charset="UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
   </head>
   <body>
     <script src="./ts/01_hello.js"></script>
   </body>
   </html>
   ```

   

## TS 的两大作用

1. 提前告知错误的位置
2. 提示数据的结构, 包括属性与方法



## ?.  可选链运算符

```
let obj = {
  a: {
    b: 200
  }
};

obj.a?.b
```

如果 a 有值, 则返回 obj.a.b, 如果 a 没有值, 则直接返回 `undefined`

## 接口

* TS 接口, 是一种类型, 是 TS 中的一种语法
* API 接口, 是服务端的一个路由规则, 可以为客户端返回数据

## TS

TS 是不知道服务端返回的数据是什么的?

因为: TS 根本就没有执行代码



## TS 重点

* 接口
* 泛型

## Promise<R>
『R』这里设置的是 Promise 实例对象成功结果值的类型

## 配置代理

1. 安装 craco

   ```
   npm i  craco-alias @craco/craco -D
   ```

2. 修改 package.json

   ```js
   "scripts": {
   -   "start": "react-scripts start",
   -   "build": "react-scripts build",
   -   "test": "react-scripts test",
   +   "start": "craco start",
   +   "build": "craco build",
   +   "test": "craco test",
   }
   ```

3. 开启服务, 注意命令行的位置, 一定要在 db.json 的文件夹下

   ```
   json-server --watch db.json --port 3001 --nc
   ```

4. 创建组件, 在 useEffect hooks 回调中发送 AJAX 请求, `别忘了在 App.tsx 中使用组件标签`

   ```
   useEffect(() => {
       axios.get('http://localhost:3001/todo')
       .then(response => {
         console.log(response);
       })
     }, [])
   ```

   > 这个时候会出现跨域请求的报错

5. 在 craco.config.js 中配置代理, 如果该文件不存在, 需要手动创建, 位置存放到项目的根目录

   ```js
   module.exports = {
     plugins: [
       //....
     ],
   
     // 开发服务器配置
   	devServer: {
   		// 激活代理服务器
   		proxy: {
   			// 将来以/dev-api开头的请求,就会被代理服务器转发到目标服务器去。
   			"/api": { // 需要转发的请求前缀
   				target: "http://localhost:3001", // 目标服务器地址  http://localhost:3001/api/todo
   				changeOrigin: true, // 为true时代理在转发时, 会将请求头的host改为target的值
   				pathRewrite: { // 路径重写, 在转发请求时自动去除路径的/dev-api
   					"^/api": "",
   				},
   			},
   		},
   	},
   };
   ```

6. 修改请求代码

   ```
   useEffect(() => {
       axios.get('/api/todo')
       .then(response => {
         console.log(response);
       })
     }, [])
   ```

7. 重启脚手架服务

## 打开文件夹的注意

以后写项目的时候, `vscode 只打开脚手架的目录`, 效率高



## 保存文件

使用`失去焦点后自动保存`, 减少脚手架重启的频率, 提高效果

## redux 使用 ts 的操作
https://www.reduxjs.cn/usage/usage-with-typescript

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_61307468/article/details/130853199

智能推荐

JavaDemo——读取硬盘物理序列号_java 硬盘物理序列号-程序员宅基地

文章浏览阅读1.2k次。通过调用wmic命令获取硬盘序列号,wmic命令很强大。Demo:/** * 2019年3月13日下午3:48:22 */package testReadDiskInfo;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;..._java 硬盘物理序列号

CentOS 编译Hadoop 2.6 32位_32位linux系统 编译hadoop-程序员宅基地

文章浏览阅读2.2k次。本文采用CenOS 6 32位,JDK1.7进行编译 (1)安装编译库yum install cmake lzo-devel zlib-devel gcc gcc-c++ autoconf automake libtool ncurses-devel openssl-devel libXtst(2)安装mavenwget http://repos.fedorapeople.org/repos/dc_32位linux系统 编译hadoop

bind mysql web_基于的django的bind dns管理平台-程序员宅基地

文章浏览阅读422次。BIND(Berkeley internet Name Daemon)也叫做NAMED,是现今互联网上使用最为广泛的DNS 服务器程序,本项目旨在更简单的维护我们内部的dns系统。环境:数据库: mysql5.6应用: bind-9.11.2环境: python3.8 , django30x01 安装数据库bash sql 建库语句use mysqlcreate database bind9; -..._使用web管理bind

Jvm基础篇-02-自动内存管理-程序员宅基地

文章浏览阅读282次。对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出问题。不过,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那排查错误、修正问题将会成为一项异常艰难的工作。====从新生代出发-XX:+UseSerialGC 可互相激活新生代 :Serial + 老年代: Serial Old 都是串行-XX:+UseParNewGC 可互相激活。_自动内存管理

自己写的轮播图,原生JavaScript,支持移动端触摸滑动。分页器圆点可以支持mouseover鼠标移入和click点击,面向对象思路_轮播图无缝链接带有小圆点且支持移动端触频滑动-程序员宅基地

文章浏览阅读529次。自己用原生javascript写的轮播图,分页器按钮Click点击与mouseover鼠标悬浮导航都支持。同时支持移动端触摸操作,自己写得感觉不足之处是图片滚动动画还不够平滑,再改改间隔与偏移量应该可以。函数接受参数应该改成对象更好,还没有改。感觉这次写的轮播图功能比较全面了哈。高手们请别笑话,不足请指正.上源码:先HTML:&lt;!DOCTYPE html&gt;&lt;html&gt;&..._轮播图无缝链接带有小圆点且支持移动端触频滑动

LAMP服务架构之传统缓存机制(Ngins+PHP+Memcache)-程序员宅基地

文章浏览阅读339次。nginx - fastcgi - php - memcache 协同下的 请求的完整访问过程用户发送http请求报文给nginx服务器nginx会根据文件url和后缀来判断请求如果请求的是静态内容,nginx会将结果直接返回给用户; 如果请求的是动态内容,nginx会将请求交给 fastcgi客户端 ,通过 fastcgi_pass 将这个请求发送给 php-fpmphp-fpm 会将请求交给 wrapperwrapper 收到请求会生成新的线程调用 php动态程序解析服务器如果用

随便推点

一文看懂Linux内核!Linux内核架构和工作原理详解_linux内核基本原理-程序员宅基地

文章浏览阅读1.9w次,点赞43次,收藏421次。linux内核相关视频解析:5个方面分析linux内核架构,让你对内核不再陌生90分钟了解Linux内存架构,numa的优势,slab的实现,vmalloc的原理手把手带你实现一个Linux内核文件系统简介作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。目前支持模块的动态装卸(裁剪)。Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。该进程负责进一步的系统初始化操作。init_linux内核基本原理

Android音乐播放器_登录即可查找最新的android应用、游戏、电影、音乐等精彩内容-程序员宅基地

文章浏览阅读939次。该音乐播放器是我研究生开学前做出来的,花了我将近一个月的闲余时间,算是有模有样的了。现在算起来,应该有一年多没搞Android,所以现在看回以前的程序已经比较模糊了,整个工程的代码量还是比较庞大的,就不把代码贴出来了,感兴趣的可以自行下载代码。欢迎先体验我的App,来一场听觉与视觉的享受吧!视觉????嗯,你没看错,安装后有惊喜,让你欲罢不能!(貌似有点夸张了)Apk下载地址:ht_登录即可查找最新的android应用、游戏、电影、音乐等精彩内容

无法注册 URL http://+:8735/Service/。另一应用程序已使用 HTTP.SYS 注册了该 URL。的解决办法。_无法注册应用去处理url地址-程序员宅基地

文章浏览阅读1.1w次。 弄了一上午,终于把使用NetTcpBinding的双工通讯给弄清楚了,也算是对wcf有所掌握了,为了解决穿透防火墙的问题,所以决定尝试一下WsDualHttpBinding的双工通信,结果问题来了。。。 “无法注册 URL http://+:8735/Service/。另一应用程序已使用 HTTP.SYS 注册了该 URL。” 晕了一种个下午,百_无法注册应用去处理url地址

Python学习资料全面总结,真的对零基础很有用-程序员宅基地

文章浏览阅读4.5k次,点赞5次,收藏52次。把手里积累了这么久的Python入门资料整理了一下,发现其实,有了这些,python入门真的不难,每天花点时间学,真的不会影响工作。下面一起来看看这些资料吧!可以学习python的地方 Python学习资料全部整理 Python可以做的事情 关于python的一些文章一、可以学习Python的地方1、实验楼:【Python基础+项目实战课程】https://www.lanqiao.cn/courses/13302、《笨办法学 Python》:这本书绝对是最简单的学习 Pyth.._python学习资料

Linux Centos yum/rpm 设置代理_linux 代理 rpm-程序员宅基地

文章浏览阅读1.2k次。yum 设置代理:vim /etc/yum.conf添加形如:proxy = http://user:pass@ip:portrpm 设置代理sudo rpm -Uvh https://xxxxx.rpm --httpproxy ip --httpport portreference: https://www.lightnetics.com/topic/3698/how-do-i-install-an-rpm-package-using-a-http-proxy..._linux 代理 rpm

Android中的图片处理(包括缓存、大小、优化等)_android 每秒收到30张图片怎么处理-程序员宅基地

文章浏览阅读3.1k次。加载一副位图到你的用户界面是很简单的,然而如果你需要马上加载一组更大的图片的话就会复杂的多.在许多情况下(例如有些组件像ListView,GridView以及ViewPager等),出现在屏幕上的图片总量,其中包括可能马上要滚动显示在屏幕上的那些图片,实际上是无限的. 那些通过回收即将移除屏幕的子视图的组件,内存使用得以保留.如果你不长期保持你对象的引用的话,垃圾收集器也会释放你所加载的位图内_android 每秒收到30张图片怎么处理

推荐文章

热门文章

相关标签