从省市区多重级联想到的,react和jquery的差别

标签: 市区 级联 react | 发表时间:2014-10-30 09:56 | 作者:bee1314
出处:http://www.iteye.com
在我们的前端项目里经常会用到级联的select,比如省市区这样。通常这种级联大多是动态的。比如先加载了省,点击省加载市,点击市加载区。然后数据通常ajax返回。如果没有数据则说明到了叶子节点。
 
针对这种场景,如果我们使用jquery来实现,要考虑很多的问题,数据部分,以及大量的dom操作。比如这个页面上显示了某个区,这时候我切换省,要把市重新初始化数据,然后区域的部分要从页面中删除。这个判断是非常烦躁的。所以之前碰到这个问题时,就直接使用递归删除省下面的所有子,然后再重新生成市。所以jquery的思路是以dom为中心,所以的操作都在围绕着dom的变化来操作。也就是jquery的核心思想是dom可变。写jQuery写长了一个最真实的感受就是,仿佛天天都在写状态机,直到罗列了页面可能出现的所有情况。
 
 
这个问题在我学习React的时候思考很久,按照jQuery的思路我完全找不到北。因为React的style中很少有DOM的操作,而且很少直接去操作dom(几乎没有)。那怎么才能实现上面的需求呢?经过苦苦的思考React的函数式的哲学,终于想明白了。
 
 
React强烈支持组件式开发,将页面的区域抽象为组件。如,最简单的一个组件
var React = require(‘react’);
var HelloComponent = React.createClass({
     render: function() {
         return <div>Hello {this.props.name}<div>;
     }
});
React.renderComponent(<HelloComponent name=“react!"/>, document.body);
 
这样我们根据我们的业务需要建立一个组件HelloCompoent,然后通过属性参数实现动态的展现效果。然后将这个组件挂载在document.body之上。
 
函数式-不可变。
函数式的风格的前端,首先一开始就和jQuery的思路迥异,函数式一上来就认为DOM不可变。那这里就让人很迷糊,纳尼?dom不可变?是的不可变。
 
DOM不可变意味着什么?意味着当你的状态或者属性发生变化时,DOM部分就该不加任何思索,re-render,是的,完全的重绘,不会像jQuery一样在你上一次的基础上重绘成新的状态,这个工作量太大了。极端的例子,上一次dom展示的是一个cat,现在的数据渲染的是一个dog,我*,这个重绘的工作量就大发了。所以re-render真是最自然最简单地办法。但是亲爱的聪明的你突然想明白了我在忽悠?每次重新re-render会带来页面大量的repaint-reflow。如果操作复杂会把页面性能拖到非常low的一个水平。哎。是的呢。那我们看看React的实现和哲学吧。
 
React的每个组件包含状态和属性,且状态和属性和dom隔离。dom的部分就是动态模板。
1. React认为页面dom不可变,所以当状态和属性发生变化时会re-render dom。
2. React的会根据新的状态和属性生成新的VirtualDOM Tree然后和旧的VirtualDOM Tree做对比(类似于版本控制的机制)。
3. 通过对比计算出最小的更新代价,然后将这些更新的方法进入队列。
4. 批量更新。
 
所以通过React实现了超高速的re-render,每秒可以60fps,这是变态的游戏级的水准。
且React强调single data flow,大大的减少了前端的复杂度。
 
且通过不断的组合组件实现更复杂的组件,这点很爽。
 
关于VirtualDOM再举个带状态栗子:计时器。
var React = require(‘react’);
 
var Timer = React.createClass({
     getInitialState: function() {
         return {secondElapsed: 0};
     },
     tick: function() {
         this.setState({
              secondElapsed: this.state.secondElapsed + 1
          });
     },
     componentDidMount: function() {
          this.interval = setInterval(this.tick, 1000);         
     },
     componentWillUnmount: function() {
         clearInterval(this.interval);
     },
     render: function() {
         return <div>Time elapsed: {this.state.secondElapsed}</div>;
     }
});
 
React.render(<Timer/>, document.body);
 
当secondElapsed变化时,React会计算出最小更新代价。
 
好了,绕了这么大一圈,还没有回到故事的原点。那怎么玩出React的Style的感觉的级联Select呢?这个时候我们考虑问题不在以我们的DOM为中心了,我们应该以我们的领域为中心,就是被Angular发扬光大的MDV(Model-Driven-View),模型驱动视图。
 
好的,省市区是我们的模型
我们的领域的描述:
items: [
     //省
     {label: ‘省’, data: [{id: 1, name: ‘安徽’}, {id: 2, name: '江苏'}]},
    //市
     {label: ’市’, data: [{id: 11, name: '蚌埠'}]},
    //区
     {label: ‘区’, data: [{id: 111, name: '怀远'}]} 
];
choose: [1, 11, 111];
 
So 明白了嘛?
比如items就是我们的省市区领域数据,
1. 第一次加载ajax得到省信息,然后push到items
这时候React发现数据的状态发生了变化,赶紧计算diff,然后re-render,省就被virtualdom渲染出来了。
 
2. 当点击某个省,ajax获取市信息,然后push到items,且记录选中省的id
React又发现了变化,根据计算diff,发现省的信息没有变嘛,so就重绘了一个市select出来。So 这就是React比较库的地方。
 
3. 点击区。。。。你懂了。
 
4. 这时候我点击省了,如果没有选中任何省,只是选中"了请选择“, 我就将市和区的信息全clean掉,React又发现数据变化了,发现省的数据没有变化,不重新渲染,发生市和区数据没有了,re-render之后页面的市和区的select直接被clean掉。
 
通过上述步骤,我们清晰的看到原本我们是对DOM费事的操作转化为对我们领域的操作。DOM会被re-render,想一想都开心呢。
 
附上代码:
/**@jsx React.DOM*/
var React = window.React = require('react');
 
var provData = {
    label: '省',
    data: [
        {id:1, name: '安徽'},
        {id:2, name: '江苏'}
    ]
};
var cityData = [{
    label: '市',
    pid: 1,
    data: [
        {id: 10, name: '蚌埠'},
        {id: 11, name: '巢湖'},
        {id: 12, name: '太湖'}
    ]
}];
 
var CascadeSelect = React.createClass({
    getInitialState: function() {
       return {
           items: [],
           choose: []
       }
    },
    componentWillMount: function() {
      //AJAX call
      this.setState({
          items: this.state.items.concat([provData])
      });
    },
    _handleChange: function(e) {
      e.preventDefault();
      var value = e.target.value.split("\:");
        var selectIndex = value[0];
        var selectValue = value[1];
 
        var newData;
        var index = selectIndex;
        var nextItems = this.state.items;
        var nextChoose = this.state.choose;
 
        if (selectValue != '') {
            index++;
            nextChoose[selectIndex] = selectValue;
            //ajax call
            newData = cityData.filter(function(item) {
                return item.pid == selectValue;
            });
        }
 
        nextChoose = nextChoose.slice(0, index);
        nextItems = nextItems.slice(0, selectIndex+1);
        if (newData!=null && newData.length > 0) {
            nextItems.push(newData[0]);
        }
 
        this.setState({
            items: nextItems,
            choose: nextChoose
        });
    },
    _handleClick: function(e) {
        e.preventDefault();
        alert(this.state.choose.join("=>"));
    },
    render: function() {
        return (
            <div>
            {this.state.items.map(function(item, i) {
                return (
                    <div key={i}>
                        <label>{item.label}</label>
                        <select data-order={i} onChange={this._handleChange}>
                            <option value={i + ":"}> ==请选择== </option>
                            {item.data.map(function(data) {
                                return (<option key={data.id} value={i + ":" + data.id}>{data.name}</option>);
                            })}
                        </select>
                    </div>
                );
            }, this)}
            <button onClick={this._handleClick}>查看选择项</button>
            </div>
        );
    }
});
 
React.renderComponent(<CascadeSelect/>, document.body);
 
时间紧,任务重,代码就先草率的写到这了。通过代码和我们预期的一样,大部分的操作在model而不是dom。
 
我们再进一步抽象数据源,设置url属性,这样下次你在任何的地方想使用这种级联的select都只需要简单地
<CascadeSelect firstLevelUrl=“/provData” secondLevelUrl=‘' />
 
 
 最后,不是因为我们用了React或者Angular就比jQuery高大上,从来不是这样的。我们的目标不是高大上,而是更好的解决我们的问题。提供更好的服务。我们要学习的是隐藏在React或者Angular背后的设计思想,对问题的抽象架构的方法。
 
最后和兄弟们共勉:
Dijkstra曰:编程的艺术就是处理复杂性的艺术。设法把代码写的精简从来就不是什么境界。代码架构内部的秩序能有效适应需求变化和化解业务的复杂度易于扩展和维护才是要追求的境界。
 
React把重点放在可预测性和数据单向同步上,它对控制复杂度很有效。
 
 
 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [市区 级联 react] 推荐:

从省市区多重级联想到的,react和jquery的差别

- - Web前端 - ITeye博客
在我们的前端项目里经常会用到级联的select,比如省市区这样. 比如先加载了省,点击省加载市,点击市加载区. 如果没有数据则说明到了叶子节点. 针对这种场景,如果我们使用jquery来实现,要考虑很多的问题,数据部分,以及大量的dom操作. 比如这个页面上显示了某个区,这时候我切换省,要把市重新初始化数据,然后区域的部分要从页面中删除.

谈谈 React Native

- - 唐巧的技术博客
几天前,Facebook 在 React.js Conf 2015 大会上推出了 React Native( 视频链接). 我发了一条微博( 地址),结果引来了 100 多次转发. 为什么 React Native 会引来如此多的关注呢. 我在这里谈谈我对 React Native 的理解. 一个新框架的出现总是为了解决现有的一些问题,那么对于现在的移动开发者来说,到底有哪些问题 React Native 能涉及呢.

Webpack 和 React 小书

- - SegmentFault 最新的文章
Webpack 和 React 小书. 这本小书的目的是引导你进入 React 和 Webpack 的世界. 他们两个都是非常有用的技术,如果同时使用他们,前端开发会更加有趣. 这本小书会提供所有相关的技能. 如果你只是对 React 感兴趣,那可以跳过 Webpack 相关的内容,反之亦然. 如果想学习更多的相关知识可以移步 SurviveJS - Webpack and React.

React入门实例学习

- - JavaScript - Web前端 - ITeye博客
        React可以在浏览器运行,也可以在服务器运行,但是在这为了尽量保持简单,且React语法是一致的,服务器的用法和浏览器差别不大,在这只涉及浏览器. 一. HTML模板.         使用React的网页源码,结构大致如下:.         1.最后一个script标签的type属性为text/jsx.

轻松入门React和Webpack

- - SegmentFault 最新的文章
小广告:更多内容可以看 我的博客和 读书笔记. 最近在学习React.js,之前都是直接用最原生的方式去写React代码,发现组织起来特别麻烦,之前听人说用Webpack组织React组件得心应手,就花了点时间学习了一下,收获颇丰. 一个组件,有自己的结构,有自己的逻辑,有自己的样式,会依赖一些资源,会依赖某些其他组件.

React 入门实例教程

- - 阮一峰的网络日志
现在最热门的前端框架,毫无疑问是 React. 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.

React Native 原理与实践

- - 掘金 前端
React Native 介绍. 什么是 React Native. React Native 是一个由 Facebook 于 2015 年 9 月发布的一款开源的 JavaScript 框架,它可以让开发者使用 JavaScript 和 React 来开发跨平台的移动应用. 它既保留了 React 的开发效率,又同时拥有 Native 应用的良好体验,加上 Virtual DOM 跨平台的优势,实现了真正意义上的:.

React Native通信机制详解

- - bang's blog
React Native是facebook刚开源的框架,可以用javascript直接开发原生APP,先不说这个框架后续是否能得到大众认可,单从源码来说,这个框架源码里有非常多的设计思想和实现方式值得学习,本篇先来看看它最基础的JavaScript-ObjectC通信机制(以下简称JS/OC). 普通的JS-OC通信实际上很简单,OC向JS传信息有现成的接口,像webview提供的-stringByEvaluatingJavaScriptFromString方法可以直接在当前context上执行一段JS脚本,并且可以获取执行后的返回值,这个返回值就相当于JS向OC传递信息.

使用 React 和 Next.js 构建博客

- -
Next.js是由 Vercel 创建和维护的基于 React 的应用程序框架. Next.js构建一个小型的博客网站:. Markdown文件生成的动态路由. 服务器端渲染(在请求时渲染). 本教程将通过创建一个简单的博客来展示. Next.js适合这样的博客的开发吗. 先来了解一下一般博客都需要什么.

ChatGPT ReAct (Reason+Act) 模式实现

- - hooopo (Hooopo)
ChatGPT 是一个语言模型,对自然语言的理解和输出比人类要强很多,对编程语言和结构化处理相关的问题更是比人类好很多. 对于开发者来说,目前 ChatGPT 存在的几个问题:. 在 Chat 模型里对话过长会出现失忆现象. 前两个问题可以通过 数据填充机制(Augmentation)解决. 后几个问题一般引入 ReAct(Reason+Act) 模式来解决.