React学习01 jsx、组件与组件的三大属性

文章目录

尚硅谷react教程+官方文档学习记录笔记01

jsx的介绍与语法

1.真实DOM和虚拟DOM

虚拟DOM本质是一般对象(Object对象),虚拟DOM的附带属性比真实DOM少,更轻量,虚拟DOM最终会被React转换成真实DOM显示在页面上。

javascript 复制代码
<body>
  <div id="h1">真实DOM</div>
    <!-- ....省略引入react有关js文件,示范虚拟DOM渲染过程-->
  <script type="text/babel">
    const vdom = <h1> Hello JS <h1/>;  // 虚拟DOM
    ReactDOM.render(vdom,document.getElementById('h1'));  // 将虚拟DOM渲染到页面
  </script>
</body>

2.jsx语法

jsx语法是react定义的js+XML的语法,本质是React.creatElement(component,props,...children)的语法糖,用来简化和创建虚拟DOM。

javascript 复制代码
// babel转译jsx成React.createElement()函数调用
const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);   // 两段代码等效

语法规则一:定义虚拟DOM变量,赋值不写引号

javascript 复制代码
// 例子 创建一个h1标签的虚拟DOM
var element = <h1> Hello JS </h1>    // 最终产生一个JS对象

// 创建嵌套标签
var element = (
    <h1>
       <span> Hello JS </span>
    </h1>
  )   // ()表示这一段为一个整体

语法规则二:标签中需要混入js表达式时使用{},表达式为可以接在=后的(let x = 表达式)

javascript 复制代码
const data = "Hello JS";
var element = (
    <h1>
       <span> {data} </span>  
       <span> {data.toLowerCase()} </span>  
    </h1>
  ) 
javascript 复制代码
// 使用jsx动态显示列表,页面显示一个列表,有a,b,c三个项,每个li标签必须有唯一的key值,这里为数组的下标
const data = [ 'a','b','c'];
const element = (
  <div>
    <ul>
      {
         data.map((item,index)=>{
             return <li key ={index}< {item} </li>
         })
      }  // 这里的花括号里面是一个表达式
    </ul>
  </div>
)

语法规则三:样式的类名使用className,内联样式使用style={{key:value}}。

javascript 复制代码
var element1 = <h1 className="title"> Hello JS </h1> 
var element2 = <h1 style={{color:'white', fontSize:'20px'}}> Hello JS </h1>   // 类似font-size由多个词连接的属性改为小驼峰写法

语法规则四:虚拟DOM只能有一个根标签

javascript 复制代码
var h1 = (
    <h1>
       <span> {data} </span>  
       <span> {data.toLowerCase()} </span>  
    </h1>
  )    // h1为根标签
  
var span = (
       <span> {data} </span>  
       <span> {data.toLowerCase()} </span>  
  )   // 错误,有两个根标签span

语法规则五:标签必须闭合,单标签以"/"结尾或者使用闭合标签

<input />
<input ></input>

语法规则六:标签的首字母规范:

1)以小写字母开头,则转换为html同名标签,若html无对应标签,报错;

2)以大写字母开头,react渲染该组件,若组件未定义,报错;

语法规则七:使用{/* 内容 */}在jsx内写注释。应避免在jsx结构内写注释

var element = (<div> 
            {/* <h1>这是注释掉的标签</h1> */}
            <h1>这是未注释标签</h1>
             </div> )

模块与模块化,组件与组件化

模块与模块化

模块:提供特定js功能的程序,一般指一个js文件;将代码拆成模块可以简化js,提高js运行效率;

模块化:应用的js都以模块编写

组件与组件化

组件:实现局部功能效果代码的资源集合,包含html,css,js,image,video等,考虑复用性,简化项目编码,提高运行效率;

组件化:应用以多组件的方式实现

React组件

在Chorme里导入ReactDevTool插件;

React事件绑定

react把js原生事件onXXX方法重写了,变成小驼峰写法,比如onclick方法变为onClick方法,使用时需要注意。函数调用时使用{}包含函数名。

<button onclick="activateLasers()"></button>  // 原生写法
<button onClick={activateLasers}></button>  // react写法

