React框架基础
React是一个“用于构建用户界面的 JavaScript 库 ”。官方提供了两种快速开始的方案,一种是使用在线的CodePen或者CodeSandbox,另一种是下载由官方提供的最小模板文件,浏览器打开即用。这两种方案都非常快捷方便,不需要事先了解Node.js和npm甚至是webpack。
JSX
JSX是一种新鲜的写法,三大框架里React独有,类似HTML和JavaScript混写。虚拟DOM是React一大亮点,一个组件即一个虚拟DOM节点,组件又是由JSX编写组成的。Babel官方提供的试用工具可以直观说明JavaScript对JSX的解析方式,在左侧写入这段代码:
右侧是这样的输出:
可见,Babel会将组件转换为函数调用,一个组件对应一个React.createElement()函数,并传入至少三个参数。第一个参数是组件名称,第二个参数是属性对象,第三个参数是组件的值,之后为可变参数,组件包含多少个子组件,就有多少个函数作为参数传入,函数之间层层嵌套,对应组件之间的嵌套关系。
React框架一般会引入两个包使用,一个是React,一个是ReactDOM。React包含框架的核心API,ReactDOM主要负责对页面节点的操作,比如上面的ReactDOM.render()向页面渲染了一个组件。正是因为React的虚拟DOM机制,使得框架在更新数据时不会更新整个页面,而只是更新必要的部分。这依赖于React的DOM diff算法。
组件
组件的定义有两种方式,一种是函数定义组件,另一种是类式组件,即定义一个继承React.Component的类。如果是简单组件,只返回简单元素内容,或者对传入参数做简单处理,可以使用函数式组件,不过最好还是使用类来定义组件,语义上更加直观。
组件可以是DOM标签,也可以是非DOM元素。如果是非DOM元素,组件名首字母必须大写。所有组件必须闭合。
组件之间通过props传递属性,例如:
代码中向ReactDOM.render()方法传入两个参数,第一个参数是要渲染的组件,只允许传入一个组件,即如果包含多个组件,必须用父组件包裹。第二个参数是要渲染到页面的具体DOM节点对象。
上面的Foo是一个自定义组件,在渲染函数中调用,并传入了属性名string。Foo的入参props是框架内置,类似于可变参数,所有传入的属性都是作为props的属性,比如props.string。当然props是形式参数,名称可以随意更改。
在传入参数属性的时候,也要注意React和HTML DOM的某些属性有一些区别。比如,想要为一个div组件传入CSS类名class,推荐使用className而不是class。说推荐的原因是,为了兼容错误的写法,React允许在传入class属性的时候仍然能够正确显示出类名,但这显然不是期望的情况。
与HTML DOM差异较大的是style属性,毕竟JavaScript不支持-作变量名,所以想要表达background-color时,可以使用backgroundColor这样的驼峰式命名法来代替。另外要注意style属性接收的值为一个对象,而不再是字符串,因此可能出现这样的情况:
这样的写法等同于:
这并不难理解,只是简单的变量代换,和Vue.js的模板语法没有关系。
最后,要知道组件的属性是只读的,官方强制规定,必须像纯函数那样使用组件的props。官方提倡单向数据流的理念。如果对props进行赋值,则编译无法通过。
事件处理
React中的事件处理与DOM元素语法不同。因为组件有两种写法,所以事件处理的方式也分两种情况。例如函数式组件:
代码渲染Foo组件到页面中,Foo组件返回了一个超链接,指向上级目录。超链接上还指定了事件处理函数doSomething(),响应click事件。
事件属性类似CSS属性名的写法,值为对象,传入函数名称,不写小括号。注意事件要写到a组件上,而不是它的父组件Foo上,或者说事件要写到DOM组件上,而不是自定义组件上。Foo组件在页面上不会被渲染出具体DOM节点,事件自然无法响应。自定义组件只是帮助更好的组织DOM组件,最终的页面上绝不会出现自定义组件。
点击事件交由doSomething()函数处理,JavaScript支持函数嵌套,可以直接在函数内定义处理函数,也可以将处理函数doSomething()提升到外部,和函数式组件并列。但并列定义的做法对代码结构并不友好。
处理函数接收事件对象e为参数,调用方法e.preventDefault()阻止组件的默认行为。React中return false并不能够阻止默认行为。
类式组件的事件处理:
注意调用类中的方法要使用this关键字。这段代码可以正常运行。
状态&生命周期
类式组件中的状态异常重要,也是React实现单向数据流的关键。ES6引入了真正意义上的class,在没有其他React特性加持的情况下,可以这么写:
程序打印出了属性v的值,但React不止于此。例如Vue.js数据的双向绑定,提供了非常大的便利。React渲染页面是通过ReactDOM.render()方法,默认只执行一次。如果想要在程序执行过程中改变属性值,并渲染到页面呢?这时就涉及到两个概念,状态和生命周期。
状态即React框架内置的一个类属性state,值类型为对象。说它重要的原因在于,React提供了setState()方法,这个方法完成两件事情:更新state的值、将state的新值渲染到页面。
在上面代码的基础上,添加一些代码:
这里处理了h1组件的点击事件,注意需要在构造函数中让处理函数绑定this。在doSomething()中调用了setState()方法,点击h1的内容,页面内容会被渲染更改。如果不使用setState(),而是直接对state赋值,比如this.state = {v: "789"}或者this.state.v = "789",state的值也同样会改变,可以在程序中打印输出验证。但是直接赋值并不会让页面重新渲染,如果不进行其它处理,新值将无法显示到页面。
关于页面渲染,下面是一张来自网络的图片:
React有多个生命周期函数供使用,可以在程序运行的各个阶段进行需要的操作。
表单
React中的表单有两种管理方式,称受控表单和非受控表单。
下面是简单的受控表单的实现:
在状态中声明属性,监听input的onchange事件,通过默认参数event实时获取input的值。在事件处理函数中,调用setState()方法,使用input值更新状态值。这样就可获取用户的输入。
另一种是非受控组件:
在使用ref属性的情况下,this.input的值是真实的input节点,input的输入值为this.input.value。这样的输入值没有被state控制,所以叫非受控组件。