总的来说
graph LR;
JSX((JSX)) --> A(概念) & B(优点) & C(用途) & D(基本语法) & E(手写babel插件)
A --> React的语法糖
B --> 编写简洁,可读性强
C --> 帮React创建DOM
D --> 定义虚拟DOM不要引号
D --> 混入js表达式怎么写?
D --> 样式的类名?
D --> 内联样式?
D --> JSX必须只有一个根标签
D --> JSX标签必须闭合标签
D --> 标签首字母?
E --> 原理
E --> 如何运行插件解析jsx
React为什么要用JSX?
一句解释JSX
JSX是javascript的语法拓展,或者说是类似于XML的ECMAScript的语法拓展。(人话:它本身没有太多的语法定义,也不希望引入更多的标准)
核心概念
JSX主要用于声明React的元素,但React本身并不强制使用JSX,即便使用了也会通过插件编译为React.createElement(...)的代码。
这么一分析:感觉JSX更像是React.createElement(...)的一个语法糖
// 不使用JSX
class Hello extends React.Component{
render(){
return React.createElement(
'div',
null,
`Hello ${this.props.toWhat}`
)
}
}
ReactDOM.render(
React.createElement(Hello,{toWhat:'World'},null),
document.getElementByld('root')
);
// 使用JSX
class Hello extends React.Component{
render(){
return <div>Hello {this.props.toWhat}</div>
}
}
ReactDOM.render(
<Hello toWhat='World'/>,
document.getElementByld('root')
);
因为React需要将组件转化为虚拟DOM树,所以开发在写代码,其实是在手写一棵结构树;**而XML 在树结构的描述上天生具有可读性强的优势。**对开发更友好。
虽然这种可读性强的代码只是给开发人员看的,因为实际在运行时都会用插件将JSX还原为 React.createElement(...) 的代码。既然能用插件,为啥不采用更友好的方式呢?
方案对比
-
模板
弱关注点分离、引入概念过多
-
字符串模板
结构描述复杂、语法提示差
-
JXON
语法提示差
总结
曾经有三份真挚的"爱情"摆在React团队面前,它选择发明了JSX,但并不强制使用它。为了代码更简洁,代码更结构层次清晰,对开发更友好。
Babel插件如何实现JSX编译为JS?
- 原理
babel读取代码并解析生成AST,再将AST传入插件进行转化,转化时就可以将JSX的结构转会为React.createElement(...)的函数代码。
// jsx-plugin 源码
module.exports=function(babel){
var t = babel.types;
return {
name:"custom-jsx-plugin",
visitor:{
JSXElement(path){
var openingElement = path.node.openingElement;
var tagName openingElement.name.name;
var = args [];
args.push(t.stringLiteral(tagName));
var attribs = t.nullLiteral();
args.push(attribs);
var reactldentifier = t.identifier("React");//object
var createElementldentifier = t.identifier("createElement");
var callee = t.memberExpression(reactldentifier,createElementldentifier);
var callExpression = t.callExpression(callee,args);
callExpression.arguments = callExpression.arguments.concat(path.node.children);
path.replaceWith(callExpression,path.node);
}
}
}
}
- 如何运行?
-
新建BabelTest,新建.babelrc文件
.babelrc是Babel的配置文件
{ "plugins":[] }
-
先实现一个解析JSX文件的插件(./jsx-parser)
代码内容主要就是把jsx文件的识别写入插件中
// ./jsx-parser module.exports function(){ return{ manipulateOptions:function manipulateOptions(opts,parserOpts){ parserOpts.plugins.push("jsx"); } }
-
将./jsx-parser文件其加入plugins
{ "plugins":["./jsx-parser"] }
-
新建jsx-plugin文件,将 jsx-plugin源码 复制进去
{ "plugins":["jsx-plugin","./jsx-parser"] }
-
最后验证
-
新建hello.jsx
function Test(){ return <div>Hellow word!!</div> }
-
执行命令
npm install babel-cli -g # 如未安装babel 则报错 = bash:command not found:babel babel hello.jsx
-
知识点支撑
React 设计初衷
React.createElement(...) 的代码对开发并不友好,React团队决定找到一种方案去解决这个问题。但React团队并不想引入除javascript本身以外的开发体系,而是希望通过合理的关注点分离保持组件化开发。(😎React:我想站着也把钱给挣了)
关注点分离
关注点分离在计算机科学中,是将代码分隔为不同部分的设计原则,是面向对象的程序设计的核心概念 关注点分离的价值在于简化程序的开发和维护 当关注点分开时,各部分可以重复使用,以及独立开发和更新;
具有特殊价值的是能够稍后改进或修改一段代码,而无须知道其他部分的细节必须对这些部分进行相应的更改。
-
模板
需要新的模板语法、模板指令
-
模板字符串
代码结构更复杂,开发工具的代码提示也困难
-
JXON
代码提示困难
JavaScript XML
JSX就是Javascript和XML结合的一种格式。React发明了JSX,可以方便的利用HTML语法来创建虚拟DOM
当遇到
<
,JSX就当作HTML解析,遇到{
就当JavaScript解析.
注意1:它不是字符串, 也不是HTML/XML标签
注意2:它最终产生的就是一个JS对象
基本语法
-
定义虚拟dom时,不要写引号
var ele=<h1>Hello JSX!</h1>
-
标签中混入js表达式时要使用 { }
<div className={staly.class} style={staly.class}></div>
-
样式的类名不使用class ,使用className
<div className='class1 class2 ...'></div>
-
内联样式要写 {{ color : 'white' }}
<div style={{zIndex:100,backgroundColor:'red'}}></div>
-
JSX必须只有一个根标签
-
JSX标签必须闭合标签
-
标签首字母
若小写字母开头,则将标签转为html中的同名元素,若html中无改标签对应的同名元素,则报错。(即小写字母开头的标签的是原生标签)
若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。(即大写字母开头的标签的是自定义标签)
当采用引入的方式写React
当你采用引入的方式写ReactJS,需要转码
<script type="text/babel"></script>
babel.js的作用
浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行
只要用了JSX,都要加上type="text/babel", 声明需要babel来处理
踩坑
-
正确的注释
<Fragment> //错误注释的写法 <div> <input value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> <button onClick={this.addList.bind(this)}> 增加服务 </button> </div> <Fragment> {/* 正确注释的写法 */} <div> <input value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> <button onClick={this.addList.bind(this)}> 增加服务 </button> </div>
-
正确的label的for使用
{/* 错误的写法 */} <div> <label for="jspang">加入服务:</label> <input id="jspang" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> <button onClick={this.addList.bind(this)}> 增加服务 </button> </div> {/* 正确的写法 */} <div> <label htmlFor="jspang">加入服务:</label> <input id="jspang" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> <button onClick={this.addList.bind(this)}> 增加服务 </button> </div>
最后一句
学习心得!若有不正,还望斧正。希望掘友们不要吝啬对我的建议。