首页

React 简单介绍

前端达人

why React?

React是Facebook开发的一款JS库,那么Facebook为什么要建造React呢,主要为了解决什么问题,通过这个又是如何解决的?

从这几个问题出发我就在网上搜查了一下,有这样的解释。

Facebook认为MVC无法满足他们的扩展需求,由于他们非常巨大的代码库和庞大的组织,使得MVC很快变得非常复复杂,每当需要添加一项新的功能或特性时,系统的复杂度就成级数增长,致使代码变得脆弱和不可预测,结果导致他们的MVC正在土崩瓦解。认为MVC不适合大规模应用,当系统中有很多的模型和相应的视图时,其复杂度就会迅速扩大,非常难以理解和调试,特别是模型和视图间可能存在的双向数据流动。

解决这个问题需要“以某种方式组织代码,使其更加可预测”,这通过他们(Facebook)提出的Flux和React已经完成。


Flux是一个系统架构,用于推进应用中的数据单向流动。React是一个JavaScript框架,用于构建“可预期的”和“声明式的”Web用户界面,它已经使Facebook更快地开发Web应用


对于Flux,目前还没怎么研究,不怎么懂,这里就先把Flux的图放上来,有兴趣或者了解的可以再分享下,这里主要说下React。

微信截图_20200602202548.png

那么React是解决什么问题的,在官网可以找到这样一句话:

We built React to solve one problem: building large applications with data that changes over time.


构建那些数据会随时间改变的大型应用,做这些,React有两个主要的特点:

  1. 简单
    简单的表述任意时间点你的应用应该是什么样子的,React将会自动的管理UI界面更新当数据发生变化的时候。
  2. 声明式
    在数据发生变化的时候,React从概念上讲与点击了F5一样,实际上它仅仅是更新了变化的一部分而已。
    React是关于构造可重用组件的,实际上,使用React你做的仅仅是构建组建。通过封装,使得组件代码复用、测试以及关注点分离更加容易。

另外在React官网上,通过《Why did we build React?》为什么我们要建造React的文档中还可以了解到以下四点:

  • React不是一个MVC框架
  • React不使用模板
  • 响应式更新非常简单
  • HTML5仅仅是个开始

React主要的原理

Virtual DOM 虚拟DOM
传统的web应用,操作DOM一般是直接更新操作的,但是我们知道DOM更新通常是比较昂贵的。而React为了尽可能减少对DOM的操作,提供了一种不同的而又强大的方式来更新DOM,代替直接的DOM操作。就是Virtual DOM,一个轻量级的虚拟的DOM,就是React抽象出来的一个对象,描述dom应该什么样子的,应该如何呈现。通过这个Virtual DOM去更新真实的DOM,由这个Virtual DOM管理真实DOM的更新。

为什么通过这多一层的Virtual DOM操作就能更快呢? 这是因为React有个diff算法,更新Virtual DOM并不保证马上影响真实的DOM,React会等到事件循环结束,然后利用这个diff算法,通过当前新的dom表述与之前的作比较,计算出最小的步骤更新真实的DOM。


微信截图_20200602202557.png

Components 组件
在DOM树上的节点被称为元素,在这里则不同,Virtual DOM上称为commponent。Virtual DOM的节点就是一个完整抽象的组件,它是由commponents组成。


component 的使用在 React 里极为重要, 因为 components 的存在让计算 DOM diff 更。

State 和 Render
React是如何呈现真实的DOM,如何渲染组件,什么时候渲染,怎么同步更新的,这就需要简单了解下State和Render了。state属性包含定义组件所需要的一些数据,当数据发生变化时,将会调用Render重现渲染,这里只能通过提供的setState方法更新数据。

好了,说了这么多,下面看写代码吧,先看一个官网上提供的Hello World的示例:


<!DOCTYPE html> <html> <head> <script src="http://fb.me/react-0.12.1.js"></script> <script src="http://fb.me/JSXTransformer-0.12.1.js"></script> </head> <body> <div id="example"></div> <script type="text/jsx"> React.render( <h1>Hello, world!</h1>,
        document.getElementById('example')
      ); </script> </body> </html>

这个很简单,浏览器访问,可以看到Hello, world!字样。JSXTransformer.js是支持解析JSX语法的,JSX是可以在Javascript中写html代码的一种语法。如果不喜欢,React也提供原生Javascript的方法。

再来看下另外一个例子:



<html>
    <head>
        <title>Hello React</title>
        <script src="http://fb.me/react-0.12.1.js"></script>
        <script src="http://fb.me/JSXTransformer-0.12.1.js"></script>
        <script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
        <script src="http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script>
        <style>
        #content{
            width: 800px;
            margin: 0 auto;
            padding: 5px 10px;
            background-color:#eee;
        }
        .commentBox h1{
            background-color: #bbb;
        }
        .commentList{
            border: 1px solid yellow;
            padding:10px;
        }
        .commentList .comment{
            border: 1px solid #bbb;
            padding-left: 10px;
            margin-bottom:10px;
        }
        .commentList .commentAuthor{
            font-size: 20px;
        }
        .commentForm{
            margin-top: 20px;
            border: 1px solid red;
            padding:10px;
        }
        .commentForm textarea{
            width:100%;
            height:50px;
            margin:10px 0 10px 2px;
        }
        </style>
    </head>
    <body>
        <div id="content"></div>
        <script type="text/jsx">
        var staticData = [
            {author: "张飞", text: "我在写一条评论~!"},
            {author: "关羽", text: "2货,都知道你在写的是一条评论。。"},
            {author: "刘备", text: "哎,咋跟这俩逗逼结拜了!"}
        ];

        var converter = new Showdown.converter();//markdown

        /** 组件结构:
            <CommentBox>
                <CommentList>
                    <Comment />
                </CommentList>
                <CommentForm />
            </CommentBox>
        */
        //评论内容组件
        var Comment = React.createClass({
            render: function (){
                var rawMarkup = converter.makeHtml(this.props.children.toString());
                return (
                    <div className="comment">
                        <h2 className="commentAuthor">
                            {this.props.author}:
                        </h2>
                        <span dangerouslySetInnerHTML={{__html: rawMarkup}} />
                    </div>
                );
            }
        });
        //评论列表组件
        var CommentList = React.createClass({
            render: function (){
                var commentNodes = this.props.data.map(function (comment){
                    return (
                        <Comment author={comment.author}>
                            {comment.text}
                        </Comment>
                    );
                });

                return (
                    <div className="commentList">
                        {commentNodes}
                    </div>
                );
            }
        });

        //评论表单组件
        var CommentForm = React.createClass({
            handleSubmit: function (e){
                e.preventDefault();
                var author = this.refs.author.getDOMNode().value.trim();
                var text = this.refs.text.getDOMNode().value.trim();
                if(!author || !text){
                    return;
                }
                this.props.onCommentSubmit({author: author, text: text});
                this.refs.author.getDOMNode().value = '';
                this.refs.text.getDOMNode().value = '';
                return;
            },
            render: function (){
                return (
                    <form className="commentForm" onSubmit={this.handleSubmit}>
                        <input type="text" placeholder="Your name" ref="author" /><br/>
                        <textarea type="text" placeholder="Say something..." ref="text" ></textarea><br/>
                        <input type="submit" value="Post" />
                    </form>
                );
            }
        });

        //评论块组件
        var CommentBox = React.createClass({
            loadCommentsFromServer: function (){
                this.setState({data: staticData});
                /*
                方便起见,这里就不走服务端了,可以自己尝试
                $.ajax({
                    url: this.props.url + "?_t=" + new Date().valueOf(),
                    dataType: 'json',
                    success: function (data){
                        this.setState({data: data});
                    }.bind(this),
                    error: function (xhr, status, err){
                        console.error(this.props.url, status, err.toString());
                    }.bind(this)
                });
                */
            },
            handleCommentSubmit: function (comment){
                //TODO: submit to the server and refresh the list
                var comments = this.state.data;
                var newComments = comments.concat([comment]);

                //这里也不向后端提交了
                staticData = newComments;

                this.setState({data: newComments});
            },
            //初始化 相当于构造函数
            getInitialState: function (){
                return {data: []};
            },
            //组件添加的时候运行
            componentDidMount: function (){
                this.loadCommentsFromServer();
                this.interval = setInterval(this.loadCommentsFromServer, this.props.pollInterval);
            },
            //组件删除的时候运行
            componentWillUnmount: function() {
                clearInterval(this.interval);
            },
            //调用setState或者父级组件重新渲染不同的props时才会重新调用
            render: function (){
                return (
                    <div className="commentBox">
                        <h1>Comments</h1>
                        <CommentList data={this.state.data}/>
                        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
                    </div>
                );
            }
        });

        //当前目录需要有comments.json文件
        //这里定义属性,如url、pollInterval,包含在props属性中
        React.render(
            <CommentBox url="comments.json" pollInterval="2000" />,
            document.getElementById("content")
        );
        </script>
    </body>
