一. Element和Component
1. 什么是Element(元素)
const div1 = React.createElement("div",{props},[child])
这就是一个React元素的创建方法,但是要出现在页面上还要配合ReactDOM.render(div1,rootElement)
使用
2. Component(组件)
const Div =()=>{
return (React.createElement("div",{props},[child]))
}
当把一个元素用一个函数包裹,他就成了一个组件,如何引用
ReactDOM.render(Div(), rootElement);
3.组件类型(2种)
3.1 函数组件
function App(props) {
return (
<div className="App">
{props.name}
<component props/>
</div>
);
}
其实就是讲上面的React.creatElement用JSX语法书写然后封装成函数,这样调用的时候直接用ReactDOM.render(<App />,rootElement)
或者在其他组件中直接用<component props/>
插入,且这个函数接收一个外部参数props,在函数内部可以使用这个参数的各种属性
props="string"|{obj}
3.2 类组件
//ES5
const A =React.createClass({
render(){
return (
<div>JSX</div>
)
}
})
//ES6
class Son extends React.Component {
constructor(props) {
super(props)
//必写
this.state{n:1}
//初始化实例的state属性
}
render(){
return (<div>{this.props.name}</div>)
//相当于以React.Component为原型生成一个实例,this指向此对象
}
}
调用方法和上面一致,<Son props/>
<Son/>
被babel-loader转译的时候会被理解成React.createElement(Son)
,所以我们以标签的形式写一个组件都会被转换成React元素最后渲染进页面,普通的HTML如<div props>...child...</div>
标签相当于React.createElement("div",props,child)
而当调用的函数是一个class时,则会自动new Son
生成一个实例并调用它的render()方法,对编译过程感兴趣可以去babel-online实践一下
4.props(外部数据)和state(内部数据)的访问 示例
4.1 类组件
props:
用this.props.name获取到对象属性,自动把参数传入render()中
state:
用this.state.name获取对象属性
用this.setState({key:newValue})写入数据
//setState会生成一个新的对象和一个对应的UI
4.2 函数组件
props:
props.name 需手动在函数中定义一个形参以接收props
state:
const [n,setN]=React.useState(initValue)
//将初始值传入数组的第一项,将改变的值传入第二项
<button onClick={() => setN(n + 1)}>+1</button>
//用setN来写入,setN并不会改变n而是产生一个新的n
//这个数组第一项第二项名字任意只是一个接口名
setN和setState都是异步的!!!
所以建议在调用的时候传入一个函数防止产生错误
add() {
this.setState(state => {
//其实默认传入state和props两个参数
const n = this.state.n + 1;
//新的n
console.log(n);
return { n: n };
//返回一个新对象
});
}
4.3 还是关于setState的补充说明示例
class Son extends React.Component {
constructor() {
super();
this.state = { n: 0,m:1 };
}
add() {
this.setState({ n: this.state.n + 1 });
}
}
当数据中有多个属性的时候,我们在调用setState的时候只改变部分属性,那么其余的部分会被覆盖成undefined么,答案是不会因为React帮我们把没变的部分沿用了相当于
add() {
this.setState(...this.state,{ n: this.state.n + 1 });
//...this.state的意思就是讲所有属性拷贝过来,后定义的同名属性覆盖
}
但是,这是一种浅拷贝,也就是说只沿用第一层属性,若{m:{x:0}}
这种结构则只复制一层m属性,所以如果有多层属性建议用上面说的展开语法"...obj"
但是函数组件中的setN则完全不会沿用之前的属性,写入即覆盖,所以函数组件中如果有多个属性则建议使用展开语法复制之前的属性然后同名覆盖
Vue和React在数据更新上的区别
Vue
Vue会将数据对象封装并监听,当数据发生变化时,将数据对应的UI部分相应的进行更新,实际实现中并不会全部替换而是使用虚拟DOM只更新改变的部分
React
React则遵循一种函数式的编程理念,他不允许对原始数据进行修改,也不会监听原始的数据变化,当数据发生改变时,直接生成一个新的数据对象和一个新的UI,然后用Diff对于虚拟DOM中不同的部分,再对页面上的UI进行局部渲染
5. React的事件绑定
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
}
addN = () => {
this.setState({ n: this.state.n + 1 });
//注意这里确实是写在construction外面但是这是下面写法的变形,所以
//本质上还是属于construction中的属性
}
}
<button onClick={this.addN}>n+1</button>
这种写法是由下面这种写法演化而来的,两种写法完全等价,用箭头函数是为了防止this指向的对象改变
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
this.addN = () => {
this.setState({ n: this.state.n + 1 });
}
}
}
<button onClick={this.addN}>n+1</button>
这种写法跟下面这种的本质区别在于,construction中的属性是定义到实例身上的,而下面这种,addN函数则是定义在Son.prototype上的也就是所有Son的实例的原型上的,是可以被所有实例调用的属性
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
}
addN(){
this.setState({ n: this.state.n + 1 });
}
}
<button onClick={()=>{this.addN()}>n+1</button>