React.js 入门(一)
React 是由 Facebook 创建的一个声明式的用于构建 UI 的 JS 库。
借助 React 通过小的、相互独立的代码片段(component,即组件)可以构建复杂的用户界面。
对已有网页添加 React 最简单的方式是,以 <script>
标签引入 React(顶层API)、React DOM(DOM相关方法)、Babel(在旧浏览器里使用ES6+)外部js文件。
但随着应用不断增长变复杂,为了处理一些如:大量的文件、组件,使用 npm 上的第三方库,提前检测语法/代码错误,热更新CSS/JS,优化打包文件等,我们引入官方建议的 toolchain。
JSX
即 JavaScript XML,它是 JavaScript 语法的扩展,在编译后转换成 React.createElement(标签名称,属性对象,子组件)
,即常规的函数调用 。
一个事实:渲染逻辑天然地与UI逻辑耦合。(比如:在 UI 中绑定事件、在某些时刻状态发生变化时需要通知到 UI,以及需要在 UI 中展示准备好的数据。)
官方建议在 React 中使用 JSX 创建树节点(描述 UI),在写 JS 代码时提供视觉上的辅助,易于阅读,同时允许 React 显示更多有用的报错和 warning 信息。
默认情况下,React DOM 在渲染前 escapes 任何嵌入 JSX 的值,因此在 JSX 中插入用户输入是安全的(不必担心XSS)。
用法
- 用 camelCase(小驼峰命名)来写属性和方法名
- 可以在 JSX 内的大括号
{ expression }
里书写任何有效的 JavaScript 表达式 - 使用双引号
"value"
指定 string literal 字符串字面量设置属性值 - 添加 css 样式时使用 className 代替 class
- 每个组件必须自闭合,如
<img />
- 为了便于阅读,可将 JSX 拆分为多行书写。同时将内容包裹在括号
()
中,避免return
末尾自动插入分号。 -
Namespaced Components 命名空间式的组件使得只需声明一个组件变量,让其他子组件作为其属性来获取。
var MyFormComponent = React.createClass({ ... }); var Form = MyFormComponent; var App = ( <Form> <Form.Row> <Form.Label /> <Form.Input /> </Form.Row> </Form> ); MyFormComponent.Row = React.createClass({ ... }); MyFormComponent.Label = React.createClass({ ... }); MyFormComponent.Input = React.createClass({ ... });
- 缺省特性值时,JSX 认为它就是
true
。因此,为了明确指定某个 attribute 是false
,要么不写这个特性,要么使用大括号赋值,如:disabled={false}
-
注释标记跟 JS 的 comment 一样,有单行和多行注释。当你为一个 tag 的子区域写注释时,使用大括号将这条注释括起来。
var content = ( <Nav> {/* child comment, put {} around */} <Person /* multi line comment */ name={window.isLoggedIn ? window.name : ''} // end of line comment /> </Nav> );
- React 会在生成的 raw HTML 中自动插入类似
<!-- react-text: 4 --><!-- /react-text -->
的注释,据说是它用来识别如何在 DOM 添加和替换节点。 - React 不会渲染原生 HTML 元素中不存在的特性,除非以
data-
作为其前缀。 - 支持以
aria-
为前缀的 Web 可用性特性,保持 kebab-case 的书写
更多用法参考这里:JSX 语法
JSX Spread Attributes 属性的传递
当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)转换为单个对象传递给组件,这个对象被称之为 “props”。
当组件的所有属性 props 可预知时,可以把它们一一写在组件上传递给子组件。但有时不能预知全部属性,而 props 作为 immutable 的对象,不要给对象写属性如:component.props.foo = x;
此时可以使用 JSX 的新特性,被称作 spread attributes。不直接对 component.props
操作也能解决前面的问题。
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
{...this.props}
属性的传递把组件上的 HTML attributes 都复制到底层的 HTML 元素上,save typing!
此外还可以用来合并老的 props
和额外的属性值,如下:
<Component {...this.props} more="values" />
注意:attribute 的书写顺序很重要,写在右边的覆盖前面的同名特性。
还可以指定哪个属性不往下面传递:
function FancyCheckbox(props) {
var { checked, ...other } = props;
var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
// `other` contains { onClick: console.log } but not the checked property
return (
<div {...other} className={fancyClass} />
);
}
ReactDOM.render(
<FancyCheckbox checked={true} onClick={console.log.bind(console)}>
Hello world!
</FancyCheckbox>,
document.getElementById('example')
);
如果既想要消费一个属性,又想把它传递下去,就在子组件上再明确地写下这个属性即可:
function FancyCheckbox(props) {
var { checked, title, ...other } = props;
var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
var fancyTitle = checked ? 'X ' + title : 'O ' + title;
return (
<label>
<input {...other} checked={checked} className={fancyClass}
type="checkbox"
/>
{fancyTitle}
</label>
);
}
React Elements
是 React 应用最小构建块。
const element = <h1>Hello, world</h1>;
React 元素也是 JSX 表达式,可等价于 JS Object。因此可以:
- 赋值给变量
- 作为函数入参
- 作为函数返回
- 在
if
声明和for
循环里使用
React DOM 负责更新 DOM 以匹配的 React element。
以 React 构建的应用只有一个 root DOM 节点。对已有的项目接入 React,则可有多个 isolated 的 root DOM 节点。
React 元素是 immutable 不可变的,一旦创建,不能改变它的子元素或属性。
目前唯一更新UI的方式是创建一个新的元素,并传给 ReactDOM.render()
。实践中,大部分 React 应用只调用一次。
React Components
从概念上讲,React 组件类似 js 函数接受任意输入 props
(properties 的缩写),组件通过 render()
返回 React element(一种对待展示的视图层次结构的轻量级描述)。
以 function、class 形式定义组件:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
const element = <Welcome name="Sara" />;
- 自定义 component 采用首字母大写的驼峰标记,与常规 HTML 元素区分开。
- 之前 React class 组件需要包含
constructor()
,现在不必了 - 函数组件没有自己的 state
- 在 .js 文件里,函数组件可以与 class 组件混用
组件拆分原则
如果 UI 中某个部分被使用多次(如:按钮、panel、avatar),或者组件自身足够复杂,最好将其提取到独立组件里。