关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JavaScript »100行代码实现的JavaScript MVC样式框架

    100行代码实现的JavaScript MVC样式框架

    2015-03-13 00:00:00 出处:阮一峰的网络日志
    分享

    介绍

    使用过 JavaScript框架(如 AngularJS, Backbone 或者Ember)的人都很熟悉在UI(用户界面,前端)中mvc的工作机理。这些框架实现了MVC,使得在一个单页面中实现根据需要变化视图时更加轻松,而模型-视图-控制器(mvc)的核心概念就是:处理传入请求的控制器、显示信息的视图、表示业务规则和数据访问的模型。

    因此,当需要创建这样一个需要在单个页面中实现切换出不同内容的应用时,我们通常选择使用上述框架之一。但是,假如我们仅仅需要一个在一个url中实现视图切换的框架,而不需要额外捆绑的功能的话,就不必使用象Angular和Ember等复杂的框架。本文就是尝试使用简单、有效方法来解决同样的问题。

    概念

    应用中的代码利用urls中的“#”实现MVC模式的导航。应用以一个缺省的url开始,基于哈希值的代码加载应用视图并且将对象-模型应用于视图模板。

    url格式像下面这样:

    http://Domain Name/index.html#/Route Name

    视图内容必须以{{Property-Name}}的方式绑定对象模型的值和属性。代码会查找这个专门的模板格式并且代替对象模型中的属性值。

    以ajax的方式异步加载的视图会被放置于页面的占位符中。视图占位符可以是任何的元素(理想的情况是div),但是它必须有一个专门的属性,代码根据这个专门的属性来定位它,这样同样有助于代码的实现。当url改变时,会重复这个场景,另外一个视图被加载。听起来很简单吧!下面的流程图解释了在这个特定的实现中的消息跳转。

    写代码

    我们以基本的模块设计模式开始,并且最终用门面设计模式的方式将我们的libs曝光于全局范围内。

    ; (function (w, d, undefined) { //rest of the code })(window, document);

    我们需要将视图元素存储到一个变量中,这样就可以多次使用。

    var _viewElement = null; //element that will be used to render the view

    我们需要一个缺省的路由来应对url中没有路由信息的情况,这样就缺省的视图就可以被加载而不是展示空白页面。

    var _defaultRoute = null;

    现在我们来创建我们的主要MVC对象的构造方法。我们会把路由信息存储在“_routeMap”中

    var jsMvc = function () {  
        //mapping object for the routes  
        this._routeMap = {};  
    }

    是时候创建路由对象了,我们会将路由、模板、控制器的信息存储在这个对象中。

    var routeObj = function (c, r, t) {  
        this.controller = c;  
        this.route = r;  
        this.template = t;  
    }

    每一个url会有一个专门的路由对象routeObj.所有的这些对象都会被添加到_routeMap对象中,这样我们后续就可以通过key-value的方式获取它们。

    为了添加路由信息到MVC libs中,我们需要曝光libs中的一个方法。所以让我们创建一个方法,这个方法可以被各自的控制器用来添加新路由。

    jsMvc.prototype.AddRoute = function (controller, route, template) {  
        this._routeMap[route] = new routeObj(controller, route, template);  
    }

    方法AddRoute接收3个参数:控制器,路由和模板( contoller, route and template)。他们分别是:

    controller:控制器的作用就是访问特定的路线。

    route:路由的路线。这个就是url中#后面的部分。

    template:这是外部的html文件,它作为这个路由的视图被加载。现在我们的libs需要一个切入点来解析url,并且为相关联的html模板页面提供服务。为了完成这个,我们需要一个方法。

    Initialize方法做如下的事情:

    1)获取视图相关的元素的初始化。代码需要一个具有view属性的元素,这样可以被用来在HTML页面中查找:

    2)设置缺省的路由

    3)验证视图元素是否合理

    4)绑定窗口哈希变更事件,当url不同哈希值发生变更时视图可以被及时更新

    5)最后,启动mvc

    //Initialize the Mvc manager object to start functioning  
    jsMvc.prototype.Initialize = function () {  
        var startMvcDelegate = startMvc.bind(this);  
    
        //get the html element that will be used to render the view    
        _viewElement = d.querySelector('[view]');          
        if (!_viewElement) return; //do nothing if view element is not found      
    
        //Set the default route  
        _defaultRoute = this._routeMap[Object.getOwnPropertyNames(this._routeMap)[0]];      
    
        //start the Mvc manager  
        w.onhashchange = startMvcDelegate;  
        startMvcDelegate();  
    }

    在上面的代码中,我们从startMvc 方法中创建了一个代理方法startMvcDelegate 。当哈希值变化时,这个代理都会被调用。下面就是当哈希值变化时我们做的操作的先后顺序:

    1)获取哈希值

    2)从哈希中获取路由值

    3)从路由map对象_routeMap中获取路由对象routeObj

    4)假如url中没有路由信息,需要获取缺省的路由对象

    5)最后,调用跟这个路由有关的控制器并且为这个视图元素的视图提供服务

    上面的所有步骤都被下面的startMvc方法所实现

    //function to start the mvc support  
    function startMvc() {  
        var pageHash = w.location.hash.replace('#', ''),  
            routeName = null,  
            routeObj = null;                  
    
        routeName = pageHash.replace('/', ''); //get the name of the route from the hash          
        routeObj = this._routeMap[routeName]; //get the route object      
    
        //Set to default route object if no route found  
        if (!routeObj)  
            routeObj = _defaultRoute;  
    
        loadTemplate(routeObj, _viewElement, pageHash); //fetch and set the view of the route  
    }

    下一步,我们需要使用XML HTTP请求异步加载合适的视图。为此,我们会传递路由对象的值和视图元素给方法loadTemplate。

    //Function to load external html data  
    function loadTemplate(routeObject, view) {  
        var xmlhttp;  
        if (window.XMLHttpRequest) {  
            // code for IE7+, Firefox, Chrome, Opera, Safari  
            xmlhttp = new XMLHttpRequest();  
        }  
        else {  
            // code for IE6, IE5  
            xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');  
        }  
        xmlhttp.onreadystatechange = function () {  
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {  
                loadView(routeObject, view, xmlhttp.responseText);  
            }  
        }  
        xmlhttp.open('GET', routeObject.template, true);  
        xmlhttp.send();  
    }

    当前只剩加载视图和将对象模型与视图模板绑定了。我们会创建一个空的模型对象,然后传递与方法相关的模型来唤醒路由控制器。更新后的模型对象会与先前已经加载的XHR调用中的HTML模板绑定。

    loadView 方法被用于调用控制器方法,以及准备模型对象。

    replaceToken方法被用于与HTML模板一起绑定模型

    //Function to load the view with the template  
    function loadView(routeObject, viewElement, viewHtml) {  
        var model = {};   
    
        //get the resultant model from the controller of the current route    
        routeObject.controller(model);   
    
        //bind the model with the view      
        viewHtml = replaceToken(viewHtml, model);   
    
        //load the view into the view element  
        viewElement.innerHTML = viewHtml;   
    }  
    
    function replaceToken(viewHtml, model) {  
        var modelProps = Object.getOwnPropertyNames(model),  
    
        modelProps.forEach(function (element, index, array) {  
            viewHtml = viewHtml.replace('{{' + element + '}}', model[element]);  
        });  
        return viewHtml;  
    }

    最后,我们将插件曝光于js全局范围外

    //attach the mvc object to the window  
    w['jsMvc'] = new jsMvc();

    现在,是时候在我们单页应用中使用这个MVC插件。在下一个代码段中,下面这些会实现:

    1)在web页面中引入这个代码

    2)用控制器添加路由信息和视图模板信息

    3)创建控制器功能

    4)最后,初始化lib。

    除了上面我们需要的链接让我们导航到不同的路径外,一个容器元素的视图属性包含着视图模板html。

    <!DOCTYPE html> 
    <html> 
    <head> 
        <title>JavaScript Mvc</title> 
        <script src="jsMvc.js"></script> 
        <!--[if lt IE 9]> <script src="jsMvc-ie8.js"></script> <![endif]--> 
    
        <style type="text/css"> 
            .NavLinkContainer {  
                padding: 5px;  
                background-color: lightyellow;  
            }  
    
            .NavLink {  
                background-color:black;  
                color: white;  
                font-weight:800;  
                text-decoration:none;  
                padding:5px;  
                border-radius:4px;  
            }  
                .NavLink:hover {  
                    background-color:gray;  
                }  
        </style> 
    </head> 
    <body> 
        <h3>Navigation Links</h3> 
        <div class="NavLinkContainer"> 
            <a class="NavLink" href="index.html#/home">Home</a>   
    
            <a class="NavLink" href="index.html#/contact">Contact</a>   
    
            <a class="NavLink" href="index.html#/admin">Admin</a>   
    
        </div> 
        <br /> 
        <br /> 
        <h3>View</h3> 
        <div view></div> 
        <script> 
            jsMvc.AddRoute(HomeController, 'home', 'Views/home.html');  
            jsMvc.AddRoute(ContactController, 'contact', 'Views/contact.html');  
            jsMvc.AddRoute(AdminController, 'admin', 'Views/admin.html');  
            jsMvc.Initialize();  
    
            function HomeController(model) {  
                model.Message = 'Hello World';  
            }  
    
            function ContactController(model) {  
                model.FirstName = "John";  
                model.LastName = "Doe";  
                model.Phone = '555-123456';  
            }  
    
            function AdminController(model) {  
                model.UserName = "John";  
                model.Password = "MyPassword";  
            }  
        </script> 
    </body> 
    </html>

    上面的代码有一段包含一个为IE的条件注释。

    <!--[if lt IE 9]> <script src="jsMvc-ie8.js"></script> <![endif]-->

    假如IE的版本低于9,那么function.bind,Object.getOwnPropertyNames和Array.forEach属性将不会被支持。因此大家要通过判断浏览器是否低于IE9来反馈代码是否支持。

    其中的内容有home.html, contact.html 和 admin.html 请看下面:

    home.html:

    {{Message}}

    contact.html:

    {{FirstName}} {{LastName}}  
    <br /> 
    {{Phone}}

    admin.html:

    <div style="padding:2px;margin:2px;text-align:left;"> 
        <label for="txtUserName">User Name</label> 
        <input type="text" id="txtUserName" value="{{UserName}}" /> 
    </div> 
    <div style="padding:2px;margin:2px;text-align:left;"> 
        <label for="txtPassword">Password</label> 
        <input type="password" id="txtPassword" value="{{Password}}" /> 
    </div>

    完整的代码可以从给定的下载链接中得到。

    如何运行代码

    运行该代码比较简单,需要在你喜欢的Web服务器上创建一个Web应用,下面以IIS为例来说明。

    首先在默认站点中新增一个Web应用.

    然后设置必填信息:别名,物理路径,应用池,用户认证信息,点击OK。

    最后定位到Web应用的内容目录,浏览你想打开的HTML页面即可。

    跑在服务器里是必要的,因为代码加载从存储于外部文件中的视图,浏览器不会允许我们的代码在非宿主服务器环境下执行。当然假如你使用Visual Studio那么直接在目标html文件上右键,选择‘View In Browser’即可。

    浏览器支持

    大部分的现代浏览器都支持本代码。针对IE8及以下的浏览器,有一份单独的代码来支持,但很不幸,这份代码远多于100行。因此这代码不是百分百跨浏览器兼容的,所以当你决定在项目中使用时需要对代码进行微调。

    兴趣点

    This example demonstrates这个示例向我们展示了对于非常明确地需求来说,真没必要全部使用js库和框架来实现。Web应用是资源密集型的,最好只使用必要的代码而丢掉其他多余部分。

    目前的代码能做的就这些了。没有诸如Web服务调用,动态事件绑定功能的。很快我会提供支持更多特性的升级版本。

    Download JavaScript-Mvc.zip – 4.6 KB JavaScript Mvc on Github Live Demo

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    帝国CMS7.5编辑器上传图片取消宽高的三种方法

    帝国cms如何自动生成缩略图的实现方法

    Windows 12即将到来,将彻底改变人机交互

    帝国CMS 7.5忘记登陆账号密码怎么办?可以phpmyadmin中重置管理员密码

    帝国CMS 7.5 后台编辑器换行,修改回车键br换行为p标签

    Windows 11 版本与 Windows 10比较,新功能一览

    Windows 11激活产品密钥收集及专业版激活方法

    IT头条

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    严重缩水!NVIDIA将推中国特供RTX 5090 DD:只剩24GB显存

    00:17

    无线路由大厂 TP-Link突然大裁员:补偿N+3

    02:39

    Meta 千万美金招募AI高级人才

    00:22

    技术热点

    微软已修复windows 7/windows 8.1媒体中心严重漏洞 用户可下载安

    卸载MySQL数据库,用rpm如何实现

    windows 7中使用网上银行或支付宝支付时总是打不开支付页面

    一致性哈希算法原理设计

    MySQL数字类型中的三种常用种类

    如何解决SQL Server中传入select语句in范围参数

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。