作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
莱昂纳多·安德里斯·加西亚·克雷斯波的头像

莱昂纳多·安德烈斯·加西亚·克雷斯波

莱昂纳多是一个终生的科技爱好者, 他总是努力学习新的东西,同时跟上自己喜欢的技术.

Previously At

塔塔咨询服务
Share

最大和最常见的问题之一 前端web开发 is state management. 自由前端开发人员 像我这样的人总是专注于保持状态对象与其视图和DOM表示同步. 用户可以通过多种方式与应用程序交互,提供从一种状态到另一种状态的干净转换是一项艰巨的任务.

With this React.Js教程,了解更多关于视图状态管理的知识.

A Bit Of History

不久以前,web应用程序的数据流要简单得多. 浏览器将向服务器发送请求, 所有应用程序逻辑都将在服务器上执行, 一个完整的HTML视图将被发送回浏览器,以便呈现给用户. 后续的用户操作(如点击、表单提交等).)会再次触发同样的流. 应用程序不必担心用户状态,并且可以通过向服务器发送新请求来重新生成每个视图.

但是,web应用程序 它们变得越来越复杂 用户对UI/UX的需求也在不断提升. 当只有一部分页面发生变化时,重新加载整个页面是低效且缓慢的. 我们需要一个快速、活泼、响应迅速的交互,并对UI产生即时影响.

JavaScript拯救了我们. 开发人员开始编写大量代码,这些代码在请求发送到服务器之前在浏览器中执行. jQuery还为前端web开发带来了重大进步, 因为它提供了简单有效的开箱即用功能,如客户端验证, modal windows, alert messages, animations, 甚至是基于ajax的部分页面更新.

Understanding Complexity

让我们看一个评估密码强度的简单示例. 如果密码是OK的,那么输入框应该有一个绿色边框,并且应该显示一条漂亮的消息. 如果密码很弱,则输入框应该有红色边框并显示警告消息. 当密码足够强大时,我们也可能会显示一个笑脸.

下面的代码演示了如何通过DOM操作来实现这一点. 这里有很多“如果”,并且代码不是很容易阅读.

if (hasInputBorder()) {
  removeInputBorder();
}
if (text.length === 0) {
  if (hasMessage()) {
    removeMessage();
  }
  if (hasSmiley()) {
    removeSmiley();
  }
}
else {
  var强度= getPasswordStrength(text);
  if (!hasInputBorder()) {
    addInputBorder();
  }
  Var color = (strength == 'weak') ? 'red' : 'green');
  setInputBorderColor(颜色);
  Var消息=(强度== '弱') ?
                 "Password is weak" :
                 “这就是我所说的密码!");
  if (hasMessage()) {
    setMessageText(message);
  } else {
    addMessageWithText(消息);
  }
  If (strength == 'weak') {
    if (hasSmiley()) {
      removeSmiley();
    }
  } else {
    if (!hasSmiley()) {
      addSmiley();
    }
  }
}

As shown above, 首先,我们需要检查用户是否提供了任何密码,并处理密码字段为空的情况. 在所有情况下,我们都需要确保所有相关的DOM元素都得到了适当的更新. 这包括消息、边框和笑脸.

我们的密码字段可以处于以下三种状态之一:空、弱或强. 如前所述,我们有三个不同的DOM元素受到密码字段状态的影响. 处理所有的组合,并确保我们的视图正确显示,增加 cyclomatic complexity 即使是这样一段简单的代码.

The DOM works in a retained mode,这意味着它只记住当前状态. 为了修改我们的观点, 我们需要为每个DOM元素提供指令并对转换进行编程.

编码转换而不是状态可能很复杂. 我们需要在代码中执行的分支和检查的数量随着要管理的视图状态的数量呈指数级增长.

在我们的示例中,我们定义了三个视图状态,这给了我们 3 * 2 = 6 transitions. 一般来说,给定N种状态,我们有 N * (N - 1) = N^2 - N 我们需要建模的转换. 试想一下,如果我们在示例中添加第四个状态,复杂性会增加多少.

通常有太多与建模相关的代码 transitions. 如果我们只定义视图状态,而不用担心从一种状态转换到另一种状态的所有细节,那就更好了.

Reducing Complexity

Assuming that we could declare 视图状态基于模型状态,而不是 explicitly 编码从一种状态到另一种状态的转换,我们可以有这样的东西:

var强度= getPasswordStrength(text);
if (text.length == 0) {
  返回div(input({type: 'password', value: text}));
} else If (strength == 'weak') {
  return div(
    输入({type: 'password', value: text, borderColor: 'red'}),
    span({}, "Weak")
  );
} else {
  return div(
    输入({type: 'password', value: text, borderColor: 'green'}),
    span({}, "这就是我所说的密码!"),
    img({类:‘icon-smiley’})
  );
}

