RequireJS简单教程

目录

介绍

页面标记

启动配置的“引导程序”

可重用组件

控制页面元素的代码

如何启动应用程序

如何构建和测试

摘要


本教程简要介绍了如何通过组件和依赖项注入将RequireJS用于实现Web应用程序。该示例应用程序很简单,具有一个表单,七个输入字段以及两个用于提交和重置表单的按钮。提交表单时,将完成字段验证,并且将显示错误状态或显示成功状态。状态显示被提取为可重用组件。本教程说明如何将所有这些拼凑在一起。

介绍

最近,我开始了另一个项目。它是一个基于Web的应用程序,需要一个JavaScript框架来支持一些简单的前端用户交互。我不需要AngularJS,因为我需要的功能很简单,也不需要像AngularJS这样的强大JavaScript库。我考虑过再次使用JQuery,但是知道单独使用它最终会造成混乱,所以我决定最好同时使用几个小型且良好的JavaScript库,并使JavaScript的结尾模块化。RequireJS可用于管理这些不同的库组件,这迫使我考虑对代码进行模块化,并使组件可在应用程序的不同区域重用。在本教程中,我将讨论有关如何使用RequireJS的基础知识。我将讨论两个基本概念:

  1. 依赖注入
  2. 在应用程序的不同部分重用组件

本教程中的示例应用程序非常简单。Web应用程序是使用Spring Boot创建的。它没有控制器。它提供的唯一功能是传递静态内容:网页和JavaScript文件。该应用程序仅包含一个网页。它显示一个表格,允许用户输入一个人的联系信息,然后单击Submit将信息发送到后端。该页面将显示状态消息,指示请求已成功发送。该应用程序还执行输入验证。如果字段输入无效,它将显示一条状态消息,指示对该字段的验证失败。

状态消息显示可以是可重用的组件。一个页面或其他许多页面中可能有许多位置,可以在其中显示验证状态消息。可以在涉及这些位置的所有JavaScript文件中一遍又一遍地重复相同的代码。或者,我们可以编写一个可重用的函数或对象,用于替换这些重复的代码。在本教程中,我将展示如何创建这样的组件。最后,该应用程序将演示依赖项配置和注入,这使该应用程序看起来像AngularJS应用程序。

我知道这可能没有多大意义,那么看看源代码又如何呢?

页面标记

让我从HTML页面标记开始。该示例应用程序只有一页,如下所示:

让我再次重申一下我之前介绍的有关此应用程序的内容。该页面接受一个人的全名和邮寄地址。在输入字段上没有基于HTML 5的验证(输入验证是通过JavaScript代码完成的)。屏幕下方有两个按钮,允许用户提交(单击Submit” 按钮)或重置(单击Clear” 按钮)表单。

用于应用程序页面标记的HTML文件存储在名为index.html的文件中。页面标记如下所示:

