PS: 这里为了观看单页面的跳转,我这里引用了一个bootstrap的框架在里面,关于bootstrap的内容不再考虑之内,不会介绍。
为了说明的更清楚我又画了一张图片来说明knockout,require,director之间的联系和集成。当然看到这里希望大家是看过前面三篇单独的介绍之后再看的,毕竟这里是在前面三篇文章的基础上进行的。好了,废话不多说,开始吧! 我们本次的介绍就从上面图来进行。PS: 图片看不清的将图片在新的连接查看就可以了。
一, 首先看一下路由改变到flag值得变化:
从上篇文章中可以看到,我们现在控制url的director。在main.js中引入require之后,我们首先执行的router的引入,先看一下现在routes.js以及router.js的变化
routes.js:
define({
'/error404/:code': 'Error /',
'/': 'CustomerIntroduction /',
//Customer
'/CustomerIntroduction': 'CustomerIntroduction /',
//Require
'/RequireIntroduction': 'RequireIntroduction /',
'/RequireCode': 'RequireCode /',
//Javascript
'/UnknowJavascriptSecond': 'UnknowJavascriptSecond /'
})
可以看到现在routes不再是路由对应一个方法,而是一个字符串,这个字符串就是将要赋给
falg的值,希望大家记住这一点。
Router.js:
define(['WebPageContrl', 'Routes', 'Router'], function (WebPageContrl, Routes, Router) {
var routes = {};
/*遍历整个路由对象,在routes中记录所有的路由信息。*/
$.each(Routes, function(key, value) {
var values = value.split(' ');
var pageName = values[0];
routes[key] = function() {
WebPageContrl.initJS(pageName);
};
});
//配置router的configure。
var router = new Router(routes).configure({
notfound: function() {
routes['/error404/:code'](404);
}
});
var urlNotAtRoot = window.location.pathname && (window.location.pathname != '/');
//路由初始化。
if (urlNotAtRoot) {
router.init();
} else {
router.init('/');
}
return router;
});
这个文件现在require一个名为‘WebPageContrl’的model。这个WebPageContrl就是我们前面以及上面图中的Page对象,一个集成三方的对象。这个后面会详细去看,还是先看一下上面的代码吧。首先会遍历routes对象,然后给每一个路由注册一个方法,执行WebPageContrl.initJs(pageName)。
那现在路由的变化已经传递到WebPageContrl中。后面的代码就是director的路由config配置,以及初始化路由的代码。
二, 现在flag已经传到WebPageContrl对象中,那么我们就可以下个步骤了:
接下来我们来看一下WebPageContrl到底有哪些东西:
define(['knockout', 'jquery', 'Router', 'Custom'], function (ko, $, Router) {
var initialRun = true;
function isEndSharp() { // url end with #
if (app.lastUrl != "" && location.toLocaleString().indexOf(app.lastUrl) != -1 && location.toLocaleString().indexOf('#') != -1
&& location.hash == "") {
return true;
}
return false;
}
var app = {
initJS: function (pageName) {
require([pageName + '-js'], function (page) {
app.init(pageName, page);
});
},
init: function (pageName, pageData) {
if (isEndSharp()) {
return;
}
pageData.init();
app.page({
name: pageName,
data: pageData
});
if (initialRun) {
ko.applyBindings(app, document.getElementsByTagName('html')[0]);
initialRun = false;
}
},
page: ko.observable({
name: '',
data: {
init: function () { }
}
}),
afterRender: function () {
if (app.page().data.afterRender) {
app.page().data.afterRender();
}
}
};
return app;
});
内部很简单,一个app对象,还记得在router.js中调用的方法吧,initJs,现在我们先看这个方法,为了更好的说明,我们从routes.js中取一个路由
'/CustomerIntroduction': 'CustomerIntroduction /',
那我们此时传进来的就是CustomerIntroduction,那我们的initJs就会先去require(‘CustomerIntroduction-js’),这行代码就会将CustomerIntroduction页面对应的js文件加载,那上图中的pageName-js就加载了进来,然后执行的app.init(pageName, pageData);相信大家也注意到app对象中有一个page属性,page受ko的监控。当我们在init方法中给page赋值,那整个page中对于app.page的使用都会发生值得变化。
init: function(pageName, pageData) {
if (isEndSharp()) {
return;
}
pageData.init();
app.page({
name: pageName,
data: pageData
});
if (initialRun) {
ko.applyBindings(app, document.getElementsByTagName('html')[0]);
initialRun = false;
}
},
对于init方法,首先回去检测url是不是以#结束,因为director是以hash值得改变来实现的,当url以#结束的时候,会直接返回,不做处理。接着执行pageData.init(),pageData是我们引入CutomerIntroduction-js这个文件返回的对象,看了本篇文章最上面的图,相信大家就知道这个init方法存在的意义,就是为了在页面加载前执行一些初始化的工作,可能数据的读取等等。再往后面是对app.page的赋值,此处就是我们一直谈到的falg,这个值得变化就会去实现单页面的跳转,当然只改变这个值是不行,肯定要在渲染html模板的地方去使用这个值才能起到作用。这个在后面我们会谈到,还是先继续上面代码的执行,这个会判断页面是不是第一次运行,因为单页面上每次刷新我们都需要让我们的项目初始化的knockout,require,director初始化,初始化之后单页面的跳转就不要再去初始化这个js了,我们会在第一次初始化的时候将knokcout对应的viewmodel绑定到html上。
那现在我们看一下渲染html的地方,还记得在第二篇文章介绍knockout的地方我们index.cshtml中的代码吗?我们先看一下:
<main id="main" data-bind="template: { name: 'KnockoutIntroduction-html', data: require('KnockoutIntroduction-js').data,
afterRender: require('KnockoutIntroduction-js').afterRender}"></main>
我们当时在这个地方只是为了渲染一个界面,name是一个定值。那我们需要通过flag值得变化来渲染不同的界面,就需要蒋这个定值变成一个 依赖于flag的值:
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<main id="main" data-bind="template: { name: page().name + '-html', data: page().data ,afterRender:afterRender}"></main>
<script data-main="/Scripts/framework/main" src="/Scripts/lib/require.js"></script>
可以看到我们现在取得值就是我们在WebPageControl中app对象中的page属性的值,这样,当我们路由的变化就会渲染对应的html模板。也就对应最上面图中的pageName-html了。
最后我在把main.js的代码贴出来:
/*
Author: rodchen
Date: 2016/11/17
Description: It's the main entry point for require.
*/
var paths = {
/* TODO: register all AMD modules by providing CamelCase aliases, exceptions are RequireJS plugins and named AMD modules, whose names are fixed */
/* follow files dictionary order */
'jquery': 'Scripts/lib/jquery',
'Routes': 'Scripts/framework/routes',
'knockout': 'Scripts/lib/knockout',
//framework
'Router': 'Scripts/lib/director',
'WebPageContrl': 'Scripts/framework/webPageContrl',
'AppRouter': 'Scripts/framework/router',
'Error-js': 'Scripts/app/Error',
'Error-html': 'templates/Error-html.html',
"knockout-amd-helpers": "Scripts/lib/knockout-amd-helpers",
"text": "Scripts/lib/text",
//bootstrap
'Custom': 'Scripts/lib/custom',
'Bootstrap': 'Scripts/lib/bootstrap.min',
//Customer
'CustomerIntroduction-html': 'templates/customer/CustomerIntroduction.html',
'CustomerIntroduction-js': 'Scripts/app/customer/CustomerIntroduction',
//require
'RequireIntroduction-html': 'templates/require/RequireIntroduction.html',
"RequireIntroduction-js": 'Scripts/app/require/RequireIntroduction',
'RequireCode-html': 'templates/require/RequireCode.html',
"RequireCode-js": 'Scripts/app/require/RequireCode',
//Javascript
'UnknowJavascriptSecond-html': 'templates/javascript/UnknowJavascriptSecond.html',
'UnknowJavascriptSecond-js': 'Scripts/app/javascript/UnknowJavascriptSecond',
};
var baseUrl = '/';
require.config({
baseUrl: baseUrl,
paths: paths,
shim: {
/* TODO: provide all needed shims for non-AMD modules */
'Router': {
exports: 'Router'
},
'Custom': {
exports: 'Custom'
},
'Custom': ['Bootstrap'],
'Bootstrap': ['jquery']
}
});
require(["knockout", "knockout-amd-helpers", "text"], function (ko) {
ko.bindingHandlers.module.baseDir = "modules";
//fruits/vegetable modules have embedded template
ko.bindingHandlers.module.templateProperty = "embeddedTemplate";
});
require(['AppRouter'], function(){
});
以上的代码在我的github上都有源代码: https://github.com/rodchen-king/knockout_require_director
三, 最后我在把页面的变化贴出来给大家看一下:
(1)初始访问main.js,接着会访问AppRouter.
(2)遍历所有的routes,赋值value就是调用WebPageControl中的initJs方法。由于初始化进来时url中的路由是'/',所以我们在routes.js中将‘/’路由指向CustomerIntroduction,则我们初始化加载的就是CustomerIntroduction对应的界面。
(3)此时所有框架已经初始化成功,由(2)步可知我们初始化的界面是CustomerIntroduction,则此时响应的路由是CustomerIntroduction,
(4)接下来会执行到webPageControl中,由图中可以看出当前传进来的就是CustomerIntroduction-js对应js中的对象,这些具体页面的html和js文件我在这个地方就没有贴出来,大家可以到我的github上去看。
(5)ko绑定html对象,然后将initRun设为false。
(6)然后整个初始化的界面就load出来了。
(7)此后我们再点击其他的url,就会直接调用router.js里我们注册的方法:例如我们点击了左边菜单中javascript中的url,就会响应对应的路由
'/UnknowJavascriptSecond': 'UnknowJavascriptSecond /'
后面的话就是按照上面4,5,6步骤进行。
(8)响应的界面load出来:
好了,到了这里所有的东西都说完了,其实还有东西的,但是本篇文章只是将大致的流程阐述了一下,其他的以后有时间的话可以继续完善一下!
最后欢迎大家的批评指正!