# outline
**Repository Path**: gzh52202/outline
## Basic Information
- **Project Name**: outline
- **Description**: 四阶段课程内容
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 4
- **Forks**: 4
- **Created**: 2022-09-19
- **Last Updated**: 2022-11-28
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 四阶段课程
## 自我介绍
### 基本信息
* 名字
* 年龄
* 学校
* 专业
* 工作年限
* 性格
### 能力
* 知识:会什么
* 经验:做过什么
* 能力:学习能力与沟通能力
* 业绩:有什么成果
## day1-1
### 面试题
### 知识点: React
* 使用react
> 需要引入两个js文件,分别为react.js + react-dom.js
```js
const target = document.querySelector('#app')
const vNode = React.createElement('div')
// v18+
const root = ReactDOM.createRoot(target)
root.render(vNode)
// v17-
ReactDOM.render(vNode,target)
```
* 版本号: 大版本.小版本.补丁
> 18.0.0 -> 18.0.1
* 节点
* 真实节点(RealDOM): 在浏览器中渲染的节点
> 创建真实节点:`document.createElement(type)`
* 虚拟节点(VirtualDOM):一个结构类似于真实节点的js对象
> 可以让用户不直接操作真实节点,进而提升页面性能
* 创建虚拟节点:`React.createElement(type)`
* JSX
> 是`React.createElement()`的语法糖
* JSX语法规范
* 在元素属性不能直接使用js关键字
* class -> className
* for -> htmlFor
* 多个单词的属性必须使用驼峰
* autofocus -> autoFocus
* onkeyup -> onKeyUp
* 必须结束标签
*
*
* 插值:{}
> 差值可以用在标签内或标签属性中
* 注释
```js
const msg = 'success'
{msg}
```
* 从0配置基于webpack的react项目环境
* 创建目录与文件
* 安装依赖
* webpack + webpack-cli + webpack-dev-server
* react + react-dom
* @babel/core + @babel/preset-react + babel-loader
* 配置webpack.config.js
```js
{
module:{
rules:[
// 编译JSX:babel
{
test:/\.js$/,
use:{
loader:'babel-loader',
// 配置babel插件
options:{
// plugins:[]
presets:['@babel/preset-react'],// 插件集合
}
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/template.html'
})
]
}
```
* npm script
* 组件
* 模块化优势
* 命名冲突
* 复用
* 分工
* 维护
* 定义组件
> 定义一个组件就是创建一个元素
* 分类
* 函数组件(推荐)
> 必须有返回值
* 类组件
> 继承自React.Component,必须包含一个render方法,且render方法必须有返回值
* 规则
* 组件名必须大写字母开头
* 只能包含一个顶层元素
* 组件刷新
* 状态(一种能监听变化的数据):React会监听状态的变化,当状态发生变化时,自动刷新组件(重新执行render函数)
* 获取状态:`this.state.xxx`
* 修改状态:`this.setState()`
* 组件的数据挂载方式
* 普通数据挂载:插值
```js
{title}
```
* state状态
> 修改状态:`this.setState()`
```js
{this.state.count}
```
* 条件渲染: 三元运算
```js
{ idx===current ? 'block' : 'none'}
{
show ? :
}
```
* 列表渲染
> key必须为唯一且稳定的值
* map()
* filter()
* ...
* 事件处理
* 使用驼峰
### 练习
## day1-2
### 面试题
* 数据类型
* 基本数据类型
* String
* Number
* Boolean
* true
* false
* Null
* null
* Undefined
* undefined
* Symbol
* BigInt
* 引用数据类型
* Object
### 知识点
* todolist待办事项
* 增 ok
* 删 ok
* 改 ok
* 查 ok
* 思维转变
> 节点操作思维 -> 数据驱动思维
* 事件
* this指向
> 默认情况下,只有在 `render`、`contructor`(调用super后)、`生命周期函数`中的this指向组件实例
* 改变this指向
* `fn.call(target,...args)` / `fn.apply(target,[...args])`: 执行fn并改变fn的this指向
* `fn.bind(target,...args)`:返回一个与fn一样的函数,并把它的this指向改成target
* target: this指向目标
* 箭头函数
* event对象
> 事件处理函数的最后一个参数
* 传参
* `bind(this,xxx)`
* 立即执行函数并返回一个函数
* 受控组件:
> 表单内容受到组件state的控制,表单与state绑定后,必须同时提供修改state的事件(onChange),否则无法修改表单内容
* 非受控组件:通过原生节点的方式制作表单
* ref引用
```js
this.input=el} />
```
* xss攻击:跨域脚本攻击
> 过滤有破坏性的代码(过滤script、style、iframe等标签)
* 组件化开发Todolist
* 拆分组件
* 复用
* 分工
* 维护
* 编写组件
* TodoHead
* TodoContent
* TodoFoot
* TodoItem
* 数据定义
* 状态提升: 如果多个组件使用同一个数据,则定义到他们共同的父级
* 组件通讯
* 父->子:props
> 子组件无法修改父组件传入的数据,如要修改数据(状态),需要遵循谁的数据谁修改
1. 传递:给子组件添加属性并传递父组件数据
2. 接收:
* 类组件:
* this.props
* constructor的第一个参数
* 子->父:
* 把父组件函数传到子组件中执行,并回传参数
### 练习
* 完成组件化todolist案例
## day1-3
### 面试题
* var, let, const
```js
// 块级作用域
var i=10
//for(var i=0;i<5;i++){
//}
// console.log(i); //5
for(let i=0;i<5;i++){
}
console.log(i); // 10
// var声明提前
console.log(b);// undefined
console.log(a); // a is not defined
let a=10;
var b=20;
// let,const在同作用域下不能多次声明
```
* 事件传播过程
* 捕获阶段
* 冒泡阶段
```js
div.onclick = function(){
console.log(666)
}
div.addEventListener('click',function(){
console.log(888)
},true)
span.onclick = function(){
console.log(999)
}
span.addEventListener('click',function(){
console.log(000)
},true)
```
* 为什么存在跨域,如何解决跨域
> 安全问题 -> 同源策略
* 解决方案
* CORS(Cross Origin Resource Sharing)跨域资源共享
* 设置响应头
* Access-Control-Allow-Origin
* Access-Control-Allow-Methods
* Access-Control-Allow-Headers
* 代理
* jsonp
* iframe
* 什么场景下出现隐式转换
> 当运算无法进行下去时,js会自动进行隐式转换,会自动调用valueOf()与toString()方法
```js
10 - '5';// 5
'10'*2; // 20
var a = 10
if(a){
}
var b = '10'
if(b == 10){
}
//
let user = {username:'laoxie'}
localStorage.setItem('user',user); // [object,Object]
let user = {
username:'laoxie',
valueOf(){
return JSON.stringify(this)
},
toString(){
return JSON.stringify(this)
}
}
localStorage.setItem('user',user); // [object,Object]
// 定义a变量,让以下条件成立
let a = {
value:1,
valueOf:function(){console.log('valueOf')
return this.value++
},
toString:function(){console.log('toString')
return this.value++
}
}
if(a==1&&a==2&&a==3){
console.log('success')
}
```
### 知识点
* 多层级组件通讯
* props逐层传递
* 操作繁琐
* 难以维护
* context 组件共享
1. 创建context
```js
const context = React.createContext()
```
2. 父组件共享数据:Provider
```js
// 子组件
```
3. 子组件接收
* 类组件
* Consumer
```js
{
function(value){
return ()
}
}
```
* contextType静态属性
```js
SubComponent.contextType = context;
// 然后在组件中就可以通过this.context访问共享的数据
```
* 函数组件
* Consumer
* Hooks钩子函数:`useContext(context)`
```js
import {useContext} from 'react'
function TodoItem(){
const value = useContext(context)
}
```
* 组件通讯
> 由于每个组件是相互独立的,组合起来后需要进行数据通讯
* 父->子:props
> 不能在子组件修改父组件的数据
* 子->父: 把父组件方法传到子组件中执行,并回传数据
* 父->孙
* props逐层传递
* context
* 兄弟:状态提升(把需要共享的数据放到他们共同的父级)
* Todolist使用技术总结
1. React + Bootstrap + JSX
2. 组件通讯
* 使用props把父组件数据传递到子组件
* 使用context共享修改数据的方法
3. 类组件与函数组件
* 使用state状态实现组件的刷新
* 组件的划分
* 复用
* 分工
* 维护
4. 受控组件与非受控组件
5. 数据挂载方式
* 组件封装
> 为了复用而封装组件
* Button按钮组件
* 使用技术
* props
```js
const size = 16
```
* children
> props.children
```js
```
* Render Props
> 使用一个值为函数的 prop 共享代码的简单技术(父组件获取子组件数据)
```js
```
* props类型校验
> 给props设置类型检查器,当传入的props类型与规定的类型不匹配时,就会报错
* 给组件添加`propTypes`静态属性
```js
import PropTypes from 'prop-types'
MyComponent.propTypes = {
}
```
* props默认值
* 结构默认值
```js
const {type='Button'} = this.props
```
* defaultProps静态属性
```js
MyComponent.defaultProps = {
type:'Button'
}
```
* 使用模块
* classnames
* 父组件获取子组件数据
* Render Props
```js
// TodoItem.js
// Button.js
class Button extends React.Component{
state = {
type:'Button'
}
render(){
return
}
}
```
* ref
> ref用在组件上,得到组件实例的引用
```js
const refObj = React.createRef()
// TodoItem.js
this.btn.state.type
```
## day1-4
### 面试题
* 前端向后端发送数据的方式
* url参数: url params
```
/api/list?username=laoxie&password=123
```
* express中如何获取
```js
router.get('/api/list',function(req,res){
const {username,password} = req.query;
})
// api/goods/123
router.get('/api/goods/:id',function(req,res){
const {id} = req.params
})
```
* 请求体: request body
```js
username=laoxie&password=123
```
* express中如何获取
```js
router.use(express.urlencoded(),express.json())
router.post('/login',(req,res)=>{
const {username,password} = req.body
})
```
* 请求头: request Header
* User-Agent
* Cookie
* Content-Type
* express中如何获取
```js
app.use('/',(req,res)=>{
// 根据请求头数据相应不同的内容
const userAgent = req.get('User-Agent')
// 百度做法
if(/windows/i.test(userAgent)){
res.send('PC端内容')
}else{
res.send('移动端内容')
}
// 淘宝做法
if(/windows/i.test(userAgent)){
res.send('PC端内容')
}else{
res.status(302)
res.set({
location:'https://main.m.taobao.com/?sprefer=sypc00'
})
}
})
```
* get请求与post请求的区别
* http与https的区别
* http: 明文传输,80
* https: 密文传输,443
* s: ssl
### 知识点
* 组件生命周期
> 组件从创建到销毁的过程
* 创建类组件
```js
// 类写法
class Button extends React.Component{
constructor(props){
super()
this.state = {
qty:10
}
}
render(){
return
}
}
// 老式写法
React.createClass({
getInitState(){
return {}
}
render(){
}
})
```
* 整个可以分成以下几个阶段,并执行相应钩子函数(生命周期函数)
> 钩子函生命周期数:在生命周期执行过程中相应的时间执行的函数,开发者可以在这些函数中实现一些功能
* 初始化阶段(Initial)
> 初始化state,props,context等
* constructor
* 挂载阶段(Mounting)
> 生成虚拟节点(oldDOM) -> 真实节点 -> 页面
* componentWillMount()
> 不推荐
* render
* componentDidMount()
* 更新阶段(Upating)
> state改变 -> 生成新的虚拟节点(newDOM) -> diff(差异算法) -> 差异项 -> 真实节点 -> 渲染到页面
* componentWillUpdate()
> 不推荐
* render
* componentDidUpdate()
* diff算法对比规则
* 只进行同级对比
* 当前节点对比时如有变化,则直接替换,不进行下层对比
```js
oldDOM = {
key:1,
type:'div',
children:[
{type:'span',children:10},
{type:'strong',children:20},
]
}
newDOM = {
key:1,
type:'span',
children:[
{type:'span',children:10},
{type:'strong',children:30},
]
}
```
* 销毁阶段:Unmounting
* componentWillUnmount
* 特殊钩子函数
* shouldComponentUpdate
* componentWillReceiveProps
> 不推荐
* 组件刷新场景
> 所谓的刷新,就是执行组件的render函数
* state改变
* props改变
* 父组件刷新导致子组件刷新
> 需要优化这样的场景
* 对比差异
* JSON.stringify()
* lodash等工具
* 继承自PureComponent
* 如何取消ajax
* 设置超时时间(超时后自动取消)
* 通过xhr.abort()手动取消
```js
let xhr = new XMLHttpRequest()
xhr.timeout = 5000;
xhr.open()
xhr.send()
```
* Hooks
> React16.8推出的新特性,用于增强函数组件的功能
* 组件刷新
* 类组件:组件刷新就是重新执行render函数
* 函数组件:组件刷新就是从头到尾把函数中的代码执行一遍
* 函数组件以前的缺点
* 无法自我刷新,只能依靠props的改变实现刷新
* 内置hooks函数
* `useState(default)`: 让函数组件也能实现 class 组件一样的状态特性(通过修改状态刷新组件)
> useState执行后返回一个数组:`[state,setState]`
* `useEffect(callback)`:callback在每轮渲染结束后执行,能实现类似于类组件中生命周期函数的效果
### 练习
* 使用hooks实现todolist案例
## day1-5
### 面试题
1. 请求类型
> 语义化
* post
* delete
* put/patch(完全修改/部分修改)
* get
* options 预检请求(浏览器自动发起的请求,一般出现在复杂跨域)
2. 请求状态码
* 200+: 成功
* 300+:
* 301 永久重定向
* 302 临时重定向
* 304 缓存
* 400+ 客户端的原因
* 401 无权限
* 402 需要支付才允许访问
* 403 禁止
* 404 不存在
* 500+ 服务端的原因
3. 函数组件与类组件的区别
* 类组件有state,生命周期,this等特性,函数组件没有
* 类组件有实例化过程,函数组件没有
* 函数组件的性能比类组件好
### 知识点
* Hooks
* 内置
* `useState(defaultValue)`
> 实现与类组件状态类似的功能,返回一个数组
```js
// class component
state = {
qty:10,
datalist:[]
}
this.setState({
qty:20
})
this.setState({
datalist:[10,20,30]
})
// FC
function Todolist(){
const [qty,setQty] = useState(10)
const [datalist,setDatalist] = useState([])
setState(qty)
}
```
* `useEffect(callback)`
> 实现与类组件生命周期函数类似的功能,callback在页面渲染完成后执行
```js
useEffect(function(){})
useEffect(function(){},[])
useEffect(function(){},[qty])
useEffect(function(){
return function(){
}
},[])
```
* `useLayoutEffect(callback)`
> useEffect的同步版本,里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成(**阻塞**了浏览器的绘制)一般用于节点操作
* `useMemo(callback)`: 缓存callback的返回值
> 一般用于编写一些比较耗费资源且无需重复执行的代码,以达到优化性能的目的,返回值为回调函数的结果
* `useCallback(callback)`:缓存callback本身
> 与 useMemo 类似,useCall缓存的是函数,一般用于定义事件处理函数
* `useRef(defaultValue)`
> 与`createRef()`对比:有默认值且有缓存特性
```js
const [page,setPage] = useState(1)
const ref = useRef()
const handle = useCallback(()=>{
console.log(page,ref.current)
},[])
```
* `useContext(content)`
> 比Consumer更简单的获取context共享数据的方式
## day2-1
### 面试题
* 本地存储存满了如何处理
* 本地存储大小
* cookie 4k
* sessionStorage 5M
* localStorage 5M
* 解决方案
* 其他本地存储
* indexDB
* webSQL
* 利用不同的域实现扩容,并使用postMessage方案实现跨域通讯
* laoxie.quxuetrip.com
* jingjing.quxuetrip.com
```js
// laoxie.quxuetrip.com
window.addEventListener('message',function(e){
console.log('data',e.data)
})
// jingjing.quxuetrip.com
laoxiewindow.postMessage('hello','http://laoxie.quexuetrip.com')
```
* webStorage如何实现cookie的有效期效果
* 临时cookie 使用 sessionStorage代理
* 具有有效期的cookie ?
```js
localStorage.setItem('username','laoxie')
localStorage.setItem('username',JSON.stringify({value:'laoxie',expires:'2022/9/30 09:05:00'}))
localStorage.getItem('username');// laoxie, null
// 重写locaStorage的原型方法
let now = new Date()
//Date.parse(now)+3*24*60*60*1000
now.setDate(now.getDate()+3)
localStorage.setItem('username','laoxie',now)
const originSetItem = Storage.prototype.setItem
Storage.prototype.setItem = function(key,value,expires){
if(expires){
value = JSON.stringify({
value,
expires: Date.parse(expires)
})
}
// 必须改变this指向
originSetItem.call(this,key,value)
}
const originGetItem = Storage.prototype.getItem
Storage.prototype.getItem = function(key){
let data = originGetItem.call(this,key); // null,'hello', '{value,expires}'
try{
data = JSON.parse(data)
if(data && data.expires){
// 判断是否过有效期
if(data.expires <= Date.now()){
data = null;
this.removeItem(key)
}else{
data = data.value
}
}
}catch(err){
}
return data
}
localStorage.getItem('username');// null,hello
localStorage.getItem('password');// hello, null
```
### 知识点
* 使用hook重写todolist
* useImperativeHandle
> 父组件获取子组件数据与方法,配合`forwardRef()`解决函数组件无法使用ref的问题
* 使用ref获取组件实例,然后通过组件实例得到子组件的数据与方法(前提是Child是类组件)
```js
// Parent(ClassComponent)
this.child=el}/>
// Parent(FunctionComponent)
const ref = useRef()
useEffect(()=>{
ref.current
},[])
return
//Child
Class Child extends React.Component{
}
```
* 子组件为函数组件
1. 使用forwardRef包裹函数组件,实现ref对象传递(函数组件的第二个参数)
2. 使用useImperativeHandle定义给ref暴露的属性
```js
function Parent(){
const ref = useRef()
// 在这里获取子组件的数据
return (
)
}
const Child = forwardRef(function(props,ref){
const [page,setPage] = useState(1)
useImperativeHandle(ref,()=>{
return {
a:10,
page
}
})
return
})
```
* useReducer
> useState的增强版,一般用于复杂数据处理
```js
const [qty,setQty] = useState(1)
const [goods,setGoods] = useState({
id:1,
name:'goods1',
price:998
})
// 修改价格
setGoods({
...goods,
price:1998
})
```
* reducer 修改状态的方法,是一个**纯函数**
> 纯函数:不修改传入的参数,固定输入得到固定输出
reducer不能修改传入的state,且必须返回一个新的state
* 参数
* state
* action
> 格式:{type}
* state 状态
```js
const [state,dispatch] = useReducer(reducer,initState)
dispatch(action)
```
* try...catch
```js
//
let a
a.test; // 这行代码报错,会停止执行后面所有的js代码
console.log('hello')
try{
// 尝试执行这里的代码,
// 如无报错,则执行执行后面的代码,
// 如有错误,则进入catch并执行后面的代码
a.test
}catch(err){
console.log('err',err)
}
console.log('hello')
```
* 自定义hook
> 自定义一个以use开头的函数
* hook函数能在函数组件和自定义hook中使用
```js
function useStorage(){
}
```
## day2-2
### 知识点: react-router
* nodejs包管理工具
* npm: node package managment
* npm install
> npm i
* npm uninstall
> npm uni
* npm init
* npm run
* npm login
> 登录npm服务器,一般用户发布npm包(注意镜像地址,类似于淘宝镜像是无法发布npm包,只能下载)
* 镜像地址
* https://registry.npm.taobao.org/
* https://registry.npmjs.org/
* 修改镜像
```bash
npm config set registry=https://registry.npmjs.org/
```
* yarn
* 安装
```bash
npm install -g yarn
```
* 使用
* yarn add
* yarn global add
* yarn xxx
* cnpm
* pnpm
* ...
* 发布npm包
1. 注册账号
2. 登录npm服务器
> npm login,需要输入用户名和密码,邮箱与验证码(验证码发送到邮箱地址)
3. 创建自己的包
* package.json
* 入口文件
* readme.md文档
4. 发布npm模块
> npm publish
* react-router
* 页面类型
* 单页面应用SPA(Single Page Application)
> 整个应用只有一个页面(index.html),利用路由实现多视图效果
* 多页面应用MPA(Multiple Page Application)
> 一个应用下有多个html页面,页面间通过a标签进行跳转
* 路由类型
> 必须指定路由类型才能使用路由配置
* hash路由:`HashRouter`
> 浏览器地址有一个`#`
* history路由: `BrowserRouter`
> 没有#号,刷新会404
* 路由配置
* `Route`
> 必须使用Routes组件包裹
* path
* element
```js
} />
// v5.0-
```
* `Routes`
* 重定向:``
```js
// / -> /home
}
```
* 路由跳转
* 组件跳转
* `Link`
* `NavLink`
> 与Link的区别是有高亮效果(配合css实现),默认当前类名`active`
* to
* replace 替换当前记录
```js
isActive?'current':null}/>
```
* 利用js跳转
* 函数组件:`useNavigate()`
```js
const navigate = useNavigate()
navigate('/home')
navigate('/mine',{replace:true})
```
* 类组件
```js
// v5-
class Home extends React.Component{
render(){
return
}
}
class App extends React.Compponent{
render(){
this.props.history;// undefined
}
}
App = withRouter(App)
// v6: 取消了props中的history,location,match,且移除了withRouter高阶组件
} />
class Login extends React.Component{
render(){
// this.props.history;// undefined
return
}
}
// 封装一个高阶组件:一个包装函数
// 需求:在类组件中实现js路由跳转
function withNavigate(InnerComponent){
return function Wrap(){
const navigate = useNavigate()
return
}
}
```
* React的UI组件库
* Ant Design(antd) 蚂蚁金服
* antd-mobile(移动端)
* Material-UI(MUI)
* React Bootstrap
> https://react-bootstrap.github.io/
* React-vant(移动端)
> https://react-vant.3lang.dev/
* axios
> api地址:http://120.76.247.5:2003/api/goods
```bash
链接:https://easydoc.net/s/58934052 密码:4wSFKBYp
```
## day2-3
### 面试题
* 防抖与节流
> 都是为了优化性能的操作,相同点都是在同一时间周期内只执行一次
* 节流:在同一时间周期内触发多次操作时,只生效第一次
* 防抖:在同一时间周期内触发多次操作时,只生效最后一次
### 知识点
* 高阶组件(HOC:High Order Component)
> 高阶组件并不是一个组件,而是一个包装函数,它的返回值才是组件
* 注意事项
* 必须传入一个函数
* 必须返回一个函数
* 必须向下传递props
* 应用场景
* 属性代理
* 提取公共代码
```js
function withNavigate(){
}
```
* 扩展运算符:`...`
* 展开
```js
const arr = [10,20,30,40]
Math.max(...arr);// 等效于 Math.max(10,20,30,40)
const props = {a:10,b:20,c:30}
// 复制对象
const newProps = {...props}
```
* 剩余
```js
const user = {name:'laoxie',age:18,gender:'male'}
const {name,...attrs} = user;// name='laoxie',attrs={age:18,gender:'male'}
function sum(){
// arguments
let total = 0
for(let i=0;i{
return args.reduce((prev,item)=>prev+item,a)
}
sum(10,20)
sum(10,20,30)
sum(10,20,30,40)
```
* React提取公共代码
* 高阶组件
```js
class Home extends React.Component{
//state = {
// list:[]
//}
//componentDidMount(){
// // 发起ajax请求(20行代码)
//}
render(){
const {list} = this.props;
return
{list}
}
}
Home = withRequestList(Home)
class List extends React.Component{
//state = {
// list:[]
//}
//componentDidMount(){
// // 发起ajax请求(20行代码)
//}
render(){
const {list} = this.props;
return
{list}
}
}
List = withRequestList(List)
class Discover extends React.Component{
//state = {
// list:[]
//}
//componentDidMount(){
// 发起ajax请求(20行代码)
//}
render(){
const {list} = this.props;
return
{list}
}
}
Discover = withRequestList(Discover)
// 利用高阶组件提取公共代码
function withRequestList(InnerComponent){
return class WrapperComponent extends React.Component{
state = {
list:[]
}
componentDidMount(){
// 发起ajax请求(20行代码)
}
render(){
return
}
}
}
```
* 自定义hook
```js
function Home(){
//const [list,setList] = useState([])
//useEffect(()=>{
// // ajax请求(20行代码)
//},[])
const list = useRequestList()
// 其他代码
// ...
return
{list}
}
function List(){
//const [list,setList] = useState([])
//useEffect(()=>{
// // ajax请求(20行代码)
//},[])
const list = useRequestList()
// 其他代码
// ...
return
{list}
}
function Discover(){
//const [list,setList] = useState([])
// useEffect(()=>{
// // ajax请求(20行代码)
//},[])
const list = useRequestList()
// 其他代码
// ...
return
{list}
}
// 利用自定义hook提取公共代码
function useRequestList(){
const [list,setList] = useState([])
useEffect(()=>{
// ajax请求(20行代码)
},[])
return list;
}
```
* 路由传参与接收
* search传参
* 传参:通过?传递
* 接收:`const [params,setParams] = useSearchParams() `
* 动态路由传参
* 传参:通过url路径传参
* 接收:`const {id} = useParams()`
* axios二次封装
> 利用axios.create()创建axios实例,并配合baseURL实现简化请求的效果
### 练习
* 实现搜索框吸顶效果
## day2-4
### 面试题
* some与every
* 如何给对象添加一个不可修改的属性
* 方案一:存储器属性之getter
```js
const source = {
age:18
}
const user = {
//age:18,
// 存储器属性getter
get age(){
return source.age
},
// 存储器属性setter
set age(newValue){
if(newValue>=18){
source.age = newValue
}else{
throw new Error('年龄不能小于18')
}
}
}
user.age;// 读取:执行getter中的代码,得到18
user.age = 20; // 写入:执行setter中的代码,并传递值
```
* 方案二:属性特性
```js
// 需求:设置一个不可修改的属性age
const user = {
age:18
}
//Object.definePropery(target,key,descriptor)
// target: 目标对象
// key: 对象的属性
// descriptor: 属性特性(值为Boolean)
// * configurable 可配置性(writable与enumberalbe的总开关)
// * writable 可写性
// * enumerable 可枚举性(是否可遍历)
// * value 值属性的值
Object.defineProperty(user,'age',{
writable:false
})
```
> 通过传统方式**添加**的属性,属性特性默认为true;通过Object.definePropery()**添加**的属性,属性特性默认为false
* 查看属性特性:Object.getOwnPropertyDescriptor()
* 对象属性分类
* 值属性:拥有值的属性
* configurable
* enumerable
* writable
* value
* 存储器属性:getter & setter
* configurable
* enumerable
* get
* set
### 知识点
* 商品分类效果
* 接口:`get /api/goods?category=男士表`
* 方案一:根据点击请求不同的数据进行渲染
* 方案二:动态路由
> 适合简单页面
* 方案三:子路由(嵌套路由)
> 使用复杂页面
* ReactRouter Hooks
* useParams() 获取动态路由参数
* useSearchParams() 获取?后的参数
* useNavigate() 获取路由跳转方法
* useLocation() 获取当前路由信息
* ``
> 类似于原生标签中的template标签或DOM中的createDocumentFragment,简写:`<>>`
## day2-5
### 面试题
* 关注点分离
```js
class Home extends React.Component{
state = {
// 功能1
datalist:[],
newlist:[]
}
componentDidMount(){
// 功能1
request.get('/xxx').then(({data})=>{
this.setState({
datalist:data.data.result
})
})
// 50 rows代码
request.get('/xxx2').then(({data})=>{
this.setState({
datalist:data.data.result
})
})
}
componentWillUnmount(){
// 功能1
cancel1()
cancel2()
}
render(){
}
}
function Home(){
// 功能1
const [datalist,setDatalist] = useState([])
useEffect(()=>{
request.get('/xxx1').then(({data})=>{
setDatalist(data.data.result)
})
return function(){
cancel1()
}
})
// 功能1
const [newlist,setNewlist] = useState([])
useEffect(()=>{
request.get('/xxx1').then(({data})=>{
setNewlist(data.data.result)
})
return function(){
cancel2()
}
})
}
```
### 知识点
* 全局状态管理
* 方案一:useReducer + context实现全局状态
* 封装Provider组件,用于向下共享数据
* 封装useUser,用于简化代码
* redux
* 数据持久化
* 本地存储
### 练习
* 购物车中的商品选择后才计算价格
* 实现数据持久化
## day2-6
### 知识点
* 数据持久化(persist)
* 数据的更新(增删改)同步到本地存储(localStorage)
* 刷新时从本地存储获取数据
* 鉴权
> 需要权限才可访问
* 页面访问权限:页面需要登录才可访问
* 功能权限:某些功能需要特定用户才可使用
> 按钮级别权限
* 数据权限:拥有权限的用户才可看到这些数据
* 页面访问权限:
> 需求:用户登录后才能访问购物车页面,没登录时回退到登录页面
* 判断条件:用户访问购物车页面时,判断用户是否已登录
* 方案一: Hooks
* 方案二:HOC
* 用户体验
* 后台管理系统
> 对内:内部使用不公开的系统,特点是需要登录才能访问,对数据进行增删改查
* 常用组件
* 表格
* 表单
* UI组件库
* ant-Design
* @ant-design/icons
```bash
npm install antd
```
* 项目搭建
* 手动配置
* CLI工具
* CRA:create-react-app
* 自定义webpack配置:react-app-rewired
> 让我们在不修改CRA配置的基础上拥有webpack的配置权
1. 的根目录中创建`config-overrides.js`文件,并修改webpack配置
```js
module.exports = function(config){
// config: 原有webpack配置
}
```
> PS: react-app-rewired2.x 已经把所有配置方法移置到了customize-cra模块
```js
const {override,addDecoratorsLegacy,disableEsLint,useBabelRc,fixBabelImports} = require('customize-cra');
module.exports = override(
addDecoratorsLegacy(), // 装饰器支持
fixBabelImports('import',{ libraryName: "antd", style: "css" })
)
```
2. 重写npm script
```bash
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test --env=jsdom",
+ "test": "react-app-rewired test --env=jsdom",
"eject": "react-scripts eject"
}
```
* Vite
1. 创建项目
```bash
npm create vite
```
2. 安装依赖
```bash
npm install
```
3. 启动项目
```bash
npm run dev
```
### 练习
* Manage页面权限访问控制
> 只有登录后才能访问
* 共享用户信息
## day2-7
### 知识点
* redux: 全局状态管理
> redux与react为两个独立产品,他们之间没有直接联系
1. 安装
```js
npm i redux
```
2. 核心API
* 创建数据仓库:store
```js
const store = createStore(reducer,state)
```
* store对象的方法
* getState() 获取state全局状态
* dispatch(action) 修改state
* subscribe(callback) 监听,当state被修改时,callback会被自行
* 全局状态:state
* 修改状态的方法:reducer
> reducer必须为一个纯函数,必须返回一个新的state
```js
const reducer = (state,action)=>{
switch(action.type){
}
}
```
* 操作全局状态的命令:action
> 格式:`{type,payload}`
3. 在react组件中操作redux全局状态
* 获取:`store.getState()`
```js
// 在Manage.jsx中获取redux数据
```
* 修改
```js
const action = {type:'login',payload:{}}
store.dispatch(action)
```
* 监听
```js
store.subscribe(function(){
})
```
* 利用自定义hook或高阶组件实现redux数据获取
* `withStore()`
* 函数柯里化
> 利用多个函数收集不同参数,然后统一处理的方式
```js
withStore()()()();
function withStore(){
return function(){
return function(){
return function(){
}
}
}
}
```
* 可选链操作符: `?.`
## day3-1
### 面试题
* 箭头函数中如何获取实参(arguments)
```js
const sum = (...args)=>{
}
sum(1,2)
sum(10,20,30)
sum(5,10,15,20)
const arr = [1,2,3,4,5]
sum(...arr);// sum(1,2,3,4,5,6)
```
### 知识点
* react-redux
1. 安装
```bash
npm i react-redux
```
2. 使用Provider组件共享Redux状态
```js
```
3. 在组件中使用redux数据
* 高阶组件: connect()
* hooks
* useStore
```js
const store = useStore()
const {userInfo} = store.getState()
```
* useSelector (推荐)
```js
const userInfo = useSelector(state=>state.userInfo)
```
* useDispatch
```js
const dispatch = useDispatch()
dispatch({type:'logout'})
```
* 后台管理系统模块:商品管理
* 商品列表:Table
* 展示商品数据 OK
* 分页 OK
> 刷新后保留分页与每页数量信息
* 添加 OK
* 编辑 OK
* 删除 OK
* 批量删除 OK
* 上下架 (练习)
* 编辑:Form
* 如何把ajax请求回的数据写入表单
* initialValues: 只有在初始化时生效,不适合ajax请求回的数据
* myform.setFieldsValue(values)
```js
// 创建表单实例
const [myform] = Form.useForm()
// 关联表单实例