<!DOCTYPE html>
<html>
   <head>
      <title>Test</title>
      <link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
      <link href="/assets/css/index.css" rel="stylesheet">

      <script src="/assets/jquery/js/jquery.min.js"></script>
      <script src="/assets/bootstrap/js/bootstrap.min.js"></script>
      <script src="/assets/requirejs/require.js"></script>
   </head>
   <body>
      <div class="container">
         <div class="row">
            <div class="col-xs-12 col-sm-offset-1 colsm-10 col-md-offset-2
                        col-md-8 col-lg-offset-3 col-lg-6">
               <h3>Require JS Sample App</h3>
               <div class="panel panel-default">
                  <div class="panel-body">
                     <form id="testAppForm">
                        <div id="statusDisp"></div>
                        <div class="form-group">
                           <label for="firstName">First Name</label>
                           <input class="form-control" type="text"

                           id="firstName" name="firstName" placeholder="First Name">
                        </div>
                        <div class="form-group">
                           <label for="lastName">Last Name</label>
                           <input class="form-control" type="text"

                           id="lastName" name="lastName" placeholder="Last Name">
                        </div>
                        <div class="form-group">
                           <label for="addressLine1">Address Line 1</label>
                           <input class="form-control" type="text"

                           id="addressLine1" name="addressLine1" placeholder="Address Line 1">
                        </div>
                        <div class="form-group">
                           <label for="addressLine2">Address Line 2</label>
                           <input class="form-control" type="text"

                           id="addressLine2" name="addressLine2" placeholder="Address Line 1">
                        </div>
                        <div class="form-group">
                           <label for="city">City</label>
                           <input class="form-control" type="text"

                           id="city" name="city" placeholder="City">
                        </div>
                        <div class="form-group">
                           <label for="state">State</label>
                           <input class="form-control" type="text"

                           id="state" name="state" placeholder="State">
                        </div>
                        <div class="form-group">
                           <label for="zipCode">Zip Code</label>
                           <input class="form-control" type="text"

                           id="zipCode" name="zipCode" placeholder="Zip Code">
                        </div>
                        <div class="row">
                           <div class="col-xs-12 col-sm-6">
                              <button class="btn btn-success form-control"

                              type="submit" id="submitBtn">Submit</button>
                           </div>
                           <div class="col-xs-12 col-sm-6">
                              <button class="btn btn-danger form-control"

                              type="reset" id="clearBtn">Clear</button>
                           </div>
                        </div>
                     </form>
                  </div>
               </div>
            </div>
         </div>
      </div>

      <script type="text/javascript">
      ...
      </script>
   </body>
</html>

这是一个非常简单的标记,是具有七个输入字段的表单。状态字段应该是一个下拉列表,并且应该从后端服务器加载状态。不在这里。取而代之的是,我匆忙执行此操作,并将其创建为文本框。现在,我还清空了底部的脚本。

自从JQuery问世以来,我们将事件处理方法移到了脚本文件或节中。如前所述,页面上的按钮具有单击事件。单击Submit按钮将触发输入验证,并且状态消息将显示在顶部,如下所示:

当所有输入数据都签出后,单击Submit按钮将触发显示以下状态消息:

为了使所有这些正常工作,我需要添加一些JavaScript代码。我要做的第一件事是创建引导程序代码,即启动配置。还记得上面空白的JavaScript部分吗?这就是引导程序JavaScript代码。

启动配置的引导程序

我要指出的一件事是,在这种情况下,当我提到“bootstrap”时,我并不是指Bootstrap UI框架,虽然该框架也用于该项目。我所说的“bootstrap”是我将要设置的配置,以便RequireJS可以从工作的Web应用程序中加载不同的组件。它与Bootstrap框架不同。

您可以在文件底部的index.html中找到我的引导程序代码。看起来是这样的:

require.config({
    paths: {
      jquery: "./assets/jquery/js/jquery.min",
      underscore: "./assets/underscore/underscore-min-1.9.2",
      statusDisplay: "./assets/app/components/statusDisplay/statusDisplay",
      stapes: "./assets/stapes/stapes-min-1.0.0",
      app: "./assets/app/app"
   }
});

require(["app"], function(app) {
   app.init();
});

这段代码分为两部分。第一部分是设置所有组件的配置。RequireJS需要知道的两件事:

  • 组件的名称。此名称可以视为哈希映射的键,并且与该键关联的值将是该组件。
  • JavaScript文件所在的位置,以便RequireJS可以加载组件的源代码。

在这一部分,我添加了另外两个库。一个叫做Stapes,它是BackboneJS框架的升级版本。但是,我认为它是JQuery的包装。另一个是UnderscoreJS。我使用其模板功能使用模板字符串呈现实际的HTML。这就是我创建可重用和交互式HTML组件的方式。我将在这两个库上创建另一个教程。

第二部分是实际应用程序的启动。由于我使用Stapes JS库进行HTML元素操作。我所要做的就是调用初始化代码,UI将自行处理交互。

这仅仅是个开始。此应用程序全部与可重用组件有关。接下来,我将向您展示如何创建此类可重用组件。

可重用组件

如前所述,状态显示可以在应用程序的许多地方使用。最好将其提取为可重用的组件。在这些位置或可以设置占位符,以便后端JS代码可以插入可重复使用的组件以形成最终的输出显示。

这是此可重用组件的完整源代码:

