关注并将「趣谈前端」设为星标

每早08:30按时推送技术干货/优秀开源/技术思维

目前react,vue,angular等框架都支持单页面程序,最近在回顾一些知识,想起刚毕业的时候接触到knockout + require + director 构建的单页面程序。所以写一篇文章回顾一下以前的单页面,或许对于现在了解单页面程序有一些帮助。

前置简要说明

从以前的多页面程序,在只有js,html,jquery的年代,路由的跳转通过链接跳转,每个页面的header和footer会在每一个页面书写一遍。那想创建一个单页面程序。会有几个问题?

  • 动态渲染部分的区域内容需要可以通过js进行控制

  • 监控路由的跳转

  • js控制动态路由的跳转渲染具体的页面,那html的渲染和js的执行如何处理,因为这里我们希望通过ko来实行一个页面和数据model的绑定,所以这里还需要添加上ko绑定页面数据data的处理,所以对于js文件的处理会划分成类似react / vue 生命周期的方式来处理

rquire

require.js的诞生,就是为了两个问题:

  1. 实现js文件的异步加载,避免网页失去响应;

  2. 管理模块之间的依赖性,便于代码的编写和维护。

我们这里使用require是为了通过require来进行html和js的文件控制,require生来是用来控制js的,那么怎么控制html呢,可以通过require的插件text进行html控制。

knockout

knockout是mvvm框架的鼻祖了。实现了双向数据绑定的功能。

Knockout是一款很优秀的JavaScript库,它可以帮助你仅使用一个清晰整洁的底层数据模型(data model)即可创建一个富文本且具有良好的显示和编辑功能的用户界面。

任何时候你的局部UI内容需要自动更新(比如:依赖于用户行为的改变或者外部的数据源发生变化),KO都可以很简单的帮你实现,并且非常易于维护。

我们这里使用的主要是knockout tempate的功能以及业务功能书写的时候用的ko的数据绑定功能。template绑定通过模板将数据render到页面。模板绑定对于构建嵌套结构的页面非常方便。

注图片来源:https://www.cnblogs.com/tangge/p/10328910.html

director

director.js 就是客户端的路由注册/解析器,它在不刷新页面的情况下,利用“#”符号组织不同的URL路径,并根据不同的URL路径来匹配不同的回调方法。通俗点说就是什么样的路径干什么样的事情。

这里用director主要是为了做单页面路由的切换调用js代码。

require

上面已经说了,我们需要通过require来进行对html,css的控制。

初始化require

<script src="js/require.js" data-main="js/main"></script>

data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><main id="main"></main><script data-main="/Scripts/framework/main" src="/Scripts/lib/require.js"></script>
</body>
</html>

我们的代码里面,加载require文件之后会执行main.js文件。

main.js文件

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(["jquery", "RequireIntroduction-js", "text!RequireIntroduction-html"],function ($, module, html) {console.log("Start test require html!");$('#main').html(html);console.log("Start test require js!");module.TestRequireJs();}
);

这个文件可能有点多了一点其他的,所以这里我们还只看重点就可以了。

require.config

使用require.config()方法,我们可以对模块的加载行为进行自定义。

require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。

我们这里将很多长文件路径的文件通过别名定义。

渲染html & 执行 js

require(["jquery", "RequireIntroduction-js", "text!RequireIntroduction-html"],function ($, module, html) {console.log("Start test require html!");$('#main').html(html);console.log("Start test require js!");module.TestRequireJs();}
);

这里在获取到当前页面RequireIntroduction的html和js文件。通过jquery的html方法渲染html。获取js模块,调用js方法。

看一下html和js的代码

<!--RequireIntroduction-html-->
<div>require introduction
</div>
// RequireIntroduction-js
define(function () {function RequireIntroductionViewModel() {var self = this;self.TestRequireJs = () => {console.log('testRequireJS');}}return new RequireIntroductionViewModel();
});

knockout

下载所需要的文件knockout.js 和knockout-amd-helpers.js,knockout-amd-helpers.js在本章节中主要的作用在于knockout在渲染模板时可以直接渲染整个html文件,而不用在当前web页面中定义模板。

现在我们想通过knockout template进行页面的渲染。

修改index.html

<main id="main"data-bind="template: { name: 'RequireIntroduction-html', data: require('RequireIntroduction-js').data, afterRender: require('RequireIntroduction-js').afterRender}"></main><script data-main="/Scripts/framework/main" src="/Scripts/lib/require.js"></script>

