65.9K
CodeProject 正在变化。 阅读更多。
Home

Classic ASP MVC 用于动态 JavaScript 页面

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (8投票s)

2014年3月21日

CPOL

17分钟阅读

viewsIcon

46006

downloadIcon

1012

Classic ASP 重启。

引言

本主题是为 Classic ASP 程序员创建一个完全动态的 JavaScript 应用程序的“操作指南”。

背景

上一主题 中讨论的 Classic ASP VBScript 应用程序中存在 MVC 模式。 [^]

现在我们将把 MVC 模式 应用到 Classic ASP 应用程序,并使用 JavaScript 作为服务器端和客户端代码的语言。
本主题的价值在于为那些使用 Classic ASP IIS 应用程序的开发者提供服务器端 MVC JavaScript Classic ASP 和客户端 MVC JavaScript 应用程序的经验。

使用代码

  • 将存档下载并解压缩到 IIS 虚拟目录。
  • 为虚拟目录设置默认页面 (default.asp)
  • 设置 404 错误处理程序 (router.asp)
  • 创建数据模型,并使用 在线 Classic ASP 代码生成器 生成您自己的 Classic ASP JavaScript MVC 应用程序。

404 处理程序和默认页面的图示说明。

服务器端代码

路由器是 MVC Web 应用程序的入口点。

路由

  • 解析 URL,计算 Controller/Action
  • 将执行传递给带参数的 Controller/Action

The image that shows the logic of custom Classic ASP router.

在 router.asp 中将会有一些其他函数(因为它是应用程序的单一入口点)

  • 加载所需的库
  • 加载 Controllers
  • 加载 Model
  • 认证/授权用户
  • 表单清理等。

您需要捕获 404 IIS 错误以启用漂亮的 URL。
您可以手动设置 IIS 错误处理程序或使用脚本。 此处 有关于如何设置 404 错误处理程序以通过自定义 Classic ASP 路由器路由漂亮格式化 URL 的说明。(从菜单中选择“架构师”。)
让我们将服务器端路由器存储在 /Router.asp 文件中。

Controller/Action

应用程序中通常有许多不同的 Controllers。Controllers 是包含应用程序业务逻辑的类。Actions 是这些类中的函数。

{Controller/Actions 业务逻辑的示例}

 
    <%
    var SomethingController = {
    
        About: function(){
            var model = someCalculation();
            //render model in the view
            %>   <!--#include file="../Views/Something/About.asp" --> <%
        },
        //read operation
        Read: function(callback){
            var model = {data : {items: SomethingHelper.SelectAll()} };
            //send model as JSON data
            callback(JSON.stringify(model),"application/json") ;
        },
        //create operation
        Create: function(callback){
            var model = {data : new Something()};
            callback(JSON.stringify(model),"application/json") ;
        },
        //create operation - put the model into DB
        CreatePost: function(callback){
            var obj = getArguments();
            var result = createModelSomething(obj);
            var model = {result : result, data: obj };
            callback(JSON.stringify(model),"application/json") ;
        },
        //update operation - get the model from DB
        Edit: function (callback,vars){
            var model = {data: readModelSomething(vars)}; 
            callback(JSON.stringify(model),"application/json") ;
        },
        //update operation - put the model back to DB
        EditPost: function (callback){
            var obj = getArguments();
            var result = updateModelSomething(obj);
            var model = {result : result, data: obj };
            callback(JSON.stringify(model),"application/json") ;
        },
        //basic delete operation - get the model from DB
        Delete: function(callback,vars){
            var model = {data: updateModelSomething(vars)}; 
            callback(JSON.stringify(model),"application/json") ;
        },
        //basic delete operation - delete the object from DB
        DeletePost: function(callback){
            var obj = getArguments();
            var result = deleteModelSomething(obj);
            var model = {result : result, data: obj };
            callback(JSON.stringify(model),"application/json") ;
        }
    };
    //list of allowed controller/action
    controllers['Something']=SomethingController;
     %>   
    

在 MVC 模式中,Controller 处理用户输入,操作 Model,并更新 View。
早期的 Web MVC 模式采用了瘦客户端的方法,将整个 Model、View 和 Controller 逻辑放在服务器上。
在这种方法中,客户端通过 Router 发送“get”或“post”请求到 Controller,然后接收一个完整且更新的网页。
这种瘦客户端方法将用于显示整个页面。例如,默认的服务器端 HomeController.Index() 将显示示例应用程序的主页。

服务器端 Controller、Action 和 View 生成整个页面时的逻辑
an image of codeflow when Controller,Action and View produce the whole page.