define(["jquery", "underscore"], function ($, _) {
   var statusHtml = '<div class="col-xs-12">\n'+
   '   <div class="<%= alertStyleVal%>"><%= msgToShow%></div>\n'+
   '</div>';

   var statusTemplate = _.template(statusHtml);
   const errorStyle = "alert alert-danger small-alert";
   const successStyle = "alert alert-success small-alert";

   function renderStatus(outerDiv, msg, style) {
      if (outerDiv) {
         if (msg != null && msg.length > 0) {
            var divToAdd = statusTemplate({
               alertStyleVal: style,
               msgToShow: msg
            });
            $(outerDiv).html(divToAdd);
         }
      }
   }

   return {
      clearRender: function(outerDiv) {
         if (outerDiv) {
            $(outerDiv).html("");
         }
      },

      renderSuccess: function (outerDiv, msg) {
         renderStatus(outerDiv, msg, successStyle);
      },

      renderError: function (outerDiv, msg) {
         renderStatus(outerDiv, msg, errorStyle);
      }
   };
});

让我一次解决所有这些疯狂问题。首先是这个外壳:

define(["jquery", "underscore"], function ($, _) {
...
});

这是一个函数调用。该函数名为define(...)。它是RequireJS的函数,用于定义组件。在这种情况下,该函数接受两个参数,第一个是数组,它接受一些字符串值,即相关组件的名称。这就是依赖注入发生的地方。对于此可重用组件,我需要JQueryUnderscoreJS。第二个参数是函数定义,参数列表与依赖项的名称顺序相同。参数$是对JQuery的引用,_是对UnderscoreJS的引用。对于这两个框架,我可以将它们用作这两个框架的使用方式。

接下来,我声明一些变量并将其实例化:

...
   var statusHtml = '<div class="col-xs-12">\n'+
      '   <div class="<%= alertStyleVal%>"><%= msgToShow%></div>\n'+
      '</div>';

   var statusTemplate = _.template(statusHtml);
   const errorStyle = "alert alert-danger small-alert";
   const successStyle = "alert alert-success small-alert";
...

前两行定义了显示HTML源代码模板。这里:

var statusHtml = '<div class="col-xs-12">\n'+
   '   <div class="<%= alertStyleVal%>"><%= msgToShow%></div>\n'+
   '</div>';

然后,我需要一个模板函数,该函数以后可用于注入一些值,并编译成HTML字符串以放置在最终的HTML页面中。这是我的声明方式:

var statusTemplate = _.template(statusHtml);

我所做的就是在UnderscoreJS库中调用一个template()方法。它接受模板源值,并返回函数引用。接下来,我们将看到这一点。以下代码定义了一个函数,该函数将模板源呈现为HTML代码,然后附加到现有的HTML元素。这里是:

function renderStatus(outerDiv, msg, style) {
   if (outerDiv) {
      if (msg != null && msg.length > 0) {
         var divToAdd = statusTemplate({
            alertStyleVal: style,
            msgToShow: msg
         });
         $(outerDiv).html(divToAdd);
      }
   }
}

该函数执行一些基本的数据验证,确保要显示的消息不为null或为空。它还检查以确保状态显示将附加到的元素也不为空。一旦检查通过,它将使用模板函数引用来呈现HTML显示字符串。然后,外部元素用于附加新创建的HTML显示值。

最后一部分是返回代表我正在定义的组件的对象。这里是:

...
return {
   clearRender: function(outerDiv) {
      if (outerDiv) {
         $(outerDiv).html("");
      }
   },

   renderSuccess: function (outerDiv, msg) {
      renderStatus(outerDiv, msg, successStyle);
   },

   renderError: function (outerDiv, msg) {
      renderStatus(outerDiv, msg, errorStyle);
   }
};
...

返回的该对象具有三种方法:

  • 第一个名为clearRender()。它获取输入HTML元素的引用,然后清除其内部HTML值。
  • 第二个将成功状态消息添加到目标HTML元素。成功状态消息具有绿色背景。
  • 第三个将错误状态消息添加到目标HTML元素。错误状态消息具有红色背景。

现在我们有了一个可重用的组件,让我们看看如何将其注入到页面组件中并加以利用。

