react框架基础
环境准备
node安装
项目的运行和启动必须依赖于环境,以前jquery时代只用在网页上写js脚本,现在要搭环境配置复杂,但代码简洁。
node.js是一个基于Chrome V8引擎的js运行环境。
作用:
1.前端开发环境
Webpack:编写插件辅助启动前端项目,
Npm插件:
Server:
2.服务端动态编程语言(后台,接口,动态脚本供前端调用,数据库对接)
Java Web:
PHP Web:
Node Web:
Npm使用(包管理工具)
官网:https://npmjs.com
语法使用:
npm -v
npm init
npm install | npm install -g
插件发布:
npm adduser | npm pulish
npm config set registry https://registry.npmjs.org/
npm config set registry https://registry.npm.taobao.org/
npm unpublish [包名] --force
Yarn使用
特点:速度快、更安全、更可靠
(一般vue,react脚手架都有内置yarn)
官网:https://yarn.bootcss.com/
安装:npm install yarn -g
更新:npm upgrade yarn -g
npm upgrade yarn@1.13.0 -g
语法使用:
yarn -v
yarn init
yarn install
yarn add/remove
yarn publish/login/logout
yarn run
React框架
React介绍
特点:
- 声明式
- 组件化(ui)
模块化:功能划分 - 灵活(单/多页面、服务端渲染、RN-App)
定义组件,渲染组件,获取标签
编写HelloWorld
引入框架:
1.CDN(多页面开发)
https://unpkg.com/react@16/umd/react.development.js
https://unpkg.com/react-dom@16/umd/react-dom.development.js
https://unpkg.com/babel-standalone@6/babel.min.js
(生产环境中不建议使用)
2.NPM
src>npm install react --save
package.json
react-demo>npm i
基本用法:
渲染标签:ReactDOM.render()
创建标签:React.createElement()
创建组件:React.Component
(es6,用extends继承组件)
es5,npm:(不建议)
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React demo</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
</head>
<body>
<div id="app"></div>
<script>
var hello = React.createElement('h1',{},"Hello World");
ReactDOM.render(hello,document.getElementById('app'));
</script>
</body>
</html>
es6,label:(使用较多)
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React demo</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
// 组件渲染,
ReactDOM.render(
<h1>Hello,World!</h1>
,document.getElementById('app'));
</script>
</body>
</html>
JSX语法介绍
JSX介绍:
1.原始
<script type="text/babel">
// 创建元素标签,并添加class,name属性
var hello = React.createElement(
'h1',{
className:'red',
name:'jack'
},"Hello World!");
// 渲染标签
ReactDOM.render(
hello
,document.getElementById('app'));
</script>
2.JSX(类名class为避免混淆,使用className,red可用于设置样式)
<script type="text/babel">
// 渲染标签
ReactDOM.render(
<h1 className="red" name="jackk">Hello,World!!</h1>
,document.getElementById('app'));
</script>
3.穿插使用
先定义标签变量,然后传入Dom树中,变量用{}
<style>
.red {
color: red;
}
</style>
<script type="text/babel">
var name = 'jack';
var ele = <h1 className="red" name="jackk">Hello,{name}!!</h1>
// 渲染标签,元素渲染到根 DOM 节点中
ReactDOM.render(
ele
,document.getElementById('app'));
</script>
元素渲染
1.如何渲染:单向绑定
(模型变化会驱动视图改变,视图发生变化同样会反映到模型中 )
元素发生变化如何更新:只更新对应节点(diff算法)
定时器例子;
在 setInterval() 回调函数,每秒都调用 ReactDOM.render()来更新(自更新需要更新的部分)
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React demo</title>
<script src="../node_modules/react/umd/react.development.js"></script>
<script src="../node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<style>
.red {
color: red;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
function tick(){
var time = new Date().toLocaleTimeString();
var ele = <div>
<h1 className="red" name="jackk">Hello,Jack!!</h1>
<h2>今天是:{time}</h2>
</div>
ReactDOM.render(
ele
,document.getElementById('app'));
}
setInterval(tick,1000);
</script>
</body>
</html>
组件和props介绍(重点)
组件抽取(头,尾,面包屑)
基本语法:
1.React.createClass()
2.函数式组件(无状态组件,return一个常规标签)
<script type="text/babel">
// 函数式组件
function Hello(props){
return <div>
<h1>Hello,{props.name}!!</h1>
<p>年龄:{props.age}</p>
<p>擅长:js</p>
</div>
}
const ele = <Hello name="jack" age="30"/>;
ReactDOM.render(
ele,
document.getElementById('app'));
</script>
执行流程:
1)我们调用 ReactDOM.render() 函数,并传入 <Hello name="jack" age="30"/>
作为参数。
2)React 调用 Hello组件,并将 {name: “jack” ,age: “30”} 作为 props 传入。
3)Hello组件将 <h1>Hello, jack</h1>
元素作为返回值。
4)React DOM 将 DOM 高效地更新为 <h1>Hello, jack</h1>
。
3.React.Component(有状态用this取值,生命周期要用render来渲染组件,继承实现)
<script type="text/babel">
// class继承
class HelloJack extends React.Component{
render(){
return <div>
<h1>Hello,{this.props.name}!!</h1>
<p>年龄:{this.props.age}</p>
<p>擅长:js</p>
</div>
}
}
ReactDOM.render(
<HelloJack name="jackk" age="22"/>
,document.getElementById('app'));
</script>
函数式组件 vs Class组件
函数式组件:
接受唯一参数props,并返回一个react元素对象
Class组件:(有状态)
继承实现、有this、有生命周期(用render来渲染)
组合组件
在一个大容器中多次渲染子组件
function App() {
return (
<div>
<Hello name="Sara" />
<Hello name="Cahal" />
<Hello name="Edite" />
</div>
);
}
// 渲染组件
ReactDOM.render(
<App />,
document.getElementById('root')
);
当组件内部有嵌套关系,导致难以复用时,需要拆分提取组件
React生命周期(逻辑处理)
组件状态:state
state 是私有的,并且完全受控于当前组件
修改 State:不要直接修改,要用this.setState()
state异步更新?
// 错误
this.setState({
counter: this.state.counter + this.props.increment,
});
让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
State 的更新会被合并?
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
当state有多个变量时,分别条用setState()来单独更新
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
浅合并,this.setState({comments}) 完整保留了 this.state.posts, 但是完全替换了 this.state.comments
组件之间如何传值:(单向数据流)
组件的 state只能向下流动
父组件可以选择把它的 state 作为 props 向下传递到它的子组件中:
<FormattedDate date={this.state.date} />
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
子组件接收的props,无法知道它是来自于 Clock 的 state,或是 Clock 的 props,还是手动输入的
四个阶段:
1.组件初始化阶段(Initialization)
2.组件加载阶段(Mounting)
componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行
3.数据更新阶段(Updation)
4.组件销毁阶段(Unmounting)
<script type="text/babel">
class Hello extends React.Component{
constructor(props) {
console.log('初始化阶段')
// 初始化props
super(props);
// 初始化状态
this.state = {
name: 'kack',
age: 32
}
}
// 组件加载前,一般在这个阶段发起ajax请求来渲染界面
componentWillMount(){
console.log('组件加载前');
}
// 组件加载后
componentDidMount() {
console.log('组件加载后')
}
// 要用箭头函数,原来的this指向button对象,现在指向当前的react实例
updateUser = () => {
// 错误语法,并不能更新数据
// this.state.name = 'Tim';
this.setState({
name: 'Tim',
age: 22
})
}
// 数据更新,组件状态发生变化
shouldComponentUpdate(){
console.log('数据是否需要更新')
return true;
// 会执行更新
}
componentWillUpdate(){
console.log('数据将要更新')
}
componentDidUpdate(){
console.log('数据已经更新')
}
// 组件状态发生变化:组件加载或数据更新
render() {
console.log('组件加载或数据更新')
return <div>
<h1>Hello,{this.state.name}!!</h1>
<p>年龄:{this.state.age}</p>
<p>擅长:js</p>
<button onClick={this.updateUser}>更新数据</button>
</div>
}
}
ReactDOM.render(
<Hello />
,document.getElementById('app'));
</script>
执行顺序:初始化、组件加载前、组件加载、组件加载后
constructor(props)、componentWillMount()、render()、componentDidMount()
组件更新:是否需要更新、数据将要更新、数据更新、数据更新完
shouldComponentUpdate()、componentWillUpdate()、render()、componentDidUpdate()
事件处理(事件绑定)
<button onClick={this.updateUser}>更新数据</button>
绑定的事件要用箭头函数,如果直接使用函数要手动再绑定this,class 的方法默认不会绑定 this
<script type="text/babel">
class Hello extends React.Component{
constructor(props) {
console.log('初始化阶段')
// 初始化props
super(props);
// 初始化状态
this.state = {
name: 'kack',
age: 32
}
// // 2.或手动绑定this
// this.updateUser = this.updateUser.bind(this);
}
// 1.要用箭头函数,原来的this指向button对象,现在指向当前的react实例
updateUser = () => {
console.log(this); //指向Hello组件
// this.setState({
// name: 'Tim',
// age: 22
// })
}
// 如果直接使用函数,要手动绑定this
updateUser() {
console.log(this); //undefined
}
// 组件状态发生变化:组件加载或数据更新
// yuab:<button onClick={this.updateUser}>更新数据</button>
// 3.<button onClick={() => this.updateUser()}>更新数据</button>
// 每次渲染 LoggingButton 时都会创建不同的回调函数
// 4.<button onClick={this.updateUser.bind(this)}>更新数据</button>
render() {
console.log('组件加载或数据更新')
return <div>
<h1 className="red">Hello,{this.state.name}!!</h1>
<p>年龄:{this.state.age}</p>
<p>擅长:js</p>
<button onClick={this.updateUser}>更新数据</button>
</div>
}
}
ReactDOM.render(
<Hello />
,document.getElementById('app'));
</script>
条件渲染
父组件根据this.state.isLogin来判断渲染哪个子组件
<script type="text/babel">
function Login(props){
return <button onClick={props.updateUser}>Login</button>
}
function Logout(props){
return <button onClick={props.updateUser}>Logout</button>
}
class App extends React.Component{
// 初始化状态
state = {
isLogin: false
}
// 1.要用箭头函数,原来的this指向button对象,现在指向当前的react实例
updateUser = () => {
console.log(this); //指向App组件
this.setState({
// isLogin: true
isLogin: !this.state.isLogin
})
}
// 组件状态发生变化:组件加载或数据更新
render() {
// 获取状态
// const isLogin =this.state.isLogin;
const {isLogin} =this.state;
let button;
// // 判断
// if(isLogin) {
// button = <Login />
// }else {
// button = <Logout />
// }
// {button}
return <div>
<h1 className="red">Hello,{this.state.name}!!</h1>
{isLogin ? <Login updateUser={this.updateUser}/> : <Logout updateUser={this.updateUser}/>}
<button onClick={this.updateUser}>更新数据</button>
</div>
}
}
ReactDOM.render(
<App />
,document.getElementById('app'));
</script>
列表渲染
循环渲染两种方式:数组的map方法、for循环
<script type="text/babel">
class List extends React.Component{
// 初始化状态
state = {
// list: [1,2,3,4,5]
list: [
{id:1, text:'java'},
{id:2, text: 'js'},
{id:3, text: 'react'},
{id:4, text: 'node'},
{id:5, text: 'php'}
]
}
// 组件加载或数据更新
render() {
const arr = this.state.list;
const listItem = []
// 列表循环
// arr.map((item)=>{
// let li = <li>{item}</li>;
// listItem.push(li);
// })
for(var i=0;i<arr.length;i++){
let li = <li key={arr[i].id}>{arr[i].text}</li>;
listItem.push(li);
}
return <div>
<ul>
{listItem}
</ul>
</div>
}
}
ReactDOM.render(
<List />
,document.getElementById('app'));
</script>
动态列表添加:TodoList实例
注意:在渲染组件时,要绑定key(利用diff算法重新渲染组件时,不会乱序)
<script type="text/babel">
class TodoList extends React.Component{
// 初始化状态
state = {
val: '',
list: []
}
handleInput = (event)=>{
this.setState({
val: event.target.value
})
}
handleAdd = ()=>{
// const val = this.state.val;
// const list = this.state.list;
// es6简写
const {val, list} = this.state;
list.push(val);
this.setState({
list
})
}
// 组件加载或数据更新
render() {
const val = this.state.val;
const arr = this.state.list;
const listItem = []
// 列表循环
arr.map((item, index)=>{
let li = <li key={index}>{item}</li>;
listItem.push(li);
})
return <div>
<div>
<input type="text" value={val} onChange={this.handleInput} />
<button onClick={this.handleAdd}>添加</button>
</div>
<ul>
{listItem}
</ul>
</div>
}
}
ReactDOM.render(
<TodoList />
,document.getElementById('app'));
</script>
表单
用js函数可以很方便的处理表单的提交, 同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用“受控组件”
受控组件
HTML中,表单元素通常自己维护 state,并根据用户输入进行更新
React中,状态的改变通常保存在组件的state中,而且只能通过setState()来更新。
state中初始化 value值,设置值为this.state.value,使得 React 的 state 成为唯一数据源,后续根据handlechange()更新
<script type="text/babel">
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
ReactDOM.render(
<NameForm />
,document.getElementById('app'));
</script>
textarea 标签
<textarea value={this.state.value} onChange={this.handleChange} />