重学React ,第二天

33 阅读4分钟

总的来说

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);
           }
       }
    }
}
  • 如何运行?
  1. 新建BabelTest,新建.babelrc文件

    .babelrc是Babel的配置文件

    {
        "plugins":[]
    }
    
  2. 先实现一个解析JSX文件的插件(./jsx-parser)

    代码内容主要就是把jsx文件的识别写入插件中

    // ./jsx-parser
    module.exports function(){
        return{
            manipulateOptions:function manipulateOptions(opts,parserOpts){
                parserOpts.plugins.push("jsx");
        }
    }
    
  3. 将./jsx-parser文件其加入plugins

    {
        "plugins":["./jsx-parser"]
    }
    
  4. 新建jsx-plugin文件,将 jsx-plugin源码 复制进去

    {
        "plugins":["jsx-plugin","./jsx-parser"]
    }
    
  5. 最后验证

    • 新建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. 注意1:它不是字符串, 也不是HTML/XML标签

  2. 注意2:它最终产生的就是一个JS对象

基本语法

  1. 定义虚拟dom时,不要写引号

    var ele=<h1>Hello JSX!</h1>
    
  2. 标签中混入js表达式时要使用 { }

    <div className={staly.class} style={staly.class}></div>
    
  3. 样式的类名不使用class ,使用className

    <div className='class1 class2 ...'></div>
    
  4. 内联样式要写 {{ color : 'white' }}

    <div style={{zIndex:100,backgroundColor:'red'}}></div>
    
  5. JSX必须只有一个根标签

  6. JSX标签必须闭合标签

  7. 标签首字母

若小写字母开头,则将标签转为html中的同名元素,若html中无改标签对应的同名元素,则报错。(即小写字母开头的标签的是原生标签)

若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。(即大写字母开头的标签的是自定义标签)

当采用引入的方式写React

当你采用引入的方式写ReactJS,需要转码

<script type="text/babel"></script>

babel.js的作用

  1. 浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行

  2. 只要用了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>
    

最后一句

学习心得!若有不正,还望斧正。希望掘友们不要吝啬对我的建议。