1 | ; (function (w, d, undefined) { //rest of the code })(window, document); |
我们需要将视图元素存储到一个变量中,这样就可以多次使用。
1 | var _viewElement = null ; //element that will be used to render the view |
我们需要一个缺省的路由来应对url中没有路由信息的情况,这样就缺省的视图就可以被加载而不是展示空白页面。
1 | var _defaultRoute = null ; |
现在我们来创建我们的主要MVC对象的构造方法。我们会把路由信息存储在“_routeMap”中
1 2 3 4 | var jsMvc = function () { //mapping object for the routes this ._routeMap = {}; } |
是时候创建路由对象了,我们会将路由、模板、控制器的信息存储在这个对象中。
1 2 3 4 5 | var routeObj = function (c, r, t) { this .controller = c; this .route = r; this .template = t; } |
每一个url会有一个专门的路由对象routeObj.所有的这些对象都会被添加到_routeMap对象中,这样我们后续就可以通过key-value的方式获取它们。
为了添加路由信息到MVC libs中,我们需要曝光libs中的一个方法。所以让我们创建一个方法,这个方法可以被各自的控制器用来添加新路由。
1 2 3 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //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方法所实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //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模板一起绑定模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //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全局范围外
1 2 | //attach the mvc object to the window w['jsMvc'] = new jsMvc(); |
现在,是时候在我们单页应用中使用这个MVC插件。在下一个代码段中,下面这些会实现:
1)在web页面中引入这个代码
2)用控制器添加路由信息和视图模板信息
3)创建控制器功能
4)最后,初始化lib。