这里我们有三个简单的代码分支,代表了应用程序的三种可能状态. 我们只根据模型状态返回每个分支中视图的规范. All DOM manipulation code is removed; we just provide the information about what we want, and not how to get there.

虽然这种方法显著降低了代码的复杂性, 它还假设有人或其他东西代表我们处理实际的DOM操作.

This is where React comes in. React将确保根据底层数据模型的状态立即管理和更新视图状态.

Reaction

React是一个由Facebook创建的JavaScript库. 它被设计用来处理web应用程序的UI部分. 你可以把它想象成MVC架构中的V. It is very focused. 它对技术栈的其余部分不做任何假设,除了呈现组件之外,它不处理任何事情. 它不提供任何路由机制, models, 或者通常捆绑在大型框架中的其他特性. 因此,您可以将它与您想要的任何其他库或框架混合使用.

React允许我们将ui定义为复合组件树. A React developer 类来定义这些组件 render function 它描述了给定输入状态的组件. That function should be pure (i.e.(它不应该有任何副作用,也不应该依赖于除显式输入之外的任何东西)。.

渲染函数返回一个视图描述,React调用它 Virtual DOM. 可以将其视为与所呈现的DOM元素相对应的JavaScript对象.

当你改变组件的状态时, 它只是重新渲染它自己和它所有的子元素, returning a new Virtual DOM.

此外,当从一种状态转换到另一种状态时,React不会做简单的HTML替换. 它将发现旧国家和新国家之间的差异, 并计算执行转换的最有效的DOM操作集.

即使心中没有表演, 代码复杂性的降低本身意义重大,它使我们能够将精力集中在应用程序中更独特、更复杂的部分上.

更具体一点, 这就是我们的教程示例将如何使用React来管理视图状态.

注意:下面的代码示例是用 JSX 预处理器,这是编写基于React的UI的常用方法.

getPasswordStrength(text) {
    //计算给定密码文本的强度的代码.
}

var PasswordWithStrength = React.createClass({
    getInitialState:函数(){
        return {value: ''};
    },

    render: function() {
        var强度= getPasswordStrength.state.value);
        if (this.state.value.length == 0) {
            return 
; } else If (strength == 'weak') { return
Weak!
; } else { return
That's what I call a password!
; } }, handleInputChange:函数(ev) { this.setState({value: ev.target.value}); } }); React.render(, document.body);

The Emoji 组件,该组件在密码强度OK时呈现 只是另一个自定义组件(就像 PasswordWithStrength). 它的定义如下:

var Emoji = React.createClass({
    render: function() {
        var emojiSrc = this.props.value + '.png';
        return ;
    }
});

React.js vs. Others

In fairness, though, 还有其他客户端JavaScript框架(比如Ember), Angular, Knockout, 也解决了视图状态管理问题, 甚至还添加了更多的功能. 那么,为什么你要使用React而不是其他框架呢?

与大多数其他库相比,React有两个关键优势.

No Data Binding

其他一些可选框架使用数据绑定将DOM元素映射到状态属性, 并通过观察属性的变化来保持它们的同步. 这种方法允许只呈现一次视图, 每次更改只会触发对受影响的DOM元素的修改. Other alternatives use dirty checking; that is, 而不是观察单个属性的变化, 它们只是在以前的状态和新的状态之间执行一个区别. React更类似于后一种方法,但是, 而不是比较状态, 它比较视图表示.

React没有数据绑定. 开发人员应该调用 setState 方法,或者在状态改变时重新呈现顶部组件. 它包含了从状态到视图的单向流.

这个概念很容易采用,因为开发人员通常不会考虑数据绑定. 重点是数据的可视化表示. 因此,您不需要考虑依赖属性, formatting, 绑定特殊HTML标签, etc. 使用React,您只需在模型更改时重新呈现组件.

要理解这里视图状态管理的区别, 让我们比较一下Ember和React. 我们将创建一个对象 person 它将输出大写的全名. 两秒钟后,我们将模拟更改并更新视图.

// EXAMPLE USING EMBER

App = Ember.Application.create();

App.Person = Ember.Object.extend({
  firstName: null,
  lastName: null,

  fullName: function() {
    return this.get('firstName') + ' ' + this.get('lastName');
  }.属性(“firstName”,“姓”)
});

var person = App.Person.create({
  firstName: "John",
  lastName:  "Doe"
});

Ember.Handlebars.Helper ('upcase', function(value) {
  return value.toUpperCase();
});

App.IndexRoute = Ember.Route.extend({
  model: function () {
    return person;
  }
});

