2018 November 01 —— improvement

Read Pocket Best Article(3)

前端领域文章阅读精选

单页面应用

单页面集成多种功能,整个系统只有一个页面,其他功能都是其通过特定方式挂载的子模块.

开发框架

复杂单页面应用的工程化问题;

jQuery 的面向上层操作的 api, 无法针对代码进行有效的约束与代码管理.

如何在代码急速膨胀的情况下对于代码进行模块的内聚,完成模块之间的交互与数据共享,大型的应用系统处理?

如何解决? 引入代码分层结构: 引入模块分层与模块间通信机制; ViewModel/

组件化

在早期前端逻辑厚度不足的时代,大部分组件是 UI 组件,而随着前端的发展,前端复杂化进一步催生了大量非 UI 组件(逻辑组件)

组件分层处理带来了组件内部的职责划分,UI 与逻辑进行剥离带来每一层的职责更加清晰,可测(单元测试);

代码隔离?

单页面应用中各种模块的代码都被聚集到了同一个作用域之中,因而代码的隔离/模块化很重要.

代码合并与加载策略

多页面应用之间的互相隔离与单页面应用中的公用(可复用)代码块按需加载.

加载=> 分块加载/懒加载 / 整体加载的取舍问题

当应用较简单时可以采用整体一次性加载而带来更加优秀的页面切换体验,如果大到一定规模时进一步做代码分块,运行时按需加载.

路由与状态管理

单页面应用子模块展示如果与页面状态绑定? 想象当前浏览的页面被分享,其他用户打开这一被分享页面,单页面应用的状态如何重复构建.

单页面应用之中页面状态的前进后退.

页面路由状态管理: VueRouter: History interface 构建

缓存与本地存储

浏览器静态文件缓存

localStorage 本地存储临时数据

服务端通信

WebSocket

Ajax

jsonp

js 内存管理

内存的生命周期:

  • 内存分配
  • 内存使用
  • 内存的释放

变量值与对象的初始化,内存的分配.

内存的使用更多聚焦于内存的获取与内存的写入.

内存的释放: 从 C 系列的手动释放进化到 GC 内存管理,垃圾收集器进行内存的垃圾属性判断与自动的内存释放.

垃圾判断算法: 从引用计数进化到标记清除算法.标记算法, 从根引用进行引用链判定不可达对象.

单页面应用样式规划

单页面应用的层次管理

单页面应用复杂化, 需要规划应用的 z-index ,规划应用中出现的弹窗,浮层等各种堆叠状态.

规划过程: 对于不同类型的组件 index 区间进行类型划分,以便于避免冲突产生.

单页面应用的产品形态

前后端应用以前后端交互 API 为界进行独立分离,各端职责分离专注于自身的功能设计.

单页面应用的部署形态

前端代码完全静态化部署,可以进行独立 CDN 部署,访问加速,优化用户体验.

单页面应用的劣势

  • SEO 劣势

  • 单页面应用性能问题

关键渲染路径

单页面应用


Web 研发的演进:

Web1.0 时代:

原始 JSP 开发环境对于前端同学不友好, 因而专职的前端同学开发处于弱势地位;

JSP 的强大,前端显示嵌入了后端控制代码,导致职责不清晰: 三不管地带, 会导致混乱代码丛生

业务的复杂化–> 而同时 JSP 业务代码复杂化,维护性差. 为了赶项目而导致 JSP 中充斥着各种混乱代码.

团队协作在业务变迁过程中永远是大问题: 一个人不可能维护一个巨型产品.

思考团队的开发模式? 如何提升团队的开发效率.

如何提升可维护性? 可维护性通常是节制开发自由度,进而提升工程级别的鲁棒性,约束新手,让新手开发的代码也符合规范.

利用工程级别的规范/约束即使是 Web 1.0时代,JSP 开发也能实现不错的可维护性.

2.0 Web

服务端架构升级: MVC

SSH / SpringMVC

职责切分: 从架构层面约束开发模式, 什么代码写在什么地方.

前后端分离开发依旧痛苦: 前端开发复杂, 要么前端开发环境搭建困难, 前端做模版开发的模式,后端套用模版的开发形式流程复杂.

