Single Page Web Applications 阅读思考笔记
Oct 5, 2016
Single Page Web Applications 简称 SPA(单页Web应用),Web2.0时代的产物。它可能会提高开发成本,但那趋近于原生应用的效果还是令人心驰神往。
简介
- 《单页Web应用:JavaScript从前端到后端》· Michael S. Mikowski Josh C. Powell 著 · 包勇明
- 本书讲解如何 设计、构建和测试全栈单页的web应用,写的很细但我并没有逐字逐句的全部看完,即使是这样还是吸收了些营养,我把它们记录在这里
内容
- 推荐使用的:Object.create(proto, [ propertiesObject ])
- 创建一个拥有指定原型和若干个指定属性的对象。
- 可以用来实现类式继承
- 可以用它来代替 浅克隆 来防止Object引用传递带来的负面影响
- 当proto参数为数组时,函数会return一个丢失数组特性的一个对象
//es5关于此函数的实现,此函数的注释来自MDN if (typeof Object.create != 'function') { Object.create = (function() { //为了节省内存,使用一个共享的构造器 function Temp() {} // 使用 Object.prototype.hasOwnProperty 更安全的引用 var hasOwn = Object.prototype.hasOwnProperty; return function (O) { // 1. 如果 O 不是 Object 或 null,抛出一个 TypeError 异常。 if (typeof O != 'object') { throw TypeError('Object prototype may only be an Object or null'); } // 2. 使创建的一个新的对象为 obj ,就和通过 // new Object() 表达式创建一个新对象一样, // Object是标准内置的构造器名 // 3. 设置 obj 的内部属性 [[Prototype]] 为 O。 Temp.prototype = O; var obj = new Temp(); Temp.prototype = null; // 不要保持一个 O 的杂散引用(a stray reference)... // 4. 如果存在参数 Properties ,而不是 undefined , // 那么就把参数的自身属性添加到 obj 上,就像调用 // 携带obj ,Properties两个参数的标准内置函数 // Object.defineProperties() 一样。 if (arguments.length > 1) { // Object.defineProperties does ToObject on its first argument. var Properties = Object(arguments[1]); for (var prop in Properties) { if (hasOwn.call(Properties, prop)) { obj[prop] = Properties[prop]; } } } // 5. 返回 obj return obj; }; })(); } //创建对象,它有一个可写的,可枚举的,可配置的属性p var o = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } }); //如果省略了的属性特性(writable、enumerable、configurable)默认为false,所以属性p是不可写,不可枚举,不可配置的 o = Object.create({}, { p: { value: 42 } }) o.p = 24 o.p //42 //此特性还可以通过Object.defineProperties(obj, props)来设置(在一个对象上添加或修改一个或者多个自有属性,并返回该对象)
- Prototype跟Constructor关系介绍:
- 每个函数对象都有名为“prototype”的属性(Function.prototype函数对象是个例外,没有prototype属性),用于引用原型对象。
- 原型对象又有名为“constructor”的属性,它反过来引用函数本身。
- 这是一种循环引用(Animal.prototype.constructor === Animal)
- 锚接口模式
- 当URL的其他字段(hash)发生变化时,引起了页面的重新渲染
- 在有些个URI中的hash值处会出现 #!,这是告诉谷歌和其它搜索引擎,这个URI是可被搜索索引的
- HTTP请求不会包含hash字段,这些字符都不会被发送到服务器端
- 每一次改变hash字段,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置(>IE7)
- onhashchange事件可以监听hash字段的变化(>IE8)
window.onhashchange = function(){ alert(location.hash); }
- 函数推荐写法示例(demo函数为一个功能模块):
function demo() { var initModule, removeSlider; initModule = function () { //... }; removeSlider = function () { //... }; return { initModule: initModule, removeSlider: removeSlider }; }
- 公共模块的维护
- 开发采用函数式编程,纯函数、无业务关系
- 清晰的注释文档(使用过SmartDoc,依赖于YUIDoc,有些功能需要手动修改工具源码)
- 采用单元测试(使用过qunit,简洁、方便)
- 理想状态:注释文档中可以直接使用单元测试,并可以直接增删单元测试用例并进行单个多个测试—待搭建
- window.resize等window的事件最好全局只有一个
- js文件和css文件的命名应该是统一的,都应该有自己的命名空间呢a.b.c.d(来表示上下级依赖关系)
- 页面中所有需要配置的字段,最好存放在对象configMap中,保证全局一致
- 用jquery定义事件,返回true和false,会影响jquery对这个事件的下一步处理,返回false时会触发stopPropagation()等方法来阻止事件冒泡
- javascript的执行时分为2轮的,第一轮的执行步骤为:
- 声明并初始化函数参数
- 声明局部变量,包括将匿名函数赋值给一个局部变量,但并不初始化它们
- 声明并初始化函数
- JavaScript的函数作用域是词法作用域,但每当函数被调用的时候,就会产生一个新的执行对象(它不是一个对象,表示运行中的函数的意思)
- 在制作web应用的时候需要先制作整体的框架图,先区分模块,然后再将每个模块按照其为第三方模块的开发模式开发,保证每个页面模块之间相互不能有干扰
- 关于 “桩”,桩文件、桩文件夹、桩对象…这个词很好的表述了我们在开发过程中需要的“占位”操作
- 为什么自己的库要放在最后加载? (我们希望自己的库的名字空间是最终声明)
- 团队开发时需要使用相同的代码风格,最好编辑器支持相关的规则自定义(目前使用vscode的jshint插件)
关于数据驱动+单向数据流+模块化开发的几点心得与杂记
–源于一个已经用于生产环境的手写的小框架
- 数据源唯一,所有的页面模块的渲染都是依赖这个数据源
- 每一个“页面模块”的函数中只应该存在简单的if判断和dom操作(职责单一,我就管我这块的页面显示)
- 需要保证当数据源中的数据对象发生变化时,使用这个数据对象的“页面模块”函数自动执行(需自动建立映射关系)
- 所有的dom操作都应该都应该被封装在“页面模块”函数中
- 所有需要调整页面展示的地方,都需要通过修改数据源中的数据,来通过因果关系,使数据“流”向具体“页面模块”
- 每个页面模块都应该显式的对应“一块”dom元素
- 数据源的设计决定了整个项目的结构,数据源应遵循“类数据库式的设计范式”,扁平化无对象嵌套(危险的对象引用传递)
- 我理解的框架就是“约定好的项目构建方式+公共模块类库+人为的约束”
- 特点:以小部分性能的损失来换取开发的便捷,模块功能职责单一
- 职责单一