setTimeout(function() {
  person.设置(“firstName”、“哈利”);
}, 2000);

// Templates:



我们创建了一个对象 firstName, lastName, and fullName properties. 由于Ember正在观察属性变化,我们必须指定它 fullName depends on the firstName and the lastName. To do this we added .属性(“firstName”,“姓”) when we defined the fullName.

After two seconds, person.设置(“firstName”、“哈利”); is executed. 这触发了视图及其绑定的更新.

现在让我们在React中做同样的事情.

// EXAMPLE USING REACT

var CurrentUser = React.createClass({
  render: function() {
    return 
The current user is: {this.props.user.fullName().toUpperCase()}
; } }); var person = { firstName: 'John', lastName: 'Doe', fullName: function() { return this.firstName + ' ' + this.lastName; } }; var currentUser = React.render(, document.body); setTimeout(function() { person.firstName = 'Harry'; currentUser.记者({用户:人}); }, 2000);

尽管Ember代码简单易读,但很明显React在简单性方面更胜一筹. The person 是一个普通的JavaScript对象,带有 fullName simply being a function.

No Templating

每个可选框架都有不同的模板处理方式. 其中一些使用编译成JavaScript的字符串,而另一些则直接使用DOM元素. 它们中的大多数使用自定义的HTML属性和标签,然后将其“编译”成HTML.

模板不是JavaScript代码的一部分. 正因为如此,每个选择 needs 一种表示常见操作、条件、迭代、调用函数等的自定义方式. 它们最终都创造了一种开发人员必须学习的新的伪语言.

React中没有模板,一切都是普通的JavaScript.

React使用JavaScript的全部功能来生成视图. 组件的呈现方法是一个JavaScript函数.

JSX可以作为一种预处理器,将“类html语法”转换为普通的JavaScript, 但是JSX是可选的,您可以自由地使用没有任何预处理器的标准JavaScript. 您还可以利用现有的JavaScript工具. 编译器、预处理器、类型注释、最小化、死代码消除等.

让我们再用一个具体的例子来比较React和另一个视图状态管理框架.

下面的教程是使用 AngularJS 列出标签和每个标签的tweet数. 该列表按计数排序,如果没有标签,则显示一条消息.



  • {{hashTag.name}} - {{hashTag.tweetCount}} tweets
No hashtags found!

为了能够列出这个列表,开发人员必须了解 AngularJS directives, ng-show and ng-repeat. 然后他需要学习 AngularJS filters to understand orderBy. 像输出列表这样简单的事情要做很多工作!

现在让我们考虑一下React的例子,它做了同样的事情:

// EXAMPLE USING REACT

函数byTweetCountDesc(h1, h2) {
  return h2.tweetCount - h1.tweetCount;
}

