1、什么是CPU
中央处理器(central processing unit,简称CPU)作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元
2、reduce
(1)定义和用法
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值;
reduce() 可以作为一个高阶函数,用于函数的 compose;
注意: reduce() 对于空数组是不会执行回调函数的;
(2)语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
(3)参数
参数 | 描述 |
---|---|
function(total,currentValue, index,arr) | 必需;用于执行每个数组元素的函数。 函数参数:参数描述total必需。初始值, 或者计算结束后的返回值。currentValue必需。当前元素currentIndex可选。当前元素的索引arr可选。当前元素所属的数组对象。 |
initialValue | 可选,传递给函数的初始值 |
3、生命周期回调函数—生命周期钩子函数—生命周期函数—生命周期钩子
(1)生命周期什么时候开始调用和写代码的顺序是没有关系的;
在代码的书写过程中,一般将构造器放在最上面,这是书写顺序的问题;
当setState()这个函数更新的时候,则会触发shouldComponentUpdate()钩子函数---此钩子函数相当于组件更新的”阀门“,在不写这个钩子函数的时候,则默认返回true,当写了这个钩子函数的时候,则需要返回一个Boolean值,若是不写返回值的话,则就是unfined;
forceUpdate()强制更新:不更改任何状态中的数据,强制更新一下
父组件通过自定义属性向子组件传递参数;
组件将要接收新的props的钩子函数
componentWillReceiveProps(props){
console.log(props);
}
总结:
1.初始化阶段:由ReactDOM.render()触发---初次渲染
constructor();
componentWillMount();
render();
componentDidMount()=====》常用,一般在这个钩子中做一些初始化的事情,例如:开启定时器、发送网络请求、订阅消息
2.更新阶段:由组件内部this.setState()或父组件render触发
1.shouldComponentUpdate();
2.componentWillUpdate();
3.render()=====》必须使用
4.componentDidUpdate();
3.卸载组件:由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount()=====》常用,一般在这个钩子中做一些收尾的事情,例如:关闭定时器、取消订阅消息
(2)新生命周期知识点总结:
//若state的值在任何时候都取决于props,那么可以使用getDerivedStateFormProps,不过这个函数的意义几乎不大,具体的说法参考官网
static getDerivedStateFormProps(props,state){
console.log('getDerivedStateFormProps',props,state);
return null;
}
getSnapshotBeforeUpdate(){
return 'shangguigu'
}
//组件更新完毕的钩子函数
componentDidUpdate(preProps,preState,snapshotValue){
console.log(preProps,preState,snapshotValue)
}
小案例主要代码:
getSnapshotBeforeUpdate(){
return this.refs.list.scrollHeight;
}
componentDidUpdate(preProps,preState,height){
this.refs.list.scrollTop += this.refs.list.scrollHeight - height;
}
总结:
1.初始化阶段:由ReactDOM.render()触发---初次渲染
constructor();
getDerivedStateFormProps();
render();
componentDidMount()=====》常用,一般在这个钩子中做一些初始化的事情,例如:开启定时器、发送网络请求、订阅消息
2.更新阶段:由组件内部this.setState()或父组件冲重新render触发
1.getDerivedStateFormProps();
2.shoulComponentUpdate();
3.render()=====》必须使用;
4.getSnapshotBeforeUpdate();
5.componentDidUpdate();
3.卸载组件:由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount()=====》常用,一般在这个钩子中做一些收尾的事情,例如:关闭定时器、取消订阅消息
(3)即将废除的钩子函数:
componentWillMount;
componentWillReceiveProps;
componentWillUpdate;
现在使用会出现警告,下一个大版本需要加上USEAFE前缀才能使用,以后可能会被彻底废弃,不建议使用
4、react应用(基于react脚手架)
(1)使用create-react-app创建react应用
A、react脚手架
B、创建项目并启动
第一步:全局安装:npm i create-react-app -g
第二部:切换到想创建项目的目录,使用命令:create-react-app hello_react
第三步:进入项目文件夹:cd hello-react
第四步:启动项目:npm start
C、index.html
<!--%PUBLIC_URL%代表Public文件夹的路径 -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico">
<!--开启理想视口,用于做移动端网页的适配-->
<meta name = 'viewport' content = "width=device-width,initial-scale=1" />
<!--用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器) -->
<meta name='theme-color' content='red'>
<!--用于指定网页添加到手机主屏幕后的图标 -->
<link rel="app-touch-icon" href="%PUBLIC_URL%/logo192.png">
<!--应用加壳时的配置文件 -->
<link rel="mainfest" href="%PUBLIC_URL%/mainfest.json">
<!--若浏览器不支持js,则展示标签中的内容-->
<noscript>您好,你不支持</noscript>
(2)react脚手架项目解构
public-----静态资源文件夹
favicon.icon --------网站页签图标
index.html-----主页面
logo192.png ------ logo图
logo512.png ------logo图
mainfest.json ------ 应用加壳的配置文件
robots.txt -------爬虫协议文件
src----源码文件夹
App.css -------App组件的样式
App.js -------- App组件
App.test.js -----样式
index.css ----- 入口文件
index.js ----- 入口文件
logo.svg ----- logo图
reportWebVitals.js ------页面性能分析文件(需要web-vitals库的支持)
setupTests.js ----- 组件单元测试的文件(需要jest-dom库的支持)
5、经典面试题目
(1)react/vue中的key有什么作用?(key的内部原理是什么?)
简单的说:KEY是虚拟DOM对象的标识,在更新显示时key起着极其中重要的作用;
详细的说:当状态中的数据发生变化时,react会根据新数据生成新的虚拟DOM,随后React进行新虚拟DOM与旧虚拟DOM的diff比较,比较规则如下:
旧虚拟DOM找到了与新虚拟DOM相同的KEY:若虚拟DOM中内容没变,直接使用之前的真实DOM;若虚拟DOM中内容变化了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM;
旧虚拟DOM中未找到与新虚拟DOM相同的key:根据数据创建新的真实DOM,随后渲染到页面
(2)为什么遍历列表时,KEY最好不要使用index?
用index作为key可能会引发的问题:若对数据进行逆序添加、逆序删除等破坏顺序的操作,会产生没有必要的真实DOM更新===》界面效果没问题,但效率低;
如果结果中还包括输入类的DOM:会产生错误虚拟DOM更新===》界面有问题;
注意:如果不存在对数据的逆序添加、逆序删除等破坏的操作,仅用于渲染列表用于展示,使用index作为key是没有问题的;
(3)开发中如何选择KEY?
最好使用每条数据的唯一标识作为key,比如:id、手机号、身份证号码、学号等唯一值;
如果确定只是简单的展示数据,用index也是可以的;
6、defaultChecked是一个刚上来是否是选中的状态,它只在第一次起作用,后期将不再起作用
7、nanoid是一个库,它里面分别暴露,其中有一个就是nanoid。nanoid是一个函数,这个函数可以随机的生成一个字符串,并且可以保证这个字符串是唯一的
8、子组件向父组件传递数据,通过父组件定义一个事件传递给子组件,子组件在合适的时间去触发,同时将数据传递给父组件,在父组件中做相应的操作
子组件:
import React, { Component } from 'react';
import { nanoid } from 'nanoid'
import './index.css'
export default class Header extends Component {
handleKeyUP = (e) => {
// 事件对象将需要的属性解构
const { target, keyCode } = e;
if (keyCode !== 13) return;
if (target.value.trim() === '') {
alert('输入的内容不能为空');
return
}
let data = {
id: nanoid(),
name: target.value,
status: false
}
this.props.updateList(data);
}
render() {
return (
<div className='header'>
<input type="text" placeholder="请输入需要做的事情" onKeyUp={this.handleKeyUP} />
</div>
)
}
}
父组件:
import Header from './components/Header'
import List from './components/List'
import Footer from './components/Footer';
import './App.css';
import React, { Component } from 'react'
export default class App extends Component {
state={
todolist:[
{
id:1,
name:'吃饭',
status:true
},
{
id:2,
name:'睡觉',
status:true
},
{
id:3,
name:'打游戏',
status:false
},
{
id:4,
name:'追剧',
status:true
},
]
}
updateList = (data) =>{
// 将原来的数据进行一个解构
const {todolist} = this.state;
// 新老数据的拼接
const newTodolist = [data,...todolist];
// 页面的更新
this.setState({
todolist:newTodolist
})
}
render() {
const {todolist} =this.state
return (
<div className="wrap">
<Header updateList = {this.updateList}/>
<List todolist={todolist}/>
<Footer />
</div>
)
}
}
9、状态在哪里,操作状态的方法就在哪里
10、想要拿到复选框以及单选框中的值,需要使用checked
11、todoList案例相关知识点
(1)拆分组件、实现静态组件,注意:className、style的写法
(2)动态初始化列表,如何确定将数据放在哪个组件的state中?
某个组件使用:放在其自身的state中;
某些组件使用:放在他们共同的父组件state中(官放称此操作为:状态提升)
(3)关于父子之间通信:
父组件给子组件传递数据:通过props传递;
子组件给父组件传递数据:通过props传递,要求父组件提前给子传递一个函数;
(4)注意defaultChecked和checked的区别,类似的还有:defaultValue和value;
(5)状态在哪里,操作状态的方法就在哪里
App.js
import Header from './components/Header'
import List from './components/List'
import Footer from './components/Footer';
import './App.css';
import React, { Component } from 'react'
export default class App extends Component {
state = {
todolist: [
{
id: 1,
name: '吃饭',
status: true
},
{
id: 2,
name: '睡觉',
status: true
},
{
id: 3,
name: '打游戏',
status: false
},
{
id: 4,
name: '追剧',
status: true
},
]
}
// 数据的一个添加操作
updateList = (data) => {
// 将原来的数据进行一个解构
let { todolist } = this.state;
// 新老数据的拼接
let newTodolist = [data, ...todolist];
// 页面的更新
this.setState({
todolist: newTodolist
})
}
//更改其中一个数据的状态
changeStatus = (id, status) => {
let { todolist } = this.state;
//将id相同的一项进行改变,其余的数据都不变
let newTodolist = todolist.map(todo => {
if (todo.id === id) {
return { ...todo, status }
} else {
// 不相同的时候,则直接返回即可,无需做其它的操作
return todo
}
})
// 更新数据
this.setState({
todolist: newTodolist
})
}
// 将选中的一条数据进行删除删除
delete = (id) => {
let { todolist } = this.state;
// let index = todolist.findIndex((todo)=>{
// return todo.id === id;
// })
// let newTodolist = [];
// //数据遍历
// for(let i = 0 ; i < todolist.length ; i++){
// if( i !== index){
// newTodolist.push(todolist[i])
// }
// }
// //数据更新
// this.setState({
// todolist:newTodolist
// })
// 还可以利用数组中提供的filter方法进行以上的操作
let newTodolist = todolist.filter(item => {
return item.id !== id
})
// 将数据更新到页面上面
this.setState({
todolist: newTodolist
})
}
allCheck = (status) => {
let { todolist } = this.state;
let newTodolist = todolist.map(item => {
return { ...item, status }
});
this.setState({
todolist: newTodolist
})
}
//将数据中已经完成的数据删除
clearDone = () => {
let { todolist } = this.state;
let newTodolist = todolist.filter(item =>{
return item.status === false
})
this.setState({
todolist:newTodolist
})
}
render() {
const { todolist } = this.state
return (
<div className="wrap">
<Header updateList={this.updateList} />
<List todolist={todolist} changeStatus={this.changeStatus} delete={this.delete} />
<Footer todolist={todolist} allCheck={this.allCheck} clearDone={this.clearDone} />
</div>
)
}
}
Header.js
import React, { Component } from 'react';
import { nanoid } from 'nanoid'
import './index.css'
export default class Header extends Component {
handleKeyUP = (e) => {
// 事件对象
const { target, keyCode } = e;
if (keyCode !== 13) return;
if (target.value.trim() === '') {
alert('输入的内容不能为空');
return
}
let data = {
id: nanoid(),
name: target.value,
status: false
}
this.props.updateList(data);
}
render() {
return (
<div className='header'>
<input type="text" placeholder="请输入需要做的事情" onKeyUp={this.handleKeyUP} />
</div>
)
}
}
List.js
import React, { Component } from 'react'
import './index.css'
export default class List extends Component {
//当状态发生变化时的操作
handleCheck = (id) =>{
return (e)=>{
//console.log(id,e.target.checked);
this.props.changeStatus(id,e.target.checked)
}
}
// 当点击删除时候的操作
delete = (id) =>{
return () =>{
if(window.confirm('确定删除吗?'))
this.props.delete(id);
}
}
render() {
const {todolist} = this.props;
return (
<ul className='clearfix'>
{todolist.map(todo =>{
return (<li className="list" key={todo.id}>
{/* 当选中的状态进行改变的时候 */}
<input type="checkbox" checked={todo.status} onChange={this.handleCheck(todo.id)}/>
<p>{todo.name}</p>
<button onClick={this.delete(todo.id)}>删除</button>
</li>)
})}
</ul>
)
}
}
Footer.js
import React, { Component } from 'react'
import './index.css'
export default class Footer extends Component {
// 全选按钮回到函数
handleAllCheck = (e) => {
this.props.allCheck(e.target.checked);
}
// 清除已经完成的任务
handleClearDone = ()=>{
this.props.clearDone();
}
render() {
const {todolist} = this.props;
const newList = todolist.filter( item =>{
return item.status === true;
});
const doneCount = newList.length;
const allCount = todolist.length;
return (
<div className='footer'>
<div className='left'>
<input type="checkbox" onChange={this.handleAllCheck} checked={ doneCount === allCount && allCount !== 0 ? true : false}/>
<p>已完成{doneCount}/全部{allCount}</p>
</div>
<button onClick={this.handleClearDone}>清除已完成任务</button>
</div>
)
}
}
12、脚手架配置代理
方式一:在package.json中,添加
“proxy”:“http://localhost:5000”
说明:
优点:配置简单,前端请求资源时可以不加任何前缀;
缺点:不能配置多个代理;
工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000(优先匹配前端资源)
方式二:在src目录中,添加setupProxy.js
在这个文件中需要时CJS语法
const proxy = require('http-proxy-middleware');
module.exports = function(app){
app.use(
proxy('/api1',{//遇见/api1前缀的请求,就会触发该代理配置
target:'http://localhost:5000',//请求转发给谁
changeOrigin:true,//控制服务器收到的请求头中Host的值
pathRewrite:{
'^/api1':''
}//重写请求路径(必须)
})
),
proxy('/api2',{
target:'http://localhost:5001',
changeOrigin:true,
pathRewrite:{
'^/api2':''
}
})
)
}
说明:
优点:可以配置多个代理,可以灵活的控制请求是否走代理;
缺点:配置繁琐,前端请求资源时必须加前缀;