控制页面元素的代码

这是示例应用程序的复杂部分。有一个包含七个输入字段和两个按钮的页面。这些按钮必须具有关联的事件处理方法。正如我在一开始所说的,我不想使用JQuery。但是它是如此流行,以至于无法放弃它。因此,我将其用作可注入组件,并且主要用作DOM查询,以及追加新元素,如上一节中所示。在本节中,我使用了Stapes框架来对用户交互进行编程。

Stapes框架基于BackboneJS。我认为这非常酷。不幸的是,它不再被维护,并且可能没有人使用此框架。无论如何,它给我留下了深刻的印象。我可能会在今年的某个时候写一个关于它的教程。对于本教程,我所使用的只是向按钮添加事件处理:

  • 当用户单击Submit按钮时,将首先进行输入数据验证,如果输入数据有任何问题,将显示红色错误状态消息。如果所有输入数据都正确,将显示成功状态消息。
  • 另一个按钮清除所有输入字段。

具有所有这些功能的文件称为app.js。以下是其内容的完整清单:

define(["jquery", "stapes", "statusDisplay"], function ($, Stapes, statusDisplay) {

   var testAppForm = Stapes.subclass({
      constructor : function() {
         var self = this;
         self.$el = $("#testAppForm");

         var statusDisp = self.$el.find("#statusDisp");
         var firstNameInput = self.$el.find("#firstName");
         var lastNameInput = self.$el.find("#lastName");
         var addrLine1Input = self.$el.find("#addressLine1");
         var addrLine2Input = self.$el.find("#addressLine2");
         var cityInput = self.$el.find("#city");
         var stateInput = self.$el.find("#state");
         var zipCodeInput = self.$el.find("#zipCode");

         self.$el.on("submit", function(e) {
            e.preventDefault();
            if (validateForm()) {
               statusDisplay.renderSuccess(statusDisp,
                             "Your request has been handled successfully.");
            }
         });

         self.$el.on("reset", function(e) {
            e.preventDefault();
            clearCommentForm();
         });

         function clearCommentForm() {
            statusDisplay.clearRender(statusDisp);
            firstNameInput.val("");
            lastNameInput.val("");
            addrLine1Input.val("");
            addrLine2Input.val("");
            cityInput.val("");
            stateInput.val("");
            zipCodeInput.val("");
         }

         function validateForm() {
            statusDisplay.clearRender(statusDisp);
            var firstNameVal = firstNameInput.val();
            if (firstNameVal == null || firstNameVal.length <= 0) {
               statusDisplay.renderError(statusDisp,
                  "Your first name cannot be null or empty");
               return false;
            }

            if (firstNameVal != null && firstNameVal.length > 128) {
               statusDisplay.renderError(statusDisp,
                  "Your first name is too long, 128 characters or fewer.");
               return false;
            }

            var lastNameVal = lastNameInput.val();
            if (lastNameVal == null || lastNameVal.length <= 0) {
               statusDisplay.renderError(statusDisp,
                  "Your last name cannot be null or empty");
               return false;
            }

            if (lastNameVal != null && lastNameVal.length > 128) {
               statusDisplay.renderError(statusDisp,
                  "Your last name is too long, 128 characters or fewer.");
               return false;
            }

            var addressLine1Val = addrLine1Input.val();
            if (addressLine1Val == null || addressLine1Val.length <= 0) {
               statusDisplay.renderError(statusDisp,
                  "Your address line #1 cannot be null or empty.");
               return false;
            }

            if (addressLine1Val != null && addressLine1Val.length > 128) {
               statusDisplay.renderError(statusDisp,
                  "Your address line #1 cannot have more than 128 characters");
               return false;
            }

            var addressLine2Val = addrLine2Input.val();
            if (addressLine2Val != null && addressLine2Val.length > 128) {
               statusDisplay.renderError(statusDisp,
                  "Your address line #2 cannot have more than 128 characters");
               return false;
            }

            var cityVal = cityInput.val();
            if (cityVal == null || cityVal.length <= 0) {
               statusDisplay.renderError(statusDisp,
                  "Your city is null or empty.");
               return false;
            }

            if (cityVal != null && cityVal.length > 48) {
               statusDisplay.renderError(statusDisp,
                  "Your city cannot have more than 48 characters");
               return false;
            }

            var stateVal = stateInput.val();
            if (stateVal == null || stateVal.length <= 0) {
               statusDisplay.renderError(statusDisp,
                  "Your state is null or empty.");
               return false;
            }

            if (stateVal != null && stateVal.length > 2) {
               statusDisplay.renderError(statusDisp,
                  "Your state cannot have more than 2 characters");
               return false;
            }

            var zipCodeVal = zipCodeInput.val();
            if (zipCodeVal == null || zipCodeVal.length <= 0) {
               statusDisplay.renderError(statusDisp,
                  "Your state is null or empty.");
               return false;
            }

            if (zipCodeVal != null && zipCodeVal.length > 12) {
               statusDisplay.renderError(statusDisp,
                  "Your zip code cannot have more than 12 characters");
               return false;
            }

            return true;
         }
      }
   });

   return {
      init: function() {
         new testAppForm();
      }
   };
});