前后端职责纠缠不清.

3.0

Ajax 带来的单页面应用时代(SPA)

职责分离:

CDN 处理静态资源高速存储

Ajax 作为前后端协同关键点.

前端逻辑分离进而开始进入前端架构时代, 前后端并行开发,前端工程化等挑战开始出现

4.0 时代

前端MV* 开发模式: MVVM/MVC 等开发模型的应用带来了前端模块化的开发模式.

Node 全栈时代

Node 时代的到来与 JPS1.0 时代的对比, 螺旋的上升模式,前端增加开发中的话语权,Node 带来了全新的工具链.

Node 作为前端独立中间件的存在的情形下, Node 与服务端的通信机制处理(Java 后台).

针对 Node 后台的部署与运维等相关知识的学习了解. 同时也需要前端对于后台相关知识链的学习与了解.

知识域与职责触手的延伸.能力越大,责任越大,权重越高.

技术的发展,技术栈的迭代,开发模式的螺旋上升过程

Web


当浏览器中输入… 之后发生什么?

https://github.com/skyline75489/what-happens-when-zh_CN

Proxy

Proxy 对象: 在目标对象之前架设一层拦截,handler, 外界对于该对象的访问需要通过该拦截,进而提供对于外界访问的过滤与改写.

通过在代理对象上注册特殊处理函数,在代理上执行各种操作时会执行相应的函数,除了可以执行原始的操作转发之外还能执行额外的逻辑.

Proxy 也被称之为 代理器.

代理在先与代理在后

代理在先模式: 操作完全与代理对象交互,通过代理对象与目标对象交互.

代理在后模式: 代理在先模式的反转,让目标与代理交互,操作直接与目标对象交互,而代理对象作为最后的访问保障.

代理在后模式通过采用原型链设置,将代理对象利用 Object.setPrototypeOf()设置为目标对象的保障.当目标对象没有对应的属性时,才通过代理对象寻找.

从__proto__和prototype来深入理解JS对象和原型链

防御性编程(No such Method/Property)


var  handler = {get(target,key,context){}},
     proxyObj = new Proxy({},handler),
     obj={foo(){}};

Object.setPrototypeOf(obj,proxyObj);

处理 undefined 值的7个建议

Proxy 在 RN 中的使用

polyfill Proxy

Polyfill: 抹平新老浏览器标准原生 API 的支持差距的封装实现.实现浏览器并不支持的原生API 代码.

Proxy is not pollyfilled in react native by default. It works in chrome debugger because react native uses chrome js engine during debugging see Document on js environment. You may try using Proxy pollyfill.

proxy-polyfill

Proxy/Reflect 元编程

元编程: 在语言层面做出修改,针对编程语言的编程.

ES6 入门-Proxy

Export / Import 模块

来自 Node 社区的CommonJS 规范

module.exports = {proxy};


const obj = require("fs") // 整体加载fs 模块,返回对应对象. 这种加载称之为动态加载,无法做编译时静态优化.

obj.proxy;// 获取proxy 对象

ES6 模块

ES6 模块的设计思想: 尽可能的静态化,进而可以在编译时确定模块依赖关系,获取输入输出变量.

利用静态编译时加载,做更多的静态分析,进一步实现更多的优化与js 语法拓展.

export

  • ES6 的导出基于文件模块,一个文件一个模块.
  • 针对模块暴露的 API 是静态的,无法在暴露之后动态修改.
  • ES6 暴露的模块是单例的,也就是说模块的导出是针对导出变量(对象)的绑定(类似于指针),export 的模块只有一个实例,其中维护了暴露模块的状态.每次其他模块导入时,引入的都是同一个实例的引用.这里与 ES6之前有非常大的差异,在 ES6之前的属性赋值是利用值赋值,而不是引用绑定,也就是如果对应导入变量的更新不修影响 API 对象的公开复制.
  • 导入模块和静态请求加载在原理上形成相同的效果,如果在浏览器中则是网络阻塞加载.

  • 无 export 导出的一切都被隐藏在模块内部保持私有属性.

export default express(表达式);

export {express  as  default};

