前端React框架学习,以此篇博客记录所学,如有误载,感谢您的指正。
视频教程地址:https://www.bilibili.com/video/BV1T7411W72T?p=1
功能和特性:
React功能:构建用户界面的JavaScript库,主要用于构建UI界面。特性:
创建第一个项目:
1、通过script引入使用,仅用于学习调试使用:
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
2、进入工作目录,打开控制台终端,通过react的脚手架,创建项目进行开发,部署。
npm install -g create-react-app
create-react-app <项目名称>
注:npm为nodejs的包管理命令。
Windows安装nodejs和换源教程链接:https://www.cnblogs.com/mlzdev/p/11520325.html
JSX创建JS元素对象:
let h1 = <h1>helloworld</h1>;
注:JSX元素对象,或者组件对象,必须只有1个根元素(根节点)
Demo:
function clock(){
let time = new Date().toLocaleTimeString()
let element = (
<div>
<h1>现在的时间是{time} </h1>
<h2>这是副标题</h2>
</div>
)
let root = document.querySelector('#root');
ReactDOM.render(element,root)
}
clock()
setInterval(clock,1000)
函数式组件渲染:
function Clock(props){
return (
<div>
<h1>现在的时间是{props.date.toLocaleTimeString()} </h1>
<h2>这是函数式组件开发</h2>
</div>
)
}
function run(){
ReactDOM.render(
<Clock date={new Date()} />,
document.querySelector('#root')
)
}
setInterval(run,1000)
表达式:
注意:
1、JSX必须要有根节点。
2、正常的普通HTML元素要小写。如果是大写,默认认为是组件。
import React from 'react';
import ReactDOM from 'react-dom';
import './App.css'
let time = new Date().toLocaleTimeString()
let str = '当前时间是:'
let element = (
<div>
<h1>helloworld</h1>
<h2>{str+time}</h2>
</div>
)
console.log(element)
let man = '发热';
let element2 = (
<div>
<h1>今天是否隔离</h1>
<h2>{man=="发热"?<button>隔离</button>:"躺床上"}</h2>
</div>
)
//let man = '发热';
let element4 = (
<div>
<span>横着躺</span>
<span>竖着躺</span>
</div>
)
man = '正常'
let element3 = (
<div>
<h1>今天是否隔离</h1>
<h2>{man=="发热"?<button>隔离</button>:element4}</h2>
</div>
)
let color = 'bgRed'
let logo = 'https://www.baidu.com/img/pc_1c6e30772d5e4103103bd460913332f9.png'
//HTML的样式类名要写className,因为class在js当中是关键词
let element5 = (
<div className={color}>
<img src={logo} />
红色的背景颜色
</div>
)
ReactDOM.render(
element5,
document.getElementById('root')
)
1、Class,style中,不可以存在多个class属性,错误的表示如下:
<div class=’abc’ class={‘active’}>
2、style样式中,如果存在多个单词的属性组合,第二个单词开始,首字母大写。或者用引号引起来,否则会报错。
let exampleStyle = {
background:"skyblue",
borderBottom:"4px solid red",
'background-image':"url(https://www.baidu.com/img/pc_1c6e30772d5e4103103bd460913332f9.png)"
}
3、多个类共存的操作
let element2 = (
<div>
<h1 className={"abc "+classStr}>helloworld</h1>
</div>
)
let classStr2 = ['abc2','redBg2'].join(" ")
let element3 = (
<div>
{/* 这里写注释 */}
<h1 className={classStr2} style={exampleStyle}>helloworld</h1>
</div>
)
4、注释
必须在括号的表达式{/* 这里写注释 */}
内书写,否则报错。
函数式组件与一般用于静态没有交互事件内容的组件页面,类组件一般会有交互或者数据修改的操作,复合式组件两者均包含。
函数式组件Demo:
function Childcom(props){
console.log(props)
let title = <h2>我是副标题</h2>
let weather = props.weather
//条件判断
let isGo = weather=='下雨' ?"不出门":"出门"
return (
<div>
<h1>函数式组件helloworld</h1>
{title}
<div>
是否出门?
<span>{isGo}</span>
</div>
</div>
)
}
类组件Demo:
class HelloWorld extends React.Component{
render(){
console.log(this)
return (
<div>
<h1>类组件定义HELLOWORLD</h1>
<h1>hello:{this.props.name}</h1>
<Childcom weather={this.props.weather} />
</div>
)
}
}
复合组件Demo:
import React from 'react';
import ReactDOM from 'react-dom';
import './04style.css';
//函数式组件
function (props){
console.log(props)
let title = <h2>我是副标题</h2>
let weather = props.weather
//条件判断
let isGo = weather=='下雨' ?"不出门":"出门"
return (
<div>
<h1>函数式组件helloworld</h1>
{title}
<div>
是否出门?
<span>{isGo}</span>
</div>
</div>
)
}
//类组件定义
class HelloWorld extends React.Component{
render(){
console.log(this)
//返回的都是JSX对象
return (
<div>
<h1>类组件定义HELLOWORLD</h1>
<h1>hello:{this.props.name}</h1>
<Childcom weather={this.props.weather} />
</div>
)
}
}
ReactDOM.render(
<HelloWorld name="老陈" weather="下雨" />,
document.querySelector('#root')
)
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
Demo:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
React使用props父传子:
props传递给子组件数据,单向流动,可以是任意的类型,不能直接子传递给父。但props可以传递父元素的函数,就可以去修改父元素的state,从而达到传递数据给父元素。Props可以设置默认值
HelloMessage.defaultProps = { name:”老陈”,msg:“helloworld” }
Demo:
//在父元素中使用state去控制子元素props的从而达到父元素数据传递给子元素
class ParentCom extends React.Component{
constructor(props){
super(props)
this.state = {
isActive:true
}
this.changeShow = this.changeShow.bind(this)
}
render(){
return (
<div>
<button onClick={this.changeShow}>控制子元素显示</button>
<ChildCom isActive={this.state.isActive} />
</div>
)
}
changeShow(){
this.setState({
isActive:!this.state.isActive
})
}
}
class ChildCom extends React.Component{
constructor(props){
super(props)
}
render(){
let strClass = null;
// if(this.props.isActive){
// strClass = ' active'
// }else{
// strClass = ""
// }
strClass = this.props.isActive?" active":"";
return (
<div className={"content"+strClass}>
<h1>我是子元素</h1>
</div>
)
}
}
ReactDOM.render(
<ParentCom />,
document.querySelector("#root")
)
props和state的区别:
区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。
调用父元素的函数从而操作父元素的数据,从而实现数据从子元素传递至父元素
Demo:
import React from 'react';
import ReactDOM from 'react-dom';
//子传父
class ParentCom extends React.Component{
constructor(props){
super(props)
this.state = {
childData:null
}
}
render(){
return (
<div>
<h1>子元素传递给父元素的数据:{this.state.childData}</h1>
<ChildCom setChildData={this.setChildData} />
</div>
)
}
setChildData=(data)=>{
this.setState({
childData:data
})
}
}
class ChildCom extends React.Component{
constructor(props){
super(props)
this.state = {
msg:"helloworld"
}
}
render(){
return (
<div>
<button onClick={this.sendData}>传递helloworld给父元素</button>
<button onClick={()=>{this.props.setChildData('直接调用props的函数')}}>传递helloworld给父元素</button>
</div>
)
}
sendData=()=>{
//console.log(this.state.msg)
//将子元素传递给到父元素,实际就是调用父元素传递进来的父元素函数
this.props.setChildData(this.state.msg)
}
}
ReactDOM.render(
<ParentCom />,
document.querySelector('#root')
)
语法:
1、React 事件绑定属性的命名采用驼峰式写法,而不是小写。
2、如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
无参Demo:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 这边绑定是必要的,这样 `this` 才能在回调函数中使用
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('example')
);
事件传参:
{/* 使用ES6箭头函数传递多个参数 */}
<button onClick={(e)=>{this.parentEvent1('msg:helloworld',e)}}>提交</button>
{/* //不使用ES6箭头函数传递多个参数的方式 */}
<button onClick={function(e){this.parentEvent1('不使用es6,msg:helloworld',e)}.bind(this)}>提交</button>
事件对象:React返回的事件对象是代理的原生的事件对象,如果想要查看事件对象的具体值,必须直接输出事件对象的属性。
注意: 原生阻止默认行为时,可以直接返回return false;React中,阻止默认必须e.preventDefault();
React中条件渲染和JavaScript中的一致,条件运算,如if…else…三元运算符等。
条件运算Demo:
import React from 'react';
import ReactDOM from 'react-dom';
function UserGreet(props){
return (<h1>欢迎登陆</h1>)
}
function UserLogin(props){
return (<h1>请先登录</h1>)
}
class ParentCom extends React.Component{
constructor(props){
super(props)
this.state = {
isLogin:true
}
}
render(){
if(this.state.isLogin){
return (<UserGreet></UserGreet>)
}else{
return (<UserLogin></UserLogin>)
}
}
}
ReactDOM.render(
<ParentCom></ParentCom>,
document.querySelector('#root')
)
三目运算Demo:
import React from 'react';
import ReactDOM from 'react-dom';
function UserGreet(props){
return (<h1>欢迎登陆</h1>)
}
function UserLogin(props){
return (<h1>请先登录</h1>)
}
class ParentCom extends React.Component{
constructor(props){
super(props)
this.state = {
isLogin:false
}
}
render(){
let element = null;
if(this.state.isLogin){
element = <UserGreet></UserGreet>;
}else{
element = (<UserLogin></UserLogin>);
}
return (
<div>
<h1>这是头部</h1>
{element}
<h1>这是三元运算符的操作</h1>
{this.state.isLogin?<UserGreet></UserGreet>:<UserLogin></UserLogin>}
<h1>这是尾部</h1>
</div>
)
}
}
ReactDOM.render(
<ParentCom></ParentCom>,
document.querySelector('#root')
)
Demo:
function ListItem(props) {
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('example')
);
组件的生命周期可分成三个状态:
Mounting
:已插入真实 DOMUpdating
:正在被重新渲染Unmounting
:已移出真实 DOM生命周期的方法有:
compontWillMount
:组件将要渲染,AJAX,添加动画前的类compontDidMount
:组件渲染完毕,添加动画compontWillReceiveProps
:组件将要接受props数据,查看接收props的数据什么shouldComponentUpdate
:组件接收到新的state或者props,判断是否更新。返回布尔值compontWillUpdate
:组件将要更新componentDidUpdate
:组件已经更新componentwillUnmount
:组件将要卸载HTML 表单元素与 React 中的其他 DOM 元素有所不同,因为表单元素生来就保留一些内部状态。在 HTML 当中,像 ,
, 和这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新。
Demo在子组件上使用表单:
/** onChange 方法将触发 state 的更新并将更新的值传递到子组件的输入框的 value 上来重新渲染界面。
需要在父组件通过创建事件句柄 (handleChange) ,并作为 prop (updateStateProp) 传递到你的子组件上。
*/
class Content extends React.Component {
render() {
return <div>
<input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} />
<h4>{this.props.myDataProp}</h4>
</div>;
}
}
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello Runoob!'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
var value = this.state.value;
return <div>
<Content myDataProp = {value}
updateStateProp = {this.handleChange}></Content>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
Demo多个表单的使用:
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
是否离开:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
访客数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
组件中写入内容,这些内容可以被识别和控制。React需要自己开发支持插槽功能。
原理:
从props中获取组件中写入的HTML。
Demo:
组件中根据HTML内容的不同,插入的位置不同。
import React from 'react';
import ReactDOM from 'react-dom';
class ParentCom extends React.Component{
render(){
console.log(this.props)
return (
<div>
<h1>组件插槽</h1>
{this.props.children}
<ChildCom>
<h1 data-position="header">这是放置到头部的内容</h1>
<h1 data-position="main">这是放置到主要的内容</h1>
<h1 data-position="footer">这是放置到尾部的内容</h1>
</ChildCom>
</div>
)
}
}
class ChildCom extends React.Component{
render(){
let headerCom,mainCom,footerCom;
this.props.children.forEach((item,index)=>{
if(item.props['data-position']==='header'){
headerCom = item
}else if(item.props['data-position']==='main'){
mainCom = item
}else{
footerCom = item
}
})
return (
<div>
<div className="header">
{headerCom}
</div>
<div className="main">
{mainCom}
</div>
<div className="footer">
{footerCom}
</div>
</div>
)
}
}
class RootCom extends React.Component{
constructor(props){
super(props)
//console.log(props)
this.state = {
arr:[1,2,3]
}
}
render(){
return (
<ParentCom>
<h2 data-name="a" data-index={this.state.arr[0]}>子组件1</h2>
<h2 data-name="b" data-index={this.state.arr[1]}>子组件2</h2>
<h2 data-name="c" data-index={this.state.arr[2]}>子组件3</h2>
</ParentCom>
)
}
}
ReactDOM.render(
<RootCom></RootCom>
,
document.querySelector("#root")
)
官方文档:https://reactrouter.com/web/guides/quick-start
安装:
npm install react-router-dom --save
react-router四大组件:
Router
:所有路由组件的根组件(底层组件),包裹路由规则的最外层容器。
属性:basename->设置此路由根路径,router可以在1个组件中写多个。
Route
:路由规则匹配组件,显示当前规则对应的组件。
Link
:路由跳转的组件。
Switch
:让switch组件内容的route只匹配1个,只要匹配到了,剩余的路由规则将不再匹配。
Demo:
import React from 'react';
//hash模式
//import {HashRouter as Router,Link,Route} from 'react-router-dom'
//history模式/后端匹配使用
import {BrowserRouter as Router,Link,Route} from 'react-router-dom'
function Home(){
return (
<div>
<h1>admini首页</h1>
</div>
)
}
function Me(props){
console.log(props)
return (
<div>
<h1>admin个人中心</h1>
</div>
)
}
function Product(){
return (
<div>
<h1>admin产品页面</h1>
</div>
)
}
function News(props){
console.log(props)
return (
<div>
新闻页,新闻id:{props.match.params.id}
</div>
)
}
class App extends React.Component{
render(){
let meObj = {
pathname:"/me",//跳转的路径
search:"?username=admin",//get请求参数
hash:"#abc",//设置的HASH值
state:{msg:'helloworld'}//传入组件的数据
};
return (
<div id="app">
<Router>
<div className="nav">
<Link to="/">Home</Link>
<Link to="/product">Product</Link>
<Link to={ meObj } replace>个人中心</Link>
<Link to="/news/4568789">新闻页</Link>
</div>
<Route path="/" exact component={Home}></Route>
<Route path="/product" component={Product}></Route>
<Route path="/me" exact component={Me}></Route>
<Route path="/news/:id" component={News}></Route>
</Router>
</div>
)
}
}
export default App
重定向组件:
如果访问某个组件时,如果有重定向组件,那么就会修改页面路径,使得页面内容显示为所定向路径的内容。
Demo:
function LoginInfo(props){
//props.loginState = 'success';
//props.loginState = "fail"
console.log(props)
if(props.location.state.loginState === 'success'){
return <Redirect to="/admin"></Redirect>
}else{
return <Redirect to="/login"></Redirect>
}
}
react-redux是state管理工具:
中文官方API文档:http://cn.redux.js.org/docs/react-redux/api.html#api