//...
render: function() {
  if (this.state.hashTags.length > 0) {
    var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) {
      return 
  • {hashTag.name} - {hashTag.tweetCount} tweets
  • ; }); return
      {comps}
    ; } else { return No hashtags found! } }

    即使我们使用“更高级”的方法和JSX, 每个对JavaScript有基本了解的web开发人员都可以很容易地阅读上面的代码并理解它的作用. 标准条件检查使用 if, iteration using map(), 标准的“sort()”对于任何开发人员来说都是很自然的, 所以不需要学习额外的语法或其他概念.

    Conclusion

    这个React的主要收获.React让你能够专注于实际的视图状态管理,而不是转换, 从而简化您的工作和应用程序.

    采用React的学习曲线相当简单. 无需掌握自定义模板语言, 无需担心数据绑定, 一切都归结为描述UI元素的JavaScript函数.

    了解更多关于使用React简化应用程序代码的信息, 来看看Steven Luscher的演讲, 用React分解代码.

    这里有一些额外的阅读给那些想要迈出下一步并开始使用React的人:

    关于总博客的进一步阅读:

    聘请Toptal这方面的专家.
    Hire Now
    莱昂纳多·安德里斯·加西亚·克雷斯波的头像

    Located in London, United Kingdom

    Member since February 10, 2014

    About the author

    莱昂纳多是一个终生的科技爱好者, 他总是努力学习新的东西,同时跟上自己喜欢的技术.

    Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

    Previously At

    塔塔咨询服务

    世界级的文章,每周发一次.

    订阅意味着同意我们的 privacy policy

    世界级的文章,每周发一次.

    订阅意味着同意我们的 privacy policy

    Toptal Developers

    Join the Toptal® community.

    \n\n\n\n\n

    我们创建了一个对象 firstName, lastName, and fullName properties. 由于Ember正在观察属性变化,我们必须指定它 fullName depends on the firstName and the lastName. To do this we added .属性(“firstName”,“姓”) when we defined the fullName.

    \n\n

    After two seconds, person.设置(“firstName”、“哈利”); is executed. 这触发了视图及其绑定的更新.

    \n\n

    现在让我们在React中做同样的事情.

    \n\n
    // EXAMPLE USING REACT\n\nvar CurrentUser = React.createClass({\n  render: function() {\n    return 
    The current user is: {this.props.user.fullName().toUpperCase()}
    ;\n }\n});\n\nvar person = {\n firstName: 'John',\n lastName: 'Doe',\n fullName: function() {\n return this.firstName + ' ' + this.lastName;\n }\n};\n\nvar currentUser = React.render(, document.body);\n\nsetTimeout(function() {\n person.firstName = 'Harry';\n currentUser.记者({用户:人});\n}, 2000);\n
    \n\n

    尽管Ember代码简单易读,但很明显React在简单性方面更胜一筹. The person 是一个普通的JavaScript对象,带有 fullName simply being a function.

    \n\n

    No Templating

    \n\n

    每个可选框架都有不同的模板处理方式. 其中一些使用编译成JavaScript的字符串,而另一些则直接使用DOM元素. 它们中的大多数使用自定义的HTML属性和标签,然后将其“编译”成HTML.

    \n\n

    模板不是JavaScript代码的一部分. 正因为如此,每个选择 needs 一种表示常见操作、条件、迭代、调用函数等的自定义方式. 它们最终都创造了一种开发人员必须学习的新的伪语言.

    \n\n

    React中没有模板,一切都是普通的JavaScript.

    \n\n

    React使用JavaScript的全部功能来生成视图. 组件的呈现方法是一个JavaScript函数.

    \n\n

    JSX可以作为一种预处理器,将“类html语法”转换为普通的JavaScript, 但是JSX是可选的,您可以自由地使用没有任何预处理器的标准JavaScript. 您还可以利用现有的JavaScript工具. 编译器、预处理器、类型注释、最小化、死代码消除等.

    \n\n

    让我们再用一个具体的例子来比较React和另一个视图状态管理框架.

    \n\n

    下面的教程是使用 AngularJS 列出标签和每个标签的tweet数. 该列表按计数排序,如果没有标签,则显示一条消息.

    \n\n
    \n\n
    \n
      0\">\n
    • \n {{hashTag.name}} - {{hashTag.tweetCount}} tweets\n
    • \n
    \n No hashtags found!\n
    \n
    \n\n

    为了能够列出这个列表,开发人员必须了解 AngularJS directives, ng-show and ng-repeat. 然后他需要学习 AngularJS filters to understand orderBy. 像输出列表这样简单的事情要做很多工作!

    \n\n

    现在让我们考虑一下React的例子,它做了同样的事情:

    \n\n
    // EXAMPLE USING REACT\n\n函数byTweetCountDesc(h1, h2) {\n  return h2.tweetCount - h1.tweetCount;\n}\n\n//...\nrender: function() {\n  if (this.state.hashTags.length > 0) {\n    var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) {\n      return 
  • \n {hashTag.name} - {hashTag.tweetCount} tweets\n
  • ;\n });\n return ;\n } else {\n return No hashtags found!\n }\n}\n
    \n\n

    即使我们使用“更高级”的方法和JSX, 每个对JavaScript有基本了解的web开发人员都可以很容易地阅读上面的代码并理解它的作用. 标准条件检查使用 if, iteration using map(), 标准的“sort()”对于任何开发人员来说都是很自然的, 所以不需要学习额外的语法或其他概念.

    \n\n

    Conclusion

    \n\n

    这个React的主要收获.React让你能够专注于实际的视图状态管理,而不是转换, 从而简化您的工作和应用程序.

    \n\n

    采用React的学习曲线相当简单. 无需掌握自定义模板语言, 无需担心数据绑定, 一切都归结为描述UI元素的JavaScript函数.

    \n\n

    了解更多关于使用React简化应用程序代码的信息, 来看看Steven Luscher的演讲, 用React分解代码.

    \n\n

    这里有一些额外的阅读给那些想要迈出下一步并开始使用React的人:

    \n\n\n","as":"div","isContentFit":true,"sharingWidget":{"url":"http://r0jm.wxzjnt.com/react/managing-view-state-with-react","title":"Tutorial: The React.js View State","text":null,"providers":["linkedin","twitter","facebook"],"gaCategory":null,"domain":{"name":"developers","title":"Engineering","vertical":{"name":"developers","title":"Developers","publicUrl":"http://r0jm.wxzjnt.com/developers"},"publicUrl":"http://r0jm.wxzjnt.com/developers/blog"},"hashtags":"JavaScript,ViewState,React,FrontEnd"}}