您应该知道它的作用:

define(["jquery", "stapes", "statusDisplay"], function ($, Stapes, statusDisplay) {
...
});

它将JQuery Stapes和我的statusDisplay组件注入该组件。RequireJS使用已定义的组件将其注册为另一个组件。

接下来,我声明一个Stapes类类型。这是我的做法:

var testAppForm = Stapes.subclass({
      constructor : function() {
         ...
      }
   });

请注意,我说的是它正在创建的类型,而不是对象。对于这种新类型,它仅包含一个方法,即构造函数。在此构造函数中,我要做的第一件事是获取输入字段和按钮的句柄,如下所示:

...
   var self = this;
   self.$el = $("#testAppForm");

   var statusDisp = self.$el.find("#statusDisp");
   var firstNameInput = self.$el.find("#firstName");
   var lastNameInput = self.$el.find("#lastName");
   var addrLine1Input = self.$el.find("#addressLine1");
   var addrLine2Input = self.$el.find("#addressLine2");
   var cityInput = self.$el.find("#city");
   var stateInput = self.$el.find("#state");
   var zipCodeInput = self.$el.find("#zipCode");
...

如此处所示,我必须使用JQuery来获取这些输入字段句柄。并将它们保存为我正在创建的这种类型的一部分。至于按钮,我附加了click事件处理方法,如下所示:

...
         self.$el.on("submit", function(e) {
            e.preventDefault();
            if (validateForm()) {
               statusDisplay.renderSuccess(statusDisp,
                             "Your request has been handled successfully.");
            }
         });

         self.$el.on("reset", function(e) {
            e.preventDefault();
            clearCommentForm();
         });
      ...

如上所示,所有这些都是JQuery代码,如何查询HTML元素以及如何将事件处理附加到按钮。对于这两个按钮,在事件处理方法中,第一件事是调用事件对象e” preventDefault()方法。这将停止按钮执行实际的提交或重置功能。然后,该方法将执行定制功能。

在上面的代码片段中,您可以看到正在使用的状态显示可重用组件。在我们的页面中有<div>

<div id="statusDisp"></div>

在上面的代码中,它得到了对此div的引用:

...
var statusDisp = self.$el.find("#statusDisp");
...

最后,代码可以将状态显示添加到页面:

...
statusDisplay.renderSuccess(statusDisp, "Your request has been handled successfully.");
...

对于Submit按钮,将对输入字段进行验证。任何验证失败都会触发错误状态消息的显示。并且,如果所有输入字段验证均成功,将显示成功消息,模拟假定的数据传输到后端服务器。

数据输入验证是漫长而乏味的。我只会展示这种方法validateForm()的一部分:

function validateForm() {
   statusDisplay.clearRender(statusDisp);
   var firstNameVal = firstNameInput.val();
   if (firstNameVal == null || firstNameVal.length <= 0) {
      statusDisplay.renderError(statusDisp,
         "Your first name cannot be null or empty");
      return false;
   }

   if (firstNameVal != null && firstNameVal.length > 128) {
      statusDisplay.renderError(statusDisp,
         "Your first name is too long, 128 characters or fewer.");
      return false;
   }

   ...

   return true;
}

