时间:2021-07-01 10:21:17 帮助过:6人阅读
学会读优秀代码,比忙着写,远远重要的多。
怎样才是优秀的代码?明确清晰。而怎样才能写出明确清晰的代码?思路清晰,逻辑清楚,语句直白不晦涩的人,自然能写出明确清晰的代码,代码自然是模块化可复用。
HTML规范就是讲应该怎么建立web文档结构,如果产品经理在设计产品结构时也能把HTML规范考虑进去,后面的UI设计师到前端工程师在工作时都会容易很多。
数据导向,一般来说都是内容决定形式,只有保证内容有效传达给受众后,才会考虑那些形式的花边。
先给幕后的数据建模,把内在数据模型映射到界面组件上。
single responsibility principle, that is, a component should ideally only do one thing.單一職責原則 SRP,一个组件应该只做一件事。
hierarchy 层次结构
从最小组件开始搭建视图层级结构,明确嵌套关系,确定终端组件和分组容器类组件。
先建立React单向数据流的静态文档,使用props实现。
在静态文档的基础上,建立反向数据流,实现交互逻辑,使用state实现。
state的要求,absolute minimal 绝对最小化,DRY: Don’t Repeat Yourself 没有冗余。
找到交互的源头,找到会改变幕后数据的终端组件,找出会引发改变的根本性数据作为state的记录项。一切可以通过其他数据计算获得的,都不作为state。
state的初始化位置,一般在所有会被这个state集合影响的组件最外层的容器组件那里。
state初始化完成后,建立监听处理函数,watch and update state。
React的流程要求,所有因为交互而导致显示改变,都必须从终端组件传递到state所在外层组件,更新state后,再从外层组件传回终端显示。
https://facebook.github.io/react/docs/thinking-in-react.html
React的核心是Virtual DOM和Synthetic Events。Virtual DOM可以理解为在JavaScript用一组对象虚拟实现了一套文档对象模型,Virtual DOM对应DOM。同理,Synthetic Events对应DOM events。
React Nodes React虚拟DOM节点:
ReactElement
ReactText (string and number)
ReactFragment (Array of ReactNodes)
虚拟DOM树的实现,是通过ReactElement对象的属性方式,子节点对象都是父节点对象的属性值。
一个ReactElement对象就是一个DOM Element在JavaScript中的虚拟再现。ReactElement对象在创建后是轻量的,无状态的,不可变的。
ReactElement对象的props属性值是一个键值对形式的集合对象。它部分对应的是DOM Element的属性和属性值。要在最终HTML标签上显示的自定义属性名字要求和W3C规范一样,必须有”data-“前缀。每个ReactElement对象创建时,React都会给它的props里自动添加一个名为data-reactid的自定义属性,它的值是这个元素在虚拟DOM中的唯一标识ID。
并不是所有props object里的属性都会一一创建为DOM Element的属性,只有符合W3C规范的属性名,比如HTML元素的属性和加了”data-“,”aria-“前缀的属性才会在DOM Element标签上显示出来。props object有一个children的属性,它是一个ReactElement对象拥有的其他ReactElement对象的集合,这个属性就不会出现在最终的HTML标签里面。
class and for虽然是HTML元素的属性名,但是因为是JavaScript的保留关键字,所以必须用className and htmlFor替代,React会正确转换成标准属性名显示在最终的HTML标签中。
props对象里的style属性的值也是一个键值对形式的集合对象。它的属性名支持所有CSS属性,只是要采用camelCased命名法,最终React也会正确转换成CSS标准属性名。属性值的要求和CSS规范一致。
ReactElement对象的ref属性值是一个回调函数,函数有一个参数就是ReactElement对象对应的native browser DOM element。
创建ReactElement对象要使用React.createElement方法。
React.DOM对象内置了快速创建特定ReactElement对象的方法,方法名和DOM HTML Element名一样。比如像创建一个虚拟的div元素,使用React.DOM.div()方法。
当需要快速批量的创建一组ReactElement对象或者根据数据的不同创建不同ReactElement对象时,需要使用ReactClass对象。根据ReactClass对象创建的ReactElement对象也叫做React Component组件。React Component是基于ReactElement对象的概念,它是对一个ReactElement对象,它可能拥有其他ReactElement对象,以及和这些对象相关的数据和行为的封装。
ReactClass对象是拥有React Components Constructors的包装器wrappers。它的原型对象是React.Component对象。
可以用React.createClass方法创建ReactClass对象。因为ReactClass对象只是拥有Constructors的wrappers,不要用new ReactClass的方式创建组件实例instances。使用React.createElement方法获得React Component的ReactElement对象。
在ECMAscript新规范中新加了基于类编程的syntactic sugar句法糖。React也提供这种实现方式,可以不用React.createClass方法,而是用extends React.Component的方式创建一个ReactClass对象。
注意,所有基于类syntactic sugar句法糖实现的ReactClass对象,属性方法都没有自动绑定this。No Autobinding。请在需要的地方显式绑定this为ReactClass对象。
React.createClass方法的参数是一个对象,这个对象必须有一个名为render的属性,这个属性值是个函数,函数返回值必须是一个ReactElement对象,它可以拥有其他ReactElement对象。
React.createElement方法的参数说明:
第一个参数,HTML Elements Name HTML元素名(加引号的字符串)或者ReactClass对象(通常是指向此对象的变量名,首字母大写);
第二个参数,可选,properties属性集合(通常是key-value pairs形式的属性对象或者指向此对象的变量名,作为根据第一个参数创建的元素的属性,可以传null);
后面是不定数可选参数,children 0个以上元素(全部是兄弟元素,父元素是根据第一个参数创建的元素)。
HTML tags are used to delimit the start and end of elements in the markup. Tags contain a tag name, giving the element’s name.
React.DOM对象的HTML元素名方法的参数就是去掉React.createElement方法的第一个参数,之后参数要求一样。
另一种快捷创建ReactElement对象的方法是React.createFactory方法,React.createFactory方法只接受一个参数,和React.createElement方法的第一个参数要求一样,返回一个生成器函数auto-generated factory function,这个生成器的使用和React.DOM对象的HTML元素名方法一样。
ReactDOM对象是用来操作DOM的,包括virtual DOM和actual DOM。有了ReactElement对象或者组件后,就需要使用ReactDOM.render方法来把它们加入到virtual DOM和actual DOM。
ReactDOM.render方法有三个参数,第一个是ReactElement对象;第二是DOM容器节点container node;第三个是虚拟元素对应的DOM元素创建成功后的回调函数,可选参数。
使用ReactDOM.render方法的时候,并不一定就会操作actual DOM,render方法做了很多事情,它会检查传入的ReactElement,是不是已经存在于虚拟DOM,如果已经存在,有没有所不同等等。
当一个ReactElement对象第一次作为参数传入ReactDOM.render方法时,React会把该对象加入到虚拟DOM Tree中,并且在真实DOM中新建对应的native browser DOM element,然后返回该DOM Element的引用。返回有例外情况,后面说。
注意:
ReactDOM.render()管理你传入的DOM容器节点的内容。在第一次调用的时候,所有这个容器节点内现存的DOM元素都会被替换掉。之后的调用会比较不同后更新不同的部分。
ReactDOM.render()不修改容器节点本身,只修改容器节点的子节点。在将来,可能只是把新的DOM Element添加到现存子节点之后,而不动现存子节点。
如果只是想获得ReactElement对象对应的ative browser DOM element的HTML markup字符串,可以通过ReactDOMServer对象。
一般约定,构造器对象Constructors的变量名,首字母大写。实例instances的变量名,首字母小写。示范代码:
var MyComponentClass = React.createClass({/*...*/});var myComponentElement = React.createElement(MyComponentClass);ReactDOM.render(myComponentElement, document.getElementById('example'));
所有使用React.createElement方法的地方,都可以替换成JSX形式。比如前面的示范代码:
var myComponentElement = React.createElement(MyComponentClass);//替换成JSXvar myComponentElement = ;
JSX is a XML-like syntax extension to ECMAScript without any defined semantics. 会写XML就会写JSX。
由于JSX最终会被React转换成JavaScript代码执行,所以不要在JSX中使用JavaScript的保留关键字Reserved Words作为标签名和属性名,ReactClass的标签名也不要使用HTML标签名,也就是在创建ReactClass的时候,ReactClass的变量名就不要使用JavaScript Reserved Words和HTML Element’s Name。
因为ReactClass变量名首字母大写,自然对应的JSX自定义标签名也一样,而用React.createElement方法创建对应HTML元素的虚拟HTML元素时,就是使用HTML元素名,所以对应的JSX标签名也直接使用HTML标签名。
在JSX中可以直接写HTML Entities,转义字符串(Escape Sequence)也称字符实体(Character Entity)。需要注意的是如果是在花括号里则会按JavaScript 字符串处理。
组件的displayName属性是用来调试的,如果没有赋值,在JSX中会自动赋一个值。
The JSX expression always evaluates to a ReactElement. JSX表达式的值是一个ReactElement。和React.createElement方法的返回值一样,JSX编译后就是一堆React.createElement方法。
Namespaced Components命名空间组件。ReactClass对象的属性可以是另一个ReactClass,这个ReactClass属性被认为是ReactClass对象的子组件。可以通过属性访问器的形式作为React.createElement方法第二个参数后面的不定参数使用。同理可以用于JSX的标签名。
在JSX中使用JavaScript,用花括号把JavaScript代码括起来就可以,但是并不是所有JavaScript代码都能在转换后有效执行。
Attribute Expressions,用于React.createElement方法第二个参数的属性对象里,key冒号后可以使用表达式。在JSX中,属性名的等号后面用花括号把表达式括起来。
Boolean Attributes ,JSX基本实现了HTML的语法规则,所以属性值是Boolean类型的情况,规则和HTML一样。如果要显式赋值,请在属性名的等号后面用花括号把Boolean值括起来。
Child Expressions,在React.createElement方法的最后的不定数参数,可以使用值是ReactElement的表达式。在JSX中用花括号把表达式括起来。
Comments,在JSX中用花括号把注释括起来。
React.createElement方法第二个参数的属性对象,可以在方法调用前,先创建好这个对象,并作为参数传入。在JSX中,可也在写属性的地方使用这个对象,并且改变对象的数据,需要JavaScript代码,比如使用spread operator,因为扩展运算符只有用于可遍历对象才有效,说明props是个可遍历对象iterable object。示例代码:
var MyComponentClass = React.createClass({/*...*/});var props = { foo: 'default' };var myComponentElement = ;console.log(myComponentElement.props.foo); // 'override'
在ReactClass对象中定义一个名为getDefaultProps的方法,可以给这个组件配置默认的props数据。
React.PropTypes对象输出一系列验证器,它能用来确保组件收到的props数据是通过了校验的。当props对象的某个属性不能通过验证器时,JavaScript console将显示一个警告。React.PropTypes对象在ReactClass对象中定义,它是ReactClass对象的一个名为propTypes的属性。
如果是基于类syntactic sugar句法糖实现的ReactClass对象,在类声明后,直接使用类属性的方式给defaultProps和propTypes属性赋值即可。
Interactivity and Dynamic UIs,交互和动态UI的实现。
最基本的实现,首先为ReactClass对象实现一个名为getInitialState方法,方法返回值必须是一个键值对形式的集合对象。ReactClass对象的其他属性方法里面,可以通过this.state访问getInitialState方法创建的对象。render方法里面可以根据state的数据来创建ReactElement,这就实现了Dynamic虚拟元素,进一步也就实现了Dynamic UIs。当然只有state数据是动态的,元素才是动态。改变state的数据,可以在ReactClass对象里定义操作state数据的函数,函数内调用this.setState方法,这个函数可以是服务器驱动,或者用户驱动。用户驱动则就实现了交互。一般是把虚拟元素的交互行为属性赋值为操作数据的函数的引用,React并没有把函数绑定到最终实际的DOM元素上,所有的函数管理都是在React内完成。
注意如果是基于类syntactic sugar句法糖实现的ReactClass对象,不用写getDefaultProps的方法,直接在constructor构造函数中,使用this.state赋值即可。
this.state属性对外是只读,不可写的。只能通过setState方法Performs a shallow merge of nextState into current state.而且使用setState方法加入state新数据,也不是即时的,而是加入缓存中,只有当访问this.state时用到了新加入的数据时,才会正式并入。
没有state数据的组件也叫做无状态组件stateless component。一般来说,除了给state赋值的那个容器组件,它包含的组件都是无状态的,它们都不应该直接通过this.state来获得数据,而是通过拥有state数据所有权的容器组件传递的props对象来获得数据。这种关系叫做owner-ownee relationship,这种数据传递叫做data flows from owner to owned。
如果是无状态组件stateless component,ReactClass对象可以通过一个普通函数创建,这种函数叫做stateless functions。通常使用箭头函数的形式。
如果是stateless components则ReactDOM.render方法返回null。对应ReactDOM.render方法,ReactDOM.unmountComponentAtNode方法是用来移除Stateful Components。除了ReactDOM.render方法会返回stateless components对应的native browser DOM element外,还可以通过ReactDOM.findDOMNode方法获得。
无状态组件要想获得对应的DOM Elementls,只能通过ref属性。
在React机制里,所有DOM元素的显示都应该是通过对应的虚拟DOM元素渲染实现,并且ReactElement的显示数据都来自容器组件的props对象。通过给虚拟表单元素的props对象的value属性赋值一个非null的值,React就会控制DOM的表单文本元素的用户输入显示。这种控制权被React接管的组件叫做Controlled Components。接管控制权的方式是把DOM原生事件nativeEvent委托delegation给React的SyntheticEvent。
input and textarea是通过DOM’s built-in oninput event handler。checkbox and radio inputs是通过DOM’s built-in click event handler。
如果不想React控制DOM表单文本元素,只要不给value赋值,或者赋值为null即可,这种组件叫做Uncontrolled Components。如果想给表单一个默认值,但又不想控制它,可以通过props对象的defaultValue或者defaultChecked属性赋值实现。
Component Lifecycle组件生存期的概念依赖于ReactDOM.render方法和虚拟DOM。组件生存期有三个关键时间点,第一个是诞生日Mounting,第二个是活动日Updating,第三个是死亡日Unmounting。
Mounting,当组件第一次传入ReactDOM.render方法时,则成为虚拟和真实DOM Tree的一个Node。这个时间点会依次调用组件的几个方法,如果是状态组件,首先会调用getInitialState(): object,然后是componentWillMount()和componentDidMount()。
Updating,只有状态组件才有的时间点,当组件的this.setState()方法被调用或者组件被再次传入ReactDOM.render方法时会触发这个事件。可能会依次调用这些方法componentWillReceiveProps(object nextProps) shouldComponentUpdate(object nextProps, object nextState): boolean componentWillUpdate(object nextProps, object nextState) componentDidUpdate(object prevProps, object prevState) 。如果是composite components,还可能会调用component.forceUpdate()。
Unmounting,组件从DOM中被移除和销毁前,调用componentWillUnmount()。
为了让不同组件可以共享一些功能。在ReactClass对象中定义一个名为mixins的属性,值是数组,数组内的数据,是拥有特定方法的对象。如果一个ReactClass对象的mixins数组有多个功能对象,并且它们定义了相同的lifecycle methods,都会执行,按数组顺序执行。注意如果是基于类syntactic sugar句法糖实现的ReactClass对象还不能支持mixins。
React插件是一组工具模块集合。
React动画插件require(‘react-addons-css-transition-group’)本身并不实现动画,动画还是靠CSS,它提供一组方法按时间来管理组件的类名,可以指定某个组件什么时间添加什么CSS类名,并且保持多长时间,再移除或者更换其他CSS类名。
React双向绑定插件require(‘react-addons-linked-state-mixin’),ReactLink is just a thin wrapper and convention around the onChange/setState() pattern,它是基于mixins特性实现的,在LinkedStateMixin对象里面,有一个linkState的方法。
React调试工具插件require(‘react-addons-test-utils’),ReactTestUtils.Simulate对象可以模拟触发一个DOM Element事件进行测试,这样就可以不需要浏览器和用户交互行为来触发事件。另外还有很多工具对象。。。
ReactFragment插件require(‘react-addons-create-fragment’),它的作用是为了在不触发DOM的unmount和remount事件前提下调整已经Mounted装载的组件。通过类似createFragment方法把已装载组件归为一块,虽然参数是key-value形式的对象,但是最终是数组形式保存。Array createFragment(object children) 。JSX使用x:frag标签形式。
至于Flux和Redux,如果有过MVC框架的使用经验就好理解了。
从react网站凌乱而简陋的文档也能感觉到react正处于高速发展的初期。