一旦您可能想为您的网页添加更多活力。客户端 JavaScript 代码将有所帮助。客户端代码将向服务器发送 AJAX 请求并更新 DOM(文档对象模型)。

然后,在进行了一些更新/添加功能后,客户端代码会变得混乱、结构不良、难以维护。为了解决这个问题,同样,也曾有过组织客户端代码的努力。

随着客户端技术的成熟,出现了许多客户端 MVC 框架,它们允许 MVC 组件部分地在客户端执行。
您可能听说过 Backbone.js、Knockout、AngularJs 等。

在本主题中,它们都不会被使用,但纯粹的 MVC 模式将应用于客户端 JavaScript 代码。
大量的客户端代码也会影响服务器端代码:大多数服务器端 Controllers/Actions 将返回数据而不是编译后的 HTML。
对于 JavaScript 代码,使用 JSON 传输数据是很自然的,因此示例应用程序中的大多数服务器端控制器将向客户端返回 JSON 数据。服务器将不会将 Model 与 Views 合并以生成 HTML。

以下示例显示了服务器端 Controller 和 Action 生成 JSON 字符串
an image of codeflow when Controller,Action produce the JSON string.

服务器端 Model

MVC 模式中“Model”一词有广义和狭义之分。

广义是指用于数据库对象的 CRUD(创建、读取、更新、删除)操作以及数据库本身的类集。

“Model”一词的狭义是指我们一次性发送到客户端的数据。它可以是序列化为 JSON 的对象(如上例),也可以是操作的结果 true/false,或者服务器可能用 HTML 模板显示或发送到客户端的任何其他数据。

我将使用类集作为数据模型。这些类将包含具有 SQL 子句的方法,用于在关系数据库中更新/检索数据。数据模型的文件将存储在 /Server/Model/(Name).asp 文件中。

服务器端 View

虽然现在选择模板引擎时有多种选择,但在本主题中我将使用 Classic ASP 模板。
Classic ASP 引擎在解析 .ASP 文件时,会提取并执行 <% %> 标签之间的代码。
我将使用“服务器端 View”这个词来称呼逻辑较少、内容较多的 .asp 页面。我在这里使用的代码生成器创建了一个单一的服务器端视图 /Server/View/Home/Index.asp


客户端代码

当用户从服务器请求页面时,浏览器会向服务器发送请求,其中包含请求资源的 URL。它构成了用户与服务器端代码之间的交互协议。

使用客户端 JavaScript 代码,这种交互通常建立在绑定到 HTML 控件的事件之上,例如 onclick 事件 [^]。像 JQuery 这样的框架可以帮助您将事件设置为适当的客户端事件处理程序。

但是,为了应用 MVC 模式,应该有一种通用方法来捕获用户交互,将其转换为对 Router 的调用。在本主题中,客户端 URL 将用于发出用户请求信号。

URL 的客户端部分。

URL 包含标识服务器端资源和客户端资源的这部分。客户端资源通常被称为“同页面链接”或“命名锚点”。例如 URL

https:///Home/Index/#User/List  

包含服务器端部分

https:///Home/Index/ 

和客户端部分

#User/List. 

当用户单击带有此类 URL 的链接时,如果服务器端部分与当前页面位置不同,浏览器将请求一个新页面。
如果服务器端部分未更改,浏览器将不会请求页面,而是会尝试在页面上找到锚点并将其焦点设置在其上。

在本主题中,我们将修改此行为。Jquery-hashchange-plugin 将捕获 URL 客户端部分的变化并将它们发送到客户端 Router。

{钩子到 URL 变化的 code}

$(document).ready(function() {
        // Bind the event.
        $(window).hashchange(function() {
            route();
        })
    });
    

路由

客户端 Router 将解析 URL 的客户端部分并将执行传递给客户端 Controller/Action。

{客户端 Router 的 code}

        var controllers = {};

    function route() {
        var defaultController = ""
        var defaultAction = ""
        var controller = null;
        var action = null;
        var vars = null;

        var globalpath = location.hash.split('#');
        if (globalpath.length < 2) return;
        //the path should be like:  /Controller/Action/var1/var2/var3
        var path = globalpath[1];

        //normalyze path
        if (path.charAt(0) != "/") path = "/" + path;

        var pathchuncks = path.split('/');
        if (pathchuncks.length) {
            if (pathchuncks.length >= 3) {
                controller = pathchuncks[1]; //suffix 'Controller' is not used on client
                action = pathchuncks[2];
            }
            if (pathchuncks.length > 3) {
                vars = pathchuncks.slice(3).join('/');
            }
            else  {vars='';}
            
        };

        if (!controller) {
            controller = defaultController;
        };
        if (!action) {
            action = defaultAction;
        };

        //alert(controller);
        //alert(action);
        if (controllers[controller]){
            if (controllers[controller][action]){
                var ControllerAction = controllers[controller][action];
                ControllerAction(null,vars);
            }
        }

    }
    

