您的位置:奥门新浦京网址 > Wed前段 > 大型单页面应用的进阶挑战,论如何在手机端w

大型单页面应用的进阶挑战,论如何在手机端w

发布时间:2019-10-20 03:41编辑:Wed前段浏览(64)

    学习React之前你需要知道的的JavaScript基础知识

    2018/07/25 · JavaScript · React

    原文出处:

    大型单页面应用的进阶挑战

    2015/09/30 · HTML5, JavaScript · 单页应用

    原文出处: 林子杰(@Zack__lin)   

    阅读须知:这里的大型单页面应用(SPA Web App)是指页面和功能组件在一个某个量级以上,举个栗子,比如 30+个页面100+个组件,同时伴随着大量的数据交互操作和多个页面的数据同步操作。并且这里提到的页面,均属于 hash 页面,而多页面概念的页面,是一个独立的 html 文档。基于这个前提,我们再来讨论,否则我怕我们 Get 不到同一个 G 点上去。

    论如何在手机端web前端实现自定义原生控件的样式

    2015/10/30 · HTML5 · 原生控件

    原文出处: 卖烧烤夫斯基   

    手机开发webapp的同学一定遇到过这样问题,如何为丑极了的手机元素应用自定义的样式。首先,要弄清楚为什么要定义手机原生控件的样式,就需要看看手机的那些原生框样式的丑陋摸样:

    android:

    图片 1

    ios:

    图片 2

    Robin   译文出处:[众成翻译

    _小生_]()   

    在我的研讨会期间,更多的材料是关于JavaScript而不是React。其中大部分归结为JavaScript ES6以及功能和语法,但也包括三元运算符,语言中的简写版本,此对象,JavaScript内置函数(map,reduce,filter)或更常识性的概念,如:可组合性,可重用性,不变性或高阶函数。这些是基础知识,在开始使用React之前你不需要掌握这些基础知识,但在学习或实践它时肯定会出现这些基础知识。

    以下演练是我尝试为您提供一个几乎广泛但简明的列表,其中列出了所有不同的JavaScript功能,以补充您的React应用程序。如果您有任何其他不在列表中的内容,只需对本文发表评论,我会及时更新。

    挑战一:前端组件化

    基于我们所说的前提,第一个面对的挑战是组件化。这里还是要强调的是组件化根本目的不是为了复用,很多人根本没想明白这点,总是觉得造的轮子别的业务可以用,说不定以后也可以用。

    其实前端发展迭代这么快,交互变化也快,各种适配更新层出不穷。今天造的轮子,过阵子别人造了个高级轮子,大家都会选更高档的轮子,所以现在前端界有一个现象就是为了让别人用自己的轮子,自己使劲不停地造。

    在前端工业化生产趋势下,如果要提高生产效率,就必须让组件规范化标准化,达到怎样的程度呢?一辆车除了底盘和车身框架需要自己设计打造之外,其他标准化零件都可以采购组装(专业学得差,有啥谬误请指正)。也就是说,除了 UI 和前端架构需要自己解决之外,其他的组件都是可以奉行拿来主义的,如果打算让车子跑得更稳更安全,可以对组件进行打磨优化完善。

    说了这么说,倒不如看看徐飞的文章《2015前端组件化框架之路》 里面写的内容都是经过一定实践得出的想法,所以大部分内容我是赞成而且深有体会的。

    无奈的选择

    看完了这些丑陋的界面元素,我们就可以理解当我们把他们暴露在产品同学的眼中时,那种层层的杀气了。可以看到,界面元素十分丑陋,产品兄弟是肯定不会接受的。但是,不得不说这些控件在触发后的效果比pc机上的要炫酷。这其中以apple机的滚筒选择最为出色.以下是它们触发后调用原生控件的效果:

    android:

    图片 3图片 4图片 5

    ios:

    图片 6图片 7图片 8

    不得不说这些样式原生弹出样式是符合我们设计的原则的,因为它即体现了UI界面的友好和体验度,又不损耗任何web性能,关键是我们什么都不需要做。产品BZJ君看到了,指明要在apple机下要滚筒的效果用来选择日期或者下来单。如果我们不能解决掉界面文本框的样式问题,那么无论后面的效果多炫酷,始终使无法让人接受的。也许你会想花时间写类似的效果?我不否认你可以写出来,但是需要多少时间的工作量呢?也很多人选择了插件的方式。通过jq插件(如果你的项目中没在使用jq,为了这个效果无奈下载jq和其插件)来实现,其实是非常吃力不讨好的事情。一个是插件这种东西出了问题或者变换了需求后它会变得异常的不好扩展,第二个当然是考虑到资源加载,在手机端尤其需要考虑。因此,选择插件是下下策!

    目录

    • 从JavaScript中学习React
    • React 和 JavaScript Classes
    • React中的箭头函数
    • 作为React中的组件的fuuction
    • React类组件语法
    • 在React中的Map, Reduce 和 Filter
    • React中的var,let和const
    • React中的三元运算符
    • React中的Import 和 Export
    • React中的库
    • React中的高阶函数
    • React中的解构和传播运算符
    • There is more JavaScript than React

    挑战二:路由去中心化

    基于我们所说的前提,中心化的路由维护起来很坑爹(如果做一两个页面 DEMO 的就没必要出来现眼了)。MV* 架构就是存在这么个坑爹的问题,需要声明中心化 route(angular 和 react 等都需要先声明页面路由结构),针对不同的路由加载哪些组件模块。一旦页面多起来,甚至假如有人偷懒直接在某个路由写了一些业务耦合的逻辑,这个 route 的可维护性就变得有些糟糕了。而且用户访问的第一个页面,都需要加载 route,即使其他路由的代码跟当前页面无关。

    我们再回过头来思考静态页面简单的加载方式。我们只要把 nginx 搭起来,把 html 页面放在对应的静态资源目录下,启动 nginx 服务后,在浏览器地址栏输入 127.0.0.1:8888/index.html 就可以访问到这个页面。再复杂一点,我们把目录整成下面的形式:

    /post/201509151800.html /post/201509151905.html /post/201509152001.html /category/js_base_knowledge.html /category/css_junior_use.html /category/life_is_beautiful.html

    1
    2
    3
    4
    5
    6
    /post/201509151800.html
    /post/201509151905.html
    /post/201509152001.html
    /category/js_base_knowledge.html
    /category/css_junior_use.html
    /category/life_is_beautiful.html

    这种目录结构很熟吧,对 SEO 很友好吧,当然这是后话了,跟我们今天说的不是一回事。这种目录结果,不用我们去给 Web Server 定义一堆路由规则,页面存在即返回,否则返回 404,完全不需要多余的声明逻辑。

    基于这种目录结构,我们可以抽象成这样子:

    /{page_type}/{page_name}.html

    1
    /{page_type}/{page_name}.html

    其实还可以更简单:

    /p/{name}.html

    1
    /p/{name}.html

    从组件化的角度出发,还可以这样子:

    /p/{name}/name.js /p/{name}/name.tpl /p/{name}/name.css

    1
    2
    3
    /p/{name}/name.js
    /p/{name}/name.tpl
    /p/{name}/name.css

    所以,按照我们简化后的逻辑,我们只需要一个 page.js 这样一个路由加载器,按照我们约定的资源目录结构去加载相应的页面,我们就不需要去干声明路由并且中心化路由这种蠢事了。具体来看代码。咱也懒得去解析了,里面有注释。

    解决方法

    问题来了,既想要弹出层的炫酷效果,又想自定义控件在界面显示的样式。怎么办呢?露珠曾经尝试过最简单的方法去重写css去改变它们的样式,但是即使在google若干小时,也没有找到满意的结果。露珠也尝试过-webkit-appearance属性,但它也显得不尽如人意。况且我们还需要兼容多机型(安卓,苹果,wp?)。无论如何,走改变原始样式的路是行不通的。露珠经过一番思考,找到了自认为非常好的解决方法,也是这篇博文的主题:既然控件在页面的样式不可以改变,那就隐藏它,但是!不是用display:none隐藏,也不是把width和height设置为0,我们希望的是看不到它们的原始样式,而希望保留对它们的tap和focus事件。但是除了以上的方法,还有什么能使它们看不见呢?聪明的你一定想到了,对,就是opacit:0, 通过将控件的不透明度设置为0,我们可以让元素继续让它留在界面上,并且保持随时响应focus事件的状态。我们要做的,是为该控件设置为绝对定位,覆盖在我们自定义样式的一个element上。这样,用户看到的是底下的element,但当他的手去触碰此element时,他实际触碰的是完全透明确留在界面上的原生控件!如下图所示:

    图片 9

    这还是第一步,接下来我们需要为控件绑定响应事件,大多数情况下我们需要绑定的事件都是onchange,一旦选择完成,就把值复制到自定义的element上去。这样大功告成了!不管你是通过表单或者post提交,你取到的值依然是控件的值,自定义的element只负责显示,不负责业务!

    图片 10

    从JavaScript中学习React

    当你进入React的世界时,通常是使用用于启动React项目的 create-react-app。设置项目后,您将遇到以下React类组件:

    JavaScript

    import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; class App extends Component { render() { return ( <div> <header> <img src alt="logo" /> <h1>Welcome to React</h1> </header> <p> To get started, edit <code>src/App.js</code> and save to reload. </p> </div> ); } } export default App;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './App.css';
     
    class App extends Component {
      render() {
        return (
          <div>
            <header>
              <img src alt="logo" />
              <h1>Welcome to React</h1>
            </header>
            <p>
              To get started, edit <code>src/App.js</code> and save to reload.
            </p>
          </div>
        );
      }
    }
     
    export default App;

    可以说,React类组件可能不是最好的起点。新手有许多东西需要消化,不一定是React:类语句,类方法和继承。导入语句也只是在学习React时增加了复杂性。尽管主要焦点应该是JSX(React的语法),但通常所有的事情都需要解释。这篇文章应该揭示所有的东西,大部分是JavaScript,而不用担心React。

    挑战三:领域数据中心化

    对于单向数据流循环和数据双向绑定谁优谁劣是永远也讨论没结果的问题,要看是什么业务场景什么业务逻辑,如果这个前提没统一好说啥都是白搭。当然,这个挑战的前提是非后台的单页面应用,后台的前端根本就不需要考虑前端内存缓存数据的处理,直接跟接口数据库交互就行了。明确了这个前提,我们接着讨论什么叫领域数据中心化。

    前面讨论到两种数据绑定的方式,但是如果频繁跟接口交互:

    • 内存数据销毁了,重新请求数据耗时浪费流量
    • 如果两个接口字段部分不一样但是使用场景一样
    • 多个页面直接有部分的数据相同,但是先来后到导致某些计数字段不一致
    • 多个页面的数据相同,其中某些数据发生用户操作行为导致数据发生变动

    因此,我们需要在业务视图逻辑层和数据接口层中间增加一个 store(领域模型),而这个 store 需要有一个统一的 内存缓存 cache,这个 cache 就是中心化的数据缓存。那这个 store 究竟是用来弄啥勒?

    图片 11

    Store 具有多形态,每个 store 好比某一类物品的仓储(领域,换个词容易理解),如蔬果店 fruit-store, 服装店 clothes-store,蔬果店可以放苹果香蕉黑木耳,服装店可以放背心底裤人字拖。如果品种过于繁多,我们可以把蔬果店精细化运营变成香蕉专卖店,苹果专卖店(!== appstore),甚至是黑木耳专卖店…( _ _)ノ|,蔬果种类不一样,但是也都是称重按斤卖嘛。

    var bannerStore = new fruitStore();

    var appleStore = new fruitStore();

    有了这些仓储之后,我们可以放心的把数据丢给视图逻辑层大胆去用。想修改数据?直接让 store 去改就行了,其他页面的 DOM 文本内容也得修改吧?那是其他页面的业务逻辑做的事,我们把事件抛出去就好了,他们处不处理那是他们的事,咱别瞎操心(业务隔离)。

    那么 store 具体弄啥勒?

    图片 12

    • 32 个赞位置可点赞或者取消,三个页面的赞数需要同步,按钮点赞与取消的状态也要同步。
    • 条目是否已收藏,取消收藏后 Page B 需要删除数据,Page A+C 需要同步状态,如果在 Page C 又有收藏操作,Page B 需要相应增减数据,Page A 状态需要同步。
    • 发评论,Page C 需要更新评论列表和评论数,Page A+B 需要更新评论数。如果 Page B 没有被加载过,这时候 Page B 拿到的数据应该是最新的,需要同步给 A+C 页面对应的数据进行更新。

    所以,store 干的活就是数据状态读写和同步,如果把数据状态的操作放到各个页面自己去处理,页面一旦多了或者复杂起来,就会产生各个页面数据和状态可能不一致,页面之前双向引用(业务耦合严重)。store 还有另一个作用就是数据的输入输出格式化,简单举个栗子:图片 13

    • 任何接口 API 返回的数据,都需要经过 input format 进行统一格式化,然后再写入 cache,因为读取的数据已按照我们约定的规范进行的处理,所以我们使用的时候也不需要理会接口是返回怎样的数据类型。
    • 某些组件需要的数据字段格式可能不同,如果把数据处理放在模板进行处理,会导致模板无法更加简洁通用(业务耦合),所以需要 output format 进行处理。

    所以,store 就是扮演着这样的角色——是数据状态读写和同步,以及数据输入输出的格式化处理。

    代码实现

    XHTML

    <html> <head> <style> body{ position: relative; } .front { position: absolute; opacity: 0; height: 30px; width: 180px; } .back { height: 30px; width: 386px; border: 1px dashed #19a39e; line-height: 30px; text-align: center; font-size: 11px; } </style> </head> <body> <input type="date" onchange="document.getElementsByClassName('back')[0].innerHTML = this.value;"> <div class="back">我是自定义element,我上面覆盖着一层看不见的input</div> </body> </html>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <html>
       <head>
           <style>
               body{
                   position: relative;
               }
               .front {
                   position: absolute;
                   opacity: 0;
                   height: 30px;
                   width: 180px;
               }
               .back {
                    height: 30px;
                    width: 386px;
                    border: 1px dashed #19a39e;
                    line-height: 30px;
                    text-align: center;
                    font-size: 11px;
               }
           </style>
       </head>
        <body>
            <input type="date" onchange="document.getElementsByClassName('back')[0].innerHTML = this.value;">
            <div class="back">我是自定义element,我上面覆盖着一层看不见的input</div>
        </body>
    </html>

     

    React和JavaScript类

    在开始时遇到React类组件,需要有关JavaScript类的基础只是。JavaScript类在语言中是相当新的。以前,只有JavaScript的原型链也可以用于继承。JavaScript类在原型继承之上构建,使整个事物更简单。

    定义React组件的一种方法是使用JavaScript类。为了理解JavaScript类,您可以花一些时间在没有React的情况下学习它们。

    JavaScript

    class Developer { constructor(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; } getName() { return this.firstname

    • ' ' + this.lastname; } } var me = new Developer('Robin', 'Wieruch'); console.log(me.getName());
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Developer {
      constructor(firstname, lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
      }
     
      getName() {
        return this.firstname + ' ' + this.lastname;
      }
    }
     
    var me = new Developer('Robin', 'Wieruch');
     
    console.log(me.getName());

    类描述了一个实体,该实体用作创建该实体实例的蓝图。一旦使用new语句创建了类的实例,就会调用该类的构造函数,该实例化该类的实例。因此,类可以具有通常位于其构造函数中的属性。此外,类方法(例如getName())用于读取(或写入)实例的数据。类的实例在类中表示为此对象,但实例外部仅指定给JavaScript变量。

    通常,类用于面向对象编程中的继承。它们在JavaScript中用于相同的,而extends语句可用于从另一个类继承一个类。具有extends语句的更专业的类继承了更通用类的所有功能,但可以向其添加其专用功能。

    JavaScript

    class Developer { constructor(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; } getName() { return this.firstname

    • ' ' + this.lastname; } } class ReactDeveloper extends Developer { getJob() { return 'React Developer'; } } var me = new ReactDeveloper('Robin', 'Wieruch'); console.log(me.getName()); console.log(me.getJob());
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Developer {
      constructor(firstname, lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
      }
     
      getName() {
        return this.firstname + ' ' + this.lastname;
      }
    }
     
    class ReactDeveloper extends Developer {
      getJob() {
        return 'React Developer';
      }
    }
     
    var me = new ReactDeveloper('Robin', 'Wieruch');
     
    console.log(me.getName());
    console.log(me.getJob());

    基本上,它只需要完全理解React类组件。 JavaScript类用于定义React组件,但正如您所看到的,React组件只是一个React组件,因为它继承了从React包导入的React Component类的所有功能。

    JavaScript

    import React, { Component } from 'react'; class App extends Component { render() { return ( <div> <h1>Welcome to React</h1> </div> ); } } export default App;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import React, { Component } from 'react';
     
    class App extends Component {
      render() {
        return (
          <div>
            <h1>Welcome to React</h1>
          </div>
        );
      }
    }
     
    export default App;

    这就是为什么render()方法在React类组件中是必需的:来自导入的React包的React组件指示您使用它在浏览器中显示某些内容。此外,如果不从React组件扩展,您将无法使用其他生命周期方法 (包括render()方法)。例如,不存在componentDidMount()生命周期方法,因为该组件将是vanilla JavaScript类的实例。并且不仅生命周期方法会消失,React的API方法(例如用于本地状态管理的this.setState())也不可用。

    但是,正如您所看到的,使用JavaScript类有利于使用您的专业行为扩展通用类。因此,您可以引入自己的类方法或属性。

    JavaScript

    import React, { Component } from 'react'; class App extends Component { getGreeting() { return 'Welcome to React'; } render() { return ( <div> <h1>{this.getGreeting()}</h1> </div> ); } } export default App;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import React, { Component } from 'react';
     
    class App extends Component {
      getGreeting() {
        return 'Welcome to React';
      }
     
      render() {
        return (
          <div>
            <h1>{this.getGreeting()}</h1>
          </div>
        );
      }
    }
     
    export default App;

    现在您知道为什么React使用JavaScript类来定义React类组件。当您需要访问React的API(生命周期方法,this.state和this.setState())时,可以使用它们。在下文中,您将看到如何以不同的方式定义React组件,而不使用JavaScript类,因为您可能不需要始终使用类方法,生命周期方法和状态。

    毕竟,JavaScript类欢迎使用React中的继承,这对于React来说不是一个理想的结果,因为React更喜欢组合而不是继承。因此,您应该为您的React组件扩展的唯一类应该是官方的React组件。

    挑战四:Hybrid App 化

    现在 Hybrid App 架构应用很火啊 _ (:3」∠)_,不搞一下都不好意思说自己是做 H5的。这里所说的 Hybrid App 可不是那种内置打包的 html 源码那种,而是直接去服务端请求 html 文档那种,可能会使用离线缓存。有的人以为如果要使用 Hybrid 架构,就不能使用 SPA 的方式,其实 Hybrid 架构更应该使用 SPA。

    遇到的几个问题,我简单列举一下:

    • 客户端通过 url 传参

      如果通过 http get 请求的 query 参数进行传参,会导致命中不到 html 文档缓存,所以通过 SPA 的 hash query 传参,可以规避这个问题。

    • 与其他 html 页面进行跳转

      这种场景下,进入新页面和返回旧页面导致 webview 会重新加载本地的 html 文档缓存,视觉体验很不爽,即使页面使用了离线缓存,而 SPA 可以规避这个问题。

    • 使用了离线缓存的页面需要支持代码多版本化

      由于采用了非覆盖性资源发布方式,所以需要仍然保留旧的代码一段时间,以防止用户使用旧的 html 文档访问某些按需加载功能或清除了本地缓存数据而拿不到旧版本代码。

    • js 和 css 资源 离线化

      由于离线缓存的资源需要先在 manifest 文件声明,你也不可能总是手动去维护需要引用的 js 和 css 资源,并且那些按需加载的功能也会因此失去按需加载的意义。所以需要将 js 和 css 缓存到 localstorage,直接省去这一步维护操作。至于用户清除 localstorage,参考第三点解决方案。

    • 图标资源离线化

      将图标文件进行 base64 编码后存入 css 文件,方便离线使用。

    结束语

    产品B君看到了丑陋的东西消失了,ios下的滚筒又炫酷滚起来了,肯定会拍拍你的肩膀说兄弟干得不错。这篇博文也不仅仅是关于解决控件样式的问题,在其他类似的情况下,用遮罩层的方法掩盖你无能为力的地方是值得借鉴的。其实在开发中类似的的小花招很多,只要找到了诀窍和方法,一行代码抵得上一万行代码(借用刘震云的小说名)。虽然是个很小的小花招,大篇幅的用一篇博客来讲解是过于夸张和繁琐,不过前端开发事无巨细,希望对遇到类似问题或者将来需要解决的同学有帮助。

    1 赞 收藏 评论

    图片 14

    React中的箭头函数

    When teaching someone about React, I explain JavaScript arrow functions pretty early. They are one of JavaScript’s language additions in ES6 which pushed JavaScript forward in functional programming.

    在教关于React时,我很早就解释了JavaScript arrow functions。它们是ES6中JavaScript的语言添加之一,它推动了JavaScript在函数式编程中的发展。

    JavaScript

    // JavaScript ES5 function function getGreeting() { return 'Welcome to JavaScript'; } // JavaScript ES6 arrow function with body const getGreeting = () => { return 'Welcome to JavaScript'; } // JavaScript ES6 arrow function without body and implicit return const getGreeting = () => 'Welcome to JavaScript';

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // JavaScript ES5 function
    function getGreeting() {
      return 'Welcome to JavaScript';
    }
     
    // JavaScript ES6 arrow function with body
    const getGreeting = () => {
      return 'Welcome to JavaScript';
    }
     
    // JavaScript ES6 arrow function without body and implicit return
    const getGreeting = () =>
      'Welcome to JavaScript';

    JavaScript箭头函数通常用在React应用程序中,以保持代码简洁和可读。尝试从JavaScript ES5到ES6功能重构我的功能。在某些时候,当JavaScript ES5函数和JavaScript ES6函数之间的差异很明显时,我坚持使用JavaScript ES6的方式来实现箭头函数。但是,我总是看到React新手的太多不同的语法可能会让人不知所措。因此,我尝试在使用它们在React中全部使用之前,使JavaScript函数的不同特性变得清晰。在以下部分中,您将了解如何在React中常用JavaScript箭头函数。

    挑战五:性能优化

    @前端农民工 在 别处 已经说得很清楚了,直接传送门过去看吧,这里不罗嗦了。

     

    1 赞 2 收藏 评论

    图片 15

    本文由奥门新浦京网址发布于Wed前段,转载请注明出处:大型单页面应用的进阶挑战,论如何在手机端w

    关键词:

上一篇:让拖放变的流行起来,你所不知道的

下一篇:没有了