技术标签: 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>
<NavLink to="/goods">商品图片</NavLink>
<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
文章浏览阅读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 硬盘物理序列号
文章浏览阅读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
文章浏览阅读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
文章浏览阅读282次。对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出问题。不过,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那排查错误、修正问题将会成为一项异常艰难的工作。====从新生代出发-XX:+UseSerialGC 可互相激活新生代 :Serial + 老年代: Serial Old 都是串行-XX:+UseParNewGC 可互相激活。_自动内存管理
文章浏览阅读529次。自己用原生javascript写的轮播图,分页器按钮Click点击与mouseover鼠标悬浮导航都支持。同时支持移动端触摸操作,自己写得感觉不足之处是图片滚动动画还不够平滑,再改改间隔与偏移量应该可以。函数接受参数应该改成对象更好,还没有改。感觉这次写的轮播图功能比较全面了哈。高手们请别笑话,不足请指正.上源码:先HTML:<!DOCTYPE html><html>&..._轮播图无缝链接带有小圆点且支持移动端触频滑动
文章浏览阅读339次。nginx - fastcgi - php - memcache 协同下的 请求的完整访问过程用户发送http请求报文给nginx服务器nginx会根据文件url和后缀来判断请求如果请求的是静态内容,nginx会将结果直接返回给用户; 如果请求的是动态内容,nginx会将请求交给 fastcgi客户端 ,通过 fastcgi_pass 将这个请求发送给 php-fpmphp-fpm 会将请求交给 wrapperwrapper 收到请求会生成新的线程调用 php动态程序解析服务器如果用
文章浏览阅读1.9w次,点赞43次,收藏421次。linux内核相关视频解析:5个方面分析linux内核架构,让你对内核不再陌生90分钟了解Linux内存架构,numa的优势,slab的实现,vmalloc的原理手把手带你实现一个Linux内核文件系统简介作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。目前支持模块的动态装卸(裁剪)。Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。该进程负责进一步的系统初始化操作。init_linux内核基本原理
文章浏览阅读939次。该音乐播放器是我研究生开学前做出来的,花了我将近一个月的闲余时间,算是有模有样的了。现在算起来,应该有一年多没搞Android,所以现在看回以前的程序已经比较模糊了,整个工程的代码量还是比较庞大的,就不把代码贴出来了,感兴趣的可以自行下载代码。欢迎先体验我的App,来一场听觉与视觉的享受吧!视觉????嗯,你没看错,安装后有惊喜,让你欲罢不能!(貌似有点夸张了)Apk下载地址:ht_登录即可查找最新的android应用、游戏、电影、音乐等精彩内容
文章浏览阅读1.1w次。 弄了一上午,终于把使用NetTcpBinding的双工通讯给弄清楚了,也算是对wcf有所掌握了,为了解决穿透防火墙的问题,所以决定尝试一下WsDualHttpBinding的双工通信,结果问题来了。。。 “无法注册 URL http://+:8735/Service/。另一应用程序已使用 HTTP.SYS 注册了该 URL。” 晕了一种个下午,百_无法注册应用去处理url地址
文章浏览阅读4.5k次,点赞5次,收藏52次。把手里积累了这么久的Python入门资料整理了一下,发现其实,有了这些,python入门真的不难,每天花点时间学,真的不会影响工作。下面一起来看看这些资料吧!可以学习python的地方 Python学习资料全部整理 Python可以做的事情 关于python的一些文章一、可以学习Python的地方1、实验楼:【Python基础+项目实战课程】https://www.lanqiao.cn/courses/13302、《笨办法学 Python》:这本书绝对是最简单的学习 Pyth.._python学习资料
文章浏览阅读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
文章浏览阅读3.1k次。加载一副位图到你的用户界面是很简单的,然而如果你需要马上加载一组更大的图片的话就会复杂的多.在许多情况下(例如有些组件像ListView,GridView以及ViewPager等),出现在屏幕上的图片总量,其中包括可能马上要滚动显示在屏幕上的那些图片,实际上是无限的. 那些通过回收即将移除屏幕的子视图的组件,内存使用得以保留.如果你不长期保持你对象的引用的话,垃圾收集器也会释放你所加载的位图内_android 每秒收到30张图片怎么处理