修改main.js

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", "RequireIntroduction-js", "knockout-amd-helpers", "text"], function (ko, RequireIntroduction) {ko.bindingHandlers.module.baseDir = "modules";//fruits/vegetable modules have embedded templateko.bindingHandlers.module.templateProperty = "embeddedTemplate";ko.applyBindings(RequireIntroduction);
});

从简要说明的时候我们知道,需要用knockout的template来进行html渲染,但是这里我们没有去定义具体的template,而是使用下面的代码:

require(["knockout", "RequireIntroduction-js", "knockout-amd-helpers", "text"], function (ko, RequireIntroduction) {ko.bindingHandlers.module.baseDir = "modules";//fruits/vegetable modules have embedded templateko.bindingHandlers.module.templateProperty = "embeddedTemplate";ko.applyBindings(RequireIntroduction);
});

因为这里采用了knockout-amd-helpers

该插件旨在成为在 Knockout.js 中使用 AMD 模块的轻量级且灵活的解决方案。这个库最初设计用于require.js或curl.js。它提供了两个主要功能:

  1. 增强默认模板引擎以允许它使用 AMD 加载器的文本插件加载外部模板。这使您可以在单独的 HTML 文件中创建模板并根据需要将它们拉入(理想情况下,在生产中模板包含在优化文件中)。

  2. 创建一个module绑定,它提供了一种从 AMD 模块加载数据的灵活方法,并将其绑定到外部模板、匿名/内联模板或模块本身的属性中定义的模板。

上面的代码执行的效果是什么呢?

我们书写代码的时候,如此调用就可以从require当中加载当前key对应的html文件。

走到这里,require已经和knockout联系在一起。knockout进行模版渲染,require进行文件控制。

页面渲染

回顾一下前面的问题

接下来主要的问题是: 监控路由变化,在系统代码里可以动态处理当前路径对应的html / js资源。然后对于js代码的执行进行ko的绑定和生命周期方式的拆分。

集成director完成单页面程序

系统层级代码的处理 webPageContrl

/** @Description: * @Author: rodchen* @Date: 2021-07-24 12:08:10* @LastEditTime: 2021-07-24 21:33:46* @LastEditors: rodchen*/
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;}// js模块的init方法执行pageData.init(); // ko绑定的数据,数据绑定的数据源是js模块里的data模块app.page({name: pageName,    // 路由对应的page标识data: pageData}); // 初次执行,ko绑定if (initialRun) {ko.applyBindings(app, document.getElementsByTagName('html')[0]); initialRun = false;}},page: ko.observable({name: '', data: {init: function() {}}}),// knockout template 加载成功之后的回掉函数afterRender: function() {if (app.page().data.afterRender) {app.page().data.afterRender();}}};return app;
});

director 处理

