1. Project Overview & Quickstart (NervJS/versioned_docs)
versioned_docs
Open-source repository NervJS/versioned_docs
Repository Details
- Repository: NervJS/versioned_docs
- Primary Language: Code
Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.
2. Official Technical Reference & Guides (NervJS/docs)
File: README.md
Nerv
一个基于virtual dom的类React组件框架.
File: guides/api.md
API
Nerv 是Nerv库的入口点。如果你通过 <script> 标签加载Nerv,这些高阶API可用于 Nerv 全局。如果你使用ES6,你可以使用 import Nerv from 'Nerv' 。如果你使用ES5,你可以使用 var Nerv = require('Nerv') 。
`Nerv.Component`
用 ES6 类 定义时,Nerv.Component是Nerv组件的基类。
class Greeting extends Nerv.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}有关 Nerv.Component 的方法和属性列表,请参阅 Nerv.Component。
`Nerv.PureComponent`
请参阅 Nerv.Component。
`createElement()`
Nerv.createElement(
type,
[props],
[...children]
)根据给定的类型创建并返回新的 Nerv element 。参数type既可以是一个html标签名称字符串(例如'div' 或 'span' ),也可以是一个 Nerv component 类型(一个类或一个函数)。
Nerv.DOM 提供了DOM组件的 Nerv.createElement() 的便捷包装。举个例子,Nerv.DOM.a(...) 是 Nerv.createELement('a', ...) 的一个便捷包装。这个用法被认为是过时的,我们推荐您使用JSX,或者直接使用 Nerv.createElement() 。
用 JSX 编写的代码会被转换成用 Nerv.createElement() 实现。如果使用了JSX,你通常不会直接调用 Nerv.createElement() 。
`cloneElement()`
Nerv.cloneElement(
element,
[props],
[...children]
)以 element 作为起点,克隆并返回一个新的Nerv元素(Nerv Element)。生成的元素将会拥有原始元素props与新props的浅合并。新的子级会替换现有的子级。来自原始元素的 key 和 ref 将会保留。
Nerv.cloneElement() 几乎相当于:
<element.type {...element.props} {...props}>{children}</element.type>然而,它也保留了 ref。这意味着,如果你通过 ref 获取到子级组件时,不会一不小心从祖先组件里窃取了它。你将获得与你新元素相同的 ref 。
`isValidElement()`
Nerv.isValidElement(object)验证对象是否是一个Nerv元素。返回 true 或 false 。
`Nerv.Children`
Nerv.Children 提供了处理 this.props.children 这个不透明数据结构的工具。
`Nerv.Children.map`
Nerv.Children.map(children, function[(thisArg)])在包含在 children 里的每个子级上调用函数,调用的函数的 this 设置为 thisArg 。如果 children 是一个嵌套的对象或数组,它将被遍历。如果 children 是 null 或 undefined ,返回 null 或 undefined 而不是一个空数组。
`Nerv.Children.forEach`
Nerv.Children.forEach(children, function[(thisArg)])类似 Nerv.Children.map() ,但是不返回数组。
`Nerv.Children.count`
Nerv.Children.count(children)返回 children 中的组件总数,相等于传给 map 或 forEach 时,回调函数被调用次数。
`Nerv.Children.only`
Nerv.Children.only(children)返回children里仅有的子级。否则抛出异常。
`Nerv.Children.toArray`
Nerv.Children.toArray(children)返回以赋key给每个子级 child 的扁平数组形式来组成不透明的 children 数据结构。如果你打算在你的渲染方法里操纵子级集合这很有用,特别是你想在 this.props.children 传下之前对它重新排序或切割。
Note:
当children是扁平列表时,
Nerv.Children.toArray()改变key来保留嵌套数组的语义。也就是说,为了在展开时保留嵌套数组的语义,toArray会自动的给数组中每个 key 加了上前缀,以便将每个元素的key被限定到包含它的输入数组。
`render()`
Nerv.render(
element,
container,
[callback]
)渲染一个Nerv元素,添加到位于提供的container里的DOM元素中,并返回这个组件的一个引用。
如果这个Nerv元素之前已经被渲染到container里去了,这段代码就会进行一次更新,并且只会改变那些反映元素最新状态所必须的DOM元素。
回调函数是可选的。如果你提供了,程序会在渲染或更新之后执行这个函数。
注意:
Nerv.render()控制你传进来的容器节点里的的内容。第一次被调用时,内部所有已经存在的DOM元素都会被替换掉。之后的调用会使用Nerv的DOM比较算法进行高效的更新。
Nerv.render()不会修改容器节点(只修改容器的子项)。你可以在不覆盖已有子节点的情况下添加一个组件到已有的DOM节点中去。
Nerv.render()目前会返回一个引用, 指向Nerv.Component的根实例。但是这个返回值是历史遗留,应该避免使用。因为未来版本的Nerv可能会在某些情况下进行异步渲染。如果你真的需要一个指向Nerv.Component的根实例的引用,推荐的方法是添加一个 callback ref 到根元素上。
`unmountComponentAtNode()`
Nerv.unmountComponentAtNode(container)从DOM元素中移除已挂载的Nerv组件,清除它的事件处理器和state。如果容器内没有挂载任何组件,这个函数什么都不会干。
有组件被卸载的时候返回true,没有组件可供卸载时返回 false。
`findDOMNode()`
Nerv.findDOMNode(component)如果这个组件已经被挂载到DOM中,函数会返回对应的浏览器中生成的DOM元素 。
当你需要从DOM中读取值时,比如表单的值,或者计算DOM元素的尺寸,这个函数会非常有用。
大多数情况下,你可以添加一个指向DOM节点的引用,从而完全避免使用findDOMNode 这个函数. 当 render 返回 null 或者 false 时, findDOMNode 也返回 null.
注意:
findDOMNode是用于操作底层DOM节点的备用方案。在大部分情况下都不提倡使用这个方案,因为它破坏了组件的抽象化。
findDOMNode只对挂载过的组件有效(也就是已经添加到DOM中去的组件)。如果你试图对一个未挂载的组件调用这个函数
(比如在一个还未创建的组件的render()函数中中调用findDOMNode()),程序会抛出一个异常。
findDOMNode不能用于函数式的组件中。
File: guides/component.md
组件
我们的页面一般都是由各种各样的组件构成的,组件就像一个一个积木一样,被用来搭建成页面。例如一个带有用户信息、logo的头部就是一个组件,而用户信息、logo也许也可以是独立组件。组件化可以让我们把一个庞大的页面切割成一个一个小块来分别进行管理,也就是分而治之的思想,同时组件化也能帮助我们更好地进行代码复用。
Nerv天生是支持组件化的,组件是Nerv中的核心概念。Nerv中的组件分为 Class Component、Stateless Component 以及 PureComponent。
Stateless Component
Stateless Component(无状态组件)是一个纯函数,它意味着只渲染 Class Component 的 render() 函数的内容。
使用 JSX:
function Hello ( { text } ) {
return Hello, { text }
}使用 createElement:
function Hello ( { text } ) {
return createElement('span', null, text)
}Stateless Component 正如它的名字一样——没有 state。但它接受一个参数 props,当 props 改变的时候,Nerv 会根据新旧 props 返回内容的不同而去渲染组件。另外一个和 Class Component 不同的地方在于,Stateless Component 没有完整的生命周期。(和 React 不一样的地方)在 Nerv 里,你可以传入一个叫 onShouldComponentUpdate 的 props 来手动判定该组件是否被更新。
Class Component
这一类组件需要继承 Nerv 的 Component 类,例如
import Nerv from 'nervjs'
class Username extends Nerv.Component {
constructor () {
super(...arguments)
this.state = {}
}
render () {
return (
Hi, Tony
)
}
}`setState`
ES2015 Component 是带有自身状态的组件,你可以在组件的内部调用 this.setState 方法来改变组件的状态。每次 setState 之后 Nerv 将会把 state 暂存到一个队列,然后在一个 eventloop 的 microtask 合并最终的 state,对比新旧 state 创建的虚拟 DOM 对象的不同,然后对真实 DOM 进行更新。或者你也可以直接调用 this.forceUpdate() 强制对比新旧虚拟 DOM,然后更新。
`forceUpdate`
一般而言,每次 setState() 更新状态都是异步的,并且 Nerv 内部会对比新旧虚拟 DOM 的不同之处再进行更新。但你可以通过调用 forceUpdate() 直接立即进行一次强制的同步更新,forceUpdate() 的造成的更新和 setState() 的更新一样,会走完整个的组件生命周期流程。
`dangerouslySetInnerHTML`
dangerouslySetInnerHTML 是 Nerv 提供的替换浏览器 DOM 中的 innerHTML 接口的一个函数。一般而言,使用 JavaScript 代码设置 HTML 文档的内容是危险的,因为这样很容易把你的用户信息暴露给跨站脚本攻击.所以,你虽然可以直接在 Nerv 中设置 HTML 的内容,但你要使用 dangerouslySetInnerHTML 并向该函数传递一个含有 __html 键的对象,用来提醒你自己这样做很危险。例如:
function createMarkup() {
return {__html: 'First · Second'};
}
function MyComponent() {
return ;
}`defaultProps`
defaultProps 是一个 Nerv.Component 的可选静态属性,当一个 Component 实例化了之后(也就是写成了 JSX 的形式或者直接被 createElement 调用),如果没有传入相应的 props ,就会给它传入默认的 defaultProps。defaultProps 只在没有传入 props 时(也就是 props 是 undefined 时)起作用,当 props 是 null 时不会传入 defaultProps。
class CustomButton extends React.Component {
static defaultProps = {
color: 'blue'
}
// ...
}`render()`
一个 render() 函数必须返回内容,返回内容是该组件将要被渲染的内容,它们必须是以下是其中任意一种类型:
- Nerv 组件。由 JSX 创建的组件,它可以是一个 Component,也可以是纯粹的 JSX 创建的虚拟 DOM,或者通过
createPortal创建的内容; - 字符串或数字。这两样内容会被渲染成 DOM 中的文本节点;
null。返回Null时 Nerv 什么也不渲染;- 布尔值。你可以这样写你的逻辑
return condition && <App />, 当condition为false时 Nerv 也不会渲染任何内容。
// 或者这样:
CustomButton.defaultProps = {
color: 'blue'
};组件生命周期
生命周期是指组件从创建到销毁一个完整的生命过程,Nerv通过定义了一系列的函数来控制组件在生命周期的各个阶段的动作。
| 名称 | 调用时机 | 传入参数 |
|---|---|---|
componentWillUnmount |
组件即将要从 DOM 中卸载 | 空 |
componentWillMount |
组件即将要加载到 DOM 中 | 空 |
componentDidMount |
组件已加载到 DOM 中 | 空 |
componentDidUnmount |
组件已从 DOM 中卸载 | 空 |
componentWillReceiveProps |
新的 props 传入组件之前 |
nextProps |
shouldComponentUpdate |
手动判断是否要更新组件,返回 false 将不会调用 render() |
nextProps, nextState |
componentWillUpdate |
组件即将要更新,在 render() 之前 |
lastProps, nextProps |
componentDidUpdate |
组件已经更新,在 render() 之后 |
lastProps, nextProps |
`PureComponent`
PureComponent 与 Component 几乎完全相同,但 PureComponent 通过 prop 和 state 的浅对比来实现 shouldComponentUpate()。
如果 Nerv 组件的 render() 函数在给定相同的 props 和 state 下渲染为相同的结果,在某些场景下你可以使用 PureComponent 来提升性能。
PureComponent的shouldComponentUpdate()只会对对象进行浅对比。如果对象包含复杂的数据结构,它可能会因深层的数据不一致而产生错误的否定判断(表现为对象深层的数据已改变视图却没有更新)。当你期望只拥有简单的 props 和 state 时,才去继承PureComponent,或者在你知道深层的数据结构已经发生改变时使用forceUpate()。或者,考虑使用 不可变对象 来促进嵌套数据的快速比较。此外,
PureComponent的shouldComponentUpate()会忽略整个组件的子级。请确保所有的子级组件也是"Pure"的。
File: guides/devtools.md
Devtools
调试 Nerv 是一件非常简单的事情。
如果你在 Chrome 安装了 React Chrome Dev Tools 之后,需要安装 nerv-devtools:
$ npm i --save nerv-devtools然后只需要在你的 Nerv.render() 函数之前引入如下代码:
import 'nerv-devtools'由于引入 devtools 之后内部会做很多不必要的操作,在你正式发布的版本中,一般而言不会包括 nerv-devtools:
if (process.env.NODE_ENV !== 'production') {
require('nerv-devtools')
}
// before Nerv.render()
Nerv.render(<App />, document.getElementById('#root'))最后就可以开心地在 Chrome Dev Tools 中使用了:
File: guides/jsx.md
JSX
本质上来讲,JSX 只是为 Nerv.createElement(component, props, ...children) 方法提供的语法糖。比如下面的代码:
<MyButton color="blue" shadowSize={2}>
Click Me
</MyButton>编译为:
Nerv.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)如果没有子代,你还可以使用自闭合标签,比如:
编译为:
Nerv.createElement(
'div',
{className: 'sidebar'},
null
)指定 Nerv 元素类型
JSX 的标签名决定了 Nerv 元素的类型。
大写开头的 JSX 标签表示一个 Nerv 组件。这些标签将会被编译为同名变量并被引用,所以如果你使用了 <Foo /> 表达式,则必须在作用域中先声明 Foo 变量。
点表示法
你还可以使用 JSX 中的点表示法来引用 Nerv 组件。你可以方便地从一个模块中导出许多 Nerv 组件。例如,有一个名为 MyComponents.DataPicker 的组件,你可以直接在 JSX 中使用它:
import Nerv from 'Nerv';
const MyComponents = {
DatePicker: function DatePicker(props) {
return Imagine a {props.color} datepicker here.;
}
}
function BlueDatePicker() {
return <MyComponents.DatePicker color="blue" />;
}首字母大写
当元素类型以小写字母开头时,它表示一个内置的组件,如 或,并将字符串 'div' 或 'span' 传 递给 Nerv.createElement。 以大写字母开头的类型,如 <Foo /> 编译为 Nerv.createElement(Foo),并它正对应于你在 JavaScript 文件中定义或导入的组件。
我们建议用大写开头命名组件。如果你的组件以小写字母开头,请在 JSX 中使用之前其赋值给大写开头的变量。
例如,下面的代码将无法按预期运行:
import Nerv from 'Nerv';
// 错误!组件名应该首字母大写:
function hello(props) {
// 正确!div 是有效的 HTML 标签:
return Hello {props.toWhat};
}
function HelloWorld() {
// 错误!Nerv 会将小写开头的标签名认为是 HTML 原生标签:
return <hello toWhat="World" />;
}为了解决这个问题,我们将 hello 重命名为 Hello,然后使用 <Hello /> 引用:
import Nerv from 'Nerv';
// 正确!组件名应该首字母大写:
function Hello(props) {
// 正确!div 是有效的 HTML 标签:
return Hello {props.toWhat};
}
function HelloWorld() {
// 正确!Nerv 能够将大写开头的标签名认为是 Nerv 组件。
return <Hello toWhat="World" />;
}在运行时选择类型
你不能使用表达式来作为 Nerv 元素的标签。如果你的确想通过表达式来确定 Nerv 元素的类型,请先将其赋值给大写开头的变量。这种情况一般会在你想通过属性值条件渲染组件时出现:
import Nerv from 'Nerv';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// 错误!JSX 标签名不能为一个表达式。
return <components[props.storyType] story={props.story} />;
}要解决这个问题,我们需要先将类型赋值给大写开头的变量。
import Nerv from 'Nerv';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// 正确!JSX 标签名可以为大写开头的变量。
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}属性
在 JSX 中有几种不同的方式来指定属性。
使用 JavaScript 表达式
你可以传递任何 {} 包裹的 JavaScript 表达式作为一个属性值。例如,在这个 JSX 中:
<MyComponent foo={1 + 2 + 3 + 4} />对于 MyComponent来说, props.foo 的值为 10,这是 1 + 2 + 3 + 4 表达式计算得出的。
if 语句和 for 循环在 JavaScript 中不是表达式,因此它们不能直接在 JSX 中使用,所以你可以将它们放在周围的代码中。
function NumberDescriber(props) {
let description;
if (props.number % 2 == 0) {
description = **even**;
} else {
description = *odd*;
}
return {props.number} is an {description} number;
}你可以在相关部分中了解有关 条件渲染 和 循环 的更多信息。
字符串常量
你可以将字符串常量作为属性值传递。下面这两个 JSX 表达式是等价的:
<MyComponent message="hello world" />
<MyComponent message={'hello world'} />当你传递一个字符串常量时,它不会对其进行 HTML 转义,所以下面两个 JSX 表达式是相同的:
<MyComponent message="<3" />
<MyComponent message={'<3'} />这种行为通常是无意义的,提到它只是为了完整性。
默认为 True
如果你没有给属性传值,它默认为 true。因此下面两个 JSX 是等价的:
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />一般情况下,我们不建议这样使用,因为它会与 ES6 对象简洁表示法 混淆。比如 {foo} 是 {foo: foo} 的简写,而不是 {foo: true}。这里能这样用,是因为它符合 HTML 的做法。
扩展属性
如果你已经有了个 props 对象,并且想在 JSX 中传递它,你可以使用 ... 作为扩展操作符来传递整个属性对象。下面两个组件是等效的:
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}当你构建通用容器时,扩展属性会非常有用。然而,这样做也可能让很多不相关的属性,传递到不需要它们的组件中使代码变得混乱。我们建议你谨慎使用此语法。
子代
在包含开始和结束标签的 JSX 表达式中,标记之间的内容作为特殊的参数传递:props.children。有几种不同的方法来传递子代:
字符串常量
你可以在开始和结束标签之间放入一个字符串,则 props.children 就是那个字符串。这对于许多内置 HTML 元素很有用。例如:
<MyComponent>Hello world!</MyComponent>这是有效的 JSX,并且 MyComponent 的 props.children 值将会直接是 "hello world!"。因为 HTML 未转义,所以你可以像写 HTML 一样写 JSX:
This is valid HTML & JSX at the same time.JSX 会移除行空行和开始和结尾处的空格。标签邻近的新行也会被移除,字符串常量内部的换行会被压缩成一个空格,所以下面这些都等价:
Hello World
Hello World
Hello
World
Hello WorldJSX
你可以通过子代嵌入更多的 JSX 元素,这对于嵌套显示组件非常有用:
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>你可以混合不同类型的子元素,同时用字符串常量和 JSX 子元素,这是 JSX 类似 HTML 的另一种形式,这在 JSX 和 HTML 中都是有效的:
Here is a list:
<li>Item 1</li>
<li>Item 2</li>一个 Nerv 组件不能返回多个 Nerv 元素,但是单个 JSX 表达式可以有多个子元素,因此,如果你希望一个组件渲染多个元素,你可以用 `` 将其包起来。
Nerv 组件也可以通过数组的形式返回多个元素:
render() {
// 不需要使用额外的元素包裹数组中的元素
return [
// 不要忘记 key :)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}JavsScript 表达式
你可以将任何 {} 包裹的 JavaScript 表达式作为子代传递。例如,下面这些表达式是等价的:
<MyComponent>foo</MyComponent>
<MyComponent>{'foo'}</MyComponent>这对于渲染任意长度的 JSX 表达式的列表很有用。例如,下面将会渲染一个 HTML 列表:
function Item(props) {
return <li>{props.message}</li>;
}
function TodoList() {
const todos = ['finish doc', 'submit pr', 'nag dan to review'];
return (
{todos.map((message) => )}
);
}JavsScript 表达式可以与其他类型的子代混合使用。这通常对于字符串模板非常有用:
function Hello(props) {
return Hello {props.addressee}!;
}函数
通常情况下,插入 JSX 中的 JavsScript 表达式将被认作字符串、Nerv 元素或这些内容的列表。然而,props.children 可以像其它属性一样传递任何数据,而不仅仅是 Nerv 元素。例如,如果你使用自定义组件,则可以将调用 props.children 来获得传递的子代:
// Calls the children callback numTimes to produce a repeated component
function Repeat(props) {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i));
}
return {items};
}
function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => This is item {index} in the list}
</Repeat>
);
}传递给自定义组件的子代可以是任何元素,只要该组件在 Nerv 渲染前将其转换成 Nerv 能够理解的东西。这个用法并不常见,但当你想扩展 JSX 时可以使用。
布尔值、Null 和 Undefined 被忽略
false、null、undefined 和 true 都是有效的子代,但它们不会直接被渲染。下面的表达式是等价的:
{false}
{null}
{undefined}
{true}这在根据条件来确定是否渲染Nerv元素时非常有用。以下的JSX只会在showHeader为true时渲染<Header />组件。
{showHeader && <Header />}
<Content />值得注意的是,JavaScript 中的一些 "falsy" 值(比如数字0),它们依然会被渲染。例如,下面的代码不会像你预期的那样运行,因为当 props.message 为空数组时,它会打印0:
{props.messages.length &&
<MessageList messages={props.messages} />
}要解决这个问题,请确保 && 前面的表达式始终为布尔值:
{props.messages.length > 0 &&
<MessageList messages={props.messages} />
}相反,如果你想让类似 false、true、null 或 undefined 出现在输出中,你必须先把它转换成字符串 :
My JavaScript variable is {String(myVariable)}.File: guides/switching-to-nerv.md
从 React 切换到 Nerv
Nerv 提供了比 React 更好的浏览器兼容性和更高的性能,与此同时也不会放弃 React 庞大的生态系统。兼容 React 生态系统是我们开发 Nerv 的重要目标之一。
假设我们有这样一段代码:
var React = require('react');
var ReactDOM = require('react-dom');
class MyComponent extends React.Component {
render() {
return Hello World;
}
}
ReactDOM.render(<MyComponent />, node);我们迁移的目标就是更改一些配置,但是不用改变代码本身任意一行。
目前支持的特性
`react`
- React.createElement
- React.cloneElement
- React.Component
- React.PureComponent
- React.Children
- React.isValidElement
- componentDidCatch (React 16)
`react-dom`
- ReactDOM.render
- ReactDOM.unmountComponentAtNode
- ReactDOM.findDOMNode
- ReactDOM.createPortal (React 16)
使用 Webpack
只需要在 alias 把 nervjs 和 react、react-dom 关联起来即可:
{
resolve: {
alias: {
'react': 'nervjs',
'react-dom': 'nervjs'
}
}
}使用 Babel
直接单独使用 babel 需要安装一个叫 babel-plugin-module-resolver 的插件,接下来就在 .babelrc 添加以下内容,就可以把 react、react-dom 和 nervjs 关联起来:
{
"plugins": [
["module-resolver", {
"root": ["."],
"alias": {
"react": "nervjs",
"react-dom": "nervjs"
}
}]
]
}使用 Browserify
使用 Browserify 也需要安装一个叫 aliasify 的插件,然后在 package.json 把 react 和 react-dom 关联到 nervjs:
{
"aliasify": {
"aliases": {
"react": "inferno-compat",
"react-dom": "inferno-compat"
}
}
}File: guides/virtual-dom.md
Virtual DOM
Virtual DOM(虚拟DOM)是Nerv从React上继承的一项非常优秀的技术思想。
DOM,文档对象模型,它是JavaScript与页面元素交互的桥梁,可以让我们使用JavaScript去方便地修改、添加页面元素。但我们都知道,直接操作DOM性能较差,人为去操作DOM可能会产生一些性能糟糕的修改,而且手工去修改DOM、维护DOM状态的成本非常高,这些都非常令人痛苦。
而为了解决操作DOM的痛点,React另辟蹊径地引入了虚拟DOM的思想。虚拟DOM是对真实DOM的抽象描述,它的运行机制是这样的:
- 创建,首先根据数据创建出虚拟DOM对象树,它将描述真实DOM树对应的每个节点的类型、属性,以及它的子节点,由虚拟DOM树我们可以创建出真实DOM树
- 修改,随后,如果数据发生改变,我们就能创建出一颗新的虚拟DOM树
- 对比更新,将新、旧虚拟DOM树进行比较、得出差异这样就能对之前生成的真实DOM做出相应的修改。
在Nerv中我们是使用Nerv.createElement() 这个方法来创建虚拟DOM的,这个方法接受三个参数,第一个是节点类型,第二个参数是节点属性,第三个是拥有的子节点,例如
import Nerv from 'nervjs'
// 创建一个简单DIV
const divSimple = Nerv.createElement('div', {
id: 'divSimple',
className: 'div_simple',
style: {
fontSize: 80,
color: '#232323'
}
})
// =>
// 创建一个包含文字的DIV
const divWidthText = Nerv.createElement('div', {
id: 'divWidthText',
className: 'div_width_text',
style: {
fontSize: 80,
color: '#232323'
}
}, 'Hello, world!')
// => Hello, world!
// 创建一个包含多个子元素的DIV
const divWidthChildren =
Nerv.createElement('div', { id: 'divWidthChildren'},
Nerv.createElement('p', { className: 'paragraph' }, '1'),
Nerv.createElement('p', { className: 'paragraph' }, '2'),
Nerv.createElement('p', { className: 'paragraph' }, '3'))
// => 123通过调用Nerv.createElement()方法就可以得到我们想要的虚拟DOM树,但是如果我们的代码都这样来写的话就会显得非常繁琐了,代码看起来也会非常枯燥,所以我们可以换一种写法,使用JSX语法来书写创建虚拟DOM的代码。
JSX语法是一种类似XML的JavaScript扩展,它可以让我们在JavaScript中书写类似XML的代码,以此来简化上文中通过调用Nerv.createElement()方法来创建虚拟DOM的代码,例如,上述代码可以简化成
const divSimple =
const divWidthText = Hello, world!
const divWidthChildren = (
1
2
3
)再配合相应工具,就可以将代码转换成正常的创建代码,更多JSX相关内容请参考JSX这一章节。