Open ufologist opened 7 years ago
技术
在经历了很多的 Web 项目之后, 我们在不同的项目中尝试了不同的解决方案. 在尝试的过程中, 前端的技术也在不停的进化, 影响着我们的技术选型. 为了给以后的技术选型做参考, 我们总结出关于一般 Web 项目的演进历程, 也可以看做是前端技术架构的演进. 演进的过程中主要伴随着 从简单到复杂 从多页到单页 前后端的分离 前端模块化/组件化 前端工程化/构建
在经历了很多的 Web 项目之后, 我们在不同的项目中尝试了不同的解决方案.
在尝试的过程中, 前端的技术也在不停的进化, 影响着我们的技术选型.
为了给以后的技术选型做参考, 我们总结出关于一般 Web 项目的演进历程, 也可以看做是前端技术架构的演进.
演进的过程中主要伴随着
其实需求很简单, 就是点击发送验证码按钮后, 自动聚焦到 input 输入框, 弹出虚拟键盘, 让其处于待输入状态, 方便用户填写验证码 一般来说我们一句代码就搞定了: input.focus() 但测试后发现, input.focus() 只是让输入框好像是被点击了一下, 但没有出现可输入的光标, 而且虚拟键盘也没有弹出来, 用户无法进行输入. 用户只能切换到其他输入框, 再切回来才能进行输入 很诡异的问题, 猜测又是 iOS 限制了这个权限. 查找资料后, 果不其然 IOS下Input元素focus后无法唤起键盘 有这样一个API:keyboardDisplayRequiresUserAction 大意就是这个API默认为true,这种情况下需要用户主动去点击元素,这样才能唤起键盘,通过focus去模拟的话是不行的。如果API设为false.这个时候是可以通过focus去模拟,并唤起键盘的。 PS: 虽然 JS 无法让输入框主动获得焦点, 但却可以让输入框失去焦点! // iOS 下无法让输入框主动获得焦点 input.focus(); // 但可以让输入框失去焦点 input.blur();
其实需求很简单, 就是点击发送验证码按钮后, 自动聚焦到 input 输入框, 弹出虚拟键盘, 让其处于待输入状态, 方便用户填写验证码
一般来说我们一句代码就搞定了: input.focus()
input.focus()
但测试后发现, input.focus() 只是让输入框好像是被点击了一下, 但没有出现可输入的光标, 而且虚拟键盘也没有弹出来, 用户无法进行输入. 用户只能切换到其他输入框, 再切回来才能进行输入
很诡异的问题, 猜测又是 iOS 限制了这个权限.
查找资料后, 果不其然
IOS下Input元素focus后无法唤起键盘
有这样一个API:keyboardDisplayRequiresUserAction
keyboardDisplayRequiresUserAction
大意就是这个API默认为true,这种情况下需要用户主动去点击元素,这样才能唤起键盘,通过focus去模拟的话是不行的。如果API设为false.这个时候是可以通过focus去模拟,并唤起键盘的。
PS: 虽然 JS 无法让输入框主动获得焦点, 但却可以让输入框失去焦点!
// iOS 下无法让输入框主动获得焦点 input.focus(); // 但可以让输入框失去焦点 input.blur();
第一个故事:我不想要那么多服务器和带宽 通过配置Http Cache Expire来消减访问压力,提高用户体验 第二个故事:失效缓存是个技术活 版本发布后为什么浏览器一直在使用旧的缓存,而不是最新的版本? 通过版本化来强制失效本地的过期缓存 缓存前端工程中的资源时,需要考虑缓存有效期的问题. 因此在前端系统中,对引用的静态资源进行版本化管理。使之既可以通过Http缓存来提升用户体验,降低服务器压力;也可以方便用户即时获得更新后的资源。 可以通过在资源引用上增加形如:<…. src="###.js?v=$version$"> 的版本化方式,来强制浏览器更新缓存。 第三个故事:更精确的缓存管理和平滑升级 每次上线以后, 前端应用引用的资源版本升级,所有缓存失效导致服务器压力突然剧增 通过内容摘要命名文件来更精确的控制缓存以及实现非覆盖式的发布 用文件hash来命名静态资源文件,使之可以按照文件来控制缓存和部署 总结: 在这个问题域中,还需要囊括文件压缩、合并、打包、重命名、目录设置等问题 配置永不过期的本地缓存——节约带宽,提升用户体验 采用文件摘要作为缓存依据——更精确的缓存控制 采用CDN——降低用户请求资源时解析DNS的延迟 利用文件摘要作为文件名——实现非覆盖式的部署,降低down time
第一个故事:我不想要那么多服务器和带宽
通过配置Http Cache Expire来消减访问压力,提高用户体验
第二个故事:失效缓存是个技术活
版本发布后为什么浏览器一直在使用旧的缓存,而不是最新的版本?
通过版本化来强制失效本地的过期缓存
缓存前端工程中的资源时,需要考虑缓存有效期的问题. 因此在前端系统中,对引用的静态资源进行版本化管理。使之既可以通过Http缓存来提升用户体验,降低服务器压力;也可以方便用户即时获得更新后的资源。
可以通过在资源引用上增加形如:<…. src="###.js?v=$version$"> 的版本化方式,来强制浏览器更新缓存。
<…. src="###.js?v=$version$">
第三个故事:更精确的缓存管理和平滑升级
每次上线以后, 前端应用引用的资源版本升级,所有缓存失效导致服务器压力突然剧增
通过内容摘要命名文件来更精确的控制缓存以及实现非覆盖式的发布
用文件hash来命名静态资源文件,使之可以按照文件来控制缓存和部署
总结: 在这个问题域中,还需要囊括文件压缩、合并、打包、重命名、目录设置等问题
大型Web应用对速度的追求并没有止步于仅仅利用浏览器缓存,因为浏览器缓存始终只是为了提升二次访问的速度,对于首次访问的加速,我们需要从网络层面进行优化,最常见的手段就是CDN(Content Delivery Network,内容分发网络)加速。通过将静态资源缓存到离用户很近的相同网络运营商的CDN节点上,不但能提升用户的访问速度,还能节省服务器的带宽消耗,降低负载。 之所以不同地区的用户访问同一个域名却能得到不同CDN节点的IP地址,这要依赖于CDN服务商提供的智能域名解析服务,浏览器发起域名查询时,这种智能DNS服务会根据用户IP计算并返回离它最近的同网络CDN节点IP,引导浏览器与此节点建立连接以获取资源。 整个前端静态资源缓存技术所带来的连锁性工程问题 整个静态资源缓存技术的最终影响的节点是前端静态资源定位问题,而且前端资源定位又会进一步影响到开发,包括代码中的模块化加载、各种资源加载等
大型Web应用对速度的追求并没有止步于仅仅利用浏览器缓存,因为浏览器缓存始终只是为了提升二次访问的速度,对于首次访问的加速,我们需要从网络层面进行优化,最常见的手段就是CDN(Content Delivery Network,内容分发网络)加速。通过将静态资源缓存到离用户很近的相同网络运营商的CDN节点上,不但能提升用户的访问速度,还能节省服务器的带宽消耗,降低负载。
之所以不同地区的用户访问同一个域名却能得到不同CDN节点的IP地址,这要依赖于CDN服务商提供的智能域名解析服务,浏览器发起域名查询时,这种智能DNS服务会根据用户IP计算并返回离它最近的同网络CDN节点IP,引导浏览器与此节点建立连接以获取资源。
整个前端静态资源缓存技术所带来的连锁性工程问题
整个静态资源缓存技术的最终影响的节点是前端静态资源定位问题,而且前端资源定位又会进一步影响到开发,包括代码中的模块化加载、各种资源加载等
SPA: 单页Web应用(single page web application,SPA)的概念 想象一下,假设网站原本需要两个页面a和b,但现在我只做一个index,然后把a和b两个页面的所有html片段都写到index里去,显示的时候,通过js来判断当前的url,如果是/a,我就只显示原本属于a的html片段;同理,如果是/b,我就显示b的html片段。 但是url的改变一般都是会引起页面跳转的,想要根据路由变化来展示不同的内容,同时页面又不跳转,这是怎么做到的? 在url中,#号之后的内容是不会引起页面跳转的,所以我们利用这一点来实现,当url是#/a的时候,显示a的html片段,同理#/b则显示b的html片段,这样一来,前端就有了自己的路由控制,可以来实现前端自己的"多页面". 这种方式我们称为前端路由。 presentational & container components react是基于组件(component)来构建页面的,从这个定义上来讲,其实页面只不过是一个更大的组件 ,它可以用来容纳和组织其他各个组件,从而编织出一个完整可用的页面。 容器组件(container component) 像页面这个所谓的大组件的目的不是被复用,而是负责为其他组件处理并注入数据,这样的组件我们称为容器组件。容器组件并不一定就是页面,它可以是各种粒度的组件组合. 展示组件(presentational component) 相对应的,一个纯粹接收外部数据(不作处理)并只负责展示的组件,我们称为展示组件. 展示组件只负责展示而不对数据做任何处理,因此它更容易被各种业务场景集成和复用 当只有逻辑改变的时候,我们应该尽量不要牵涉到界面;同理,当只有界面改变的时候,我们也不该去影响逻辑。但是当组件集成了这两者的时候,我们就很难避免这类情况发生,比如不经意间就在jsx里写计算和装饰的逻辑了。 我们需要把逻辑和视图分开,写成两个部分,一部分负责逻辑计算(容器组件),一部分负责展示(展示组件),最后,逻辑计算部分会引入展示部分,并且将展示部分需要的参数注入 我们在设计页面的时候,应该尽量避免深层次的嵌套关系,尽量保持扁平。因此深层次的嵌套可能会造成通讯的困难.当然,这种问题也是有解决方法的,比如可以使用一个 event bus(事件总线) 来实现;另外,也可以借助redux来实现,这是后话了。 SPA带来的问题以及解决方案探索 SPA最终是把所有页面集合到了一个页面当中,那么,就css而言,我们如何做到页面Index与页面About的样式不会互相干扰?首页体积会不会变得很大?首屏的加载时间是不是会变得很长?SEO怎么做? 全局污染(css规划) 通过命名空间的方式来隔离这些样式,使得各个页面之间不会互相造成影响 SEO 通过后端模板引擎在首页做静态内容输出,其他页面都按照原来的方式走api;比较复杂的,可以考虑使用同构应用,让js构建的页面也可以在后端完成渲染,最后输出静态页面。但是以上方案实现起来都比较麻烦,特别是后者,对开发者能力要求比较高。 体积过大,加载缓慢 webpack code splitting 实现路由按需加载
SPA: 单页Web应用(single page web application,SPA)的概念
想象一下,假设网站原本需要两个页面a和b,但现在我只做一个index,然后把a和b两个页面的所有html片段都写到index里去,显示的时候,通过js来判断当前的url,如果是/a,我就只显示原本属于a的html片段;同理,如果是/b,我就显示b的html片段。
但是url的改变一般都是会引起页面跳转的,想要根据路由变化来展示不同的内容,同时页面又不跳转,这是怎么做到的?
在url中,#号之后的内容是不会引起页面跳转的,所以我们利用这一点来实现,当url是#/a的时候,显示a的html片段,同理#/b则显示b的html片段,这样一来,前端就有了自己的路由控制,可以来实现前端自己的"多页面". 这种方式我们称为前端路由。
presentational & container components
react是基于组件(component)来构建页面的,从这个定义上来讲,其实页面只不过是一个更大的组件 ,它可以用来容纳和组织其他各个组件,从而编织出一个完整可用的页面。
容器组件(container component)
像页面这个所谓的大组件的目的不是被复用,而是负责为其他组件处理并注入数据,这样的组件我们称为容器组件。容器组件并不一定就是页面,它可以是各种粒度的组件组合.
展示组件(presentational component)
相对应的,一个纯粹接收外部数据(不作处理)并只负责展示的组件,我们称为展示组件. 展示组件只负责展示而不对数据做任何处理,因此它更容易被各种业务场景集成和复用
当只有逻辑改变的时候,我们应该尽量不要牵涉到界面;同理,当只有界面改变的时候,我们也不该去影响逻辑。但是当组件集成了这两者的时候,我们就很难避免这类情况发生,比如不经意间就在jsx里写计算和装饰的逻辑了。
我们需要把逻辑和视图分开,写成两个部分,一部分负责逻辑计算(容器组件),一部分负责展示(展示组件),最后,逻辑计算部分会引入展示部分,并且将展示部分需要的参数注入
我们在设计页面的时候,应该尽量避免深层次的嵌套关系,尽量保持扁平。因此深层次的嵌套可能会造成通讯的困难.当然,这种问题也是有解决方法的,比如可以使用一个 event bus(事件总线) 来实现;另外,也可以借助redux来实现,这是后话了。
SPA带来的问题以及解决方案探索
SPA最终是把所有页面集合到了一个页面当中,那么,就css而言,我们如何做到页面Index与页面About的样式不会互相干扰?首页体积会不会变得很大?首屏的加载时间是不是会变得很长?SEO怎么做?
全局污染(css规划)
通过命名空间的方式来隔离这些样式,使得各个页面之间不会互相造成影响
SEO
通过后端模板引擎在首页做静态内容输出,其他页面都按照原来的方式走api;比较复杂的,可以考虑使用同构应用,让js构建的页面也可以在后端完成渲染,最后输出静态页面。但是以上方案实现起来都比较麻烦,特别是后者,对开发者能力要求比较高。
体积过大,加载缓慢
webpack code splitting 实现路由按需加载
在web开发领域内,我认为可以将所有的技术分成三大类,它们可以分别称为后端,前端,工具(同时分成前端工具,后端工具,其它工具) 在服务器端运行的技术统称是后端技术,服务器端即后端 在浏览器端运行下的技术是前端技术,流览器端即前端 工具是工具,辅助前后端。工具是可以前,可以后,也可以什么都不是。工具具有其灵活性 HTML不是区分前后端的界线 早期的动态网页的工作主要是运行于服务器端的,并且生成html与业务都在后端 之后的前端技术不断发展,不但可以完全处理html,css这样的展现,并且可以处理很大一部分的后端业务,以后的前端开发将不需要将html放在服务器端处理 随着web技术的广泛应用,桌面技术与移动应用更多的采用html技术作为ui界面 所以前端与后端不是以你的技术区分的,而是根据你的运行环境来区分的。运行环境是唯一的前后端分界线
在web开发领域内,我认为可以将所有的技术分成三大类,它们可以分别称为后端,前端,工具(同时分成前端工具,后端工具,其它工具)
HTML不是区分前后端的界线
所以前端与后端不是以你的技术区分的,而是根据你的运行环境来区分的。运行环境是唯一的前后端分界线
反向代理 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已 负载均衡 简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡 HTTP服务器(包含动静分离) 正向代理 正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理
反向代理
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已
负载均衡
简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡
正向代理
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理
社工好可怕, 上网需谨慎...
产品
阅读《软技能》一书的笔记和随想 职业篇 你所能犯的最大错误就是相信自己是在为别人工作。这样一来你对工作的安全感依然尽失。职业发展的驱动力一定是来自个体本身。记住:工作是属于公司的,而职业生涯却是属于你自己的 自我营销篇 如果想让别人喜欢你,想和你一起工作,你必须要为他们提供价值。自我营销无非就是学习如何控制好自己要传达的信息,塑造好自己的形象,扩展信息送达的人群 学习篇 十步学习法 了解全局 确定范围 定义目标 寻找资源 创建学习计划 筛选资源 开始学习, 浅尝辄止 动手操作, 边玩边学 全面掌握, 学以致用 乐为人师, 融会贯通 发现自己的知识短板:消除短板的关键就是定位短板,然后通过十步学习法用心掌握它 生产力篇 理财篇 健身篇 精神篇
阅读《软技能》一书的笔记和随想
职业篇
你所能犯的最大错误就是相信自己是在为别人工作。这样一来你对工作的安全感依然尽失。职业发展的驱动力一定是来自个体本身。记住:工作是属于公司的,而职业生涯却是属于你自己的
自我营销篇
如果想让别人喜欢你,想和你一起工作,你必须要为他们提供价值。自我营销无非就是学习如何控制好自己要传达的信息,塑造好自己的形象,扩展信息送达的人群
学习篇
十步学习法
发现自己的知识短板:消除短板的关键就是定位短板,然后通过十步学习法用心掌握它
技术
产品