1.DOM树
DOM树
是用来处理HTML和XML文件的,将文档作为一个树形结构,树的每个结点表示了一个HTML标签或标签内的文本项,DOM模型不仅描述了文档的结构,还定义了结点对象的行为,利用对象的方法和属性,可以方便地访问、修改、添加和删除DOM树的结点和内容。
<div>我是div元素</div>
<p id="p">我是p元素</p>
//通过id或者标签名对p标签进行访问
var div = document.getElementsByTagName('div')
var p = document.getElementById('p')
//往dom树里添加一个a标签
var a = document.createElement('a')
document.body.append(a)
//通过标签名删除dom树中的某一个结点
document.body.removeChild(div)
//修改标签里的内容
div.innerHTML = '我修改了div的文本内容'
2.React的作用
- React为了高效的维护页面,将前端页面的dom树在内存里复制了一份一模一样的,当发现内存里的dom树真正发生变化的时候才会修改前端页面的dom树。
- 在用React框架时,不会直接手写js,而是写
jsx
,写完后会将jsx重新编译成js。jsx是HTML和JS组合的文件
3.node.js
- 在没有node之前,js是运行在浏览器中,有了node后,js可以运行在不用的操作系统上,可以操作文件,读取数据库,可以当做一个后端语言来运行。
- 安装node后会附带安装一个npm命令,npm是一个node包的管理工具,用npm下载更新jQuery、vue、react对应的包,比如下载jquery:
nmp i jQuery
- package.json文件是指我们项目所依赖的所有的依赖包的信息都存储在里面,我们在存储代码时只需要存储package.json文件,这样就相当于存储了项目的所有文件,如果想要把项目从git上下下来,我们只需
npm i package.json
就可以将整个项目下下来
4.安装create-react-app命令
create i -g create-react-app
其中-g是将这个命令全局安装,全局安装是指安装在操作系统上,意思是在操作系统的任何一个目录下都可以使用这一命令。
5.创建react项目
用刚才装的命令create-react-app
来创建一个项目: creat-react-app react-app
在本地启动服务:在react-app目录下git-bash,输入npm start即可,会在本地开一个3000端口。
node_module
: 未来安装的所有依赖会在node_modules里。
index.html
的body内只有一个div标签,其id是root,react在渲染的时候,在index.js里把root这个标签获取出来(dom树),然后在标签里通过render()函数去渲染。
6.babel
babel
是将jsx代码编译成js代码
将标签转换成函数
7. 组件
类似于class,它是将HTML标签、数据、事件函数写在一块,就称为组件
1. 下载bootstrap库,并且引入到文件
npm i bootstrap
import 'bootstrap/dist/css/bootstrap.css'
习惯在src下创建一个目录,用来存所有的组件,比如box.jsx组件里面是一个类继承component,component类是从react引入进来的
凡是在jsx中的HTML标签里写js,我们都要用{}括起来
state
是一个局部变量
render
函数是component类的函数,用来返回当前的组件最后渲染的HTML的结构
2. 数据驱动改变styles的属性
其中传递参数时用箭头函数,给了两种写法,一种是简写,一种没有简写,简写是直接把this.xx
换成{},如果return只有一句话,可以删掉{}、return
import React, { Component } from 'react';
class Box extends Component { //定义类组件Box
state = {
x: 0,
colors: ['red', 'green', 'blue']
};
hadleClickLeft = (step) => {
this.setState({
x: this.state.x - step
})
}
hadleClickRight = (step) => {
this.setState({
x: this.state.x + step
})
}
hadeleClickRightTemp = () =>{
return this.hadleClickRight(10);
}
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.state.x}</div>
<button onClick={() => this.hadleClickLeft(10)} className="btn btn-primary m-2"> left </button>
<button onClick={this.hadeleClickRightTemp} className="btn btn-success m-2">right</button>
{this.state.colors.map(
color => (
<div key={color}>{color}</div> //如果是遍历的写法的话,那么需要给每一个标签一个唯一的key,否则会warning
)
)}
</React.Fragment>
);
}
getStyles() {//函数里可以随便写if else for,但是在一个标签中间的{}内不能写if else for,可以调用函数
let styles = {
width: "50px",
height: "50px",
backgroundColor: "lightblue",
marginLeft: this.state.x,
}
if (this.state.x === 0)
styles.backgroundColor = 'orange'
return styles;
}
}
export default Box;
8. 用react框架写一个题解页面
其中的删除操作用到了filter函数
,参考见:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
import React, { Component } from 'react';
class Solution extends Component {
state = {
solution:[
{number:1, title:"进击", views: 100},
{number:2, title:"进击", views: 100},
{number:3, title:"进击", views: 100},
{number:4, title:"进击", views: 100},
{number:5, title:"进击", views: 100},
{number:7, title:"进击", views: 100},
{number:6, title:"进击", views: 100},
{number:8, title:"进击", views: 100},
]
}
handleDelete = (s) => {
const result = this.state.solution.filter(s1 => s1 !== s);
this.setState({
solution: result
})
}
render() {
return (
<table className="table">
<thead>
<tr>
<th>#</th>
<th>标题</th>
<th>阅读</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{this.state.solution.map(solution =>(
<tr key={solution.number}>
<td>{solution.number}</td>
<td>{solution.title}</td>
<td>{solution.views}</td>
<td><button onClick={() => this.handleDelete(solution)} className='btn btn-danger'>删除</button></td>
</tr>
))
}
</tbody>
</table>
);
}
}
export default Solution;
第二节课 组合Components
1. 父元素给子元素传递信息,Boxes给Box来传信息
通过属性props
来传数据,可以将想要传的数据放在Box标签</Box x={box.x}>
里,props
里存的是除了key
以外的所有的数据
也可以在 <Box> xxx </Box>
之间写一些想要传的数据,通过this.props.children[id]
来取
boxes:
import React, { Component } from 'react';
import Box from './box'
class Boxes extends Component {
state = {
boxes: [
{id:1, x:0},
{id:2, x:1},
{id:3, x:0}
]
}
render() {
return (
<React.Fragment>
{this.state.boxes.map(box => (
<Box
x={box.x}
key={box.id}>
<h1>Box</h1>
<p>{box.id}</p>
</Box>
))}
</React.Fragment>
);
}
}
export default Boxes;
box:
import React, { Component } from 'react';
class Box extends Component { //定义类组件Box
state = {
x: this.props.x,//通过boxes里传过来的参数来初始化
};
hadleClickLeft = (step) => {
this.setState({
x: this.state.x - step
})
}
hadleClickRight = (step) => {
this.setState({
x: this.state.x + step
})
}
hadeleClickRightTemp = () =>{
return this.hadleClickRight(10);
}
render() {
console.log(this.props);
return (
<React.Fragment>
{this.props.children[0]}
{this.props.children[1]}
<div style={this.getStyles()}>{this.state.x}</div>
<button onClick={() => this.hadleClickLeft(10)} className="btn btn-primary m-2"> left </button>
<button onClick={this.hadeleClickRightTemp} className="btn btn-success m-2">right</button>
</React.Fragment>
);
}
getStyles() {
let styles = {
width: "50px",
height: "50px",
backgroundColor: "lightblue",
marginLeft: this.state.x,
}
if (this.state.x === 0)
styles.backgroundColor = 'orange'
return styles;
}
}
export default Box;
2. 子元素给父元素传递数据
我们希望点击box.jsx里的Delete按钮可以调用boxes.jsx里的删除函数
boxes里的render和handledelete函数
handleDelete = (boxId) =>{
const b = this.state.boxes.filter(
b => b.id !== boxId
);
this.setState({boxes:b});//父组件的state只能在父组件里的函数里修改,不能在其他组件里修改
}
render() {
return (
<React.Fragment>
{this.state.boxes.map(box => (
<Box
x={box.x}
key={box.id}
id={box.id}
onDelete={this.handleDelete}/>
))}
</React.Fragment>
);
}
box里的render函数
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.state.x}</div>
<button onClick={() => this.hadleClickLeft(10)} className="btn btn-primary m-2"> left </button>
<button onClick={this.hadeleClickRightTemp} className="btn btn-success m-2">right</button>
<button onClick={
() => this.props.onDelete(this.props.id)}
className='btn btn-danger -m2'
>Delete</button>
</React.Fragment>
);
}
- 在boxes里修改了state,但是在前端页面里没有显示。这是因为前端页面里显示的state是box里的state,而我们修改的是boxes里的state。但是box里的state每次都用boxes里传的props里赋值了呀。box里的state只会在初始化时执行一次,所以不管后面怎么修改props里x的值,box里的x都不会发生变化。
boxes代码
import React, { Component } from 'react';
import Box from './box'
class Boxes extends Component {
state = {
boxes: [
{id:1, x:0},
{id:2, x:1},
{id:3, x:0}
]
}
handleReset = () => {
const boxes = this.state.boxes.map(b => {
return{
id: b.id,
x: 0,
}
});
this.setState({boxes});
console.log(this.state);
}
handleDelete = (boxId) =>{
const b = this.state.boxes.filter(
b => b.id !== boxId
);
this.setState({boxes:b});//父组件的state只能在父组件里的函数里修改,不能在其他组件里修改
}
render() {
return (
<React.Fragment>
<button
onClick={this.handleReset}
className='btn-btn info'>Reset</button>
{this.state.boxes.map(box => (
<Box
x={box.x}
key={box.id}
id={box.id}
onDelete={this.handleDelete}/>
))}
</React.Fragment>
);
}
}
export default Boxes;
box代码
import React, { Component } from 'react';
class Box extends Component { //定义类组件Box
state = {
x: this.props.x,//通过boxes里传过来的参数来初始化
};
hadleClickLeft = (step) => {
this.setState({
x: this.state.x - step
})
}
hadleClickRight = (step) => {
this.setState({
x: this.state.x + step
})
}
hadeleClickRightTemp = () =>{
return this.hadleClickRight(10);
}
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.state.x}</div>
<button onClick={() => this.hadleClickLeft(10)} className="btn btn-primary m-2"> left </button>
<button onClick={this.hadeleClickRightTemp} className="btn btn-success m-2">right</button>
<button onClick={
() => this.props.onDelete(this.props.id)}
className='btn btn-danger -m2'
>Delete</button>
</React.Fragment>
);
}
getStyles() {
let styles = {
width: "50px",
height: "50px",
backgroundColor: "lightblue",
marginLeft: this.state.x,
}
if (this.state.x === 0)
styles.backgroundColor = 'orange'
return styles;
}
}
export default Box;
- 所以我们直接在box里不存state了,需要用到state就直接用boxes里的state:将boxes里的state通过参数传递。相应的box里的需要用到state的函数,比如handleclickleft、right都拿到boxes里来存,然后在box里通过参数来调用这两个函数。
boxes代码
import React, { Component } from 'react';
import Box from './box';
class Boxes extends Component {
state = {
boxes:[
{id:1, x:1},
{id:2, x:3}
]
}
handleReset = () => {
const b = this.state.boxes.map(bx => {
return{
id: bx.id,
x: 0,
}});
this.setState({boxes:b});
console.log(this.state.boxes);
};
hadleClickLeft = (box) => {
const boxes = [...this.state.boxes];
const k = boxes.indexOf(box);
boxes[k].x --;
this.setState({boxes});
}
hadleClickRight = (box) => {
const boxes = [...this.state.boxes];
const k = boxes.indexOf(box);
boxes[k].x ++;
this.setState({boxes});
}
handleDelete = (boxId) =>{
const b = this.state.boxes.filter(
bx => bx.id !== boxId
);
this.setState({boxes:b});
}
render() {
return (
<React.Fragment>
<button
onClick={this.handleReset}
style={{marginBottom : "15px"}}
className='btn btn-info'
>Reset</button>
{this.state.boxes.map(box => (
<Box
key={box.id}
box={box}
onClickLeft={() => this.hadleClickLeft(box)}
onClickRight={() => this.hadleClickRight(box)}
onDelete={this.handleDelete}/>
))}
</React.Fragment>
);
}
}
export default Boxes;
box代码
import React, { Component } from 'react';
class Box extends Component { //定义类组件Box
hadeleClickRightTemp = () =>{
return this.hadleClickRight(10);
}
render() {
return (
<React.Fragment>
<div style={this.getStyles()}>{this.props.box.x}</div>
<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>
<button onClick={
() => this.props.onDelete(this.props.id)}
className='btn btn-danger -m2'
>Delete</button>
</React.Fragment>
);
}
getStyles() {
let styles = {
width: "50px",
height: "50px",
backgroundColor: "lightblue",
marginLeft: this.props.box.x,
}
if (this.props.box.x === 0)
styles.backgroundColor = 'orange'
return styles;
}
}
export default Box;
实现兄弟组件之间传数据
创建一个App组件,里面存boxes和navbar,要实现将boxes里的信息传到navbar中(boxe里有几个box传到navbar中),我们需要把boxes里的一些函数放到App这个组件里,然后通过App这个组件来给navbar传信息
App.jsx
import React, { Component } from 'react';
import NavBar from './navbar';
import Boxes from './boxes';
class App extends Component {
state = {
boxes:[
{id:1, x:1},
{id:3, x:1},
{id:2, x:3}
]
}
handleReset = () => {
const b = this.state.boxes.map(bx => {
return{
id: bx.id,
x: 0,
}});
this.setState({boxes:b});
console.log(this.state.boxes);
};
hadleClickLeft = (box) => {
const boxes = [...this.state.boxes];
const k = boxes.indexOf(box);
boxes[k].x --;
this.setState({boxes});
}
hadleClickRight = (box) => {
const boxes = [...this.state.boxes];
const k = boxes.indexOf(box);
boxes[k].x ++;
this.setState({boxes});
}
handleDelete = (boxId) =>{
const b = this.state.boxes.filter(
bx => bx.id !== boxId
);
this.setState({boxes:b});
}
render() {
return (
<React.Fragment>
<NavBar boxesCount={this.state.boxes.filter(b => b.x !== 0).length}/>
<div className='container'>
<Boxes
boxes={this.state.boxes}
onReset={this.handleReset}
onClickLeft={this.hadleClickLeft}
onClickRight={this.hadleClickRight}
onDelete={this.handleDelete}
/>
</div>
</React.Fragment>
);
}
}
export default App;
navbar.jsx
import React, { Component } from 'react';
class NavBar extends Component {
state = { }
render() {
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;
boxes.jsx
import React, { Component } from 'react';
import Box from './box';
class Boxes extends Component {
render() {
return (
<React.Fragment>
<button
onClick={this.props.onReset}
style={{marginBottom : "15px"}}
className='btn btn-info'
>Reset</button>
{this.props.boxes.map(box => (
<Box
key={box.id}
box={box}
onClickLeft={() => this.props.onClickLeft(box)}
onClickRight={() => this.props.onClickRight(box)}
onDelete={this.props.onDelete}/>
))}
</React.Fragment>
);
}
}
export default Boxes;