</html>


乍一看挺多,主要看脚本部分就可以了。方便起见,这里都没有走后端。定义了一个全局的变量staticData,可权当是走服务端,通过浏览器的控制台改变staticData的值,查看下效果,提交一条评论,查看下staticData的值的变化。

应用情况

国外应用的较多,facebook、Yahoo、Reddit等。在github可以看到一个列表Sites-Using-React,国内的话,查了查,貌似比较少,目前知道的有一个杭州大搜车。大多技术要在国内应用起来一般是较慢的,不过React确实感觉比较特殊,特别是UI的组件化和Virtual DOM的思想,我个人比较看好,有兴趣继续研究研究。

比较分析

和其他一些js框架相比,React怎样,比如Backbone、Angular等。

  • React不是一个MVC框架,它是构建易于可重复调用的web组件,侧重于UI, 也就是view层
  • 其次React是单向的从数据到视图的渲染,非双向数据绑定
  • 不直接操作DOM对象,而是通过虚拟DOM通过diff算法以最小的步骤作用到真实的DOM上。
  • 不便于直接操作DOM,大多数时间只是对 virtual DOM 进行编程

作者:RK_CODER
链接:https://www.jianshu.com/p/ae482813b791
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

手机appUI界面设计赏析(二)

前端达人



与传统PC桌面不同,手机屏幕的尺寸更加小巧操作,方式也已触控为主,APP界面设计不但要保证APP功能的完整性和合理性,又要保证APP的功能性和实用性,在保证其拥有流畅的操作感受的同时,满足人们的审美需求。

接下来为大家介绍几款手机appui界面设计



点击查看原图

                                                                                    --手机appUI设计--

微信图片_20200602200240.jpg

                                                                                     --手机appUI设计--

微信图片_20200602200243.png

                                                                                   --手机appUI设计--

微信图片_20200602200246.png

                                                                                          --手机appUI设计--

点击查看原图

                                                                                             --手机appUI设计--

点击查看原图

                                                                                          --手机appUI设计--

点击查看原图

                                                                                     --手机appUI设计--

点击查看原图

                                                                                           --手机appUI设计--

点击查看原图

                                                                                      --手机appUI设计--

微信图片_20200602200304.jpg

                                                                                           --手机appUI设计--

微信图片_20200602200306.jpg

                                                                                    --手机appUI设计--

微信图片_20200602200309.jpg

                                                                                --手机appUI设计--

微信图片_20200602200311.jpg

                                                                                  --手机appUI设计--

微信图片_20200602200454.jpg

                                                                                    --手机appUI设计--

微信图片_20200602200457.jpg

                                                                               --手机appUI设计--

微信图片_20200602200459.jpg

                                                                                    --手机appUI设计--

微信图片_20200602200501.jpg

                                                                                           --手机appUI设计--

微信图片_20200602200504.jpg

                                                                                          --手机appUI设计--


(以上图片均来源于网络)



  蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服




    更多精彩文章:

       手机appUI界面设计赏析(一)

    





JavaScript必须掌握的基础 --- 闭包

seo达人

闭包(Closure)的定义

闭包是一个让初级JavaScript使用者既熟悉又陌生的一个概念。因为闭包在我们书写JavaScript代码时,随处可见,但是我们又不知道哪里用了闭包。

关于闭包的定义,网上(书上)的解释总是千奇百怪,我们也只能“取其精华去其糟粕”去总结一下。

  1. 即使函数在当前作用域外调用,但是还能访问当前作用域中的变量和函数
  2. 有权访问另一个函数作用域中的变量(函数)的函数。
  3. 闭包是指那些能够访问自由变量的函数

ECMAScript中,闭包指的是:

  1. 从理论角度:所有的函数都是闭包。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量也就相当于是在访问自由变量,这个时候使用最外层的作用域。
  2. 从实践角度:一下才算是闭包:

    • 即使创建它的上下文已经销毁,它仍然存在。
    • 在代码中引用了自由变量。

闭包跟词法作用域,作用域链,执行上下文这几个JavaScript中重要的概念都有关系,因此要想真的理解闭包,至少要对那几个概念不陌生。

闭包的优点:

  1. 可以是用函数内部的变量(函数),也可以说是可以访问函数作用域。
  2. 拥有私有变量,避免污染全局变量

