index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { configureStore } from '@reduxjs/toolkit';
import { combineReducers } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import App from './components/app';
//redux里面的每一个状态一般值是存到叶节点上,每一个内部节点都是用来关联叶节点的值
//定义一个reducer,这个函数会传入两个值,第一个是上一个state的值,第一次调用的时候会传入一个undefined,我们需要给他一个默认值,可以看成state的一个初始值,
//另外还会传入一个参数action,action是一个对象,里面有一个名字叫type,
const f1=(state=0,action)=>{
switch(action.type){
case 'add':
// return state+1;
// return 可以传参数,例如state不加1了想加一个具体值,由于传入的action是一个对象,所以这里action可以传一个值
return state+action.value;
case 'sub':
// return state-1;
return state-action.value;
default:
return state;
}
};
const f2=(state=":",action)=>{
switch(action.type){
case 'concat':
return state+action.character;
default:
return state;
}
};
// 将两个reducer合并成一个reducer,他初始的话就是一个字典,f3的作用主要是将f1和f2组合起来
//组合两个值的方法就是把子节点f1f2的值作为他的父节点值的字典,对象里面的两个属性
// const f3=(state={},action)=>{
// return{
// f1:f1(state.f1,action),
// f2:f2(state.f2,action),
// }
// };
const f3=combineReducers({
number:f1,
string:f2,
})
//假设当前树里面只有一个节点,我们要把当前节点构建成一个树
//store就是将函数f1构建成一颗状态树,传入一个对象,
const store=configureStore({
// reducer: f1
reducer: f3
});
//订阅函数,可以让我们在每次dispatch执行完之后都可以执行一个函数
// store.subscribe(()=>{console.log(store.getState())});
// //修改这个值,dispatch是传入一个action,action就是一个对象,对象里面只用到了type,所以这个对象里面只有一个值叫type
// //这个函数的意思就是将action这个参数传递给我们整棵树的所有的reducer函数,他会调用整棵树所有的reducer函数
// //当前树里只有一个reducer函数就是f1,所以我们执行这句话的话就会调用一下f1,然后将{type:"add"}传给f1的action
// //调用完之后可以看到action的值是add,所以会执行state+1,那么它会返回state+1,返回的这个值就会作为state的新值
// //这个action其实就是我们传入的这个值
// store.dispatch({type:"add",value:1});
// store.dispatch({type:"add",value:1});
// store.dispatch({type:"add",value:1});
// store.dispatch({type:"sub",value:1});
// store.dispatch({type:"sub",value:1});
// store.dispatch({type:"concat",character:"aaa"});
// store.dispatch({type:"concat",character:"xxx"});
//求整颗树的值
// console.log(store.getState());
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
//如果想在项目里面用到redux的话,就需要用provider将整个项目包含起来
//provider一定要提供一个属性store,这个store就是存我们刚刚创建的store
<Provider store={store}>
<App />
</Provider>
);
app.jsx
import React, { Component } from 'react';
import Number from './number';
import String from './string';
class App extends Component {
state = { }
render() {
return (
// app里有两个组件
<React.Fragment>
<Number />
<hr />
<String/>
</React.Fragment>
);
}
}
export default App;
number.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Number extends Component {
state = { }
handleClick=()=>{
this.props.concat('y');
}
render() {
console.log(this.props);
return (
<React.Fragment>
<h3>Number:</h3>
<div>{this.props.number}</div>
<button onClick={this.handleClick}>添加</button>
</React.Fragment>
);
}
}
//在组件里面像是store的值,用connect
//connect返回一个函数,可以将我们自己定义的组件传入进去然后返回一个组装好的组件
//组装的话,可以组装两个东西,第一个是可以将我们的值组装到我们组件的props这个属性里面
//mapStateToProps将我们store里的state这个值绑定到我们组件的props属性上
//传入的第一个函数有两种取值,第一个是state,这个state就是我们store getState之后的结果,也就是我们整个状态树的树结构,状态树的树结构这个值是不包含reducer函数的,值包含state值
//第二个属性是props也就是我们原始的props
const mapStateToProps=(state,props)=>{
// 返回一个字典,返回一个对象,对象里面我们可以定义一下,比如说我们想取出state里面的number这个值,然后我们把number这个值赋到哪个属性上,可以把他赋到number这个属性上
return{
number:state.number,
}
}
//修改
//将dispatch这个函数映射到props里面,只会在创建的时候调用一次
const mapDispatchToProps={
// 在number里面修改string的值 concat是一个函数他最后会返回一个对象,这个对象就是我们dispatch里面用到的action,action就是我们传入的包含type的对象,所以他最后会返回一个对象这个对象里面一定要包含一个action
//在string里添加一个字符,修改string的话,要用到'concat'这个字符串,,concat可以传入一个变量c,c表示我们要添加的字符,要用action的哪个属性这里在传的时候就要用哪个属性
concat:(c)=>{
return {
type:"concat",
character:c,
}
}
}
//将number值绑定到组件上要用到connect函数,connect(mapStateToProps)函数返回值会返回一个函数,函数可以将我们Number这个组件作为输入参数,它会返回一个新的组件,这个新的组件就会将我们state里的number(右边state.number)绑定到我们当前组件的props里面的number(左边number)这个属性上
export default connect(mapStateToProps,mapDispatchToProps)(Number);
string.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
class String extends Component {
state = { }
handleClickAdd=()=>{
this.props.add(10);
}
handleClickSub=()=>{
this.props.sub(1);
}
render() {
console.log(this.props);
return (
<React.Fragment>
<h3>String:</h3>
<div>{this.props.string}</div>
<button onClick={this.handleClickAdd}>加</button>
<button onClick={this.handleClickSub}>减</button>
</React.Fragment>
);
}
}
const mapStateToProps=(state,props)=>{
// 返回一个字典,返回一个对象,对象里面我们可以定义一下,比如说我们想取出state里面的number这个值,然后我们把number这个值赋到哪个属性上,可以把他赋到number这个属性上
return{
string:state.string,
}
}
const mapDispatchToProps={
add:(x)=>{
return {
type:"add",
value:x,
}
},
sub:(x)=>{
return{
type:"sub",
value:x,
}
}
}
//将number值绑定到组件上要用到connect函数,connect(mapStateToProps)函数返回值会返回一个函数,函数可以将我们Number这个组件作为输入参数,它会返回一个新的组件,这个新的组件就会将我们state里的number(右边state.number)绑定到我们当前组件的props里面的number(左边number)这个属性上
export default connect(mapStateToProps,mapDispatchToProps)(String);
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './components/app';
import { BrowserRouter } from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
//如果要给前端页面加路由的话,一定要把组件包含在BrowserRouter里面
<BrowserRouter>
<App />
</BrowserRouter>
);
app.jsx
import React, { Component } from 'react';
import NavBar from './navbar';
import Home from './home';
import Linux from './linux';
import Django from './django'
import Web from './web';
import WebContent from './webContent';
import NotFound from './notFound';
import {Routes,Route} from 'react-router-dom';
import { Navigate } from 'react-router-dom';
//写路由:根据当前的url去选择我们在下面显示哪一个组件
class App extends Component {
state = { }
render() {
return (
<React.Fragment>
<NavBar />
{/* 如果想写多个路由的话,一定要将所有路由包含在routes里面 */}
{/* container也是bootstrap里的一个类,他可以动态的调整填充区域的内容,响应式的调整填充区域的内容 */}
<div className='container'>
<Routes>
<Route path='/' element={<Home />}/>
{/* 嵌套路由 */}
<Route path='/linux' element={(<Linux />
// <div>
// hhh
// <a href="/">返回首页</a>
// </div>
)}>
<Route path='homework' element={<h4>homework的内容</h4>}></Route>
<Route path='terminal' element={<h4>terminal的内容</h4>}></Route>
<Route path='*' element={<h4>其他</h4>}></Route>
</Route>
{/* 给组件Django传一个参数,直接在组件里写就可以了 */}
<Route path='/django' element={<Django id='hhh' />}/>
<Route path='/web' element={<Web />}/>
{/* 将/web/conntent/1中1变成一个变量可以写成:chapter,这样1这个位置的内容就会被保存到名字是chapter这个变量里面 */}
{/* 如果有多个参数每一个参数用一个冒号 */}
{/* <Route path='web/content/:chapter/:section' element={<WebContent />}/> */}
{/* 用第二种方式来获取参数,路由匹配的时候,他只匹配到问号前面的部分,问号后面的部分它是不匹配的 */}
<Route path='/web/content' element={<WebContent />}/>
<Route path='/404' element={<NotFound />} />
{/* 重定向
如果打开一个新的页面之后,如果这个页面不存在的话,就把他重定向到404
通配符* path="*"表示它可以匹配所有的路径
Navigate的属性to表示我们需要跳到哪一个链接里面*/}
<Route path='*' element={<Navigate replace to="/404" />}/>
</Routes>
</div>
</React.Fragment>
);
}
}
export default App;
navbar,js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class NavBar extends Component {
state = { }
render() {
return (
<nav className="navbar navbar-expand-lg bg-body-tertiary">
<div className="container-fluid">
{/* 实现每次不重复加载页面,Link组件可以直接让当前页面假装跳到另外一个页面,但其实并没有向后端发请求
将所有的a标签都变成Link,
<a className="navbar-brand" href="/">讲义</a> */}
<Link className='navbar-brand' to='/'>讲义</Link>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
{/* <a className="nav-link active" aria-current="page" href="/">Home</a>
<a className="nav-link" href="/linux">Linux</a>
<a className="nav-link" href="/django">Django</a>
<a className="nav-link" href="/web">Web</a> */}
{/* 前端渲染,每次点完一个按钮之后,他并没有真的打开这个链接 */}
{/* 如果想给后端发请求返回整个页面的话就用一般的Link(a标签),如果不想给后端发请求,只想假装改变页面的话就用link(Link标签) */}
<Link className="nav-link" aria-current="page" to="/">Home</Link>
<Link className="nav-link" to="/linux">Linux</Link>
<Link className="nav-link" to="/django">Django</Link>
<Link className="nav-link" to="/web">Web</Link>
</div>
</div>
</div>
</nav>
);
}
}
export default NavBar;
home.jsx
import React, { Component } from 'react';
class Home extends Component {
state = { }
render() {
return (
<h1>Home</h1>
);
}
}
export default Home;
linux.js
import React, { Component } from 'react';
import { Outlet } from 'react-router-dom';
// Outlet可以用来填充我们linux子组件里的路由的内容
class Linux extends Component {
state = { }
render() {
return (
<React.Fragment>
<h1>Linux</h1>
<hr />
{/* 在地址栏输入http://localhost:3000/linux/homework可以渲染出来Linux子组件homework的内容
在地址栏输入http://localhost:3000/linux/terminal可以渲染出来Linux子组件terminal的内容 */}
<Outlet />
</React.Fragment>
);
}
}
export default Linux;
django.jsx
import React, { Component } from 'react';
class Django extends Component {
state = { }
render() {
console.log(this.props.id);
return (
<h1>Django</h1>
);
}
}
export default Django;
web.jsx
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class Web extends Component {
state = {
webs:[
{id:1,title:"HTML"},
{id:2,title:"CSS"},
{id:3,title:"JavaScript"},
{id:4,title:"拳皇"},
{id:5,title:"React"},
]
}
render() {
//要渲染一个标题和一个内容所以要渲染两个东西,两个东西就需要先将他用React.Framement先括起来
return (
<React.Fragment>
<h1>Web</h1>
<hr />
<div>
{this.state.webs.map(web=>(
//循环里面每一个组件都有一个唯一的key,唯一的key可以搞成id
//给每一个元素加上一个链接,前端渲染加链接要放到Link里面
<div key={web.id}>
{/* <Link to={`/web/content/${web.id}`}>{web.id+"."+web.title}</Link> */}
{/* 2 */}
<Link to={`/web/content?chapter=${web.id}`}>{web.id+"."+web.title}</Link>
</div>
))}
</div>
</React.Fragment>
);
}
}
export default Web;
webContent.jsx
import React, { Component } from 'react';
// 1
import { useParams } from 'react-router-dom';
// 2
import { useSearchParams } from 'react-router-dom';
import { Link } from 'react-router-dom';
//传参数的方式
// 1.直接在url里面写
// http://localhost:3000/web/content/1/3
// 2.通过url里面传参数的话,可以在问好后面写,变量名等于什么值
// http://localhost:3000/web/content?chapter=1§ion=3
//如果是类组件的话,不能直接在类组件中访问useParams
//有两种修改方式,1.直接将类组件变成函数组件
//2.虽然在类里不能用但在调用这个类之前可以用,即在类外面用
class WebContent extends Component {
state = {
// useSearchParams是一个数组有两个值,第一个值是获取参数,第二个值是设置参数
//这个两个都是函数
searchParams:this.props.params[0],//获取某一个参数
setSearchParams:this.props.params[1],//将我们链接里面的参数设置成什么然后重新渲染当前页面
};
render() {
// 1
// console.log(this.props.params);
// 2
// console.log(this.state.searchParams.get('section'));
return (
<React.Fragment>
{/* 把那个数字取出来 */}
<h1>Web - {this.state.searchParams.get('chapter')}</h1>
<div>内容</div>
<hr />
<Link to='/web'>返回</Link>
</React.Fragment>
);
}
}
// export default WebContent;
//返回一个匿名函数,这个匿名函数会返回一个组件,这个匿名函数就是一个匿名组件,其实就是一个函数组件,外面本质上还是在函数组件里用的,但是外面相当于是在类组件外面套了一层函数组件
export default (props)=>(
<WebContent
{...props}//把函数组件里的属性展开
params={useSearchParams()}//再将它的值作为一个属性传进去就可以了
/>
);
//sfc函数组件
// const WebContent = () => {
// console.log(useParams());
// return (
// <h1>Web - 1</h1>
// );
// }
// export default WebContent;
notFound.jsx
//当找不到页面的时候返回404页面
import React, { Component } from 'react';
class NotFound extends Component {
state = { }
render() {
return (
<h1>NotFound</h1>
);
}
}
export default NotFound;
index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';
import App from './components/app';
//index.js是整js的入口
//将定义好的组件渲染出来
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
box.js
import React, { Component } from 'react';
class Box extends Component {
//如果想在外面修改当前state的值,就不能在当前组件里面存储state
//就要在当前组件渲染外面state的值
//当前这个组件里面所有用到state的地方,都需要修改成我们传过来的值
// state = {
// //把值传过来之后,可以将这个值更新到state里面
// x:this.props.x,//他就可以将x初始化进来
// // x:0,
// }
//将当前组件里的state删掉,如果要删掉的话,handleClickLeft和handleClickRight也会修改自己state的值
//删完之后我们就不能修改自己state的值了,所以这两个函数也需要移到外面
//将这两个函数剪切在boxes里
// handleClickLeft=(step)=>{
// this.setState({
// // 只会在初始化的时候执行一次
// x:this.state.x-step
// })
// console.log("click left",this);
// }
// handleClickRight(step){
// this.setState({
// x:this.state.x+step
// })
// console.log("click right",this);
// }
//1Mount周期
// constructor(){
// super();
// console.log("Box - Constructor");
// }
// //3Mount周期
// componentDidMount(){
// console.log("Box - Mounted");
// }
//2 Update周期
// componentDidUpdate() {
// console.log("Box - Updated");
// }
//Unmount周期
//每一个组件可能会绑定一些事件,我们可以在删除之前做一个清理,把一些事件、数据清理掉
componentWillUnmount(){
console.log("Box - Unmout");
}
//将所有用到state的地方都变成this.props.box
render() {
//2Mount周期
// console.log("Box - Rendered");
// 1.4.2 从上往下传递数据
//props里存储的就是在这个组件在Boxes中定义的时候里面填写的各种属性,除了key以外
// console.log(this.props);
return (
<React.Fragment>
{/* 1.4.3 传递子节点 */}
{/* {this.props.children[0]} */}
<div style={this.getStyles()}>{this.toString()}</div>
{/* {this.props.children[1]} */}
<button onClick={this.props.onClickLeft} className='btn btn-primary m-2'>left</button>
<button onClick={this.props.onClickRight} className='btn btn-success m-2'>right</button>
{/* 在box里面调用boxes的函数 */}
<button onClick={()=>this.props.onDelete(this.props.box.id)} className='btn btn-danger m-2'>Delete</button>
</React.Fragment>
);
}
getStyles(){
let styles = {
width: 50,
height:50,
backgroundColor: "lightblue",
color:"white",
textAlign:"center",
lineHeight:"50px",
borderRadius:"5px",
marginLeft: this.props.box.x,
};
if(this.props.box.x<=0) styles.backgroundColor='orange';
return styles;
}
toString() {
const {x}=this.props.box;
return `x:${x}`;
}
}
export default Box;
boxes.jsx
import React, { Component } from 'react';
import Box from './box';
//在维护state值的时候,同一个数据只能存一份,如果存多份的时候就会出现有些时候我们改完之后会发现,有些地方的值变了,但是有些地方的值不变,就会发生数据不同步的情况
//把boxes变成一个函数组件sfc
//函数组件相当于是只有函数组件
//对象的解构可以在参数里面做,解构完之后就可以将props删掉了
// const Boxes = ({onReset,boxes,onDelete,onClickLeft,onClickRight}) => {
// return (
// <React.Fragment>
// <button
// onClick={onReset}
// style={{marginBottom:"15px"}} className='btn btn-dark'>Reset</button>
// {boxes.map(box=>(
// <Box
// key={box.id}
// box={box}
// onDelete={onDelete}
// onClickLeft={()=>onClickLeft(box)}
// onClickRight={()=>onClickRight(box)}
// />
// ))}
// </React.Fragment>
// );
// }
// export default Boxes;
class Boxes extends Component {
//将state的所有内容放到app里面去,将定义和操作他的所有内容都放过去
// state = {
// // 把所有Box的所有信息存到state里面
// //boxes存一个数组,boxes里面每一个需要存一个x,另外需要存一个id,因为react组件如果有若干个儿子的话,儿子的key需要不一样
// //删完之后直接在box组件里面直接用boxes组件里面的state值就可以了
// boxes: [
// {id:1,x:1},
// {id:2,x:2},
// {id:3,x:3},
// {id:4,x:4},
// ]
// }
// //实现在当前组件里改完x之后,能够影响到box里面的值:
// // 可以将box里的state值直接删掉,由于box里面的state的值不能在外面修改,所以直接将他弃用(删掉)就可以了
// handleClickLeft=(box)=>{
// //注意: 不要直接修改this.state的值,因为setState函数可能会将修改覆盖掉。
// //所以先将boxes复制一遍
// const boxes=[...this.state.boxes];
// //indexOf可以在一个数组里面找到某一个书的下标
// const k=boxes.indexOf((box));
// //将第k个boxes的值重新克隆出来,因为boxes[k]他其实存的是原数组里的第k个值
// //把他克隆出来可以再展开一遍,再展开一遍就相当于是把它创建了一个新的
// boxes[k]={...boxes[k]};
// boxes[k].x--;//将新的这个值得x减减
// this.setState({boxes});//将对于boxes的修改更新到state里面
// }
// handleClickRight(box){
// const boxes=[...this.state.boxes];
// const k=boxes.indexOf((box));
// boxes[k]={...boxes[k]};
// boxes[k].x++;
// this.setState({boxes});
// }
// handleReset = () => {
// const boxes=this.state.boxes.map(b=>{
// // 点完reset之后,当前这个组件boxes里面的state里面的值确实变成0了
// // 但是并没有真的将dom里面的值变成0
// // 因为如果向修改某一个组件值,一定只能在组件内部修改,不能在另外一个组件里修改这个组件的值
// // (即,不能在boxes里修改box的x值),修改的只是当前组件boxes里的state,
// // 但是最后在div中显示出来的x值其实应该是box组件里的state
// // box里面的state x:this.props.x,这句话他只会在第一次执行的时候将box里面的state的值改变成从boxes传过来的值
// // 但是后面每次修改的时候x:this.props.x这句话不会重复执行,他只会在初始化的时候执行一次
// // 所以后面不管怎么去修改x:this.props.x里面的值,x都不会发生变化了
// return{
// id:b.id,
// x:0,
// }
// });
// this.setState({boxes});
// console.log(this.state)
// }
// handleDelete = (boxId) => {
// console.log("handle delete", boxId)
// const boxes=this.state.boxes.filter(b=>b.id!==boxId);
// //语法糖:在一个对象里如果key和value是一样的话,可以只写一个
// // this.setState({ boxes: boxes });
// this.setState({ boxes});
// }
//1Mount周期
// constructor(){
// super();
// console.log("Boxes - Constructor");
// }
// //3Mount周期
// componentDidMount(){
// console.log("Boxes - Mounted");
// }
//2 Update周期
// componentDidUpdate() {
// console.log("Boxes - Updated");
// }
render() {
//2Mount周期
// console.log("Boxes - Rendered");
return (
// 当一个组件里面包含多个并列的元素的时候,我们需要用一个标签将他们括起来
// 可以用react里的一个虚拟标签将他们括起来<React.Fragment>
<React.Fragment>
<button
onClick={this.props.onReset}
style={{marginBottom:"15px"}} className='btn btn-dark'>Reset</button>
{/* 改成用一个map来写
<Box />
<Box />
<Box />
<Box /> */}
{this.props.boxes.map(box=>(
//可以将所有要传的数据的参数写在<Box />里面,可以任意起名
//例如向传一个x,写完x之后,就可以在Box组件里面找到这个属性了
<Box
key={box.id}
// x={box.x}
//如果只定义了名称没定义值的话,值就是true
// name='xxx'
//也可以传一个对象 box={box}
// id={box.id}
box={box}
// 将函数传给box里面的onClick
onDelete={this.props.onDelete}
//为了能够在box组件内部调用组件外部的函数,需要将组件外部的函数作为属性传过去
onClickLeft={()=>this.props.onClickLeft(box)}
onClickRight={()=>this.props.onClickRight(box)}
/>
// {/* 如果在定义的组件之间加一些子元素的话,子元素的值也是可以传过来的 */}
// <h1>Box:</h1>
// <p>#{box.id}</p>
// </Box>
))}
</React.Fragment>
);
}
}
export default Boxes;
navbar.jsx
import React, { Component } from 'react';
// 函数组件sfc
//函数组件相当于是只有一个render函数,把render函数的内容复制过来,然后将其余内容删掉
//类组件是可以通过this.props传递信息的,但是函数组件props是传在参数里面,要将所有的this都删掉
// const NavBar = (props) => {
// return (
// <nav className="navbar bg-body-tertiary">
// <div className="container-fluid">
// <a className="navbar-brand" href="/">
// Navbar <span>Boxes Count: {props.boxesCount}</span>
// </a>
// </div>
// </nav>
// );
// }
// export default NavBar;
class NavBar extends Component {
// 类组件里如果没有局部变量的话,就可以把他变成一个无状态的函数组件
state = { }
//1Mount周期
// constructor(){
// super();
// console.log("NavBar - Constructor");
// }
// //3Mount周期
// componentDidMount(){
// console.log("NavBar - Mounted");
// }
//2 Update周期
// componentDidUpdate() {
// console.log("NavBar - Updated");
// }
render() {
//2Mount周期
// console.log("NavBar - Rendered");
return (
<nav className="navbar bg-body-tertiary">
<div className="container-fluid">
{/* /表示根目录 */}
<a className="navbar-brand" href="/">
Navbar <span>Boxes Count: {this.props.boxesCount}</span>
</a>
</div>
</nav>
);
}
}
export default NavBar;
// 在navbar里面存储一些boxes的信息,例如存储一下boxes里面有多少个元素,存一个count,涉及到不同组件之间传递信息,兄弟组件之间传递信息
// 在navar里调用boxes里的信息,只能把这两个节点要调用的信息放到他们的最近公共祖先App里,所以只能将boxes的state放到app里面
app.jsx
import React, { Component } from 'react';
import NavBar from './navbar';
import Boxes from './boxes';
//有两个组件navbar和boxes
class App extends Component {
state = {
boxes: [
{id:1,x:1},
{id:2,x:2},
{id:3,x:3},
{id:4,x:4},
]
}
// Mount周期
//3.componentDidMount是说整个对象都已经被渲染出来了
//如果想从服务器加载一些数据渲染出来,需要先将整个组件挂载完之后,再往里填数据
//所以挂载完之后可以写一个ajax方法,从服务器加载数据
//从服务器加载完之后,就可以通过setState函数将我们从数据库加载的信息渲染到我们当前组件里
// componentDidMount(){
// // ajax();
// // this.setState();
// console.log('App - Mounted');
// }
// //构造函数,由于它是从某一个类里面继承过来的,那么在构造函数里第一句话一定要写super,先初始化构造函数
// //1.先创建,Mount周期
// constructor(){
// super();
// console.log("App - Constructor");
// }
//2 Update周期
//prevProps和prevState存储我们上一个属性(即修改之前)和上一个状态
// componentDidUpdate(prevProps,prevState) {
// console.log("App - Updated");
// // console.log("prevProps",prevProps);
// //输出上一个state的值和更新完之后的值
// console.log("prevState",prevState,this.state);
// //当发现状态不一样的时候,可能会更新数据库里的数据
// //所以一般往数据库里写数据的时候,很多时候都会写componentDidUpdate函数里面
// // if(prevState.boxes[0].x!==this.state.boxes[0].x){
// // ajax()//更新数据库
// // }
// }
handleClickLeft=(box)=>{
const boxes=[...this.state.boxes];
const k=boxes.indexOf((box));
boxes[k]={...boxes[k]};
boxes[k].x--;
this.setState({boxes});
}
handleClickRight=(box)=>{
const boxes=[...this.state.boxes];
const k=boxes.indexOf((box));
boxes[k]={...boxes[k]};
boxes[k].x++;
this.setState({boxes});
}
// handleClickRight(box){
// const boxes=[...this.state.boxes];
// const k=boxes.indexOf((box));
// boxes[k]={...boxes[k]};
// boxes[k].x++;
// this.setState({boxes});
// // console.log("click right")
// }
handleReset = () => {
const boxes=this.state.boxes.map(b=>{
return{
id:b.id,
x:0,
}
});
this.setState({boxes});
}
handleDelete = (boxId) => {
console.log("handle delete", boxId)
const boxes=this.state.boxes.filter(b=>b.id!==boxId);
this.setState({ boxes});
}
render() {
//2.渲染 Mount周期
//在render里面会挂载新的组件,所以在render里面会递归调用每一个子组件的constructor() -> render() -> componentDidMount()
// console.log("App - Rendered");
return (
<React.Fragment>
{/* 当前boxes里的元素信息是存在了app里面,如果想在navbar里调用app里的内容,相当于是从上往下传数据,就可以通过props传数据了
这里把长度传给navbar
求一下有多少个元素不为0*/}
<NavBar boxesCount={this.state.boxes.filter(b=>b.x!==0).length} />
{/* Boxes放到container里面,bootstrap一般都会将内容放到container里面,container是一个自适应的内容填充区域,根据屏幕的宽度来自适应调节内容区域,可以让内容区域看起来他的宽度比较合适一些 */}
<div className='container'>
<Boxes
boxes={this.state.boxes}
onReset={this.handleReset}
onClickLeft={this.handleClickLeft}
onClickRight={this.handleClickRight}
onDelete={this.handleDelete}
/>
</div>
</React.Fragment>
);
}
}
export default App;
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import Solution from './components/solution';
import 'bootstrap/dist/css/bootstrap.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// container可以自适container,可以将所有内容放到comtainer里面
<React.StrictMode>
<div className="container">
<Solution />
</div>
</React.StrictMode>
);
solution.jsx
// imrc
import React, { Component } from 'react';
// cc
class Solution extends Component {
state = {
// 定义一堆solutions,solutions是一个列表,列表可以定义他的一些属性
solutions:[
{key:0 ,number:1164, title:"加工零件1",views:2930},
{key:1 ,number:1165, title:"加工零件2",views:2931},
{key:2 ,number:1166, title:"加工零件3",views:2932},
{key:3 ,number:1167, title:"加工零件4",views:2933},
{key:4 ,number:1168, title:"加工零件5",views:2934},
{key:5 ,number:1169, title:"加工零件6",views:2935},
{key:6 ,number:1170, title:"加工零件7",views:2936},
{key:7 ,number:1171, title:"加工零件8",views:2937},
]
};
// 我们需要知道删除的是谁,所以删除函数需要给一个参数
// 我们需要找到对应的solution然后将他删掉
handleDelete=(s)=>{
// 数组或者列表有一个函数filter(),可以帮我们过滤数组,它会依次将solutions里的每一个元素作用一遍我们的函数
// filter里面传一个函数这个函数有一个返回值,返回值是true或者false
// 如果返回值是true的话就将他保留,false就删除,应该保留所有跟solution不一样的元素
// 因为我们要删除的是solution,那么其余的都要保留
// 遍历一下solutions里的所有元素如果不一样的话,就把他保留下来,然后将这个值存到const solutions里面
const solutions=this.state.solutions.filter(solution=>solution!==s)
//更新solutions
this.setState({
solutions:solutions
// 如果key和value是一样的可以只写一个
// 写成:solutions
})
}
handleAdd=(s)=>{
const solutions=[...this.state.solutions,{
key:this.state.solutions[this.state.solutions.length-1].key+1 ,number:s.number, title:s.title,views:s.views
}];
this.setState({
solutions:solutions
})
}
render() {
//当我们把所有元素删完之后,把table删掉换一个新的写法
if(this.state.solutions.length===0){
return <p>没有题解拉!</p>
}
return (
// table.table>thead>tr>th*4
<table className="table">
<thead>
<tr>
<th>#</th>
<th>标题</th>
<th>阅读</th>
<th>操作1</th>
<th>操作2</th>
</tr>
</thead>
<tbody>
{/* 遍历所有solutions,凡是换行的地方都要加小括号 */}
{this.state.solutions.map(solution=>(
<tr key={solution.key}>
<td>{solution.number}</td>
<td>{solution.title}</td>
<td>{solution.views}</td>
{/* button里面绑定一个删除函数 */}
<td><button onClick={()=>this.handleDelete(solution)} className='btn btn-outline-danger'>删除</button></td>
<td><button onClick={()=>this.handleAdd(solution)} className='btn btn-outline-success'>添加</button></td>
</tr>
))}
</tbody>
</table>
);
}
}
export default Solution;
index.css
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
p{
font-size: 24px;
font-weight: 600;
color: gray;
margin:50px;
}
powershell
(以管理员方式打开)cd D:\Webstudy\react
(cd
到要创建react
项目的文件夹)create-react-app box-app
(创建box-app
)Happy hacking!
就是创建成功了npm start
vsCode
中打开对应目录下的box-app
npm start
即可打开React App
webpack compiled successfully
就是连接成功了目录结构:
初始化:
目录、index.html、index.js
中一些没用的东西components
box.jsx
box.js
中输入imrc
(把react import
进来)按回车或者tap
键index.js
里面把定义的component
加进来(import Box
回车)Box
加到<React.StrictMode>
(<Box />
)Hello World
修改box.jsx
代码
// imrc
import React, { Component } from 'react';
// cc
class Box extends Component {
state = { }
render() {
return <h1>Hello World</h1>;
}
}
export default Box;
修改index.js
代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';
import Box from './components/box';
//index.js是整js的入口
//将定义好的组件渲染出来
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
{/* 名称和类名一样 */}
<Box />
</React.StrictMode>
);
安装bootstrap:
VS Code
终端中输入ctrl+c
VS Code
终端中输入npm install bootstrap
index.js
中将bootstrap
引入进来import 'bootstrap/dist/css/bootstrap.css';
npm start
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';
import Box from './components/box';
//index.js是整js的入口
//将定义好的组件渲染出来
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
{/* 名称和类名一样 */}
<Box />
</React.StrictMode>
);
box.jsx
//定义组件
// imrc把react import进来
import React, { Component } from 'react';
//写一个box,box有两个按钮,按钮可以控制box左移还是右移
//定义一个类组件 cc
class Box extends Component {
//局部变量
state = {
x:0,
// 1.3.7 渲染列表
colors:['red','green','blue']
}
// this是undefined因为onClick函数,我们不知道react是在什么地方调用的,
// 但是我们希望让我们的this指向我们当前的class
// 这个this我们并不知道他在调用的时候react在什么地方是什么东西调用的onClick,
// 但我们知道他应该不是在我们的box类上调用的这个函数,所以this就会变成一个很奇怪的值
// 但是我们希望让我们的this取值取成我们thisclass:
// 1.用箭头函数(箭头函数是不会重新绑定this值的,由于箭头函数内部是不会给this重新赋值的,所以这个this就是外面这个this就是Box)
// 2.用bind .bind(this)它可以将函数里面的this绑定成我们当前的this
handleClickLeft=(step)=>{
// 想让state的值的修改影响到render函数的话,就要用setState函数
// setState函数里边不仅会修改state的值,而且会将当前组件的render函数重新调用一遍
this.setState({
x:this.state.x-step
})
//点击左按钮让x减一
// this.state.x--;
console.log("click left",this);
}
handleClickRight(step){
this.setState({
x:this.state.x+step
})
// this.state.x++;
console.log("click right",this);
}
// 1.3.11 给事件函数添加参数
// 1.定义一个临时函数
// handleClickLeftTmp=()=>{
// return this.handleClickLeft(10);
// }
// 2.不一定要把这个函数定义出来,可以直接用一个箭头函数,或者匿名函数把他定义出来
//定义css样式
// styles = {
// width: "50px",
// height:"50px",
// backgroundColor: "lightblue",
// }
//Component类的一个函数,用来返回当前这个组件最后渲染的html的结构是什么
//react里面的每一个元素都必须是一个标签
render() {
return (
//虚拟元素,渲染的时候不渲染他,他只会帮我们在react里合法而已
<React.Fragment>
{/* <div>{this.state.x}</div> */}
{/* <div style={this.styles}>{this.toString()}</div> */}
{/* 所有在jsx的html标签里面写的逻辑多要用大括号括起来,style标签里面需要写一个对象,对象使用两个大括号括起来的 */}
{/* style外面的括号是指明他是一个表达式,里层的大括号表示里面是一个对象 */}
{/* <div style={{
width: "50px",
height:"50px",
backgroundColor: "lightblue",
color:"white",
textAlign:"center",
lineHeight:"50px",
borderRadius:"5px",
}}>{this.toString()}</div> */}
<div style={this.getStyles()}>{this.toString()}</div>
{/* bootstrap里面面margin的简写方式:m-2 */}
{/* 1.3.9 绑定事件 onClick绑定事件,onClick是绑定一个函数, 并不是让我们调用这个函数,
所以这里传的是一个函数,而不是执行这个函数,所以不能加(),加()是在渲染的时候就把值求出来,
但是其实我们是把这个函数传个他,并不是把函数的返回值传给他*/}
{/* <button onClick={this.handleClickLeftTmp} className='btn btn-primary m-2'>left</button> */}
<button onClick={()=>{
// 匿名函数写法
return this.handleClickLeft(10);
}} className='btn btn-primary m-2'>left</button>
{/* 匿名函数简写方式 */}
<button onClick={()=>this.handleClickRight(10)} className='btn btn-success m-2'>right</button>
{/* 1.3.8 Conditional Rendering,利用逻辑表达式的短路原则
当数组里面没有元素的时候输出一句话 */}
{this.state.colors.length === 0 && <p>No colors</p>}
{/* map函数将数组里面的每一个元素color依次转化为另外一个元素div
如果用遍历类写法,一定要给每一个标签都加一个属性key,并且key一定要不一样才可以*/}
{this.state.colors.map(color=>(
<div key={color}>{color}</div>
))}
</React.Fragment>
);
}
// 1.3.6 数据驱动改变Style
getStyles(){
let styles = {
width: 50,
height:50,
backgroundColor: "lightblue",
color:"white",
textAlign:"center",
lineHeight:"50px",
borderRadius:"5px",
// 让值同时改变小方块的位置
marginLeft: this.state.x,
};
if(this.state.x<=0) styles.backgroundColor='orange';
return styles;
}
toString() {
//解构函数
const {x}=this.state;
return `x:${x}`;
// return `x:${this.state.x}`;
//可以在任何地方定义html,在html里写js的话都要用括号括起来
// return <h1>{x}</h1>
}
}
export default Box;
在git bash会出现
'"node"' is not recognized as an internal or external command,
operable program or batch file.
在powershell里可以打开成功
将本地项目上传到AC Git云端:
在git bash
里复制粘贴(右键paste/shift+insert
)
Git
全局设置:git config --global user.name "yq c"
git config --global user.email "2329949700@qq.com"
注意‼️如果在本地有多个项目,最好把–global去掉(–global是所有项目都用同一个配置,不太好)
git remote add origin git@git.acwing.com:cyq/kof.git
git status
git add .
git status
git commit -m "Initial commit"
git push -u origin master
AC Git
更新方式:git status
git add .
git status
git commit -m "提示信息"
git push
index.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>
<link rel="stylesheet" href="/static/css/index.css">
<script src="https://cdn.acwing.com/static/jquery/js/jquery-3.3.1.min.js"></script>
</head>
<body>
<script type="module">
import {
main
} from "/static/js/index.js";
main();
</script>
<!-- <div xxx="18"><span>span-1</span></div>
<input type="text" name="" id="">
<button id="hide-btn">隐藏</button>
<button id="show-btn">显示</button> -->
<div class="div-1">
<div class="div-2">
<div class="div-3">
</div>
</div>
</div>
</body>
</html>
index.css
div {
width: 300px;
height: 300px;
background-color: lightblue;
}
.my-div {
background-color: orange;
}
#ID {
background-color: orange;
}
index.js
//取出第一个div
// let div = document.querySelector('div');
//用jQuery选取div
// let $div = $('div');
let $btn_hide = $('#hide-btn');
let $btn_show = $('#show-btn');
let main = function () {
// console.log($div)
//同样一个事件可能会绑定很多个函数,只解绑click.name2
/*$('div').on('click.name1', function (e) {
console.log('click div1');
$('div').off('click.name2');
})
$('div').on('click.name2', function (e) {
console.log('click div2');
})*/
/*$btn_hide.click(function () {
// $div.hide(1000);//表示需要1000ms来隐藏掉
$div.fadeOut();//淡出
});
$btn_show.click(function () {
// $div.show(1000 );
$div.fadeIn();//淡入
});*/
//构造一个标签
let $a = $(`<a href="https://www.acwing.com">
AcWing
<span>!!!</span>
</a>`);
//单击添加
// $div.click(function () {
// // $div.append($a);
// /*等价于
// $div.append($(`<a href="https://www.acwing.com">
// AcWing
// <span>!!!</span>
// </a>`));*/
// // $div.addClass('my-div')
// // console.log($div.css('background-color'));
// // $div.css({
// // width: "200px",
// // height: "200px",
// // //如果字符串里包含减号要加引号,因为这个减号不知道是作用到字符串里还是视为减号会有歧义
// // "background-color": "orange"
// // });
// //获取属性
// // console.log($div.attr('xxx'));
// //设置属性,将属性id变为ID,div就会调用css中#ID里的内容
// // $div.attr('id', 'ID');
// //输出文本内容
// console.log($div.text());
// //修改div内容
// // console.log($div.text('hello'));
// //输出标签内容
// console.log($div.html());
// //获取值
// console.log($('input').val());
// $('input').val('hahaha');
// });
//双击删除
// $div.dblclick(function () {
// // $a.remove();
// $div.removeClass('my-div');
// })
// let $div3 = $('.div-3');
// console.log($div3.parent('.div-2'));
// console.log($div3.parents('.div-1'));
let $div1 = $('.div-1');
console.log($div1.find('div'));
//ajax可以在不刷新页面的情况下只从服务器端获取某些数据
}
export {
main
}
indx.css
<!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>
<link rel="stylesheet" href="/static/css/index.css">
<script src="https://cdn.acwing.com/static/jquery/js/jquery-3.3.1.min.js"></script>
</head>
<body>
<script type="module">
import {
main
} from "/static/js/index.js";
main();
</script>
<div></div>
</body>
</html>
index.js
let main = function () {
let $div = $('div');
let func_id;
// $div.click(function () {
// // setTimeout(function () {
// // console.log('hhh');
// // }, 2000);
// if (func_id) return false;
// func_id = setInterval(function () {
// console.log('hhh');
// }, 500);
// });
// $div.dblclick(function () {
// console.log("dblclick");
// clearInterval(func_id);
// });
let last_timestep = 0;
let step = (timestep) => {
console.log(timestep - last_timestep);
last_timestep = timestep;
$('div').width($('div').width() + 4);
if (timestep / 1000 <= 10)
func_id = requestAnimationFrame(step);
};
func_id = requestAnimationFrame(step);
$('div').click(function () {
cancelAnimationFrame(func_id);
});
}
export {
main
}
index.js
let main = function () {
// let map = new Map();
// map.set('name', 'abc');
// map.set('age', 18);
// console.log(map);
// console.log(map.get('name'));
// console.log(map.has('xx'));
// console.log(map.size);
// // map.delete('age');
// // map.clear();
// for (let [key, value] of map) {
// console.log(key, value);
// }
// map.forEach(function (value, key) {
// console.log(key, value);
// })
let set = new Set();
set.add('abc');
set.add(18);
set.add(() => {
console.log('hhh');
});
console.log(set);
set.delete(18);
console.log(set.has(18));
console.log(set.size);
set.clear();
}
export {
main
}
index.js
let main = function () {
/*
查看内容:
右键检查,点击应用程序,点击本地存储左边的三角形,点击链接即可查看
*/
localStorage.setItem('name', 'abc');
localStorage.setItem('age', 18);
console.log(localStorage.getItem('name'));
console.log(localStorage);
// localStorage.clear();
// localStorage.removeItem('name');
}
export {
main
}
index.js
let main = function () {
let obj={
name:'abc',
age:18,
};
let str=JSON.stringify(obj); //string
let new_obj=JSON.parse(str); //object
}
export {
main
}
index.js
let main = function () {
// console.log(Date.now());
// console.log(Date.parse("2022-04-15T15:30:00.000+08:00"))
let starttime = new Date("2023-05-20T15:30:00.000+08:00");
//两个时间段经过的毫秒数
console.log(new Date() - starttime);
let time = new Date();
console.log('星期' + time.getDay());
console.log('日' + time.getDate());
console.log('月' + time.getMonth());
console.log('年' + time.getFullYear());
console.log('小时' + time.getHours());
console.log('分钟' + time.getMinutes());
console.log('秒' + time.getSeconds());
console.log('毫秒' + time.getMilliseconds());
}
export {
main
}
index.js
let main = function () {
// $('div').css('cursor', 'pointer');
$('div').click(function () {
// window.open("https://www.acwing.com");
// location.reload();
location.href = "https://www.acwing.com"
});
}
export {
main
}
index.html
<html>
<head>
<script type="application/javascript">
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
// ctx.fillStyle="rgb(200,0,0)" 表示用什么样的样式去画,例如字体、颜色等
ctx.fillRect(25, 25, 100, 100); //fill打头的函数是填充
ctx.clearRect(45, 45, 60, 60);
ctx.strokeRect(50, 50, 50, 50); //stroke表示画边框
}
}
</script>
</head>
<body onload="draw();">
<canvas id="canvas" width="150" height="150"></canvas>
</body>
</html>
let buf='';
process.stdin.on('readable',function(){
let chunk=process.stdin.read();
if(chunk) buf+=chunk.toString();
});
let n;
let path=[];
let st=[];
function dfs(u){
if(u==n){
// let line='';
// for(let i=0;i<n;i++) line+=path[i]+' ';
// console.log(line);
console.log(path.join(' '));
}
for(let i=0;i<n;i++){
if(!st[i]){
st[i]=true;
path[u]=i+1;
dfs(u+1);
st[i]=false;
}
}
}
process.stdin.on('end',function(){
n=parseInt(buf);
dfs(0);
});