当一个 controller 被初始化时,它被添加到允许的 controllers 列表中。

       var ProjectController = {
            List:       function(callback, args){ ... },
            Create:     function(callback, args){ ... },
            CreatePost: function(callback, args){ ... },
    ...
            Details:    function(callback, args){ ... }
       }

       //add to list of allowed controllers/actions
       controllers['Project']=ProjectController;
    

所以,在这一行

 var ControllerAction = controllers['Project']['List'];

变量 ControllerAction 获取 ProjectController.List() 函数的指针,该函数在下一行被调用。

    ControllerAction(null,vars);    
使用了javascript 对象方括号表示法

客户端路由器位于 /Client/router.js 文件中

客户端 Controllers/Actions

当用户单击链接且 URL 已更改时,“hashchange”事件将被触发,并调用客户端 Router。Router 解析 URL。它调用客户端 Controller/Action。

客户端 Controller/Action 可以访问 DOM(文档对象模型)并更新界面,但它应该首先获取 Model 和 View 以生成 HTML。

因此,将进行 2 次对服务器的调用以获取 Model 和 View。Model 是服务器端 Controller/Action 发送的 JSON,而 View 只是服务器文件系统中的一个文件。

客户端 Controller/Action 执行以下操作:

  • 准备回调函数,以便在 Model 和 View 就绪时更新 DOM。
  • 调用服务器端 controller/action 并通过 ModelProxy 接收 JSON 数据作为 Model。
  • 调用服务器端 controller/action 以通过 ViewProxy 检索 View。
  • 当回调被调用,并且 Model 和 View 都就绪时,客户端 Controller/Action 将 Model 渲染到 View 中以生成 HTML - 页面的一个部分。
  • 使用新内容更新页面部分。

客户端 Controller ProjectController /action List 的示例

      var ProjectController = {
            List: function(callback, args){
                var view=null, model=null;
                
                //3. this is called via callback when the Model or View is ready.
                // thus it is called twice. When both Model and View are ready - then the final part: 
                //locally render the model in the view when they are ready, and update the DOM
                var UpdateDOM = function(Model,View) {
                    if (Model != null){ model = Model; }
                    if (View != null){ view = View; }
                    if ((model != null) && (view != null)) {
                        //render the Model into the Template
                        var templateOutput = Mustache.render(view, model);
                        //update the DOM:
                        $("#dialog1-body").empty();
                        $("#dialog1-body").append(templateOutput);
                        $("#dialog1-label").empty().append("list of Project");
                        $("#dialog1").modal("show");
                        NormalizeHash();
                    }
                }
                
                //1. this is called first
                //call for the model, async
                ModelProxy(UpdateDOM, 'Project','List', args, 'get');
                
                //2. this is called second, simultaneously with the first call
                //get the view, async
                ViewProxy(UpdateDOM, 'Project','List');
                
            },

            //get the view and model and update the DOM
            Create: function(callback, args){
                var view=null, model=null;
                
                //locally render the model in the view when they are ready, and update the DOM
                var UpdateDOM = function(Model,View) {
                    if (Model != null){ model = Model; }
                    if (View != null){ view = View; }
                    if ((model != null) && (view != null)) {
                        var templateOutput = Mustache.render(view, model);
                        $("#dialog2-body").empty();
                        $("#dialog2-body").append(templateOutput);
                        $("#dialog2-label").empty().append("");
                        $("#dialog2").modal("show");
                        NormalizeHash();
                    }
                }
               
                //call for the model, async
                ModelProxy(UpdateDOM, 'Project','Create', args, 'get');
                
                //get the view, async
                ViewProxy(UpdateDOM, 'Project','Create');
                
            },

            //this is called from dynamic script from the /Client/Views/Project/Create.html
            CreatePost: function(callback, args){
                var view=null, model=null;
                
                //locally render the model in the view when they are ready, and update the DOM
                var UpdateDOM = function(Model,View) {
                    if (Model != null){ model = Model; }
                    if (View != null){ view = View; }
                    
                    //may show the form/components on the client here
                    if ((model != null) && (view != null)) {
                        if (model.result == true) {
                            $("#dialog2-body").empty();
                            $("#dialog2").modal("hide");
                            NormalizeHash();
                            var templateOutput = Mustache.render(view, model);
                            //find the table Projectdatatable 
                            //find the last row  and inset the row with attribute rowid='id'
                        
                            if ($("#Projectdatatable")[0]!=undefined){
                               $("#Projectdatatable tr:last").after(templateOutput);
                               bindEvents();
                            }
                            
                        }
                        if (model.result == false) {
                            var templateOutput = Mustache.render(view, { error: model.result });
                            $("#dialog2-label").empty().append(templateOutput);
                        }
                    }
                }
                
                //call for the model, async
                ModelProxy(UpdateDOM, 'Project','CreatePost', args, 'post');
                
                //get the view, async
                ViewProxy(UpdateDOM, 'Project','CreatePost');

            },
    ...
    }


          //add to list of allowed controllers/actions
          controllers['Project']=ProjectController;

    