/**  // router.js* @Description: * @Author: rodchen* @Date: 2021-07-24 12:08:10* @LastEditTime: 2021-07-24 19:59:11* @LastEditors: rodchen*/
define(['WebPageContrl', 'Routes', 'Router'], function (WebPageContrl, Routes, Router) {var routes = {};$.each(Routes, function(key, value) {var values = value.split(' ');var pageName = values[0];routes[key] = function() {WebPageContrl.initJS(pageName);  // 通过director路由的回掉函数,然后执行系统文件的initJS方法};});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;
});
// Routesdefine({'/error404/:code': 'Error /','/': 'CustomerIntroduction /','': 'CustomerIntroduction /',//Customer'/CustomerIntroduction': 'CustomerIntroduction /',//Require'/RequireIntroduction': 'RequireIntroduction /','/RequireCode': 'RequireCode /',//Javascript'/UnknowJavascriptSecond': 'UnknowJavascriptSecond /'
})

串一个整个流程来说明

代码地址:https://github.com/rodchen-king/frontend_knonckout_require_director

// 代码执行 通过http-server执行,因为是相对路径,需要挂靠在服务 npm install global http-server

参照

  • https://www.ruanyifeng.com/blog/2012/11/require_js.html

  • https://www.cnblogs.com/tangge/p/10328910.html

  • https://www.cnblogs.com/Showshare/p/director-chinese-tutorial.html

❤️ 看完三件事

如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  • 点个【在看】,或者分享转发,让更多的人也能看到这篇内容

  • 关注公众号【趣谈前端】,定期分享 工程化 可视化 / 低代码 / 优秀开源

从零搭建全栈可视化大屏制作平台V6.Dooring

从零设计可视化大屏搭建引擎

Dooring可视化搭建平台数据源设计剖析

可视化搭建的一些思考和实践

基于Koa + React + TS从零开发全栈文档编辑器(进阶实战

点个在看你最好看

前端复盘: knockout + require + director 构建单页面程序相关推荐

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

    接下来介绍knockout的在前一篇文章中的基础上的应用. 当然首先的显示下载本章所需要的文件knockout.js 和knockout-amd-helpers.js,knockout-amd-hel ...

  2. 给大家推荐一个Vue 单页面程序无法SEO的解决办法

    给大家推荐一个vue 单页面搜索引擎无法SEO的解决办法 这两天用Vue3做了一个免费下载书籍的小网站,https://book.usejs.cn:大家可以先看下效果 前端项目做完.部署了之后想让搜索 ...

  3. 基于angularjs的单页面实例_基于AngularJs的单页面程序

    基于AngularJs的单页面程序 在Abpzero的后台管理系统是一个AngularJs的单页面程序.当你登陆后,系统会跳转到"ApplicationController",然后 ...

  4. [项目实战] 使用Idea构建单页面Vue3项目(不使用node、npm)

    前言 某天张三对小花说,我需要在一台新电脑上实现一个前端的漂亮页面:比如京东手机首页(m.jd.com). 小花这时吭哧吭哧的去新电脑上安装nodejs.npm,然后在本地使用npm构建vue3项目, ...

  5. 前端:你要懂的单页面应用和多页面应用

    单页面应用(SinglePage Web Application,SPA) 只有一张Web页面的应用,是一种从Web服务器加载的富客户端,单页面跳转仅刷新局部资源 ,公共资源(js.css等)仅需加载 ...

  6. 为什么说单页面程序SEO不友好?

    搜索引擎的工作原理 在搜索引擎网站的后台会有一个非常庞大的数据库,里面存储了海量的关键词,而每个关键词又对应着很多网址,这些网址是被称之为"搜索引擎蜘蛛"或"网络爬虫&q ...

  7. vue单页面程序对谷歌GA事件的应用

    公司运营最近在对网页做用户转化率调查这块,找到了谷歌GA事件.按照之前GA官网给出的文档,把代码加入到了index.html里面,一周后发现统计出来的数据跟后台数据库对比对不上. 经过一番研究后,发现 ...

  8. 新手vue构建单页面应用实例

    本人写的小程序,功能还在完善中,欢迎扫一扫提出宝贵意见!           步骤: 1.使用vue-cli创建项目 2.使用vue-router实现单页路由 3.用vuex管理我们的数据流 4.使用 ...

  9. 前端单页面富应用(SPA)的实现

    一. 什么是单页面富应用? 单页面应用:Single Page Application 概念:Web应用即使不刷新也在不同的页面间切换,解决浏览器前进.后退等机制被破坏等问题.并且页面访问会被浏览器保 ...

最新文章

  1. 小红书做直播的背后,隐藏了什么秘密?
  2. python django事务transaction源码分析
  3. J-LINK 操作使用指南
  4. 九章算术卷第九 句股
  5. mac远程连接linux 服务器桌面by VNC
  6. MacOS平台上编译 hadoop 3.1.2 源码
  7. 启动一个SpringBoot的maven项目
  8. 服务器 16路直连 英特尔,Intel 10nm服务器怪咖:八通道+16条内存
  9. 辨析:工作路径与脚本路径
  10. poco mysql 安装_linux 下 POCO 安装
  11. win10系统启动wifi服务器,windows10系统下开启wifi共享的两种方法
  12. 4.2 Hive SQL
  13. 【程序人生】机灵鹤七月份的月度总结
  14. Java实现swap交换函数的数组方法
  15. 【ppp概念股龙头】PPP项目落地显著加速 四大板块牛股或受益
  16. django自带模板使用及语法
  17. 编程算法基础-常数变易法
  18. 学渣小论文投稿期刊记录总结
  19. “踔厉奋发 笃行不怠”第十三届中国优秀数据中心峰会暨第28届年会在北京召开
  20. 亚马逊AWS服务器下载kaggle竞赛数据

热门文章

  1. 读书法---态度至上
  2. 快上车 荣耀手表2体验分享
  3. 2011年武汉东湖樱花节(摄影)
  4. 钥道不止,目录在线:瞎找不如阅目录
  5. QQ浏览器无法正常播放优酷视频的解决方法
  6. java堆是什么_java中的堆是什么意思?
  7. java的第一范式,数据库第一范式第二第三范式关系详解
  8. matlab 基金业绩归因,基金的绩效归因方法及应用.ppt
  9. 存储:lun与卷(LVM)
  10. python 最短路径算法_最短路径python