看起来二者一样,但是实际上第一种导出的是在导出时函数表达式的绑定,即使之后在对应导出模块内部给 express 重新赋值赋值,并不会干扰导出.而第二种则隐含了导出的是绑定到 express 的标识符,也就是绑定行为.

  • 不允许双向绑定,也就是导入的变量不允许被修改.

import

import  标识符  from "模块指定符"

标识符:

标识符必须匹配模块的 export API.

与export 类似,import 也有 es6推荐的语法糖:

import foo from "foo";

import {default as foo} from "foo";

// 混合引入 default 与其他
import foo, {a,b,c} from "foo";

//命名空间导入,将模块中所有暴露的 API 全部导入到单个模块命名空间绑定
//命名空间的导入,要么指定导入模块全有要么全无 
import * as foo from "foo";

所有导入的绑定都是不可变的,如果尝试对于导入之后的模块进行赋值操作,会抛出异常 TypeErrors

指定符:

模块指定符必须是字符串字面量,且由于 import 需要被用于静态分析,其中也不能引入变量.

模块加载器将该指定符解释为决定去何处寻找所需模块的指令.(URL 路径/本地文件路径)

export 与 module.exports 以及 require 与 import

module.exports 遵循 CommonJS 规范;

require 使用相当于 module.exports 对称使用方式, module.exports 内容是什么, require 获取的结果就是什么,相当于吧 require 与 module.exports 进行平行空间的位置重叠.

与之对应的 import 则与 require 运行时赋值不同, import 是编译时,必须防止在文件头,使用格式确定,不会整个模块运行后赋值给某个变量,性能相对 require 更好.

但需要注意的是,如果利用 babel 做 transpiling 转译, import 语法为了兼容浏览器会被转码为 require. 这也解释了为什么用 module.exports, 在引入时也可用 import,因为本质是转换成了 require.

  • module.exports 初始值为空对象{}
  • exports 指向 module.exports 的引用
  • require 返回 module.exports

exports 和 module.exports 的区别

HTTP负载均衡

HTTP 集群负载均衡设计: 高性能系统优化

没有一种解决方案能够解决所有问题,因地制宜,合适的才是最好的

用户的扩张, 从早期的单机器负载到机器集群负载的变化.

Q1:多服务器之间如何均衡流量,为用户选择合适的服务器进行响应处理? 如何组成高性能集群的?

负载均衡: 将用户访问流量通过负载均衡器,根据某种转发策略, 均匀分发到多端后台服务器上.后台的服务器均可以独立响应与处理用户请求,从而实现分散负载目的.

负载均衡方案

  • 基于 DNS
  • 基于硬件 如: F5
  • 基于软件 如: Nginx/Squid
DNS 方案优劣:

优势: 配置简单,实现成本低
劣势: 配置修改后,由于 DNS 多级缓存存在,IP 变更不及时,时效性上影响负载均衡效果.

DNS 实现方式: 基于地域/ IP 轮询

基于硬件:

优势: 简单,性能强大
劣势: 成本高昂

实现方式: 通过网络硬件设备(类似交换机),通过硬件来进行流量压力

软件负载均衡:

实现方式:

  • 传输层负载均衡方案: LVS
  • 应用层负载均衡方案: Nginx

均衡算法

  • 轮询算法:

    不关注服务器本身状态,利用轮询轮流转发请求.(顺序轮询/随机轮询/权重轮询)

  • 负载度策略:

    转发前评估后端服务器状态,根据负载压力的计算来进行动态分配流量

  • 响应策略

    收集所有服务器响应速度,进行排行榜,永远最优先选择最快响应的服务器响应用户请求.

  • 哈希策略

    将请求信息中的某个值进行 hash 运算, 根据后端服务器台数取模,得到对应策略值,如果相同值的请求被转发到同一台服务器上.

HTTP负载均衡

Google 前端JS渲染爬虫爬取文章

  1. 对应的服务端能够支撑海量的爬虫请求
  2. js 与 css 可被爬虫获取,便于正确理解内容
  3. 页面最好有优雅的降级渲染方案
  4. js 的执行逻辑不应该太隐晦
  5. js 的内容不应该阻止爬虫
上一篇
下一篇
Loading Disqus comments...
Table of Contents