소개
JavaScript 프레임워크(예: AngularJS, Backbone 또는 Ember)를 사용해 본 사람들은 UI(사용자 인터페이스, 프런트엔드)에서 mvc의 작동 메커니즘에 익숙합니다. 이러한 프레임워크는 MVC를 구현하여 단일 페이지에서 필요에 따라 뷰를 더 쉽게 변경할 수 있도록 합니다. mvc(Model-View-Controller)의 핵심 개념은 들어오는 요청을 처리하는 컨트롤러, 정보를 표시하는 뷰 및 프레젠테이션 모델입니다. 비즈니스 규칙 및 데이터 액세스를 위해.
따라서 단일 페이지에서 다양한 콘텐츠를 전환해야 하는 애플리케이션을 만들어야 할 때 일반적으로 위 프레임워크 중 하나를 사용하도록 선택합니다. 그러나 추가적인 번들 기능 없이 URL에서 뷰 전환을 구현하는 프레임워크만 필요하다면 Angular, Ember와 같은 복잡한 프레임워크를 사용할 필요가 없습니다. 이 기사는 동일한 문제를 해결하기 위해 간단하고 효과적인 방법을 사용하려는 시도입니다.
콘셉트
애플리케이션의 코드는 URL의 "#"을 사용하여 MVC 모드 탐색을 구현합니다. 애플리케이션은 기본 URL로 시작하고 해시 기반 코드는 애플리케이션 보기를 로드하고 개체 모델을 보기 템플릿에 적용합니다.
URL 형식은 다음과 같습니다.
http://도메인명/index.html#/경로명
뷰 콘텐츠는 객체 모델의 값과 속성을 {{Property-Name}} 형식으로 바인딩해야 합니다. 코드는 이 특수한 템플릿 형식을 찾아 개체 모델의 속성 값을 대체합니다.
Ajax를 통해 비동기적으로 로드된 뷰는 페이지의 자리 표시자에 배치됩니다. 뷰 자리 표시자는 모든 요소(이상적으로는 div)일 수 있지만 특수 속성이 있어야 하며 코드는 이 특수 속성을 기반으로 위치를 지정하며 이는 코드 구현에도 도움이 됩니다. URL이 변경되면 시나리오가 반복되고 다른 보기가 로드됩니다. 간단하게 들리나요? 아래 흐름도에서는 이 특정 구현의 메시지 점프를 설명합니다.
코드 작성
기본 모듈 디자인 패턴으로 시작하여 마침내 Facade 디자인 패턴을 사용하여 라이브러리를 글로벌 범위에 노출시켰습니다.
;(function(w,d,undefined){//restofthecode})(window,document);
뷰 요소를 여러 번 사용할 수 있도록 변수에 저장해야 합니다.
var_viewElement=null;//elementthatwillbeusedtorendertheview
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 개체에 추가되므로 나중에 키-값을 통해 얻을 수 있습니다.
MVC libs에 라우팅 정보를 추가하려면 libs에 메서드를 노출해야 합니다. 이제 각 컨트롤러에서 새 경로를 추가하는 데 사용할 수 있는 메서드를 만들어 보겠습니다.
jsMvc.prototype.AddRoute = function (controller, route, template) { this._routeMap[route] = new routeObj(controller, route, template); }
AddRoute 메소드는 컨트롤러, 경로, 템플릿이라는 3가지 매개변수를 받습니다. 그들은:
컨트롤러: 컨트롤러의 기능은 특정 경로에 액세스하는 것입니다.
경로: 라우팅 경로입니다. URL에서 # 다음 부분입니다.
템플릿: 이 경로의 보기로 로드되는 외부 HTML 파일입니다. 이제 우리 libs에는 URL을 구문 분석하고 관련 HTML 템플릿 페이지를 제공하기 위한 진입점이 필요합니다. 이를 달성하려면 방법이 필요합니다.
초기화 메서드는 다음을 수행합니다.
1) 뷰 관련 요소의 초기화를 가져옵니다. 코드에는 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) 경로 맵 객체 _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服务调用,动态事件绑定功能的。很快我会提供支持更多特性的升级版本。
以上所述就是本文的全部内容了,希望能对大家熟练掌握javascript有所帮助。