闭包的缺点:

  1. 私有变量一直存在,占用内存。

我们来一步一步引出闭包。

自执行函数 ( IIFE )

自执行函数也叫立即调用函数(IIFE),是一个在定义时就执行的函数。

var a=1;
(function() { console.log(a)
})()

上述代码是一个最简单的自执行函数。

在ES6之前,是没有块级作用域的,只有全局作用域和函数作用域,因此自执行函数还能在ES6之前实现块级作用域。

// ES6 块级作用域 var a = 1; if(true) { let a=111; console.log(a); // 111 } console.log(a); // 1 

这里 if{} 中用let声明了一个 a。这个 a 就具有块级作用域,在这个 {} 中访问 a ,永远访问的都是 let 声明的a,跟全局作用域中的a没有关系。如果我们把 let 换成 var ,就会污染全局变量 a 。

如果用自执行函数来实现:

var a = 1;
(function() { if(true) { var a=111; console.log(a); // 111 }
})() console.log(a); // 1

为什么要在这里要引入自执行函数的概念呢?因为通常我们会用自执行函数来创建闭包,实现一定的效果。

来看一个基本上面试提问题:

for(var i=0;i<5;i++) {
    setTimeout(function() { console.log(i);
    },1000)
}

在理想状态下我们期望输出的是 0 ,1 ,2 ,3 ,4。但是实际上输出的是5 ,5 ,5 ,5 ,5。为什么是这样呢?其实这里不仅仅涉及到作用域,作用域链还涉及到Event Loop、微任务、宏任务。但是在这里不讲这些。

下面我们先解释它为什么会输出 5个5,然后再用自执行函数来修改它,以达到我们预期的结果。

提示:for 循环中,每一次的都声明一个同名变量,下一个变量的值为上一次循环执行完同名变量的值。

首先用var声明变量 for 是不会产生块级作用域的,所以在 () 中声明的 i 为全局变量。相当于:

// 伪代码 var i; for(i=0;i<5;i++) {
    setTimeout(function() { console.log(i);
    },1000)
}

setTimeout中的第一个参数为一个全局的匿名函数。相当于:

// 伪代码 var i; var f = function() { console.log(i);
} for(i=0;i<5;i++) {
    setTimeout(f,1000)
}

由于setTimeout是在1秒之后执行的,这个时候for循环已经执行完毕,此时的全局变量 i 已经变成了 5 。1秒后5个setTimeout中的匿名函数会同时执行,也就是5个 f 函数执行。这个时候 f 函数使用的变量 i 根据作用域链的查找规则找到了全局作用域中的 i 。因此会输出 5 个5。

那我们怎样来修改它呢?

  • 思路1:让setTimeout匿名函数中访问的变量 i 不再访问全局作用域中的 i 。因此把它包裹在一个函数作用域中。这时 匿名函数访问变量 i 时,会先去包裹它的函数作用域中查找。
for(var i=0;i<5;i++) {
    (function (){ setTimeout(function() { console.log(i);
        },1000)
    })();
}

上述例子会输出我们期望的值吗?答案是否。为什么呢?我们虽然把 setTimeout 包裹在一个匿名函数中了,但是当setTimeout中匿名函数执行时,首先去匿名函数中查找 i 的值,找不到还是会找到全局作用域中,最终 i 的值仍然是全局变量中的 i ,仍然为 5个5.

那我们把外层的匿名函数中声明一个变量 j 让setTimeout中的匿名函数访问这个 j 不就找不到全局变量中的变量了吗。

for(var i=0;i<5;i++) {
    (function (){ var j = i;
        setTimeout(function() { console.log(j);
        },1000)
    })();
}

这个时候才达到了我们预期的结果:0 1 2 3 4。

我们来优化一下:

for(var i=0;i<5;i++) {
    (function (i){ setTimeout(function() { console.log(i);
        },1000)
    })(i);
}

*思路2:用 let 声明变量,产生块级作用域。

for(let i=0;i<5;i++) {
    setTimeout(function() { console.log(i);
    },1000)
}

这时for循环5次,产生 5 个块级作用域,也会声明 5 个具有块级作用域的变量 i ,因此setTimeout中的匿名函数每次执行时,访问的 i 都是当前块级作用域中的变量 i 。

理论中的闭包

