knockout + require + director 构建单页面程序(integration)

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上去看。

https://github.com/rodchen-king/knockout_require_director/tree/master/knockout_require_director/knockout_require_director/Scripts/app


(5)ko绑定html对象,然后将initRun设为false。


(6)然后整个初始化的界面就load出来了。



(7)此后我们再点击其他的url,就会直接调用router.js里我们注册的方法:例如我们点击了左边菜单中javascript中的url,就会响应对应的路由

'/UnknowJavascriptSecond': 'UnknowJavascriptSecond /'

后面的话就是按照上面4,5,6步骤进行。

(8)响应的界面load出来:



好了,到了这里所有的东西都说完了,其实还有东西的,但是本篇文章只是将大致的流程阐述了一下,其他的以后有时间的话可以继续完善一下!

最后欢迎大家的批评指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值