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

来源:互联网 发布:java中反射的应用 编辑:程序博客网 时间:2024/05/29 17:43

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出来:



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

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

0 0