什么是理论中的闭包?就是看似像闭包,其实并不是闭包。它只是类似于闭包。

 function foo() { var a=2; function bar() { console.log(a); // 2 }
    bar();
}
foo();

上述代码根据最上面我们对闭包的定义,它并不完全是闭包,虽然是一个函数可以访问另一个函数中的变量,但是被嵌套的函数是在当前词法作用域中被调用的。

实践中的闭包

我们怎样把上述代码foo 函数中的bar函数,在它所在的词法作用域外执行呢?

下面的代码就清晰的展示了闭包:

function foo() { var a=2; function bar() { console.log(a);
    } return bar;
} var baz=foo();
baz(); // 2 —— 朋友,这就是闭包的效果。

上述代码中 bar 被当做 foo函数返回值。foo函数执行后把返回值也就是 bar函数 赋值给了全局变量 baz。当 baz 执行时,实际上也就是 bar 函数的执行。我们知道 foo 函数在执行后,foo 的内部作用域会被销毁,因为引擎有垃圾回收期来释放不再使用的内存空间。所以在bar函数执行时,实际上foo函数内部的作用域已经不存在了,理应来说 bar函数 内部再访问 a 变量时是找不到的。但是闭包的神奇之处就在这里。由于 bar 是在 foo 作用域中被声明的,所以 bar函数 会一直保存着对 foo 作用域的引用。这时就形成了闭包。

我们先看个例子:

var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope;
    } return f;
} var foo = checkscope();
foo();

我们用伪代码来解释JavaScript引擎在执行上述代码时的步骤:

  1. JavaScript引擎遇到可执行代码时,就会进入一个执行上下文(环境)
  2. 首先遇到的是全局代码,因此进入全局执行上下文,把全局执行上下文压入执行上下文栈。
  3. 全局上下文创建时会先在内部创建VO/AO,作用域链,this。然后执行代码。
  4. 当遇到 checkscope 函数执行时,进入checkscope的执行上下文,然后压入执行上下文栈。
  5. checkscope 执行上下文创建时会先在内部创建VO/AO,作用域链,this。然后执行代码。
  6. 当checkscope 函数执行完毕时,会从执行上下文栈中弹出,此时它的AO也会被浏览器回收。(这是理想状态下)
  7. 执行foo函数,向上查找foo的值,发现foo的值为checkscope函数内部函数f。因此这一步为执行 checkscope 内部函数f。
  8. 执行f函数同执行 checkscope 的步骤一致。
  9. f 函数执行完毕,从执行上下文栈中弹出。

但是我们想一个问题,checkscope函数执行完毕,它的执行上下文从栈中弹出,也就是销毁了不存在了,f 函数还能访问包裹函数的作用域中的变量(scope)吗?答案是可以。

理由是在第6步,我们说过当checkscope 执行函数执行完毕时,它的执行上下文会从栈中弹出,此时活动对象也会被回收,按理说当 f 在访问checkscope的活动对象时是访问不到的。

其实这里还有个概念,叫做作用域链:当 checkscope 函数被创建时,会创建对应的作用域链,里面值存放着包裹它的作用域对应执行上下文的变量对象,在这里只是全局执行上下文的变量对象,当checkscope执行时,此时的作用域链变化了 ,里面存放的是变量对象(活动对象)的集合,最顶端是当前函数的执行上下文的活动对象。端是全局执行上下文的变量对象。类似于:

checkscope.scopeChain = [
    checkscope.AO
    global.VO
] 

当checkscope执行碰到了 f 函数的创建,因此 f 函数也会创建对应的作用域链,默认以包裹它的函数执行时对应的作用域链为基础。因此此时 f 函数创建时的作用域链如下:

checkscope.scopeChain = [
    checkscope.AO
    global.VO
]

当 f 函数执行时,此时的作用域链变化如下:

checkscope.scopeChain = [
    f.AO
    checkscope.AO
    global.VO
]

当checkscope函数执行完毕,内部作用域会被回收,但是 f函数 的作用域链还是存在的,里面存放着 checkscope函数的活动对象,因此在f函数执行时会从作用域链中查找内部使用的 scope 标识符,从而在作用域链的第二位找到了,也就是在 checkscope.AO 找到了变量scope的值。

正是因为JavaScript做到了这一点,因此才会有闭包的概念。还有人说闭包并不是为了拥有它采取设计它的,而是设计作用域链时的副作用产物。

闭包是JavaScript中最难的点,也是平常面试中常问的问题,我们必须要真正的去理解它,如果只靠死记硬背是经不起考验的。

日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档