刚开始不会直接使用组件化进行开发,因此需要引入在线的几个js库
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
基础知识
虚拟DOM
由于React
使用jsx
的语法,因此我们编写的代码需要通过babel
的翻译成js
,因此type
要写成text/babel
<script type="text/babel"></script>
比如,我们可以这样创建一个虚拟的DOM
const VDOM = <h1>Hello,React</h1>
ReactDOM.render(VDOM, document.getElementById("app"))
当我们的虚拟DOM有多层结构的时候,需要统一由一个父标签所包裹,如下所示:
const VDOM = (<div>
<div>Hello</div>
<div>World</div>
</div>)
ReactDOM.render(VDOM,document.querySelector('.test'))
JSX语法
- 定义虚拟DOM的时候,不能使用
""
进行包裹(创建的是虚拟DOM而不是字符串) - 标签中如果需要混入
js
表达式,那么需要使用{}
包裹 - 样式的类名指定不能使用
class
,而要用className
- 元素的style样式要使用
{{}}
包裹 - 对于一个虚拟DOM来说,有且仅有一个根标签
- 标签必须闭合,自闭合也可以
- 如果标签是小写字母开头,那么就转化为原生
html
中的同名元素,如果找不到,则报错;如果是大写字母开头,则去找对应的组件,找不到就报错JSX小练习
根据动态数据渲染列表(对数据使用map函数)const data = ['A','B','C']
const VDOM = (
<div>
<ul>
{
data.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
ReactDOM.render(VDOM,document.querySelector('.test'))
组件化
同Vue
一样,React
中也是可以将某个部分拆分成某个独立的组件,进行组件化开发函数式组件
定义一个函数,并且接收一个可选的**props**
参数不是所有的组件都会从外界接受参数,因此
props
参数是可选的
function MyList(props) {
return <div>name: {props.name}</div>
}
ReactDOM.Render(<Welcom name="yxr" />,document.getElementById("app"));
类式组件
如果使用类式组件,那么对
JavaScript
的运用本领提出了一定的要求,主要是this
的指向问题
最后的优化结果如下
class MyList extends React.Component {
state = {
isHot: false
}
render() {
const {isHot} = this.state;
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉快'}</h1>
}
changeWeather = () => {
const {isHot} = this.state;
this.setState({
isHot: !isHot
})
}
}
在学习的过程中,发现这一块涉及到
js
面向对象的部分还是蛮多的,并且有些复杂
- 第一版本:对
state
的使用出错
打开浏览器,发现控制台报错,不能直接对state
中的值进行修改
而是要用React
给我们提供的方法进行修改:
- 第二版本:方法绑定出错
这一部分,由于涉及到this
指向的问题,稍微有些难度
现在假设我们有如下所示的代码
class Person {
constructor(name) {
this.name = name;
}
say() {
// console.log("Hello,World");
console.log(this);
}
}
let p = new Person('Tom');
let fun = p.say;
p.say();
fun();
我们将实例P上的say()
方法赋值给了fun
变量,并进行调用,在js
的严格模式下,输出如下结果:
也就是说,只有当我们通过某个类的实例调用类中的方法时,this
的指向才指向实例本身
如果通过上述这种方式进行调用,则this
的指向为undefined
同理,我们现在将代码修改成如下图片所示
相当于将
click
方法赋值给了某个变量,并交由onClick
调用
如果看明白上面的描述,那么你应该知道,这种写法会出错,并报undefined
异常
原因是我们的click
方法中,调用了this
如何处理这种this指向的问题?有如下两种方式
- 绑定this
在我们的构造函数中,调用bind
方法,绑定this的指向
但假如我们有很多的方法,那么就需要很多个绑定处理,不够优雅
- 使用箭头函数
与普通函数不同的是,箭头函数本身不具有this
,因此他会找到外面一级,并将this
指向它
我们修改上述示例js
中的代码,如下所示:
由于箭头函数的特性,我们不难分析到,两次函数调用输出的结果应该是一样的,都是指向实例本身:
因此,我们可以将上述React
代码改写:
- 第三版本:没有分清楚赋值和调用
如下所示,上面的是将函数的返回值绑定给onClick
,而下面是将函数本身绑定给onClick
但假如我们想使用调用
的那种方式,就需要使用高阶函数,如下所示:
受控组件和非受控组件
在
React
中,数据是单向的,而不是像vue
中的双向数据绑定 因此组件也分成了:受控组件和非受控组件两种
- 受控组件:表单组件的输入组件随着输入的变化,将内容存储到状态中
- 非受控组件:表单组件的输入组件的内容在有需求的时候,才存储到状态中
受控组件
监听输入类组件的数据变化,并不断存储到状态中
非受控组件
只有在需要的时候,才写入到状态中
在React
中,更推荐使用受控组件组件实例的三大属性
state
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。 React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
对写法进行简化,我们不再在构造函数中初始化状态,而是直接作为属性写入
并且,在React中,并不能直接修改state
中的值来重新渲染页面中的内容,而是要用给我们提供好的setState方法
this.setState(partialState, [callback]);
有如下两种写法:
写法1:
this.setState({
weather: "凉爽"
})
写法2:
this.setState((state) => {
return {
count: state.count + 1,
};
});
setState
操作是合并操作,而不是替换操作render()
的执行次数是1+n(每一次的setState
都会自动调用一次render
)props
与state
不同的是,state
是组件自身自带的状态,而props
是外界传递给组件的值- 通过在组件标签上传递值,在组件中就可以获取到所传递的值
- 在构造器里的props参数里可以获取到 props
- 可以分别设置 propTypes 和 defaultProps 两个属性来分别操作 props的规范和默认值,两者都是直接添加在类式组件的原型对象上的(所以需要添加 static)
- 同时可以通过…运算符来简化
refs
同vue
中的refs
一样,React
提供了一种方式,允许我们访问DOM
节点或者在render
方法中创建的React
元素
不用再使用
document.getElementById()
这样的方法来获取的DOM节点了
一共有三种创建refs
的方法,分别是:
- 字符串形式
- 回调形式
createRef
形式
字符串形式
虽然目前这种方法因为性能问题已经被废弃,但是临时使用一下还是很方便的
回调形式
createRef形式