针对React系列的博客,笔者起始已经是酝酿了很久,不是因为自己对React有多深的理解,相反笔者对React仍停留在使用的层面,说到深入理解的话谈不上,科普的话算勉强;因为笔者是React的忠实粉丝,有计划得深入学习React也是自己在前端技术领域进一步发展的第一站。所以本系列文章更多是笔者一边学习一边分享学习心得的文章,算不上散播知识散播爱也达不到网上大佬文章的高度。只想和在座的每一位学习React的同仁一起学习,一起进步。其次本系列文章并不会过多得谈及高级概念或者专业词汇(因为笔者也懒得去理解它们,很累),所以本系列文章将大篇幅使用浅显易懂的词句分享笔者的学习心得。
JSX起源
JSX的前身可以追溯到一种名为JSX的XML文本语言,它最早是由微软开发的.NET语言中的一种语法扩展,用于描述XML文档的结构和内容。微软的JSX与JavaScript的JSX没有直接关联,但它们在语法上有一些相似之处。微软的JSX主要用于XML文档的处理,而不是构建用户界面。
React中的JSX是受到微软的JSX启发而创建的一种语法,旨在提供一种更直观的方法来构建用户界面。它结合了JavaScript和XML标记的语法,使开发人员能够以声明性的方式描述组件的结构,从而提高代码的可读性和维护性。随着React的成功,JSX逐渐成为前端开发的标准,不仅在React中广泛使用,还在其他框架中得到采纳,因为它简化了组件开发和提供了更强大的开发工具,如类型检查和编译器。通过将JavaScript和XML结合,JSX的发展展现了前端开发从初始尝试到逐渐普及的演进,使开发者能够更高效地构建现代Web应用程序。
JSX初识
JSX是个好东西啊!!!
作为React的核心之一, JSX语法深得React Developer的喜爱。首次接触它的人可能会被这个JSX
词给吓到,认为这会是一个很难学习的东西然而事实并不是残酷的反而是宁人欣慰的, FaceBook选择JSX作为React的首选开发语法契合React本身组件化的原则, 如上所说,JSX是将 XML
和 JavaScript
融合而成的一种新的结构性语法
, 可以用写XML的方式写JavaScript, 使得用JSX写组件更为便捷、结构更加清晰;举个例子:
如果我们想写如下的结构的组件:
js
let comp = (
<ul class="list">
<li>one</li>
<li>two</li>
</ul>
)
JSX的写法与上面一致,只是需要将class改成className(因为class是html的保留字,JSX中需要使用className代替)
。
js
let comp = (
<ul className="list">
<li>one</li>
<li>two</li>
</ul>
)
而JavaScript的写法比较繁琐:
js
let li1 = React.createElement('li', null, 'one');
let li2 = React.createElement('li', null, 'two');
let comp = React.createElement('ul', { className: 'list' }, li1, li2);
可以看出,使用JSX写组件方便的不是一点半点。点个赞!
其次,因为JSX写出来的代码并不能直接在目前任何一款浏览器上运行,因此在运行前需要预先使用工具把它翻译成浏览器所能识别的ES5代码,目前主流的是使用 WebPack + Babel
进行编译、打包。使用create-react-app
搭建的项目中可以看到相关的WebPack配置文件(需要先运行 npm run eject
将文件释放出来,配置文件在 config 目录中), 打开配置文件可以看到这么一段
js
// Process JS with Babel.
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
},
},
可以看出Webpack处理 js | jsx | mjs
类型文件的时候用babel-loader
进行翻译
,最终生成浏览器可直接运行的ES5代码。
JSX写法介绍
写JSX代码,笔者最大的体会就是:不是在HTML中写JavaScript就是在JavaScript中写HTML
。虽然很狗血,但是真的是这样。下面一个一个看:
写组件
React中最简单的组件可以简单到什么程度?答案是就好像写一个HTML中的DOM节点一样
js
<h1>Hello World</h1>
这个在React体系中就算一个最简单的组件,是不是和写HTML很像?因为编译器在遇到<>
会将该对象当做组件解析,遇到{}
会当做JavaScript解析。所以这也是为什么可以想在HTML中写js一样,举个例子:
js
function title(){
const name = 'World';
return (
<h1>Hello {name}</h1>
)
}
那么此时这个方法返回的组件就是
js
<h1>Hello World</h1>
是不是很简单呢? 当然React也支持编写复杂组件,方法主要有两种:
第一种是使用ES6中class的写法:
js
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div></div>
);
}
}
export default Demo;
第二种是函数组件的写法,墙裂推荐这种写法
:
js
import React from 'react'
export default function Demo() {
return (
<div>code</div>
)
}
在JSX中JS代码
上面有提到过在组件中的{}
里写JavaScript, 这里笔者主要提及两个地方:
第一个:
因为JSX本质上也是写js,因此可以像常规使用变量一样在{}
中使用变量(只要能访问到),比如我们写如下一个组件:
jsx
import React from 'react'
export const component = () => {
const age = 26;
return (
<div>明年我的年龄是:{age + 1} 岁</div>
)
}
此时的展示内容是:
可以看出,我们不仅可以在{}
访问某变量,还可以做逻辑处理。
第二个:
可以在组件的{}
中访问Class组件
的 this 对象。本文不对该对象进行介绍,后续会有相关博客。不过笔者想说这个this很重要, 有了它可以做很多事
。写例子吧, 我们将刚刚例子中的age
变量放在this中试一下:
jsx
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
this.age = 26
}
render() {
return (
<div>明年我的年龄是:{this.age + 1} 岁</div>
);
}
}
export default Demo;
效果与上一条一致。
逻辑判断
书写React组件的时候无法使用if...else
相关判断, 只能使用三元运算
。举个例子,我们实际开发中可以使用某个变量进行判断而渲染出不同的内容,比如:
jsx
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
this.age = 18
}
render() {
return (
<div>
{(this.age >= 18) ? <p>成年了</p> : <p>未成年</p>}
</div>
);
}
}
export default Demo;
当年龄大于等于18岁
的时候,就渲染出:
如果小于18岁
的话,就渲染出:
这种写法在实际开发中经常用到,但是还有一个值得注意的是:尽量不要在JSX中写渲染过程,因为对于复杂的业务场景,我们需要做一些额外数据转换或者 try...catch等,所以建议写渲染函数。
改下上面的例子:
jsx
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
this.age = 18
}
renderContent(){
if(this.age>=18){
return <p>成年了</p>
}
return <p>未成年</p>
}
render() {
return (
<div>
{renderContent()}
</div>
);
}
}
export default Demo;
这种写法有利于提高代码灵活性和健壮性。希望大家多多注意,这是笔者多年开发的经验所得。
样式处理
在React中添加样式和常规HTML+CSS开发是一样的,可以使用行内样式也可以使用class(React中是className)添加样式。这里主要介绍行内样式的处理,因为使用className处理样式又涉及到另外的技术了,有兴趣的朋友可了解下css module
JSX语法写样式和平常我们写HTML行内样式大同小异,到底异在哪儿?我们平时写HTML样式是这样写的:
html
<div style="background:red"></div>
就是给DOM元素添加style
属性, 然后用分号分开的样式组成的字符串进行赋值,其实JSX的写法类似,只是赋值的不是字符串而是一个对象:
js
<div style={{"background":"red"}}></div>
修改下刚才的例子:
jsx
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
this.age = 17
}
render() {
return (
<div>
{(this.age >= 18) ? <div style={{'background': 'red'}}>成年了</div> : <div style={{'background': 'red'}}>未成年</div>}
</div>
);
}
}
export default Demo;
结果显示:
可能会有疑问为什么是对象?虽然是React的规定,但是笔者认为JSX本质也是JavaScript, 编译器去编译JSX代码时候读取一个对象远比分析一段字符串要来得快。哈哈。。。一方之言,看看就好。当然,如果样式对象有多个该肿么办?很简单,使用ES6的解构,举个例子:
jsx
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
this.age = 17
}
render() {
const style1 = {'background': 'red'};
const style2 = {'color': 'yellow'};
return (
<div>
{(this.age >= 18) ? <div style={{...style1, ...style2}}>成年了</div> : <div style={{...style1, ...style2}}>未成年</div>}
</div>
);
}
}
export default Demo;
这样也是可以正常显示:
批量渲染
最后来说一下批量渲染。什么意思呢?假如我们要从一个数据里取出数据并且渲染成一条一条的菜单项或者列表项,该如何做?前面说过,可以在组件的{}
中写逻辑处理。最直接的方法如下:
jsx
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
this.list = ['one', 'two', 'three']
}
render() {
return (
<ul>
{
this.list.map(function (item) {
return (
<li>{item}</li>
)
})
}
</ul>
);
}
}
export default Demo;
页面渲染效果如下
这样写可以实现需求,但是有个问题是组件代码结构复杂且冗余、维护代价高,尤其是当需要渲染的项目有别的复杂的需求的话,那么此处的复杂度就无法预计了,因此最有效最可靠的办法就是将生成项目的逻辑单独写在一个方法里并把项目放入数组中返回,也就是前面说的渲染函数
,可以这样写:
jsx
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
this.list = ['one', 'two', 'three']
}
createList = (listData) => {
return listData.map(function (item) {
return (
<li key={item}>{item}</li>
)
});
};
render() {
const lists = this.createList(this.list);
return (
<ul>{lists}</ul>
);
}
}
export default Demo;
这样不管需要渲染的项有多复杂,只需要关注那个方法即可。不过有两点需要注意:
1 组件中自定义的方法建议必须使用ES6的箭头函数,因为它能保证this的正确指向。
2 在组件同一个节点渲染多个相同结构项目的时候请记得加上
key
,并且值需要是对于它们
(这几个项目)而言都是独一无二的。有利于优化。
事件绑定
最后一个重头戏了。前端开发中给DOM绑定事件的动作到处都是。虽说React对开发者和用户屏蔽了DOM但也提供了绑定事件的接口,幸运的是它和普通DOM事件绑定大同小异,异在哪儿?请参考样式绑定。。。哈哈
平时我们给DOM绑定事件很简单
html
<div onClick='function'></div>
这样就实现了事件的绑定。React给组件的某个节点绑定事件也是如此:
jsx
import React, {Component} from 'react';
class Demo extends Component {
constructor(props) {
super(props);
}
handler = () => {
alert('Click!')
};
render() {
return (
<div onClick={this.handler}>
点我
</div>
);
}
}
export default Demo;
演示效果如下:
之所以onClick后面是{}是因为需要从组件的this对象中取出该方法对象进行赋值。同时,我们在组件中定义的方法都在this对象中
好了,差不多JSX的介绍就结束了, 日常开发常用的东西都在这里了(仅限笔者日常开发哇!)。其实如果要把JSX讲解清楚,这种篇幅的文章根本不够用。所以需要的是我们在实际开发中不断得摸索和研究,实践方能出真知。我们下期见...