函数式组件

定义一个函数,函数名首字母大写,返回虚拟DOM,调用react的render方法时,使用闭合标签<函数名/>作为第一个参数

javascript 复制代码
// 函数组件
<body>
  <div id="test"></div>
  <!--引入react有关js文件-->
  <script type="text/javascript" src="react.development.js"></script>
  <script type="text/javascript" src="react-dom.development.js"></script>
  <script type="text/javascript" src="babel.min.js"></script>
  <script  type="text/babel">
  // 创建函数式组件
    function Demo(){
        return <h1> Hello JS <h1/>;
    }
    // 渲染页面。过程:React解析组件标签,寻找Demo组件,发现组件是函数Demo定义的,调用Demo函数,返回虚拟DOM
    ReactDOM.render(<Demo/>,document.getElementById('test'));  
  </script>
</body>

类式组件

定义一个类,继承React.Component类,无变量需要传递获取,不写supper方法,必须写render方法,返回虚拟DOM

javascript 复制代码
// 类组件
<body>
  <div id="test"></div>
  <!--....省略引入react有关js文件-->
  <script  type="text/babel">
// 创建类式组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello</h1>;
  }
}
 // 渲染页面。过程:React解析组件标签,寻找Welcome组件,发现组件是类Welcome定义的,然后new一个Welcome实例对象,通过Welcome实例对象调用Welcome类里的render方法,返回虚拟DOM
    ReactDOM.render(<Welcome/>,document.getElementById('test'));  
  </script>
</body>

简单组件(函数组件)与复杂组件(类组件)区别在于有无状态State

组件属性state

状态(state)驱动页面。

类组件对象实例自带state属性,值为null。当需要使用state取值时,借助构造器初始化state为对象,将需要的变量挂在state对象上。

javascript 复制代码
// 初始化并获取state
<body>
  <div id="test"></div>
  <!--....省略引入react有关js文件-->
  <script  type="text/babel">
class Weather extends React.Component {
  // 定义构造器
  constructor(props){
    super(props);
    this.state = {isHot:false}  // 借助构造器初始化state
  }
  render() {
    return <h1>天气{this.state.isHot ? '热' : '凉'}</h1>;  // 获取state
  }
}
    ReactDOM.render(<Weather/>,document.getElementById('test'));  
  </script>
</body>
javascript 复制代码
// 实现点击事件
class Weather extends React.Component {
  constructor(props){
    super(props);
    this.state = {isHot:false};
    this.changeWeather = this.changeWeather.bind(this); // 3. bind方法返回一个新函数,绑定在参数this上;这里bind(this)绑定了类的实例对象,这一行右边表示将类的changeWeather方法绑定在实例对象上,左边将绑定的函数取名为changeWeather
  }
  render() {
    return <h1 onClick={this.changeWeather}>天气{this.state.isHot ? '热' : '凉'}</h1>;   // 2.添加点击事件,这里相当于把类方法changeWeather作为onClick的回调,所以当用户点击时,this不是实例对象,且严格模式不能指向window,所以为undefined
  }
  // 点击事件
  changeWeather(){
    console.log(this);  // 1.类中的方法默认开启局部严格模式,非对象实例调用时,this指向undefined
  }
}
javascript 复制代码
// 实现点击事件,理解以上注释部分
class Weather extends React.Component {
  constructor(props){
    super(props);
    this.state = {isHot:false};
    this.change = this.changeWeather.bind(this); 
  }
  render() {
    return <h1 onClick={this.change}>天气{this.state.isHot ? '热' : '凉'}</h1>;  
  }
  // 点击事件
  changeWeather(){
    console.log(this);  
  }
}

state不能直接更改,更新要借助api setState()方法。

javascript 复制代码
 // 点击事件里更改state里的变量 -- 直接更改
  changeWeather(){
    const isHot = this.state.isHot;   // 获取原来的isHot值
    this.state.isHot = !isHot;   // 点击后,借助开发插件可以看到this.state.isHot值没有变化,页面也无变化,react不允许直接改变状态
  }

 // 点击事件里更改state里的变量 -- 借助setState()方法
 changeWeather(){
    const i = this.state.isHot;  // 获取原来的isHot值
    this.setState({isHot:!i}); // 点击后,借助开发插件可以看到this.state.isHot值发生变化,页面也随state改变而改变
  }

setState方法起到的是一个合并的动作。

// 原state
state = {a:1,b:2,c:0}
// 使用setState()更新
this.setState({a:99});
// 更新后的state为
state = {a:99,b:2,c:0}

精简state写法,避免有多属性和多方法需要绑定。在类组件的类里使用属性名+赋值语句+值/函数。赋值的函数定义必须使用箭头函数。

javascript 复制代码
class Weather extends React.Component {
  state = {isHot:false} // 往实例对象上挂一个state属性,值为一个对象,里面有isHot属性
  
  render() {
    return <h1 onClick={this.changeWeather}>天气{this.state.isHot ? '热' : '凉'}</h1>;  
  }
  
  changeWeather = ()=>{
    const i = this.state.isHot;   
    this.setState({isHot:!i});  
  } // 往实例对象上挂一个changeWeather属性,值为一个方法,方法里更新state。为什么使用箭头函数不使用function定义?因为箭头函数没有自己的this,箭头函数的this指向上下文的this,也就是实例对象
}

总结

1.state是对象,包含一个或多个key-value的组合,不能直接修改更新。

2.通过setState()方法更新state,从而从新渲染页面。

3.render()方法中的this指向的也是类组件的实例对象。

4.组件自定义方法的this为undefined时,在构造器里使用bind方法或使用赋值语句箭头函数改变this。

5.适用于用户数据交互渲染页面。

组件属性props

类组件对象实例自带props属性,值为{}。在ReactDOM.render方法内使用key="value"的方式传参,jsx内使用this.props.属性的方式接收。

javascript 复制代码
// props的基本使用
class Person extends React.Component{     
        render(){
                return (
                      <ul>
                        <li>name:{this.props.name}</li>
                        <li>sex:{this.props.sex}</li>
                        <li>age{this.props.age}</li>
                      </ul>
                      )
            }
        }
ReactDOM.render(<Person name="tom" age="18" sex="男"/>,document.getElementById('test')); // 这里传递的age为字符串
ReactDOM.render(<Person name="tom" age={18} sex="男"/>,document.getElementById('test')); // 这里传递的age为Number

传递多个props属性时,使用{}+扩展运算符"..."

javascript 复制代码
// 传递多个props属性
class Person extends React.Component{     
        render(){
                return (
                      <ul>
                        <li>name:{this.props.name}</li>
                        <li>sex:{this.props.sex}</li>
                        <li>age{this.props.age}</li>
                      </ul>
                      )
            }
        }

// 定义变量,包含需要的属性
const p = {name:"tom",age:"18",sex:"男"};
// 传递时使用{}
ReactDOM.render(<Person {...p}/>,document.getElementById('test'));

对props属性做限制,如参数类型,默认值,必要性

// 写法1--在类的外部
// 对类组件定义一个propTypes属性,属性内规定了props各个属性的类型
类名.propTypes = {
  属性1: PropTypes.string.isRequired,  // 使用isRequired标定为必要字段
  属性2: PropTypes.number
}
// 对类组件定义一个defaultProps属性,属性内规定了props内各个属性的默认值
类名.defaultProps = {
  属性1:'默认值',
  属性2:'默认值'
}

// 写法2--在类的内部
class 类名{
  static propTypes = {
  属性1: PropTypes.string.isRequired,  
  属性2: PropTypes.number
  }
  static defaultProps = {
  属性1:'默认值',
  属性2:'默认值'
  }
  // ...state,render等
}
javascript 复制代码
// ... 增加一个引入文件,以使用对标签属性增加限制的方法
<script type="text/javascript" src="prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component{     
        render(){
                return (
                      <ul>
                        <li>name:{this.props.name}</li>
                        <li>sex:{this.props.sex}</li>
                        <li>age{this.props.age}</li>
                      </ul>
                      )
            }
  }
// 对标签属性进行类型和必要性限制
Person.propTypes = {
  name: PropTypes.string.isRequired,
  age:PropTypes.number
}
// 对标签属性指定默认值
Person.defaultProps = {
  sex:'未知'
}
const p = {name:"tom",age:"18",sex:"男"};
ReactDOM.render(<Person {...p}/>,document.getElementById('test'));
</script>

对类组件传递方法

javascript 复制代码
// 写法--在类的外部
Person.propTypes = {
  speak: PropTypes.func
}
function speak(){
  console.log('say something')
}
ReactDOM.render(<Person speak={speak}/>,document.getElementById('test'))

函数式组件使用props,组件三大属性中只有props可以在函数式组件使用。

function Person(props){
  return (
            <ul>
                <li>name:{props.name}</li>
                <li>sex:{props.sex}</li>
                <li>age{props.age}</li>
            </ul>
        )
}
Person.propTypes = {
  name: PropTypes.string.isRequired,
  age:PropTypes.number
}
Person.defaultProps = {
  sex:'未知'
}
ReactDOM.render(<Person  name="tom" age={18} sex="男"/>,document.getElementById('test'))

总结

1.props属性只读,使用this.props.属性=值会报错。

2.组件标签的所有属性都保存在props中,通过标签属性从组件外向组件内传递变化的数据,组件内不要修改props数据。

3.适用于父传子,组件间通信。

组件属性ref

类组件对象实例自带refs属性,值为{}。类似原生html标签的id属性。

<标签 ref="属性名"></标签>  // 在标签内定义
this.refs.属性名   // 使用
javascript 复制代码
// refs基本使用
class Demo extends react.Component{
  showData = ()=>{
    console.log(this.refs); // {input1:input,button1:button,input2:input} input1是input标签的ref值,冒号后的input表示的是ref为input1的这一个input标签节点
    console.log(this.refs.input1); // <input type="text" placeholder=""/>
  }
  render(){
    return (
      <div>
        <input ref="input1" type="text" placeholder="点击按钮"/>
        <button ref="button1" onClick="{this.showData}">点击提示左侧数据</button>
        <input ref="input2" type="text" placeholder=""/>
      </div>
    )
  }
}
ReactDOM.render(<Demo/>,document.getElementById('test'));

以上为refs的字符串写法,ref属性都为字符串类型,会有效率问题。因此有了回调函数形式的ref。

<标签 ref={箭头函数}></标签>  // 在标签内定义,在创建节点时自动调用箭头函数
<标签 ref={(a)=>{this.属性名=a}}></标签>  // 在标签内定义,指定ref属性名
this.属性名   // 使用,因为直接挂在了实例对象上,所以可以直接使用this
javascript 复制代码
// refs回调函数形式,使用内联函数
class Demo extends react.Component{
  showData = ()=>{
    console.log(this.input2.value)
  }
  render(){
    return (
      <div>
        <input ref={(a)=>{console.log(a)}} type="text" placeholder="点击按钮"/>  // 打印<input type="text" placeholder="点击按钮"/>
        <input ref={(a)=>{this.input2=a}} type="text" placeholder=""/> // 获取当前所在的节点(a,也就是input标签),将这个节点挂在组件实例对象上,并命名为input2
        <button ref="button1" onClick="{this.showData}">点击提示左侧数据</button>
      </div> 
    )
  }
}
ReactDOM.render(<Demo/>,document.getElementById('test'));

以上是使用内联函数的形式定义ref回调函数,内联函数在组件更新的时候会被执行两次,第一次传入参数null,第二次才传入DOM元素。因为每次调用render方法进行渲染时,都会创建一个新的函数实例,React会清空旧的ref然后设置一个新的,当旧的被清空,无法找到当前节点,所以第一次传入null。

javascript 复制代码
// refs回调函数形式,使用内联函数,组件更新二次执行回调函数问题
class Demo extends react.Component{
  state = {isHot:'hot'}
  showData = ()=>{
    alert(this.input2.value)
  }
  showWeather = ()->{
    const isHot = this.state.isHot;
    this.setState({isHot:!isHot});
  }
  render(){
    return (
      <div>
        <input ref={(a)=>{this.input2=a;console.log(a)}} type="text" placeholder=""/> 
        <button ref="button1" onClick="{this.showData}">点击提示左侧数据</button>// 点击提示数据按钮,内联函数调用一次,打印<input  type="text" placeholder=""/>
        <button  onClick="{this.showWeather}">点击提示天气</button>// 点击显示天气按钮,内联函数调用两次,第一次打印null第二次打印<input  type="text" placeholder=""/>
      </div> 
    )
  }
}
ReactDOM.render(<Demo/>,document.getElementById('test'));

下面类绑定式的ref写法可以避免调用两次的情况,但使用内联函数调用两次的影响无关紧要,可继续使用。

函数名=(a)=>{this.属性名=a}  // 在类组件内定义函数
<标签 ref=this.函数名></标签>  // 在标签内定义
javascript 复制代码
// refs回调函数形式,使用类绑定的形式,解决组件更新时二次执行回调函数问题
class Demo extends react.Component{
  state = {isHot:'hot'}
  showData = ()=>{
    alert(this.input2.value)
  }
  showWeather = ()->{
    const isHot = this.state.isHot;
    this.setState({isHot:!isHot});
  }
  showInput = (a)=>{this.input2=a;console.log(a)}
  render(){
    return (
      <div>
        <input ref={this.showInput} type="text" placeholder=""/> 
        <button ref="button1" onClick="{this.showData}">点击提示左侧数据</button>// 点击提示数据按钮,内联函数调用一次,打印<input  type="text" placeholder=""/>
        <button  onClick="{this.showWeather}">点击提示天气</button>// 点击显示天气按钮,内联函数不会被频繁调用
      </div> 
    )
  }
}
ReactDOM.render(<Demo/>,document.getElementById('test'));

React.createRef调用后返回一个容器,容器内存储被ref标识的一个节点,每个被ref标识的节点都有专属自己的容器。

// 在类组件内,挂在类组件的实例对象上
myRef = React.createRef();
// 在虚拟DOM中
<input ref={this.myRef} type="text" />
javascript 复制代码
// React.createRef的基本使用
class Demo extends React.Component{
  myRef = React.createRef()
  myRef2 = React.createRef()
  showRef(){
    console.log(this.myRef); // {current:input}
    console.log(this.myRef.current); //<input type="text" />
    console.log(this.myRef.current.value); // 输入框输入的值
  }
  render(){
    return (
      <div>
        <input ref={this.myRef} type="text" />
        <input ref={this.myRef2} type="text" />
        <button onClick={this.showRef}>点击</button>
      </div>
    )
  }
}

总结

  1. 应该尽量避免字符串的ref,使用内联回调函数的形式的ref。
  2. react推荐使用createRef,将ref绑定到组件实例上使用。
  3. ref适用于处理焦点、文本选择、媒体播放、触发动画、集成第三方DOM库。
相关推荐
程序视点25 分钟前
【安全漏洞】Vue UI库Vant组件遭恶意投毒,字节RspacK也中招!请紧急修复!
前端·vue.js·ui
洛阳泰山1 小时前
最新版本开发对接飞书网页应用免登录接口教程
前端·javascript·飞书
Catherinemin1 小时前
CSS|10 内填充padding&外边距margin
前端·css
lover_putter2 小时前
ai学习报告:训练
人工智能·学习
123yhy传奇2 小时前
【学习总结|DAY020】Java FIle、字符集、IO流
java·开发语言·学习
m0_748254882 小时前
前端大屏自适应方案
开发语言·前端·javascript
小刘鸭!2 小时前
notepad++快捷键-多行编辑中如何使所有行的光标都向后移动一个单词的长度(每行单词长度不一定一致)
前端·javascript·notepad++
power-辰南2 小时前
大厂 Java 架构师面试题全解析
java·前端·面试
夜色呦2 小时前
创新驱动医疗变革:SSM+Vue 医院预约挂号系统的设计与实践
前端·数据库·vue.js
来一碗刘肉面2 小时前
antdv-<a-table>的使用
前端·javascript·anti-design-vue