最后,清除输入字段的功能是将所有字段的值设置为空字符串:

function clearCommentForm() {
   statusDisplay.clearRender(statusDisp);
   firstNameInput.val("");
   lastNameInput.val("");
   addrLine1Input.val("");
   addrLine2Input.val("");
   cityInput.val("");
   stateInput.val("");
   zipCodeInput.val("");
}

这就是我使用Stapes创建的类型的全部内容。接下来,我将返回该组件,以便可以在页面HTML中使用它。这里是:

define(["jquery", "stapes", "statusDisplay"], function ($, Stapes, statusDisplay) {
...
   return {
      init: function() {
         new testAppForm();
      }
   };
});

这是应用程序组件。它只有一种名为init()的方法。它所做的就是从我创建的Stapes类型实例化一个对象。它怎么会工作呢?好吧,它是可行的,因为类型构造函数将从页面中找到所有HTML元素句柄,并附加所有事件处理程序。因此,所有对元素和事件处理方法的引用都将保存,直到页面卸载为止。

如何启动应用程序

现在重新回到页面源代码。JavaScript源代码部分具有以下内容:

...
require(["app"], function(app) {
   app.init();
});
...

RequireJS函数require()的调用基本上会调用作为第二个参数提供的函数。第一个是依赖项数组。提供的函数调用对象应用程序的init()方法,该方法将创建我使用Stapes创建的类型的实例。实例化将在页面上设置事件处理。如上所示,此应用程序的整个设置非常简单。

现在已经讨论了有关此应用程序的所有内容,让我们看看如何测试该应用程序。

如何构建和测试

在构建此应用程序之前,请将示例项目中的所有文件从* .sj重命名为* .js

一旦获得示例项目并将其解压缩到本地计算机中,就需要具有Java 1.8Maven 3.0或更高版本才能进行编译。编译示例对象的方式是运行以下命令:

mvn clean install

成功完成构建后,可以运行以下命令来启动Web应用程序:

java -jar target/testapp-0.0.1-SNAPSHOT.jar

命令成功启动后,您可以使用浏览器通过访问以下URL来测试应用程序:

http://localhost:8080/

如果启动命令成功运行,则页面应类似于顶部的截图#1。您可以在页面上输入一些信息,然后单击Submit按钮以查看将显示什么状态消息。

摘要

在本教程中,我基本介绍了如何通过组件和依赖注入将RequireJS用于实现Web应用程序。使用RequireJS,可以轻松地将应用程序分解为不同的组件,然后将它们组合在一起成为一个可以正常工作的应用程序。模块化应用程序始终是一个好主意。它将应用程序分解为不同的齿轮,这些齿轮可以组合在一起,并且其中一些可以在同一应用程序的不同位置重用。模块化可以使应用程序干净、整洁,有时易于测试。

我使用JQuery已有很长时间了,对它成为许多复杂应用程序中使用的唯一框架感到不满意,并且使用了可怕的折衷方法,并安装了丑陋的代码段。这次,我决定采用不同的方式,因此我选择了RequireJSStapesUnderscoreJS。框架是否过时或没有人使用它真的无关紧要。只要它可以在应用程序中发挥作用,就可以并且应该使用它。我从未使用过这三个库中的任何一个。因此,将它们用于此简单的示例应用程序。而且我觉得这些库非常有效。

我汇总的示例应用程序是我刚刚组装的概念性应用程序。我将在这里完成的所有工作转移到我构建并仍在构建的应用程序中。在此示例应用程序中,该应用程序很简单,它具有一个表单,七个输入字段以及两个用于提交和重置表单的按钮。提交表单时,将完成字段验证,并且将显示错误状态显示或成功状态显示。状态显示被提取为可重用组件。本教程说明了如何将所有这些拼凑在一起。对我而言,这些库在如何正确使用它们来创建出色的应用程序方面显示出巨大的潜力。

发布了69 篇原创文章 · 获赞 146 · 访问量 49万+

猜你喜欢

转载自blog.csdn.net/mzl87/article/details/104728368