目录
起步
hello, react
引入 react 核心库:
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
创建容器:
<body>
<div id="example"></div>
</body>
创建虚拟DOM并渲染到页面:
<script type="text/babel"> //此处一定要用 babel
// 1. 创建虚拟 DOM
const VDOM = <h1>hello, react!</h1>
// 2. 渲染虚拟 DOM 到页面
ReactDOM.render(VDOM, document.getElementById('example'))
</script>
jsx 语法规则
1. 创建虚拟 dom 时不能加引号,且根节点只有一个;
2. 标签中有 js表达式/变量 时用 {};
3. 标签:行内样式:style={ { key:value, key:value } }
class命名:className
4. 标签必须闭合;
5. 标签首字母:
(1). 小写字母开头:将该标签转为html中同名元素,若html中无该标签对应的同名元素则报错
(2). 大写字母开头:react 渲染对应的组件,若组件未定义则报错
<script type="text/babel">
const title = '标题'
const arr = ['a', 'b', 'c']
const VDOM = (
<div>
<h1 style={ {color:'red'} }>hello, react</h1>
<p className="title">{ title }</p>
<input type="text"/>
<ul>
{
arr.map((item, index) => {
return <li key={index}>{ item }</li>
})
}
</ul>
</div>
)
ReactDOM.render(VDOM, document.getElementById('example'))
</script>
React 组件类型
函数式组件(适用于简单组件)
执行了 ReactDOM.render(<MyComponent />, ...)之后,发生了什么?
1. React 解析组件标签,找到了 MyComponent 组件;
2. 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转化为真实DOM,展示在页面中。
<script type="text/babel">
// 创建 MyComponent 组件
function MyComponent () {
console.log(this) // undefined, 因为babel编译后开启了严格模式
return (
<div>
<h2>I am FunctionComponent</h2>
<p>I like vue</p>
</div>
)
}
ReactDOM.render(<MyComponent />, document.getElementById('example'))
</script>
类式组件(适用于复杂组件)
执行了 ReactDOM.render(<MyComponent />, ...)之后,发生了什么?
1. React 解析组件标签,找到了 MyComponent 组件;
2. 发现组件是使用类定义的,随后 new 出来该类的实例,并通过该实例调用到原型链上的 render 方法;
3. 将 render 返回的虚拟DOM转为真实DOM,展示在页面中。
<script type="text/babel">
class MyComponent extends React.Component {
render () {
console.log(this) // MyComponent 实例对象
return (
<div>
<h2>I am ClassComponent</h2>
<p>welcome</p>
</div>
)
}
}
ReactDOM.render(<MyComponent />, document.getElementById('example'))
</script>
state
<script type="text/babel">
class MyComponent extends React.Component {
constructor (props) {
super(props)
this.state = { // 初始化状态
isHappy: true
}
}
render () {
console.log(this) // new出实例后自动调用render,所以this为:MyComponent实例对象
const { isHappy } = this.state
return <h2 onClick={this.changeMood}>我今天很{ isHappy ? '开心' : '不开心' }</h2>
}
changeMood () {
console.log(this) // onClick 事件的回调,并非通过new实例调用,所以this为:undefined
}
}
ReactDOM.render(<MyComponent />, document.getElementById('example'))
</script>
为了让 changeMood 方法能够指向 this,我们在 constructor 构造器中,对方法进行绑定:
在方法内用 setState({}) 改变初始化state:
<script type="text/babel">
class MyComponent extends React.Component {
constructor (props) { // 调用 1 次
super(props)
this.state = {
isHappy: true
}
this.changeMood = this.changeMood.bind(this) // 将原型上的方法绑定到实例对象,解决方法中this的指向问题
}
render () { // 调用 1+n 次
const { isHappy } = this.state
return <h2 onClick={this.changeMood}>我今天很{ isHappy ? '开心' : '不开心' }</h2>
}
changeMood () { // 调用 n 次
const { isHappy } = this.state
this.setState({
isHappy: !isHappy
})
// 注意:state不可以直接修改!!state不可以直接修改!!state不可以直接修改!!
//this.state.isHappy = !isHappy // 这个是错误写法!!!
}
}
ReactDOM.render(<MyComponent />, document.getElementById('example'))
</script>
那么问题来了,如果每次想在方法中绑定 this 都需要在constructor中bind(this)的话,太过于繁琐,所以采用如下写法:
<script type="text/babel">
class MyComponent extends React.Component {
state = {
isHappy: true
}
render () {
const { isHappy } = this.state
return <h2 onClick={this.changeMood}>我今天很{ isHappy ? '开心' : '不开心' }~</h2>
}
changeMood = () => {
const { isHappy } = this.state
this.setState({
isHappy: !isHappy
})
}
}
ReactDOM.render(<MyComponent />, document.getElementById('example'))
</script>
1. 在 React 中,构造函数仅用于以下两种情况:
(1). 通过给 this.state 赋值对象来初始化内部 state;
(2). 为事件处理函数绑定实例。【注意】!!在 constructor() 函数中不要调用 setState() 方法,如果你的组件需要使用内部的state, 请直接在构造器中为 this.state 赋值初始 state
constructor (props) {
super(props)
this.state = { counter: 0 } // 不要在这里使用 this.setState()
this.handleClick = this.handleClick.bind(this)
}
props
语法:<MyComponent {...propsObject} />
<script type="text/babel">
class MyComponent extends React.Component {
render () {
const { name, age, sex } = this.props // 取到组件中传入的 props 值
return (
<div>
<ul>
<li>姓名:{ name }</li>
<li>年龄:{ age + 1 }</li>
<li>性别:{ sex }</li>
</ul>
</div>
)
}
}
// age为number类型,用{}
// ReactDOM.render(<MyComponent name="laowang" age={18} sex="male" />, document.getElementById('example'))
const info = { name: "Lucy", age: 18, sex: "female" }
ReactDOM.render(<MyComponent {...info} />, document.getElementById("example"))
</script>
propTypes (类型检测)
引入 CDN
<script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.5.6/prop-types.js"></script>
在类中写入static静态规则:
<script type="text/babel">
class MyComponent extends React.Component {
static propTypes = { // 类型
name: PropTypes.string.isRequired,
age: PropTypes.number,
sex: PropTypes.string,
speak: PropTypes.func // 函数
}
static defaultProps = { // 默认值
sex: 'female'
}
render () {
...
}
}
const info = { name: "Marry", age: '18' } // 未传入sex,用 defaultProps 中sex默认值
ReactDOM.render(<MyComponent {...info} />, document.getElementById("example"))
</script>
由于传入的age类型不正确,控制台报错:
===============================================================
es6 类知识点回顾
1. p1.sleep() 是通过 People 实例调用的方法,所以this指向People实例;
p2() 相当于直接用一个函数体执行,相当于指向 window,而类中的方法默认开启了局部的严格模式,所以 this 打印为 undefined。
class People {
constructor (name, age) {
this.name = name
this.age = age
}
sleep () {
console.log(this)
}
}
var p1 = new People('张三', 18)
p1.sleep() // People 实例
var p2 = p1.sleep
p2() // undefined
p2()this打印为undefined, 因为sleep方法在原型上,不在new实例对象上;p1.sleep() 找的是原型上的方法,this打印情况如下:
<script>
class People {
constructor (name, age) {
this.name = name
this.age = age
this.sleep = this.sleep.bind(this)
}
sleep () {
console.log(this)
}
}
var p1 = new People('张三', 18)
p1.sleep()
var p2 = p1.sleep
p2()
</script>
p1.sleep() 和 p2() this打印情况如下: