JSX语法出现的原因:
为了在项目中不用老是使用 React 提供的 JS API 来创建虚拟DOM节点,于是React 官方,就提出了一套 JSX 语法规范,能够让我们在 JS 文件中,书写类似于 HTML 那样的代码,快速定义虚拟DOM结构
JSX:
符合 XML 规范的 JS 语法
JSX的原理:
JSX内部在运行的时候,也是先把 类似于HTML 这样的标签代码,转换为了 React.createElement 的形式;也就是说哪怕我们写了 JSX 这样的标签,也并不是直接把 我们的 HTML 标签渲染到页面上,而是先转换成 React.createElement 这样的JS代码,再渲染到页面中
JSX语法的本质:
还是以 React.createElement 的形式来实现的,并没有直接把 用户写的 HTML代码,渲染到页面上
JSX的使用:
先运行 `cnpm i babel-preset-react -D`
然后再 `.babelrc` 中添加 语法配置,在语法presets所对应的数组中增加react
{
"presets": ["env", "stage-0", "react"],
"plugins": ["transform-runtime"]
}
编写要渲染的虚拟DOM元素对应的html代码
var myDiv = <div>
这是使用jsx语法创建的div元素
<h1>JSX</h1>
<p>符合 XML 规范的 JS 语法</p>
</div>
使用 ReactDOM 把元素渲染到页面指定的容器中
ReactDOM.render(myDiv, document.getElementById('app'))
如果要在 JSX 语法内部,书写 JS 代码了,那么,所有的JS代码,必须写到 {} 内部;
var mytitle = '这是使用变量定义的 tilte 值'
var myDiv = <div>
这是使用jsx语法创建的div元素
<h1 title={mytitle}>JSX</h1>
<p>符合 XML 规范的 JS 语法</p>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
当 编译引擎,在编译JSX代码的时候,如果遇到了`<`那么就把它当作 HTML代码去编译,如果遇到了 `{}` 就把 花括号内部的代码当作 普通JS代码去编译;在{}内部,可以写任何符合JS规范的代码;
var mytitle = '这是使用变量定义的 tilte 值'
var myDiv = <div>
这是使用jsx语法创建的div元素
<h1 title={mytitle+'abcd'}>JSX</h1>
<p>符合 XML 规范的 JS 语法</p>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
在JSX中,如果要为元素添加`class`属性了,那么,必须写成`className`,因为 `class`在ES6中是一个关键字
var mytitle = '这是使用变量定义的 tilte 值'
var myDiv = <div>
这是使用jsx语法创建的div元素
<h1 title={mytitle+'abcd'}>JSX</h1>
<p class='myp'>符合 XML 规范的 JS 语法</p>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
解决办法把p标签的class属性改写为className
var mytitle = '这是使用变量定义的 tilte 值'
var myDiv = <div>
这是使用jsx语法创建的div元素
<h1 title={mytitle+'abcd'}>JSX</h1>
<p className='myp'>符合 XML 规范的 JS 语法</p>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
和`class`类似,label标签的 `for` 属性需要替换为 `htmlFor`
var mytitle = '这是使用变量定义的 tilte 值'
var myDiv = <div>
这是使用jsx语法创建的div元素
<h1 title={mytitle+'abcd'}>JSX</h1>
<p className='myp'>符合 XML 规范的 JS 语法</p>
<label for=""></label>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
解决办法把label标签的for属性改写为htmlFor
var mytitle = '这是使用变量定义的 tilte 值'
var myDiv = <div>
这是使用jsx语法创建的div元素
<h1 title={mytitle+'abcd'}>JSX</h1>
<p className='myp'>符合 XML 规范的 JS 语法</p>
<label htmlFor=""></label>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹
循环元素:
{}里面只能简单表达式,不能放程序语句
{
for (var i = 0; i < 10; i++) {
var p = <p className="myp" key={i}>符合 XML 规范的 JS 语法</p>
arr.push(p)
}
}
会报错
var mytitle = '这是使用变量定义的 tilte 值'
var arr = []
for (var i = 0; i < 10; i++) {
var p = <p className="myp">符合 XML 规范的 JS 语法</p>
arr.push(p)
}
var myDiv = <div>
这是使用 jsx 语法创建的div元素
<h1 title={mytitle + 'aaaaa'}>JSX</h1>
<p className="myp">符合 XML 规范的 JS 语法</p>
<label htmlFor=""></label>
{arr}
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
需要加入key属性唯一标识每个循环到的元素
var mytitle = '这是使用变量定义的 tilte 值'
var arr = []
for (var i = 0; i < 10; i++) {
var p = <p className="myp" key={i}>符合 XML 规范的 JS 语法</p>
arr.push(p)
}
var myDiv = <div>
这是使用 jsx 语法创建的div元素
<h1 title={mytitle + 'aaaaa'}>JSX</h1>
<p className="myp">符合 XML 规范的 JS 语法</p>
<label htmlFor=""></label>
{arr}
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
如果要写注释了,注释必须放到 {} 内部
var myDiv = <div>
/* 这是多行注释,你肯定看不到我 */
{
// 这样写注释,不要和花括号放到一行
}
{ /* 这样写注释,可以和花括号放到一行 */ }
</div>
ReactDOM.render(myDiv, document.getElementById('app'))
第一种创建组件的方式
创建最基本的组件的方式
在React中,构造函数,就是一个最基本的组件
如果想要把组件放到页面中,可以把 构造函数的名称,当作 组件的名称,以 HTML标签形式引入页面中即可
注意:React在解析所有的标签的时候,是以标签的首字母来区分的,如果标签的首字母是小写,那么就按照 普通的 HTML 标签来解析,如果 首字母是大写,则按照 组件的形式去解析渲染,所以组件的首字母必须是大写
function Hello(props) {
return <div>
<h1>这是在Hello组件中定义的元素</h1>
</div>
}
ReactDOM.render(<div>
<Hello></Hello>
</div>,document.getElementById('app'))
父组件向子组件传递数据
在组件中,如果想要使用外部传递过来的数据,必须,显示的在 构造函数参数列表中,定义 props 属性来接收;
通过 props 得到的任何数据都是只读的,不能从新赋值
function Hello(props) {
return <div>
<h1>这是在Hello组件中定义的元素---{props.name}---{props.age}---{props.person.address}</h1>
</div>
}
var name = 'axiaoha'
var age = 22
var person = {
name: 'aha',
age: 20,
gender: '女',
address: '广州'
}
ReactDOM.render(<div>
<Hello name={name} age={age} person={person}></Hello>
</div>,document.getElementById('app'))
将组件封装到单独的文件中
如果把子组件单独提取出来放到一个jsx文件中去,需要注意的几点:
在jsx文件中要记得导入包
import React from 'react'
在webpack.config.js文件中要加入第三方加载器加载jsx文件的匹配规则,和js文件使用的是同一个第三方加载器
{ test: /\.jsx?$/, use: 'babel-loader', exclude: /node_modules/ }
子组件所对应的Hello.jsx文件代码:
import React from 'react'
function Hello(props) {
return <div>
<h1>这是在Hello组件中定义的元素 --- {props.name}</h1>
<p id="">哈哈哈哈</p>
</div>
}
// 把创建的组件暴露出去
export default Hello
在main.js中,需要导入子组件
import Hello from './components/Hello.jsx'
使用 ReactDOM 把元素渲染到页面指定的容器中
ReactDOM.render(<div>
{/* <Hello name={person.name} age={person.age} gender={person.gender} address={person.address}></Hello> */}
<Hello {...person}></Hello>
</div>, document.getElementById('app'))
第二种创建组件的方式
class关键字基本概念
https://blog.csdn.net/zerobaek/article/details/84553617
使用class关键字创建组件
使用 class 创建的类,通过 extends 关键字,继承了 React.Component 之后,这个类,就是一个组件的模板了
class Hello2 extends React.Component{
}
如果想要引用这个组件,可以把 类的名称, 以标签形式,导入到 JSX 中使用
ReactDOM.render(<div>
<Hello2></Hello2>
</div>, document.getElementById('app'))
会报错:
No `render` method found on the returned component instance: you may have forgotten to define `render`.通过分析报错,在 class 实现的组件内部,必须定义一个 render 函数
解决,在Hello2类中加入render函数:
class Hello2 extends React.Component{
render(){
}
}
然后会报第二个错误:
Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.通过分析报错,在 render 函数中,还必须 return 一个东西,如果没有什么需要被return 的,则需要 return null
解决,在render函数中加一个return返回值:
class Hello2 extends React.Component{
render(){
return <div>
<h1>这是使用class类创建的组件</h1>
</div>
}
}
父组件向子组件传值
ReactDOM.render(<div>
<Hello2 address="广州" info="天河区"></Hello2>
</div>, document.getElementById('app'))
class Hello2 extends React.Component{
render(){
return <div>
<h1>这是使用class类创建的组件</h1>
<h3>外界传递过来的参数:{this.props.address} --- {this.props.info}</h3>
</div>
}
}
在function定义的组件中,如果想要使用props,必须先定义,否则无法直接使用,但是在class定义的组件中,可以直接使用this.propsz来直接访问,不需要预先接受props;在 constructor 中,如果想要访问 props 属性,不能直接使用 this.props, 而是需要在 constructor 的构造器参数列表中,显示的定义 props 参数来接收,才能正常使用;
constructor(props){
// 注意: 如果使用 extends 实现了继承,那么在 constructor 的第一行,一定要显示调用一下 super()
// super() 表示父类的构造函数
super(props)
console.log(this.props)
console.log(props)
}
两种创建组件方式的对比
使用 function 构造函数创建的组件,内部没有 state 私有数据,只有 一个 props 来接收外界传递过来的数据;
使用 class 关键字 创建的组件,内部,除了有 this.props 这个只读属性之外,还有一个 专门用于 存放自己私有数据的 this.state 属性,这个 state 是可读可写的
基于上面的区别:我们可以为这两种创建组件的方式,下定义了: 使用 function 创建的组件,叫做【无状态组件】;使用 class 创建的组件,叫做【有状态组件】
有状态组件和无状态组件,最本质的区别,就是有无 state 属性;同时, class 创建的组件,有自己的生命周期函数,但是,function 创建的 组件,没有自己的生命周期函数;
什么时候使用 有状态组件,什么时候使用无状态组件:
如果一个组件需要存放自己的私有数据,或者需要在组件的不同阶段执行不同的业务逻辑,此时,非常适合用 class 创建出来的有状态组件;
如果一个组件,只需要根据外界传递过来的 props,渲染固定的 页面结构就可以了,此时,非常适合使用 function 创建出来的 无状态组件;(使用无状态组件的小小好处: 由于剔除了组件的生命周期,所以,运行速度会相对快一点)