架构概览

an image with overview of the architecture.

客户端 View (mustache 模板)

客户端 controller 通过 ModelProxy 接收带有数据的 JSON 字符串。这些数据应转换为 HTML。

可以在 JavaScript 中解析 JSON 数据为 HTML。但是,更好的方法是使用模板和专门的模板库。

有许多 JavaScript 模板引擎:dustjs、underscore.js、jade.js、mustache 等。在本主题中,我将使用 **mustache**。

将客户端 Views 存储在 /Client/Views/(Controller)/(Action).html 中很方便

Views 存储在服务器上,但它们在客户端代码中转换为 HTML。这就是为什么它们被称为客户端 Views。

在客户端 Controller/Action 生成 HTML 后,它会更新 DOM。例如,它可以是:显示从服务器接收的数据,显示将表单发布到服务器时的状态,在表中添加/删除行,显示/隐藏对话框、菜单等。

一组客户端 controllers 将位于文件 /Client/Controllers/(nameController).js 中

我将使用的代码生成器为每个 controller 创建一个文件。

客户端视图 /Views/Project/List.html 的示例

          {{#data}}<div id='ProjectListContainer'>
          <ul class="pagination pagination-sm  pull-right">
  • ?
  • {{#paginator}}
    • {{#previous}}
    • {{/previous}} {{#links}}
    • {{n}} {{/links}} {{#next}}
    • {{/next}}
    {{/paginator}} {{#items}} {{/items}}
    ProjectNamePOP3AddressActive
    {{ProjectName}}{{POP3Address}}{{Active}}
    {{^items}}
    No records
    {{/items}} <script type="text/javascript"> ProjectListModule = (function() { // private var onSelectorClick = function(_selector) { if ($(_selector).hasClass('active')) { $(_selector).removeClass('active'); disableButtons(); } else { $(_selector).addClass('active').siblings().removeClass('active'); $(_selector).find("tr").addClass('active'); var rID = $(_selector).attr('rowid'); enableButtons(rID); } }; var enableButtons = function(id) { $("div#ProjectListContainer li.button_container").fadeIn(100); $("div#ProjectListContainer #edit_button")[0].href = '#/Project/Edit/' + String(id); $("div#ProjectListContainer #delete_button")[0].href = '#/Project/Delete/' + String(id); $("div#ProjectListContainer #details_button")[0].href = '#/Project/Details/' + String(id); }; var disableButtons = function() { $("div#ProjectListContainer li.button_container").fadeOut(100); $("div#ProjectListContainer #edit_button")[0].href = '#'; $("div#ProjectListContainer #delete_button")[0].href = '#'; $("div#ProjectListContainer #details_button")[0].href = '#'; }; var bindEvents = function() { $("div#ProjectListContainer #Projectdatatable > tbody tr").each(function() { $(this).unbind(); $(this).bind('click', function() { onSelectorClick(this); }); }); }; //public return {bindEvents : bindEvents , disableButtons : disableButtons }//end public members })(); //end ProjectListModule $(document).ready(function() { ProjectListModule.bindEvents(); }); </script>
    {{/data}}

    这些模板的工作方式类似于 Web Applet。它们可以包含 HTML 以及支持的客户端 JavaScript 代码。当生成的 HTML 添加到 DOM 时,两者都会动态加载。模板在缓存方面效果更好,如后面讨论的。

    正如您所见,一部分客户端逻辑存储在 Controllers/Actions 之外。架构受到了影响,但编码更容易。

    其他注意事项

    典型的基于服务器的 MVC 应用程序具有非常简单的用户和服务器代码之间的接口:只有一个 get 或 post HTTP 请求/响应。

    浏览器发出请求,服务器发送响应,然后浏览器加载整个页面,然后用户单击链接或提交表单。

    然而,对于具有大量客户端代码的应用程序,不存在这种简洁性。一个页面可以与多个服务器端 URL 进行数据获取和发布,而无需刷新整个页面。

    具有大量客户端代码并采用 MVC 模式构建的应用程序通常称为“单页应用程序”。它并不意味着应用程序应该只有一个页面。它意味着视角(或注意力焦点)应该转移到您正在处理的页面上。

    您应该查找您页面的资源。开发变得非常以页面为中心。在页面上,重点在于以下问题:

    • 页面应该调用哪里来获取用户输入的表单
    • 或者页面应该从哪里获取列表数据
    • 或者表单应该将用户输入发送到哪里,等等。

    页面变得非常类似于 Microsoft 的 WebForms 及其回调。然而,最大的区别在于,这里的事件被驱动到多个服务器端 controllers/actions,请求是从客户端代码发送的(无需重新加载页面),界面的更新发生在客户端代码中。

    让我们看看这个系统如何处理标准任务,例如:获取、显示和发送表单,加载和显示列表和网格,加载重型 bootstrap 轮播滑块等。

    表单

    表单显示为页面上的 bootstrap 模态对话框。代码从服务器加载 Model 和 Template,生成 HTML 表单并更新 DOM:将带有表单的 HTML 片段插入对话框。

    显示编辑表单的代码示例 (/Client/Controllers/Project.js)

            //get the view and model and update the DOM
            Edit: function(callback, args){
                var view=null, model=null;
                
                var UpdateDOM = function(Model,View) {
                    if (Model != null){ model = Model; }
                    if (View != null) { view = View; }
                    if ((model != null) && (view != null)) {
                        var templateOutput = Mustache.render(view, model);
                        $("#dialog2-body").empty();
                        $("#dialog2-body").append(templateOutput);
                        $("#dialog2-label").empty().append("Update Project");
                        $("#dialog2").modal("show");
                        
                        NormalizeHash();
                    }
                }
                
                //call for the model, async
                ModelProxy(UpdateDOM, 'Project','Edit', args, 'get');
                
                //get the view, async
                ViewProxy(UpdateDOM, 'Project','Edit');
            }
        

    客户端代码验证表单,并通过 POST 请求将其发送到服务器。表单的发布发生在单独的客户端 POST Controller/Action 中。

    处理“编辑”表单值的代码示例 (/Client/Views/Project/Edit.html)

            var SubmittingProjectEditForm = function() {
                //alert("The form has been validated. Now send the form...");
                //make an AJAX call
                var args = {id: $('#ProjectEditContainer #Projectid').val(),
                    ProjectName: $('#ProjectEditContainer #ProjectName').val() ,
                    POP3Address: $('#ProjectEditContainer #POP3Address').val() ,
                    Active:($('#ProjectEditContainer #Active').is(':checked')) ? "on" : ""
                };
                //call Controller/Action eaither via controllers or directly
                //controllers['Project']['EditPost'](null, args);
                ProjectController.EditPost(null, args);
            };
        

    服务器端 controller 发送 POST 表单处理结果。结果可在客户端代码中通过服务器端的数据或状态获得。

    处理“编辑”表单值并将其发布到服务器的代码示例 (/Client/Controllers/Project.js)

            var ProjectController = {
                EditPost: function(callback, args){
                    //locally render the model in the view when they are ready, and update the DOM
                    var UpdateDOM = function(Model,View) {
                        show results of post request here
                    };
                    //call for the model, async
                    ModelProxy(UpdateDOM, 'Project','EditPost', args, 'post');
                }
            }
        
    列表(网格)

    当您需要显示列表时,请调用适当的 Controller/Action,例如 /ProjectController/List

    客户端 controller (ProjectController.List()) 将调用服务器端 controller/action 并获取列表数据(Model)。同时,它将加载适当的视图 - 一个 mustache 模板 (/Client/Views/Project/List.html)。

    然后,客户端 Controller/Action 将更新 DOM:在页面选定的元素中显示渲染后的列表。

    重型 bootstrap 轮播滑块、菜单、语言资源

    动态应用程序的一个巨大优势是能够首先加载轻量级页面,然后稍后加载和更新重型组件。Bootstrap 滑块可能包含大量图像。如果您将重型图像嵌入页面,加载页面可能需要一些时间。您可以先加载一两个项目的轮播滑块,然后按以下方式加载其余项目:

    在“页面加载”事件上调用客户端 Controller/Action。它将调用服务器端 controller/action 并获取轮播数据(Model)。

    同时,它将加载适当的视图:一个用于渲染 bootstrap 轮播的 mustache 模板。

    然后,客户端 Controller/Action 将更新 DOM:在页面选定的元素中显示渲染后的轮播项目。

    我正在使用这个后台页面更新类来在代码生成器中显示菜单和模型。

    客户端缓存

    您可以在客户端实现 Models 和 Templates 的缓存,以启用离线应用程序使用。现代浏览器中有 Web SQL、IndexedDB 和 localStorage。ModelProxy 和 ViewProxy 是用于 Models 和 Views 缓存的固定点。

    客户端 View 缓存

    如果支持缓存,在客户端缓存 Views 是一个显而易见的主意。优点是:减少网络流量,调用本地 controller/action 时延迟更低。

    {不带缓存的 ViewProxy}

              function ViewProxy (callback, controller, action) {
    
                  //Load the view
                  $.get('Client/Views/' + controller + '/' + action + '.html', {}, function(view) {
                       callback(null,view);
                      //may put the template into the client's local cache;
                  }).fail(function() {
                      //alert("error");
                      //failed to load the view, try render with empty one
                      var view = "";
                      callback(null,view);
                  });
              };
        

    使用不带缓存的 ViewProxy 的截图 中,您可以看到每次单击(项目)链接时都会发出 2 个查询。请注意,其中一些是由浏览器缓存的。

    {带缓存的 ViewProxy}

        function ViewProxy(callback, controller, action) {
            //may load template from/to cache: either Web SQL , IndexedDB or localStorage
    
            var cache = new ViewCache(controller, action);
            if (!cache.read(callback)) {
                //Load the view
                $.get(baseurl + 'Client/Views/' + controller + '/' + action + '.html', {}, function(view) {
                    //Template = Mustache.compile(view);
                    callback(null, view);
                    //may put the template into the client's local cache;
                    cache.write(view);
                    
                }).fail(function() {
                    //alert("error");
                    //failed to load the view, try render with empty one
                    var view = "";
                    callback(null, view);
                });
            }
        };
    
    
        //
        // This defines the cache
        //
        ViewCache = function(controller, action, data) {
            var enabled = true;
            var Controller = String(controller);
            var Action = String(action);
            var Data = null;
            var appName = "test";
            if (data)
                Data = String(data);
            var storagename = appName+ "/Views/" + Controller + "/" + Action;
            return {
                write: function(data) {
                    //if (!enabled) return false;  //write anyway
                    if (data)
                        Data = String(data);
                    if (typeof (Storage) !== "undefined") {
                        // Yes! localStorage and sessionStorage support!
                        localStorage[storagename] = Data;
                        return true;
                    }
                    else {
                        // Sorry! No web storage support..
                        return false;
                    }
                },
                read: function(callback) {
                    if (!enabled) return false;
                    if (typeof (Storage) !== "undefined") {
                        // Yes! localStorage and sessionStorage support!
                        Data = localStorage[storagename];
                        if (Data) {
                            if (Data.length > 1) {
                                callback(null, Data);
                                return true;
                            }
                        }
                        return false; //read was not successful
                    }
                    else {
                        // Sorry! No web storage support..
                        return false;
                    }
                },
                clear: function(callback) {
                    if (!enabled) return false;
                    if (typeof (Storage) !== "undefined") {
                        // Yes! localStorage and sessionStorage support!
                        //localStorage.clear();
                        //clear removes all cache values for domain. for subdomains remove values via iteration.
                        for (var key in localStorage) {
                            if (key.substring(0, appName.length) === appName)
                                localStorage.removeItem(key);
                        }
                        return true;
                    }
                    else {
                        // Sorry! No web storage support..
                        return false;
                    }
                }
            }//end public 
        }//end ViewCache
        

    ViewProxy 使用缓存的截图 中,您可以看到每次单击(项目)链接时只会发出 1 个查询。

    Model(数据)缓存

    虽然 View 缓存的原因很明显,但对于数据缓存来说,它们并不那么明显。

    • 为什么您可能需要它

      您可能需要数据缓存来存储经常使用但很少更新的数据。它可以是菜单或下拉列表值,或渲染到界面的某些数据,例如语言资源等。在本主题中,缓存将应用于下拉列表。

    • 为什么您可能不需要它

      尽管数据缓存有一些好处,但也有不使用它的理由

      • 某些操作需要实时(无条件非缓存)数据。这包括创建、更新、删除操作。
      • 上述操作会更改数据,因此需要缓存失效。
      • 在多用户系统中,缓存失效无济于事:其他人可能在本地缓存仍标记为有效时更改了数据库。
    • 它可能带您走向何方

      如果您实现了本地数据缓存,那么将来它可能会带您到以下地方:

      • CRUD 操作的数据缓存可以为您提供离线/在线应用程序的功能。当离线时,您的客户端代码可以读取、更新、删除缓存中的数据,并在重新上线时与服务器同步更改。
      • 您可以将数据缓存替换为客户端存储。这可以为您提供一种构建混合移动应用程序的简单方法。

    {不带缓存的 ModelProxy 代码}

        function ModelProxy(callback, controller, action, vars, method) {
            //var Callback = callback;
            //could load the model from the local storage: either Web SQL , IndexedDB or localStorage
    
            //Load model
            if (!method) method = 'get';
            method = method.toLowerCase();
            if (method == 'post') {
                $.post( baseurl + controller + '/' + action + '/', vars, function(model, status) {
                    callback(model,null);
                });
            }
            else if (method == 'get') {
                //this string is for MVC sites
                $.get( baseurl + controller + '/' + action + '/' + vars, {}, function(model, status) {
                    callback(model,null);
                });
            }
        };      
        

    {带缓存的 ModelProxy 代码(仅缓存 DropDowns)}

        //ModelProxy extracts the data from cache or from server(with the call to server-side Controller and Action).
        function ModelProxy(callback, controller, action, vars, method) {
            //could load the model from the local storage: either Web SQL , IndexedDB or localStorage
            var cache = new ModelCache(controller, action);
    
            if (cache.cacherequired) {
                if (cache.read(callback)) {
                    //successful cache read, value is passed in the callback function
                    return;
                }
            }
    
            //Load model
            if (!method) method = 'get';
            method = method.toLowerCase();
            if (method == 'post') {
                $.post(baseurl + controller + '/' + action + '/', vars, function(model, status) {
                    callback(model, null);
                    if (cache.cacherequired) { cache.write(model); }
                });
            }
            else if (method == 'get') {
                //this string is for MVC server-side code
                $.get(baseurl + controller + '/' + action + '/' + vars, {}, function(model, status) {
                    callback(model, null);
                    if (cache.cacherequired) { cache.write(model); }
                });
            }
        };
    
        //
        // This defines the cache
        // it reads/writes the data to localStorage
        // can invalidate cache either internally or via call
        ModelCache = function(controller, action, data) {
            //private
            var enabled = true;
            var Controller = String(controller);
            var Action = String(action);
            var Data = null;
            var appName = "test";
            if (data) { Data = JSON.stringify(data); }
            var storagename = appName + "/Model/" + Controller + "/" + Action;
    
            //this defines which Actions to cache. usually this is a heavy dropdowns
            var cachedActions = new Array("DropDown");
    
            //this defines which Actions to cause the cache invalidation. Usually this is a data change actions
            var dataChangeActions = new Array("CreatePost", "EditPost", "DeletePost");
    
            var invalidate = function() {
                if (typeof (Storage) !== "undefined") {
                    // Yes! localStorage and sessionStorage support!
                    var iAction;
                    for (iAction in cachedActions) {
                        localStorage.removeItem(appName + "/Model/" + Controller + "/" + cachedActions[iAction]);
                    }
                }
            };
    
            var invalidationrequired = (dataChangeActions.indexOf(action) >= 0);
            if (invalidationrequired) {
                invalidate();
            }
            var cacherequired = (cachedActions.indexOf(action) >= 0);
    
            //public
            return {
                write: function(data) {
                    //if (!enabled) return false;  //write anyway
                    if (data)
                        Data = JSON.stringify(data);
                    if (typeof (Storage) !== "undefined") {
                        // Yes! localStorage and sessionStorage support!
                        localStorage[storagename] = Data;
                        return true;
                    }
                    else {
                        // Sorry! No web storage support..
                        return false;
                    }
                },
                read: function(callback) {
                    if (!enabled) return false;
                    if (typeof (Storage) !== "undefined") {
                        // Yes! localStorage and sessionStorage support!
                        Data = localStorage[storagename];
                        if (Data) {
                            if (Data.length > 1) {
                                Data = JSON.parse(Data);
                                callback(Data, null);
                                return true;
                            }
                        }
                        return false; //read was not successful
                    }
                    else {
                        // Sorry! No web storage support..
                        return false;
                    }
                },
                // a reference to internal function
                invalidate: invalidate,
                cacherequired: cacherequired,
                invalidationrequired: invalidationrequired
            }//end public 
        }//end ModelCache
        
    代码组装和放置。常规

    JavaScript 是一种解释型语言。我们需要将代码存储在单独的文件中。有许多方法可以将代码模块作为一个系统保存在一起。

    • 在编写意大利面条式代码时,有很多全局变量被重用。您无法简单地连接文件;它们将无法工作。使用 MVC 模式,您可以简单地将所有脚本代码文件连接在一起,从路由器开始,按包含的顺序。如果仍然可以工作,那么您可以使用一个免费的可用服务(例如 http://jscompress.com/)来压缩/缩小代码。额外的好处是缩小提供了自然的混淆。
      服务器端和客户端代码都可以被压缩。
    • 将代码与内置代码链接器一起保存
    服务器端代码链接

    Classic ASP 使用 #include 指令来链接模块。Router.asp 文件包含链接ControllersModel 文件、Lib 文件的指令。这 3 个作为模块工作并包含相应的自定义文件。

    可以使用 Classic ASP Server.Execute() 命令来包含代码模块。

    包含服务器端模块还有另一种鲜为人知的选项:带有 **runat="server"** 选项的标准 <script> 标签。


            <script language="JScript"  src="Controller/controllers.inc" runat="server"></script>
            <script language="JScript" src="Model/lib.inc"  runat="server"></script>
            <script language="JScript" src="lib/utils.inc"  runat="server"></script>
        

    最后一个选项来自 MS Scripting Engine。以这种方式包含文件允许不同脚本语言中的代码,例如 VBScript

            <script language="VBScript" src="lib/VBModule.inc" runat="server"></script>
        

    但是,您应该仔细验证您的 IIS 如何处理您模块的文件扩展名。如果您将模块保留在“.vbs”或“.js”文件中,并且有人直接从服务器请求这些文件,它们将被原样发送到客户端。这将暴露您代码中的服务器端逻辑和常量。“.inc”扩展名在 IIS 默认设置中应该是安全的。

    如果您对访问服务器端代码存有疑虑,可以将整个服务器端代码移出 IIS 文件夹结构。由于我们这里使用的模式假定应用程序的单一入口点,因此在 IIS 应用程序文件夹的单个文件中可以有一个单独的 **#include** 指令。


    客户端代码链接

    在客户端链接 JavaScript 有 2 种通用方法:

    • 使用标准 <script> 标签链接脚本
    • 通过动态加载脚本到代码中链接脚本:使用 Ajax 加载脚本,然后使用 **eval()** 调用它们。

    当 JavaScript 代码文件不太大时,第一种选择是可以的,否则加载它们所有可能需要一些时间。如果脚本很大,不幸的是,标准 <script> 标签还有一个问题:浏览器不会并行加载脚本和其他资源,因此首次显示页面需要更长的时间。

    第二种选择很容易使用 jQuery.getScript()、RequireJS、labjs 等库。但是在使用它们时可能会遇到一些问题:

    • 当 DOM 加载完毕但支持的 JavaScript 库尚未加载时,可能会出现界面故障。
    • 可能会出现事件序列中断:假设您有一个模块,它从 Require.js 加载。jQuery document.ready() 可能不会调用模块中定义的函数,因为它在事件触发时仍在加载。

    调试

    您可以使用 MS Visual Studio 调试服务器端应用程序。您需要

    优点和缺点

    • 服务器和客户端使用的相同 JavaScript 方言非常相似。由于客户端和服务器语言版本相似,因此上下文切换更少。
    • 服务器和客户端上使用的相同 MVC 模式的实现相似。由于客户端和服务器语言版本相似,因此上下文切换更少。
    • 代码具有可移植性,只需最少的修改即可在客户端和服务器之间进行重新定位。
    • 在进行服务器端 JavaScript 编程时,可以从最意想不到的来源获得 JavaScript 库。
    • 它是 IIS。您可以带来您的 VBScript 代码并将其与服务器端混合。不要忘记通过 Server.CreateObject() 和 ASP.NET 页面进行 Windows 上的 ActiveX 扩展。
    • 脚本为每次服务器调用加载和卸载,因此它是真正无状态的,但应该比内存中系统慢。
    • Classic ASP 不支持 异步 代码执行。
    • Classic ASP 可用于从 XP 开始的许多 MS 操作系统。

    关注点

    • MVC 模式应用于 Classic ASP 服务器端 JavaScript 代码
    • MVC 模式应用于客户端 JavaScript 代码
    • 代码生成器已创建
    • 已应用客户端缓存和模板
    Classic ASP 重启。
    © . All rights reserved.