在workspace目录下打开cmd窗口,依次输入:
create-react-app react-demo (react-demo项目名)
cd react-demo
npm install
npm start //运行
运行成功,弹出react-demo的欢迎页面.
进入 react-demo/src 目录,以下是注释:
index.js
负责渲染 react-demo/public/index.html (即欢迎页面)
import React from 'react';//核心库
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';//导入根组件(App)
import * as serviceWorker from './serviceWorker';//负责快速渲染
//向index.html部署根组件
//html中有一个id为'root'的div标签
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
App.js
根组件;通过向其添加子组件完成UI设计
import React from 'react';
import './App.css';
function App() {
return (
<div className="App">
<div>
<h2>你好,根组件</h2>
</div>
</div>
);
}
export default App;
App.js:(更改后)
import Home from './Home' /*---引入子组件---*/
function App() {
return (
<div className="App">
<Home /> /*---添加子组件---*/
</div>
);
}
export default App;
Home.js
子组件;
格式如下:
整体上是ES6的class模板
前半段为js语法;后半段(render)为JSX语法(JavaScript+html);
通过 render()方法展示视图的层次结构;
在JSX中,用{}把JavaScript 表达式括起来,{}之外写html代码
import React from 'react';
import logo from './nothing.jpg';//导入图片
class Home extends React.Component{//ES6终于引入了class概念
constructor(props){
super(props);
this.state={
input:'张三',
name:'李四'
}
}
print(){//自定义方法
alert(this.state.input);
}
setName(newName){//自定义方法
this.setState({name:newName});
}
render(){//模板!
return(//JSX语法
<div>
<p />{this.name}<br />
<img src={logo} alt="logo"/><br/>
<img src="https://www.baidu.com/img/dong_18baaa7524814ab080a65d15e2da33fd.gif" alt=""/>
<hr/>
<br />
<button onClick={this.print.bind(this)}>执行print方法</button>
<br />
<button onClick={this.setName.bind(this,'新的名字!')}>执行setName方法</button>//鼠标点击事件
</div>
)
}
}
export default Home;//方能被APP.js识别
注意:
- render中先整个div,把所有内容写到里面;
- 修改state必须交给setState()来完成;
- 在以类继承的方式定义的组件中,通常需要将事件处理函数中的this指向当前组件实例。
如何让this指向当前组件实例?
方法1:在标签中使用bind()函数
run(){
alert(this.state.name);
}
...
<button onClick={this.run}>按钮</button>//错误写法
<button onClick={this.run.bind(this)}>按钮</button>
解释:button组件中的this指向当前这个组件实例;
但当外部环境运行到这里,调用run函数时,就会丢失这个this指向(组件实例);
函数中的this会被解释为指向这个外部环境,虽然其本意是指向组件实例;
所以需要bind函数;
它会以创建它时传入bind方法的第一个参数作为函数的this
方法2:箭头函数
run=()=>{
alert(this.state.name);
}
...
<button onClick={this.run}>按钮</button>
解释:箭头函数内部的this是词法作用域,由上下文确定;
即箭头函数内部的this总是指向词法作用域,即外层调用者
1问:点击一个按钮,然后如何获取这个按钮的某些属性(比如id)?
答:
run(event){
var id = event.target.getAttribute('id');// biu
alert(id);
}
...
render(){
return(
<button id = 'biu' onClick={this.run.bind(this)}>运行run方法</button>
)
}
2问:如何将输入框的文本信息传到后台?
答:
方法1:通过事件获取输入信息
inputChange(event){
var val = event.target.value;
this.setState({userName:val})
}
...
<input type="text" onChange={this.inputChange.bind(this)} />
方法2:获取dom节点——通过设置属性’ref’获取输入信息
inputChange(){
var val = this.refs.userName.value;
this.setState({userName:val})
}
...
<input type="text" ref='userName' onChange={this.inputChange.bind(this)} />
3问:如何让可以监听键盘敲击事件?且键入回车就传值?
答:
inputKeyDown(e){
if(e.keyCode === 13){//如果敲击了回车键
var val = e.target.value;
this.setState({userName:val});
e.target.value='';
}
}
...
<input type="text" onKeyDown={this.inputKeyDown.bind(this)} />//onKeyUp也行
注意:event事件和view是双向绑定的;
所以可以通过event事件反过来影响视图(上面重置了输入框的value)
4问:如何实现双向数据绑定?(view <==> model)
答:
value
属性绑定数据;onChange()
方法(必须实现);设置defaultValue属性并不能动态刷新view
change(e){
var val = e.target.value;
this.setState({userName:val});
}
...
<input type="text" value={this.state.userName} onChange={this.change.bind(this)}/>
changeSex(e) {
var val = e.target.value;
this.setState({
sex: val
});
}
...
<input type="radio" value='男' checked={this.state.sex=='男'} onChange={this.changeSex.bind(this)}/>男
<input type="radio" value='女' checked={this.state.sex=='女'} onChange={this.changeSex.bind(this)}/>女
注意:checked属性为true,则显示选中;为false,显示不选中
select
<select>
/*JSX*/
this.state = {
city: 'defaultCity',
citys: ['成都', '北京', '深圳']
}
...
handleChange(e) {
var val = e.target.value;
this.setState({
city: val
})
}
...
<select value={this.state.city} onChange={this.handleChange.bind(this)}>
{this.state.citys.map(function(value,key){return <option key={key}>{value}</option>; })}
</select>
<button type='submit'>确认</button>
<br/>
{this.state.city}
<h2>new</h2>
<select>
Array.map(function(index1,index2)):
结果:返回一个新的数组;
计算方法:新数组的每个元素由原来的每个元素调用function得到;
按顺序,第一个index获取元素的值,第二个index获取元素的下标
checkbox
/*JSX*/
this.state = {
hobby: [{
'title': '睡觉',
'checked': true
}, {
'title': '吃饭',
'checked': false
}, {
'title': '打豆豆',
'checked': true
}]
}
...
handleHobby(key) {
var hobby = this.state.hobby;
hobby[key].checked = !hobby[key].checked;
this.setState({
hobby: hobby
});
console.log(this.state.hobby);
}
...
{
this.state.hobby.map((value,key)=>{
return (
<span key={key}>
{value.title}<input type="checkbox" checked={value.checked} onChange={this.handleHobby.bind(this,key)}/>
</span> )
})
}
注意return的形式!return的代码最好全写在一个根节点中
以Home.js为父组件,Header.js为子组件 为例:
1.在调用子组件的时候定义一个属性
2.子组件里面 {this.props.msg}
说明:父组件不仅可以给子组件传值,还可以给子组件传方法,以及把整个父组件传给子组件。
如果父组件调用子组件的时候不给子组件传值,可以在子组件中使用defaultProps定义的默认值
{this.props.title}//网页成功显示"标题"
Header.defaultProps = {//在class外部
title: '标题'
}
propTypes:在子组件中验证父组件传值的类型合法性
//Home.js
...
<Header name=123 />
...
//Header.js
import PropTypes from 'prop-types';
...
Header.propTypes = {//在class外部
name: PropTypes.string
}
结果:网页运行报错(期待string类型,而传入了number类型)