函数式 UI:Web开发终于摆脱了框架的束缚指南攻略_web小白攻略

本文要点用户界面都是响应式系统,由用户界面应用程序接收的事件与应用程序必须在接口系统上执行的动作之间的关系来定义流行的 UI 框架(如 React、Vue 或 Angular)通常具有很高的次生复杂性,它们的状态和效果零散地分散在一个组件树中,并且由 上帝组件 处理大量无关的问题函数式 UI 是用于用户界面应用程序的一组实现技术,它强调了应用程序的效果(ef

函数式 UI:Web开发终于摆脱了框架的束缚指南攻略

本文要点

  • 用户界面都是响应式系统,由用户界面应用程序接收的事件与应用程序必须在接口系统上执行的动作之间的关系来定义
  • 流行的 UI 框架(如 React、Vue 或 Angular)通常具有很高的次生复杂性,它们的状态和效果零散地分散在一个组件树中,并且由 上帝组件 处理大量无关的问题
  • 函数式 UI 是用于用户界面应用程序的一组实现技术,它强调了应用程序的效果(effectful)和纯函数部分之间的明确界限
  • 函数式 UI 从概念上讲是很简单的,可以更直接地反映应用程序的规范(specification),将 UI 框架降级为一个单纯的库,允许开发人员对用户场景进行单元测试,并减少应用程序的设计和实现错误。
  • 函数式 UI 会针对正确性进行优化,同时会为开发人员创建一些选项,以便在将来获得更多信息时重新考虑诸如 UI 框架或远程数据获取机制之类的关键决策。

为什么要使用函数式 UI?

顾名思义,用户界面允许用户与其他系统 交互 ,其理念是:相比直接与其他系统互动,这种交互界面会提供一些用户期望的好处。用户通过某种输入方式(例如按键或声音输入)表达意图,然后用户界面通过在接口系统上预定义的动作来做出响应。 用户界面基本上是天然的响应式系统 。用户界面的任何规范技术都必须详细说明 用户界面输入和接口系统上的动作之间的对应关系 ,也就是应用程序的 行为 规范。这样一来,就可以根据用户发起或应用程序接受的一系列事件,以及系统对应的预期反应来定义一个用户故事。

函数式 UI:Web开发终于摆脱了框架的束缚指南攻略_web小白攻略

许多用来实现用户界面的框架(Angular2、Vue 和 React 等)都使用回调 过程 或事件处理程序,后者会作为事件的结果而 直接执行 相应的动作。决定要执行哪个动作(例如输入验证、本地状态更新、错误处理或数据获取等),通常意味着要访问和更新某些状态,而这些状态并不总是在作用域内。因此框架会包含一些状态管理或通信能力,以处理所需的相关状态的传递,并在允许和要求时更新状态。

基于组件的用户界面实现往往包含一些状态,而动作以不明显的方式沿着组件树散布开来。例如,一个 待办事项列表应用程序 可以写为 。假设一个 TodoItem 管理其删除操作,则必须将删除操作与更新的项目列表沿着结构向上传递给要调用的父级 TodoList。假设是由父级的 TodoList 管理项目的删除操作,它可能还是要将删除操作传递给子级的 TodoItem(也许执行一些清理动作)。

这里的底线是要将动作与给定的事件匹配,我们需要查看每个组件实现以了解事件及其处理的动作,以及它与组件树中依赖它的组件所使用的消息传递协议,然后对依赖组件重复相同的过程,直到下面没有依赖组件为止。只有这样,我们才能生成一个事件触发动作的完整列表。此外,组件通常是给定框架专属的,其选项取决于这个框架中可用的内容。

但是,我们选择的的框架是与规范分离的实现细节。实现应用程序和组件间消息传递的组件树,其特定形态(shape)在很大程度上也与规范紧密关联。于是考虑这样的问题:当用户遵循某个用户故事时,比如说当应用程序收到给定的事件序列 [X,Y,…] 时会发生什么情况?回答这类问题需要驯服来自于框架的特性、组件、状态管理和通信机制的 次生复杂性 。

但是如果不回答这个问题,我们就不能确定实现是否符合规范,而符合规范就是软件的存在价值。随着用户故事的数量和大小继续增长,这种信心只会愈加脆弱。

而函数式 UI 技术试图从事件 / 动作对应关系中导出函数等式,从而直接反映用户界面的规范。由于等式是直接从规范中得出的,因此我们可以让实现尽可能接近规范。一般来说,这会减少实现错误的生存空间,并且会在开发的早期阶段就发现规范错误。由于函数式 UI 依赖于纯函数,因此可以轻松、可靠和快速地对用户故事进行单元测试。在某些情况下(状态机建模),甚至可以高度自动化地生成实现和测试。因为函数式 UI 只是标准的函数式编程,所以它不依赖于任何框架魔术。函数式 UI 可以很好地对接任何 UI 框架,需要的话也可以不使用任何框架。

本文将介绍函数式 UI 的意义,及其背后的基本函数等式,还会展示这种技术的具体用法示例,以及如何测试以这种风格编写的应用程序。与此同时,本文将努力揭示在 Web 应用程序开发中使用函数式 UI 方法的优缺点。

但什么是函数式 UI 呢?

任何用户界面应用程序都会隐式或显式地实现以下内容:

  1. 一个接口,应用程序通过它来接收事件
  2. 事件和动作之间的一种关系(~),形如:event ~ action,其中
  • 〜称为响应关系
  • event 是通过用户界面接收并触发接口系统上一个 action 的事件。事件可以是
    • 用户发起的(如按钮点击)
    • 系统发起的,即由环境或外部世界生成的(如 API 响应)
  1. 一个与外部系统对接的接口,必须通过该接口执行用户预期的动作

因为大多数响应式系统都是有状态的,所以一般来说关系〜不是一个 数学函数 (也就是 只将一个 输出关联到一个输入)。切换按钮就是一个简单的有状态 UI 应用程序。按下按钮一次,应用程序将呈现一个切换后的按钮。再按一次,应用程序将呈现一个切换前的按钮。由于相同的用户事件会在对接的输出设备(屏幕)上执行不同的渲染动作,因此应用程序是有状态的,无法定义一个数学函数使 action = f(event)。

我们称 函数 **** 式 UI 为用户界面应用程序的一组实现技术,其重点在于以下内容:

  • 将事件表示与事件调度分离开来
  • 将动作表示与动作执行分离开来
  • 将应用程序执行的动作与应用程序接收到的事件关联在一起的显式纯函数( 响应函数 )

因此,函数式 UI 隔离了应用程序的效果部分(调度事件,运行效果),并将它们与纯函数链接在一起。结果,函数式 UI 自然会产生分层的架构,其中每一层仅与相邻层交互。最简单的分层架构由三层组成,可以表示如下:

函数式 UI:Web开发终于摆脱了框架的束缚指南攻略_web小白攻略命令处理程序(command handler)模块负责执行通过每个接口系统定义的编程接口所接收的命令。接口系统(interfaced system)可以将针对之前 API 调用的响应作为事件,发送给命令处理程序。接口系统还可以通过一个调度程序(dispatcher)将事件发送给应用程序。DOM 通常就是这种情况,它是以渲染命令的结果来做更新的,并且包含事件处理程序,它们只会调度事件。

这样的概念框架建立起来后,我们来介绍实现函数式 UI 的基本等式。

响应式系统的基本等式

在大多数情况下,一个响应式系统的状态可以表述为这样的形式:(action, new state) = f(state,event),其中:

  • f 是一个纯函数,
  • state 包含由环境和响应式系统的规范带来的所有可变性,这样 f 就是纯粹的。

这里的 f 被称为 响应函数 。如果我们用自然整数按时间顺序来索引,以使索引 n 对应于发生的第 n 个事件,则以下条件成立:

  • (action_n, state_n+1) = f(state_n, event_n) ,其中:
    • n 是响应式系统处理的第 n 个事件,
    • state_n 是 处理第 n 个事件时 响应式系统的状态,
    • 因此,在事件的发生和用于计算(compute)系统响应的状态之间存在一个隐式的 时间关系 。

基于这些观察结果而诞生的实现技术依赖于一个响应函数 f,该函数为每个事件 显式 计算响应式系统的新状态,以及要执行的动作。这方面知名的例子有:

  • Elm :其中 update :: Msg -> Model -> (Model, Cmd Msg) 函数严格对应响应函数 f,Msg 对应 events,Model 对应状、states,Cmd Msg 对应 actions。
  • Pux(PureScript):其中 foldp :: Event -> State -> EffModel State Event 函数是 Pux 框架中的等效公式。在 Pux 中,EffModel State Event 是包含新状态值和一组效果(动作)的一个记录,这些效果可能会生成新的事件供应用程序处理。
  • Seed(Rust) :其更新函数 fn update(msg: Msg, model: &mut Model, _: &mut impl Orders ) 对应的是 Elm 更新函数(Cmd 变成了 Orders),同时利用了 Rust 带来的可变性。

下面我们来看一些具体示例。在纯函数式语言中,函数式 UI 是使用这类语言编程的自然结果。在其他语言(例如 JavaScript)中,开发人员需要努力遵循函数式 UI 的原则。下文提供了分别使用纯函数式语言 Elm 和香草 JavaScript 编写函数式 UI 的示例。

示例

Elm

下面展示一个 简单的 Elm 应用程序 的示例,其在单击一个按钮时显示随机的小猫动图:

-- 按一个按钮,发送一个 GET 请求来获取随机的小猫动图。
-- 工作机制介绍: https://guide.elm-lang.org/effects/json.html

(some imports...)

-- MAIN
main =
  Browser.element
    { init = init
    , update = update
    , view = view
    }

-- MODEL
type Model
  = Failure
  | Loading
  | Success String

-- Initial state
init : () -> (Model, Cmd Msg)
init _ =
  (Loading, getRandomCatGif)

-- UPDATE
type Msg
  = MorePlease
  | GotGif (Result Http.Error String)

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    MorePlease ->
      (Loading, getRandomCatGif)

    GotGif result ->
      case result of
        Ok url ->
          (Success url, Cmd.none)

        Err _ ->
          (Failure, Cmd.none)

-- VIEW
view : Model -> Html Msg
view model =
  div []
    [ h2 [] [ text "Random Cats" ]
    , viewGif model
    ]

viewGif : Model -> Html Msg
viewGif model =
  case model of
    Failure ->
      div []
        [ text "I could not load a random cat for some reason. "
        , button [ onClick MorePlease ] [ text "Try Again!" ]
        ]

    Loading ->
      text "Loading..."

    Success url ->
      div []
        [ button [ onClick MorePlease, style "display" "block" ] [ text "More Please!" ]
        , img [ src url ] []
        ]

-- HTTP
getRandomCatGif : Cmd Msg
getRandomCatGif =
  Http.get
    { url = "https://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=cat"
    , expect = Http.expectJson GotGif gifDecoder
    }

gifDecoder : Decoder String
gifDecoder =
  field "data" (field "image_url" string)

从代码中可以推断出:

  • 该应用程序始于某个初始状态,并运行一个初始命令(init _ = (Loading, getRandomCatGif))
  • 该初始状态会显示一个由 view 函数生成的初始视图
  • 点击一个 view 按钮会将 MorePlease 消息发送到 Elm 的运行时([ button [ onClick MorePlease, … ])
  • 其中 update 函数 update msg model = case msg of MorePlease -> (Loading, getRandomCatGif) 将确保有一个 MorePlease 消息来获取一张随机的小猫动图,同时将应用程序的状态(model)更新为 Loading(从而使用户界面显示一条加载消息)。
  • 如果获取成功,它将返回一个 URL(GotGif Ok url 消息),使用户界面显示相应的图像(img [ src url ])

除了 update 函数外,Elm 还定义了一个运行时,负责接收事件,将事件传递给更新函数,并执行所计算的(computed)命令。因此,开发人员只需要定义应用程序状态和更新函数的内容。有了一个 单独的,中心化 的 update 函数来计算针对事件的响应,我们就能轻松回答 ” 当事件 [X,Y,……] 发生时会出现什么情况 ” 这样的问题。

香草 JavaScript

在 JavaScript 世界中, Hyperapp 这个框架采用的架构深受 Elm 的影响,只是细节略有不同。Hyperapp 非常轻巧(2KB),其中大多数代码(80%)专门用来处理它自己的虚拟 DOM 实现。但是,Hyperapp 不会公开一个纯粹的响应函数,而是像 Elm 一样使用一个 view 函数。与 Elm 不同,这里的 view 函数不仅将某个状态作为其第一个参数来接收,还将包含应用程序可执行的所有动作的对象作为第二个参数来接收。

因此 view 函数不是纯函数,而是 Jessica Kerr 所描述的 隔离函数 。这意味着该函数仅有的依赖项是它的参数。纯函数是隔离的,但是隔离函数不一定是纯函数,因为它们的参数可能是生成效果的函数,或受外部世界控制的变量。但是如有必要,我们仍然可以通过 mocking 隔离函数的参数来对它们进行单元测试。于是乎,Hyperapp 无法遵循函数式 UI 的原则,但仍然保留了函数式 UI 的某些长处。

想要了解如何使用 Hyperapp 构建相对复杂的应用程序,读者可以参考 Hyperapp 的一个名为 Conduit 的( Medium 克隆版示例应用 )实现。这个应用程序也有一个 Elm 实现 ,以及其他十几个框架中的实现版本。

但在使用 JavaScript 实现用户界面时,无需放弃任何函数式 UI 原则。在一个假想的实现中,应用程序外壳负责将事件源连接到更新函数,并用类似的方式将更新函数连接到执行所计算的动作的模块,从而复制各种事件循环。update 函数可以采用以下形式(举例),用单个{command, params}对象编码其返回值(在 Elm 中为 Cmd Msg 类型)。

这里我们考虑使用前面讨论过的,显示随机小猫动图的应用程序,做一个 JavaScript 的等效实现 。更新函数如下:

// Update function
function update(event, model) {
  // Event has shape `{[eventName]: eventData}`
  const eventName = Object.keys(event)[0];
  const eventData = event[eventName];

  if (eventName === MORE_PLEASE) {
    return {
      model: LOADING,
      commands: [
        { command: GET_RANDOM_CAT_GIF, params: void 0 },
        { command: RENDER, params: void 0 }
      ]
    };
  } else if (eventName === GOT_GIF) {
    if (eventData instanceof Error) {
      return {
        model: FAILURE,
        commands: [{ command: RENDER, params: void 0 }]
      };
    } else {
      const url = eventData;
      return {
        model: SUCCESS,
        commands: [{ command: RENDER, params: url }]
      };
    }
  }

  // 一些预期外的 event, 应该什么都不会做
  return {
    model: model,
    commands: []
  };

这里有一个基本的事件发射器用来调度事件。尽管这里可以使用任何 UI 框架的渲染函数,但这个简单演示中的渲染函数是通过直接 DOM 克隆来实现的。因此,命令执行如下:

 复制代码

[MORE_PLEASE, GOT_GIF].forEach(event => {
  eventEmitter.on(event, eventData => {
    const { model: updatedModel, commands } = update(
      { [event]: eventData },
      model
    );
    model = updatedModel;

    if (commands) {
      commands.filter(Boolean).forEach(({ command, params }) => {
        if (command === GET_RANDOM_CAT_GIF) {
          getRandomCatGif()
            .then(response => {
              if (!response.ok) {
                console.warn(`Network request error`, response.status);
                throw new Error(response);
              } else return response.json();
            })
            .then(x => {
              if (x instanceof Error) {
                eventEmitter.emit(GOT_GIF, x);
              }
              if (x && x.data && x.data.image_url) {
                eventEmitter.emit(GOT_GIF, x.data.image_url);
              }
            })
            .catch(x => {
              eventEmitter.emit(GOT_GIF, x);
            });
        }
        if (command === RENDER) {
          if (model === LOADING) {
            setDOM(initViewEl.cloneNode(true), appEl);
          } else if (model === FAILURE) {
            setDOM(failureViewEl.cloneNode(true), appEl);
          } else if (model === SUCCESS) {
            const url = params;
            setDOM(successViewEl(url).cloneNode(true), appEl);
          }
        }
      });
    }
  });

如上所述,自己来实现函数式 UI 是非常简单的。如果你想重用现有的解决方案,可以考虑 raj 或 ferp 项目这些很有用的库,它们严格遵循函数式 UI 原则。你不必担心它们会超出你的应用程序预算。整个 raj 库非常小(33 行代码),因此可以完整粘贴在这里:

exports.runtime = function (program) {
  var update = program.update
  var view = program.view
  var done = program.done
  var state
  var isRunning = true

  function dispatch (message) {
    if (isRunning) {
      change(update(message, state))
    }
  }

  function change (change) {
    state = change[0]
    var effect = change[1]
    if (effect) {
      effect(dispatch)
    }
    view(state, dispatch)
  }

  change(program.init)

  return function end () {
    if (isRunning) {
      isRunning = false
      if (done) {
        done(state)
      }
    }
  }
}

尽管类似 Elm 的实现从根本上讲很简单,但与基于组件的实现相比,用它通常可以更好地了解应用程序的行为。一般来说,基于组件的实现可以让你很快搞明白用户界面会长什么样,但你可能不得不费力地从组件的实现细节中分辨出界面的行为(发生事件 X 时出现的情况)。换句话说,基于组件的实现可通过组件重用来优化生产力,而 函数 **** 式 UI 实现可将用例与实现匹配,从而提升正确性 。

单元测试用户场景

响应式系统运行时会产生踪迹(trace),也就是运行期间发生的(events, actions)序列。为了让响应式系统的行为正确,应设置一组允许的踪迹。相对应的,测试响应式系统时要验证实际踪迹与许可踪迹的集合是否匹配。从我们的基本等式得出的另一个纯函数可用于此用途:

For all n: (action_n, state_n+1) = f(state_n, event_n)

先前的等式意味着:

(action_0, state_1) = f(state_0, event_0)
(action_1, state_2) = f(state_1, event_1)
(action_2, state_3) = f(state_2, event_2)
...
(action_n, state_n+1) = f(state_n, event_n)

如果我们将 h 定义为将事件序列映射到相应动作序列的函数:

h([event_0]) = [action_0]
h([event_0, event_1]) = [action_0, action_1]
h([event_0, event_1, event_2]) = [action_0, action_1, action_2]
h([event_0, event_1, event_2, ..., event_n]) = [action_0, action_1, action_2, ..., action_n]

那么 h 就是一个 纯函数 !这意味着 h 可以很容易地测试,只需向其提供输入并检查它是否产生了预期的输出即可。请注意,在 h 中不会再提及应用程序的状态。由此以来我们就有了以下结果:

  • 可以单独测试用户场景 ,也就是说可以对各个用户场景进行单元测试,因为各个用户场景都是具有各自期望动作的事件序列
  • 针对应用程序的指定行为进行测试,而不是针对实现细节(例如应用程序状态的形状,或者用来获取数据的 HTTP 或套接字)进行测试
  • 对用户场景进行单元测试使开发人员能够遵循 测试金字塔 原则,并在他们的一大堆单元测试中添加少量针对性的集成和端到端测试
  • 因此,开发人员无需执行运行时间过长或不稳定的测试,他们的工作效率就会更高(集成和端到端测试编写起来昂贵且难以维护)
  • 开发人员可以选择任何测试框架(或哪个都不用)

当用户场景测试可以快速编写和执行时,就可以在给定的时间内设想和测试更多的用户场景。由于用户场景是简单的序列,因此更容易自动生成此类序列。在使用状态机对用户界面行为建模的情况下,实际上我们可以 自动生成数以千计的测试 ,这样比起来手工且痛苦地编写测试,我们就可以覆盖更多用户场景和边缘案例。

最终的成果是我们能较早发现设计和实现错误,从而带来更快的迭代和更高的软件质量。毫无疑问,这是函数式 UI 技术的主要卖点,也是在安全性优先的软件开发项目中使用它们的关键因素所在。

结论

用户界面都是响应式系统,因此可以使用一个纯响应函数,将用户界面接受的事件映射到接口系统上的动作来定义用户界面。利用函数式编程的实现技术可以让实现更接近规范,更易推理和测试。函数式 UI 可以让开发人员摆脱不兼容的 UI 和测试框架带来的麻烦,并将重点转移到实现(how)上的规范(what)上。也许有人怀疑没有 UI 框架就没法开发严肃的应用程序,但我们要知道 GitHub 网站就不依赖任何 UI 框架 。

使用函数式 UI(它强调隔离的,单关注点的动作)和 UI 组件时,大多数时候我们只关注视图——一些框架将此类组件称为 纯 组件。此外,应用程序外壳程序会调用 UI 框架,而不是 UI 框架调用用户提供的框架感知函数。简而言之,UI 框架仍然可以使用,但它们现在只充当简单的库而已。

另一面来说,使用函数式 UI 时很难重用非纯组件,从而降低了框架组件生态系统的价值。此外,函数式 UI 需要前端开发人员在心态和方法上都做出转变,以前大家相比应用程序行为要更重视渲染(在屏幕上生成内容),并且更在乎生产效率(编写代码) 而非正确性(需要编写全面的测试)。

但是,Elm 在其七年的发展历程中已经验证了函数式 UI 方法的可行性,并证明只要有适当的工具,开发人员就可以 快速学习并享受这种方法 。

海计划公众号
(0)
上一篇 2020/03/24 05:43
下一篇 2020/03/24 05:43

您可能感兴趣的内容

  • Nodejs核心模块简介使用指南_模块基础入门

    学习nodejs必须要掌握其核心,就像学JavaScript必须掌握函数、对象、数据类型、BOM、DOM等。nodejs核心也不少,这里介绍几个核心:Events模块、fs模块、stream的使用、http模块。Events事件驱动、非阻塞异步IO是nodejs的特点,所以Events是非常重要的模块。并且node中绝大多数模块都继承了Events。事件是发

    2020/03/29
  • 直播交友软件推荐:直播软件哪个好用?小白常识_直播零基础入门

    手机交友软件有很多,强大的社交软件如微信与QQ,作为社交领域的领头羊让更多的网络中的朋友联系在一起。当然像陌陌这种在强大压力下生存下来的交友app也是很受大家的欢迎,这次小编为喜欢交朋友的各位推荐以下几款手机交友软件,必然能让你在交友的时候事半功倍,不过要注意一点,现在网络骗子的花样很多,交友的同时要慎重,看清对方的身份,防止被骗。 V聊 靠谱又真实的快速

    2020/03/23
  • Convertow基础知识入门_在线免费图片和PDF转换工具

    Convertow基础知识入门 官方网址:http://convertow.com/ 简介描述:在线免费图片和PDF转换工具 Convertow是一个在线免费的不同文件转换工具,支…

    2020/03/11
  • 为什么会产生微服务架构?基础入门_微服务基础知识教程

    Web应用架构受系统用户量、开发人员组织方式影响严重。过去二十年互联网迅速发展,Web架构也从单体式演进出微服务,背后还有比如 Martin Fowler 提出的理论支撑。虽然每个人都听说过微服务,但是很多人并不太清楚为什么要这么做,应该怎么做,怎么拆。要回答这个问题我认为需要从Web架构的演化历史的高度去理解这些架构设计中的取舍。首先我们改进系统架构的目的

    2020/03/24
  • 函数式编程杂谈入门百科_编程菜鸟教程下载

    比起命令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断演进,逐层推导出复杂的运算。本文通过函数式编程的一些趣味用法来阐述学习函数式编程的奇妙之处。一、编程范式综述编程是为了解决问题,而解决问题可以有多种视角和思路,其中普适且行之有效的模式被归结为“编程范式”。编程语言日新月异,从汇编、Pascal、C、C++

    2020/03/24
  • 开发直播软件可能会用到的第三方服务有哪些?小白常识_直播基础教程

    直播软件的开发,虽然现在很多技术都已经趋向成熟,但是有些东西不是仅仅技术就可以实现的,在考虑到时间、成本、风险、功能稳定性等多种因素的情况下,大部分直播平台开发时都会预留第三方服务的接口来节约开发的成本。一、第三方服务器1.CDN在玩游戏或看电影时经常会出现画面撕裂、卡顿等现象,为了解决网络延迟卡顿的问题,就需要用到CDN服务。CDN即内容分发网络,是构建在

    2020/03/23
  • Js的6种基本数据类型基础入门_类型基础教程

    数据类型数据类型指的就是字面量类型在JS中一共有六种数据类型 String:字符串 Number:数值 Boolean:布尔值 Null:空值 Undefined:未定义 Object:对象;其中String,Number,Boolean,Null,Undefin

    2020/03/29
  • JS面向切面编程AOP教程视频_编程小白帮助

    什么是AOP?AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。把这些功能抽离出来之后, 再通过“动态织入”的方式掺入业务逻辑模块中。 AOP能给我们带来什么好处?AOP的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块。 J

    2020/03/23
  • JavaScript 数据类型与类型判断详解入门百科_类型教程视频

    一、JavaScript 数据类型JavaScript 数据类型有两种,分别是基本数据类型和引用数据类型。基本数据类型NumberStringBooleanUndefinedNullSymbol (ES6 新增,表示独一无二的值)引用数据类型ObjectFunctionArray我们来详细了解一下这两种数据类型的特点1.基本数据类型值不可变基本类型的值是不可

    2020/03/22
  • Event是什么?注册事件监听的方式有哪些?入门教程_事件小白知识

    MDN中的解释Event 接口表示在 DOM 中发生的任何事件(常见事件); 一些是用户生成的(例如鼠标或键盘事件),而其他由 API 生成(例如指示动画已经完成运行的事件,视频已被暂停等等)。事件通常由外部源触发,同样也会以编程方式触发,例如执行一个 element 的一个 HTMLElement.click( ) 方法,或通过定义事件,然后使用 Even

    2020/03/26
  • js中(function(){…})()立即执行函数写法理解菜鸟教程下载_函数小白攻略

    ( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法,最初我以为是一个括号包裹匿名函数,再在后面加个括号调用函数,最后达到函数定义后立即执行的目的,后来发现加括号的原因并非如此。要理解立即执行函数,需要先理解一些函数的基本概念。函数声明、函数表达式、匿名函数函数声明:functio

    2020/03/26
  • scalable.js入门指南一款可以使任何HTML元素动态缩放适合父元素的js插件

    scalable.js小白知识 官方网址:https://scriptartist.github.io/Scalable/ GitHub:https://github.com/Sc…

    2020/03/06
  • 推荐最佳的5个资源网站菜鸟教程网_资源基础入门

    搜索引擎在我们工作生活中是举足轻重的工具了,想要在如此庞大的互联网资源库中找到自己需要的信息也是不简单的,这里给大家分享5个优质的资源网站,看完你肯定有相见恨晚的感觉。1、纳米学习这是一个集合了网易云课堂、网易公开课、MOOC等公开课学习网站,也有大学生、互联网、职业技能的学习网站,有了它再也不用一个个收藏那些学习网站了。2、资源猫这个资源搜索网站基本可以满

    2020/03/29
  • Vue触发隐藏input file的方法入门基础教程_file使用攻略

    1、使用input透明覆盖法将input的z-index设置为1以上的数字并覆盖到需点击的内容上,将input的样式opacity设置为0(即为透明度为0),这样通过绑定在input上的change事件触发

    <input type="file" @change="picUpload($event)" accept="

    2020/03/29
  • js Date对象菜鸟攻略_对象入门基础教程

    1、创建Date对象 方法一:var date1=new Date(‘2018/12/25 10:30:50’);
    方法二:var date2=new Date(2018,11,25,10,30,50);2、获取当前的系统时间new Date();返回值就是当前系统时间 var date3=new Date();3、new Date(1000*

    2020/03/24
  • 如何写出高性能的CSS3动画菜鸟攻略_css3动画基础指南

    小伙伴们在写CSS3动画时,会发现在手机上很多时候会感到卡顿,然后Google到的解决方案大多是开启GPU加速transform: translate3d(0,0,0); 可解决,但是为什么开启GPU加速就能让动画顺滑呢?
    我们从浏览器内部去理解下JS是单线程的,但是浏览器可以开启多个线程,渲染一个网页需要两个重要的线程来共同完成:Main Thread 主

    2020/04/06