肇庆企业网站关键词优化教程,如何在网站上做社交的链接,h5做网站,做网站公司佛山目录
一、移动端跨平台开发方案
Hybrid App
React Native
Weex
Flutter
PWA #xff08;Progressive Web App#xff09;
小程序
Cordova
html5
组件和模块的区别
组件化
模块化
前端代码规范
前端工程化理解
网站性能监测与优化策略
1.网络传输性能优化 页…目录
一、移动端跨平台开发方案
Hybrid App
React Native
Weex
Flutter
PWA Progressive Web App
小程序
Cordova
html5
组件和模块的区别
组件化
模块化
前端代码规范
前端工程化理解
网站性能监测与优化策略
1.网络传输性能优化 页面加载时大致可以分为以下几个步骤
1.1.浏览器缓存
1.2.资源打包压缩
1.3.图片资源优化
1.4.网络传输性能检测工具——Page Speed
1.5.使用CDN
2.页面渲染性能优化
2.1.浏览器渲染过程Webkit
浏览器渲染的流程如下
2.2.DOM渲染层与GPU硬件加速
2.3.重排与重绘
常见引起重排属性和方法
浏览器的渲染队列
强制刷新队列:
重排优化建议
1. 分离读写操作
2. 样式集中改变
3. 缓存布局信息
4. 离线改变dom
5. position属性为absolute或fixed
6. 优化动画
7 . visibility: hidden 减小重绘的压力
8.压缩DOM的深度
9.图片在渲染前指定大小
3.JS阻塞性能
js方面的性能优化小建议
4.【拓展】负载均衡
4.2.pm2实现Node.js“多进程”
4.3.nginx搭建反向代理
RESTful架构
资源Resources
表现层Representation
状态转化State Transfer
综述
误区 一、移动端跨平台开发方案
Hybrid App
我们可以把纯前端技术开发的 Web 网页称为 Web App纯原生技术开发的应用称为 Native App它们各有优缺点纯 Native 的迭代太慢不能动态更新且不能跨平台而纯 Web 则有很多功能无法实现虽然其动画效果体验差强人意但跟原生比还是有些差距。
Hybrid App混合应用是指介于这两者之间的 App兼具“Native App 良好用户交互体验的优势”和“Web App 跨平台开发的优势”。
Hybrid 的技术本质是在 WebView 的基础上与原生客户端建立 JS Bridge 桥接以达到 JS 调用 Native API 和 Native 执行 JS 方法的目的。
React Native
React Native 官网: facebook.github.io/react-nativ…React Native 中文网: reactnative.cn/GitHub 上有个开源库 awesome-react-native收集了大量 React Native 的开发资源值得关注: github.com/jondot/awes…
Weex
Weex 官网: weex.incubator.apache.org/Weex 中文网: weex.incubator.apache.org/cn/GitHub 上的 Weex 资源收集项目 awesome-weex: github.com/joggerplus/…
Flutter
Flutter 官网: flutter.io/Flutter 中文网: flutterchina.club/GitHub 上的 awesome-flutter 汇集了大量的 Flutter 资源: github.com/Solido/awes…此外知识小集团队在 GitHub 上开了一个 repo用于收集 Flutter 国内相关开发资源欢迎关注并和我们一起完善github.com/awesome-tip…
PWA Progressive Web App
PWA 官网: developers.google.com/web/progres…GitHub 上的 awesome-pwa: github.com/hemanth/awe…同样地我们也在 GitHub 上开了一个 repo用于收集 PWA 国内相关的开发资源欢迎关注并和我们一起完善: github.com/awesome-tip…参考https://juejin.im/post/5b076e3af265da0dce48fe95
小程序
小程序开发本质上还是前端 HTML CSS JS 那一套逻辑它基于 WebView 和微信自己定义的一套 JS/WXML/WXSS/JSON 来开发和渲染页面。微信官方文档里提到小程序运行在三端iOS、Android 和用于调试的开发者工具三端的脚本执行环境以及用于渲染非原生组件的环境是各不相同的
在 iOS 上小程序的 JavaScript 代码是运行在 JavaScriptCore 中是由 WKWebView 来渲染的环境有 iOS 8在 Android 上小程序的 JavaScript 代码是通过 X5 JSCore 来解析是由 X5 基于 Mobile Chrome 53/57 内核来渲染的在 开发工具上 小程序的 JavaScript 代码是运行在 nwjs 中是由 Chrome Webview 来渲染的。
更多细节请查阅微信小程序官网
mp.weixin.qq.com/cgi-bin/wx
支付宝后来也上线了自己的小程序平台
open.alipay.com/channel/min…
因为微信或支付宝都可以在 Android 和 iOS 上运行所以某种意义上我们也可以把小程序理解为是一种跨平台开发。
Cordova
Cardova 官网: cordova.apache.org/Cardova 中文网: cordova.axuer.com/
html5
HTML5 是定义 HTML 标准的最新的版本。 该术语通过两个不同的概念来表现
它是一个新版本的HTML语言具有新的元素属性和行为它有更大的技术集允许构建更多样化和更强大的网站和应用程序。这个集合有时称为HTML5和它的朋友们不过大多数时候仅缩写为一个词 HTML5。
根据其功能分为几个组
语义能够让你更恰当地描述你的内容是什么。连通性能够让你和服务器之间通过创新的新技术方法进行通信。离线 存储能够让网页在客户端本地存储数据以及更高效地离线运行。多媒体使 video 和 audio 成为了在所有 Web 中的一等公民。2D/3D 绘图 效果提供了一个更加分化范围的呈现选择。性能 集成提供了非常显著的性能优化和更有效的计算机硬件使用。设备访问 Device Access能够处理各种输入和输出设备。样式设计: 让作者们来创作更加复杂的主题吧
参考https://developer.mozilla.org/zh-CN/docs/Web/Guide/HTML/HTML5
组件和模块的区别
组件化
含义就是基础库或者“基础组件意思是把代码重复的部分提炼出一个个组件供给功能使用。
使用Dialog各种自定义的UI控件、能在项目或者不同项目重复应用的代码等等。
目的复用解耦。
依赖组件之间低依赖比较独立。
架构定位纵向分层位于架构底层被其他层所依赖。
总结组件相当于库把一些能在项目里或者不同类型项目中可复用的代码进行工具性的封装。
模块化
含义就是业务框架或者“业务模块也可以理解为“框架”意思是把功能进行划分将同一类型的代码整合在一起所以模块的功能相对复杂但都同属于一个业务。
使用按照项目功能需求划分成不同类型的业务框架例如注册、登录、外卖、直播.....
目的隔离/封装 高内聚。
依赖模块之间有依赖的关系可通过路由器进行模块之间的耦合问题。
架构定位横向分块位于架构业务框架层。
总结模块相应于业务逻辑模块把同一类型项目里的功能逻辑进行进行需求性的封装。
参考https://www.jianshu.com/p/cac0beae8876
前端代码规范
参考https://guide.aotu.io/docs/html/code.html
CommonJs与ES6的模块加载机制
目前阶段通过 Babel 转码CommonJS 模块的require命令和 ES6 模块的import命令可以写在同一个模块里面但是最好不要这样做。因为import在静态解析阶段执行所以它是一个模块之中最早执行的。
ES6模块
ES6 的模块自动采用严格模式不管你有没有在模块头部加上use strict;。
严格模式主要有以下限制。
变量必须声明后再使用函数的参数不能有同名属性否则报错不能使用with语句不能对只读属性赋值否则报错不能使用前缀 0 表示八进制数否则报错不能删除不可删除的属性否则报错不能删除变量delete prop会报错只能删除属性delete global[prop]eval不会在它的外层作用域引入变量eval和arguments不能被重新赋值arguments不会自动反映函数参数的变化不能使用arguments.callee不能使用arguments.caller禁止this指向全局对象不能使用fn.caller和fn.arguments获取函数调用的堆栈增加了保留字比如protected、static和interface
浏览器加载 ES6 模块也使用script标签但是要加入typemodule属性。
script typemodule src./foo.js/script上面代码在网页中插入一个模块foo.js由于type属性设为module所以浏览器知道这是一个 ES6 模块。
浏览器对于带有typemodule的script都是异步加载不会造成堵塞浏览器即等到整个页面渲染完再执行模块脚本等同于打开了script标签的defer属性。
script typemodule src./foo.js/script
!-- 等同于 --
script typemodule src./foo.js defer/script如果网页有多个script typemodule它们会按照在页面出现的顺序依次执行。
对于外部的模块脚本上例是foo.js有几点需要注意。
代码是在模块作用域之中运行而不是在全局作用域运行。模块内部的顶层变量外部不可见。模块脚本自动采用严格模式不管有没有声明use strict。模块之中可以使用import命令加载其他模块.js后缀不可省略需要提供绝对 URL 或相对 URL也可以使用export命令输出对外接口。模块之中顶层的this关键字返回undefined而不是指向window。也就是说在模块顶层使用this关键字是无意义的。同一个模块如果加载多次将只执行一次。ES6 模块与 CommonJS 模块的差异
讨论 Node.js 加载 ES6 模块之前必须了解 ES6 模块与 CommonJS 模块完全不同。
它们有两个重大差异。
CommonJS 模块输出的是一个值的拷贝ES6 模块输出的是值的引用。CommonJS 模块是运行时加载ES6 模块是编译时输出接口。
第二个差异是因为 CommonJS 加载的是一个对象即module.exports属性该对象只有在脚本运行完才会生成。而 ES6 模块不是对象它的对外接口只是一种静态定义在代码静态解析阶段就会生成。
下面重点解释第一个差异。
CommonJS 模块输出的是值的拷贝也就是说一旦输出一个值模块内部的变化就影响不到这个值。
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候遇到模块加载命令import就会生成一个只读引用。等到脚本真正执行时再根据这个只读引用到被加载的那个模块里面去取值。换句话说ES6 的import有点像 Unix 系统的“符号连接”原始值变了import加载的值也会跟着变。因此ES6 模块是动态引用并且不会缓存值模块里面的变量绑定其所在的模块。
ES6 模块不会缓存运行结果而是动态地去被加载的模块取值并且变量总是绑定其所在的模块。
不同的脚本加载相同模块得到的都是同一个实例。
Node.js 加载
Node.js 对 ES6 模块的处理比较麻烦因为它有自己的 CommonJS 模块格式与 ES6 模块格式是不兼容的。目前的解决方案是将两者分开ES6 模块和 CommonJS 采用各自的加载方案。从 v13.2 版本开始Node.js 已经默认打开了 ES6 模块支持。
Node.js 要求 ES6 模块采用.mjs后缀文件名。也就是说只要脚本文件里面使用import或者export命令那么就必须采用.mjs后缀名。Node.js 遇到.mjs文件就认为它是 ES6 模块默认启用严格模式不必在每个模块文件顶部指定use strict。
如果不希望将后缀名改成.mjs可以在项目的package.json文件中指定type字段为module。
{type: module
}
一旦设置了以后该目录里面的 JS 脚本就被解释用 ES6 模块。
# 解释成 ES6 模块
$ node my-app.js
如果这时还要使用 CommonJS 模块那么需要将 CommonJS 脚本的后缀名都改成.cjs。如果没有type字段或者type字段为commonjs则.js脚本会被解释成 CommonJS 模块。
参考https://es6.ruanyifeng.com/#docs/module-loader 前端工程化理解
前端工程化本质上就是将前端开发流程标准化、规范化、工具化、自动化、简单化。其主要目的为了提高效率和降低成本即提高开发过程中的开发效率减少不必要的重复工作时间。提高编码、测试、维护阶段的生产效率。让前端开发自成体系 参考https://juejin.im/post/5e999cecf265da47cd357a24
网站性能监测与优化策略
从性能优化的三大方面工作逐步展开介绍其中包括网络传输性能、页面渲染性能以及JS阻塞性能系统性地带着读者们体验性能优化的实践流程。
1.网络传输性能优化 Resource Timing API提供了有关每个资产接收时间的详细信息。请求生命周期的主要阶段是
重新导向 立即开始startTime。如果发生重定向则也将redirectStart开始。如果在此阶段结束时发生重定向redirectEnd则将采取重定向。应用缓存观察网页加载顺序第一个都是document文件先加载之后查看document里需要哪些文件如果缓存有则不再重复请求所以是否可将“应用缓存”放在“处理响应文件”那一步 如果应用缓存满足了请求fetchStart则将花费一些时间。域名解析 domainLookupStart 时间是在DNS请求开始时开始的。domainLookupEnd 时间是在DNS请求结束时开始的。TCP协议 connectStart 最初连接服务器时采用。如果使用TLS或SSLsecureConnectionStart则将在握手开始以确保连接安全时开始。connectEnd 与服务器的连接完成后进行。请求 requestStart 对资源的请求已发送到服务器后将采用此命令。响应 responseStart 是服务器最初响应请求的时间。responseEnd 是请求结束并检索数据的时间。处理响应文件 domLoading 开始解析渲染 DOM 树的时间此时 Document.readyState 变为 loading并将抛出 readystatechange 相关事件domInteractive 表示浏览器已经解析好dom树了当前网页DOM结构结束解析、开始加载内嵌资源domContentLoadedEventStart 开始触发DomContentLoaded事件,DOM 解析完成后网页内资源加载开始的时间domContentLoadedEnd DomContentLoaded事件结束,DOM 解析完成后网页内资源加载完成的时间如 JS 脚本加载执行完毕domComplete DOM 树解析渲染完成且资源也准备就绪的时间包括图像、脚本文件、CSS 文件等Document.readyState 变为 complete并将抛出 readystatechange 相关事件loadEventStart load 事件发送给文档也即 load 回调函数开始执行的时间loadEventEnd load 事件的回调函数执行完毕的时间
参考http://www.alloyteam.com/2015/09/explore-performance/
这是navigation timing监测指标图从图中我们可以看出浏览器在得到用户请求之后经历了下面这些阶段重定向→拉取缓存→DNS查询→建立TCP链接→发起请求→接收响应→处理HTML元素→元素加载完成。
例如发送一个请求会经过如下
排队
排队的请求表明
该请求被渲染引擎推迟了因为它被认为比关键资源例如脚本/样式的优先级低。这通常发生在图像上。该请求被暂停以等待将要释放的不可用的TCP套接字。由于浏览器在HTTP 1上每个源仅允许六个TCP连接因此该请求被暂停。花在制作磁盘缓存条目上的时间通常非常快。堵转/堵转
请求等待发送之前花费的时间。它可能正在等待队列中描述的任何原因。此外此时间包括在代理协商中花费的任何时间。 代理协商
与代理服务器连接进行协商所花费的时间。
DNS查询
执行DNS查找所花费的时间。页面上的每个新域都需要完整的往返来进行DNS查找。 初始连接/连接
建立连接所花费的时间包括TCP握手/重试和协商SSL。 SSL协议
完成SSL握手所花费的时间。 请求发送/发送
发出网络请求所花费的时间。通常为一毫秒的时间。
等待中TTFB
等待初始响应所花费的时间也称为第一个字节的时间。除了等待服务器传递响应所花费的时间之外该时间还捕获了到服务器的往返延迟。 内容下载/下载
接收响应数据所花费的时间。
参考https://developers.google.cn/web/tools/chrome-devtools/network/understanding-resource-timing 页面加载时大致可以分为以下几个步骤 开始解析HTML文档结构 加载外部样式表及JavaScript脚本 解析执行JavaScript脚本 DOM树渲染完成 加载未完成的外部资源如 图片 页面加载成功
1.1.浏览器缓存
我们都知道浏览器在向服务器发起请求前会先查询本地是否有相同的文件如果有就会直接拉取本地缓存这和我们在后台部属的Redis、Memcache类似都是起到了中间缓冲的作用我们先看看浏览器处理缓存的策略 浏览器默认的缓存是放在内存内的但我们知道内存里的缓存会因为进程的结束或者说浏览器的关闭而被清除而存在硬盘里的缓存才能够被长期保留下去。很多时候我们在network面板中各请求的size项里会看到两种不同的状态from memory cache 和 from disk cache前者指缓存来自内存后者指缓存来自硬盘。而控制缓存存放位置的不是别人就是我们在服务器上设置的Etag字段。在浏览器接收到服务器响应后会检测响应头部Header如果有Etag字段那么浏览器就会将本次缓存写入硬盘中。
之所以拉取缓存会出现200、304两种不同的状态码取决于浏览器是否有向服务器发起验证请求。 只有向服务器发起验证请求并确认缓存未被更新才会返回304状态码。
【特别注意】在我们配置缓存时一定要切记浏览器在处理用户请求时如果命中强缓存浏览器会直接拉取本地缓存不会与服务器发生任何通信也就是说如果我们在服务器端更新了文件并不会被浏览器得知就无法替换失效的缓存。所以我们在构建阶段需要为我们的静态资源添加md5 hash后缀避免资源更新而引起的前后端文件无法同步的问题。
1.2.资源打包压缩
我们之前所作的浏览器缓存工作只有在用户第二次访问我们的页面才能起到效果如果要在用户首次打开页面就实现优良的性能必须对资源进行优化。我们常将网络性能优化措施归结为三大方面减少请求数、减小请求资源体积、提升网络传输速率。现在让我们逐个击破
结合前端工程化思想我们在对上线文件进行自动化打包编译时通常都需要打包工具的协助这里我推荐webpack我通常都使用Gulp和Grunt来编译node
在对webpack进行上线配置时我们要特别注意以下几点
①JS压缩这点应该算是耳熟能详了就不多介绍了
②HTML压缩
③提取公共资源
④提取css并压缩
⑤使用webpack3的新特性ModuleConcatenationPlugin
⑥在服务器上开启Gzip传输压缩它能将我们的文本类文件体积压缩至原先的四分之一效果立竿见影
如果你在网站请求的响应头里看到这样的字段那么就说明咱们的Gzip压缩配置成功啦 【特别注意】不要对图片文件进行Gzip压缩不要对图片文件进行Gzip压缩不要对图片文件进行Gzip压缩我只会告诉你效果适得其反至于具体原因还得考虑服务器压缩过程中的CPU占用还有压缩率等指标对图片进行压缩不但会占用后台大量资源压缩效果其实并不可观可以说是“弊大于利”所以请在gzip_types 把图片的相关项去掉。针对图片的相关处理我们接下来会更加具体地介绍。
1.3.图片资源优化
刚刚我们介绍了资源打包压缩只是停留在了代码层面而在我们实际开发中真正占用了大量网络传输资源的并不是这些文件而是图片如果你对图片进行了优化工作你能立刻看见明显的效果。
1.3.1.不要在HTML里缩放图像
需要用多大的图片时就在服务器上准备好多大的图片尽量固定图片尺寸。
1.3.2.使用雪碧图CSS Sprite
这里给大家推荐一个自动化生成雪碧图的工具www.toptal.com/developers/…
1.3.3.使用字体图标iconfont
阿里矢量图标库网址www.iconfont.cn/ 图片能做的很多事情矢量图都能作而且它只是往HTML里插入字符和CSS样式而已和图片请求比起来在网络传输资源的占用上它们完全不在一个数量级如果你的项目里有大量的小图标就用矢量图吧。
1.3.4.使用WebP
WebP格式是谷歌公司开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3并能节省大量的服务器带宽资源和数据空间。Facebook、Ebay等知名网站已经开始测试并使用WebP格式。
我们可以使用官网提供的Linux命令行工具对项目中的图片进行WebP编码也可以使用我们的线上服务这里我推荐叉拍云网址www.upyun.com/webp 。但是在实际的上线工作中我们还是得编写Shell脚本使用命令行工具进行批量编码不过测试阶段我们用线上服务就足够了方便快捷。
1.4.网络传输性能检测工具——Page Speed
谷歌监测网络传输性能的插件——PageSpeed Insights for Chrome
Page Speed最人性化的地方便是它会对测试网站的性能瓶颈提出完整的建议我们可以根据它的提示进行优化工作。这里我的网站已经优化到最好指标了(•́⌄•́๑)૭✧Page Speed Score表示你的性能测试得分100/100表示已经没有需要优化的地方。
1.5.使用CDN
再好的性能优化实例也必须在CDN的支撑下才能到达极致。
当然凭着我们单个人的资金实力除非你是王思聪是必定搭建不起来CDN的不过我们可以使用各大企业提供的服务诸如腾讯云等配置也十分简单这里就请大家自行去推敲啦。
其实我们的CDN域名一般是和我们的网站主域名不同的大家可以看看淘宝、腾讯的官方网站看看他们存放静态资源的CDN域名都是和主域名不一样的。为什么要这么做主要有两个原因[内容摘选自bbs.aliyun.com/simple/t116… ]
①便于CDN业务独立能够独立配置缓存。为了降低web压力CDN系统会遵循Cache-Control和Expires HTTP头标准对改请求返回的内容进行缓存便于后面的请求不在回源起到加速功能。而传统CDNWeb与CDN共用域名的方式需要对不同类型的文件设置相应的Cache规则或者遵循后端的HTTP头但这样难以发挥CDN的最大优势因为动态请求回源的概率非常之大如果访客与源站的线路并不慢通过CDN的请求未必快于直接请求源站的。 大型网站为了提升web性能到极致通常缓存头设置比较大像谷歌JS设置一年缓存百度首页logo设置十年缓存如果将静态元素抽取出来就可以很方便的对所有静态元素部署规则而不用考虑动态请求。减少规则的条数可以提升CDN的效率。
②抛开无用cookie减小带宽占用。我们都知道HTTP协议每次发送请求都会自动带上该域名及父级域名下的cookie但对于CSSJS还有图片资源这些cookie是没用的反而会浪费访客带宽和服务器入带宽。而我们的主站为了保持会话或者做其他缓存都会存放着大量的cookie所以如果将CDN与主站域名分离就能解决这一问题。
不过这样一来新的问题就出现了CDN域名与主站域名不同DNS解析CDN域名还需要花费额外的时间增加网络延迟。不过这难不住我们伟大的程序员前辈DNS Prefetch闪亮登场。
如果大家翻看大型网站的HTML源代码都会在头部发现这样的link链接这里以淘宝首页为例 这就是DNS Prefetch。DNS Prefetch是一种DNS预解析技术当我们浏览网页时浏览器会在加载网页时对网页中的域名进行预解析并缓存这样在浏览器加载网页中的链接时就无需进行DNS解析减少用户的等待时间提高用户体验。DNS Prefetch现已被主流浏览器支持大多数浏览器针对DNS解析都进行了优化典型的一次DNS解析会耗费20~120ms减少DNS解析时间和次数是个很好的优化措施。
大公司的静态资源优化方案基本上要实现这么几个东西
配置超长时间的本地缓存 —— 节省带宽提高性能强制浏览器使用本地缓存cache-control/expires不要和服务器通信。采用内容摘要作为缓存更新依据 —— 精确的缓存控制静态资源CDN部署 —— 优化网络请求静态资源和动态网页分集群部署静态资源会被部署到CDN节点上更资源发布路径实现非覆盖式发布 —— 平滑升级
参考https://www.zhihu.com/question/20790576/answer/32602154
2.页面渲染性能优化
2.1.浏览器渲染过程Webkit 浏览器渲染的流程如下
HTML被HTML解析器解析成DOM 树css则被css解析器解析成CSSOM 树结合DOM树和CSSOM树生成一棵渲染树(Render Tree)生成布局flow即将所有渲染树的所有节点进行平面合成将布局绘制paint在屏幕上
第四步和第五步是最耗时的部分这两步合起来就是我们通常所说的渲染。
网页生成的时候至少会渲染一次。
在用户访问的过程中还会不断重新渲染
重新渲染需要重复之前的第四步(重新生成布局)第五步(重新绘制)或者只有第五个步(重新绘制)。
注意 css加载不会阻塞DOM树的解析 css加载会阻塞DOM树的渲染由于Render Tree是依赖于DOM Tree和CSSOM Tree的所以他必须等待到CSSOM Tree构建完成也就是CSS资源加载完成(或者CSS资源加载失败)后才能开始渲染。因此CSS加载是会阻塞Dom的渲染的。 css加载会阻塞后面js语句的执行 link和import的区别
结论
就结论而言强烈建议使用link标签慎用import方式。 这样可以避免考虑import的语法规则和注意事项避免产生资源文件下载顺序混乱和http请求过多的烦恼。
区别
1.从属关系区别import是 CSS 提供的语法规则只有导入样式表的作用link是HTML提供的标签不仅可以加载 CSS 文件还可以定义 RSS、rel 连接属性等。
2.加载顺序区别 加载页面时link标签引入的 CSS 被同时加载import引入的 CSS 将在页面加载完毕后被加载。
3.兼容性区别import是 CSS2.1 才有的语法故只可在 IE5 才能识别link标签作为 HTML 元素不存在兼容性问题。
4.DOM可控性区别 可以通过 JS 操作 DOM 插入link标签来改变样式由于 DOM 方法是基于文档的无法使用import的方式插入样式。
5.权重区别(该项有争议下文将详解)link引入的样式权重大于import引入的样式。
css优化建议:
1. 内联首屏关键CSSCritical CSS
Github上有一个项目Critical CSS4可以将属于首屏的关键样式提取出来大家可以看一下该项目结合自己的构建工具进行使用。当然为了保证正确大家最好再亲自确认下提取出的内容是否有缺失。
2. 文件压缩
相信大家都早已习惯对CSS进行压缩现在的构建工具如webpack、gulp/grunt、rollup等也都支持CSS压缩功能。压缩后的文件能够明显减小可以大大降低了浏览器的加载时间。
3. 去除无用的css
虽然文件压缩能够降低文件大小。但CSS文件压缩通常只会去除无用的空格这样就限制了CSS文件的压缩比例。那是否还有其他手段来精简CSS呢答案显然是肯定的如果压缩后的文件仍然超出了预期的大小我们可以试着找到并删除代码中无用的CSS。
一般情况下会存在这两种无用的CSS代码一种是不同元素或者其他情况下的重复代码一种是整个页面内没有生效的CSS代码。
当然如果手动删除这些无用CSS是很低效的。我们可以借助Uncss7库来进行。Uncss可以用来移除样式表中的无用CSS并且支持多文件和JavaScript注入的CSS。
4. 有选择地使用选择器
大多数朋友应该都知道CSS选择器的匹配是从右向左进行的这一策略导致了不同种类的选择器之间的性能也存在差异。相比于#markdown-content-h3显然使用#markdown .content h3时浏览器生成渲染树render-tree所要花费的时间更多。
我们在使用选择器时只需要记住以下几点其他的可以全凭喜好。
保持简单不要使用嵌套过多过于复杂的选择器。通配符和属性选择器效率最低需要匹配的元素最多尽量避免使用。不要使用类选择器和ID选择器修饰元素标签如h3#markdown-content这样多此一举还会降低效率。不要为了追求速度而放弃可读性与可维护性。
5. 减少使用昂贵的属性
在浏览器绘制屏幕时所有需要浏览器进行操作或计算的属性相对而言都需要花费更大的代价。当页面发生重绘时它们会降低浏览器的渲染性能。所以在编写CSS时我们应该尽量减少使用昂贵属性如box-shadow/border-radius/filter/透明度/:nth-child等。
6. 不要使用import
参考 https://juejin.im/post/5b6133a351882519d346853f 浏览器的解释器是包括在渲染引擎内的我们常说的Chrome现在使用的是Blink引擎和Safari使用的Webkit引擎Firefox使用的Gecko引擎指的就是渲染引擎。而在渲染引擎内还包括着我们的HTML解释器渲染时用于构造DOM树、CSS解释器渲染时用于合成CSS规则还有我们的JS解释器。不过后来由于JS的使用越来越重要工作越来越繁杂所以JS解释器也渐渐独立出来成为了单独的JS引擎就像众所周知的V8引擎我们经常接触的Node.js也是用的它。
2.2.DOM渲染层与GPU硬件加速 页面的真实样子就是这样是由多个DOM元素渲染层Layers组成的实际上一个页面在构建完Render Tree之后是经历了这样的流程才最终呈现在我们面前的
①浏览器会先获取DOM树并依据样式将其分割成多个独立的渲染层
②CPU将每个层绘制进绘图中
③将位图作为纹理上传至GPU显卡绘制
④GPU将所有的渲染层缓存如果下次上传的渲染层没有发生变化GPU就不需要对其进行重绘并复合多个渲染层最终形成我们的图像
从上面的步骤我们可以知道布局是由CPU处理的而绘制则是由GPU完成的。
把那些一直发生大量重排重绘的元素提取出来单独触发一个渲染层那样这个元素不就不会“连累”其他元素一块重绘了对吧。
那么问题来了什么情况下会触发渲染层呢大家只要记住
Video元素、WebGL、Canvas、CSS3 3D、CSS滤镜、z-index大于某个相邻节点的元素都会触发新的Layer其实我们最常用的方法就是给某个元素加上下面的样式
transform: translateZ(0);
backface-visibility: hidden;
这样就可以触发渲染层啦 。
我们把容易触发重排重绘的元素单独触发渲染层让它与那些“静态”元素隔离让GPU分担更多的渲染工作我们通常把这样的措施成为硬件加速或者是GPU加速。大家之前肯定听过这个说法现在完全清楚它的原理了吧。
2.3.重排与重绘
①重排reflow渲染层内的元素布局发生修改都会导致页面重新排列比如窗口的尺寸发生变化、删除或添加DOM元素修改了影响元素盒子大小的CSS属性诸如width、height、padding。
②重绘repaint绘制即渲染上色所有对元素的视觉表现属性的修改都会引发重绘。
不论是重排还是重绘都会阻塞浏览器。要提高网页性能就要降低重排和重绘的频率和成本近可能少地触发重新渲染。正如我们在2.3中提到的重排是由CPU处理的而重绘是由GPU处理的CPU的处理效率远不及GPU并且重排一定会引发重绘而重绘不一定会引发重排。所以在性能优化工作中我们更应当着重减少重排的发生。
常见引起重排属性和方法
任何会改变元素几何信息(元素的位置和尺寸大小)的操作都会触发重排下面列一些栗子
添加或者删除可见的DOM元素元素尺寸改变——边距、填充、边框、宽度和高度内容变化比如用户在input框中输入文字浏览器窗口尺寸改变——resize事件发生时计算 offsetWidth 和 offsetHeight 属性设置 style 属性的值浏览器的渲染队列
思考以下代码将会触发几次渲染
div.style.left 10px;
div.style.top 10px;
div.style.width 20px;
div.style.height 20px;根据我们上文的定义这段代码理论上会触发4次重排重绘因为每一次都改变了元素的几何属性实际上最后只触发了一次重排这都得益于浏览器的渲染队列机制
当我们修改了元素的几何属性导致浏览器触发重排或重绘时。它会把该操作放进渲染队列等到队列中的操作到了一定的数量或者到了一定的时间间隔时浏览器就会批量执行这些操作。
强制刷新队列:
div.style.left 10px;
console.log(div.offsetLeft);
div.style.top 10px;
console.log(div.offsetTop);
div.style.width 20px;
console.log(div.offsetWidth);
div.style.height 20px;
console.log(div.offsetHeight);这段代码会触发4次重排重绘因为在console中你请求的这几个样式信息无论何时浏览器都会立即执行渲染队列的任务即使该值与你操作中修改的值没关联。
因为队列中可能会有影响到这些值的操作为了给我们最精确的值浏览器会立即重排重绘。
强制刷新队列的style样式请求
offsetTop, offsetLeft, offsetWidth, offsetHeightscrollTop, scrollLeft, scrollWidth, scrollHeightclientTop, clientLeft, clientWidth, clientHeightgetComputedStyle(), 或者 IE的 currentStyle
我们在开发中应该谨慎的使用这些style请求注意上下文关系,避免一行代码一个重排这对性能是个巨大的消耗
重排优化建议
就像上文提到的我们要尽可能的减少重排次数、重排范围这样说很泛下面是一些行之有效的建议大家可以参考一下。
1. 分离读写操作
div.style.left 10px;
div.style.top 10px;
div.style.width 20px;
div.style.height 20px;
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);还是上面触发4次重排重绘的代码这次只触发了一次重排
在第一个console的时候浏览器把之前上面四个写操作的渲染队列都给清空了。剩下的console因为渲染队列本来就是空的所以并没有触发重排仅仅拿值而已。最最最客观的解决方案就是不用JS去操作元素样式这也是我最推荐的。
2. 样式集中改变
div.style.left 10px;
div.style.top 10px;
div.style.width 20px;
div.style.height 20px;虽然现在大部分浏览器有渲染队列优化不排除有些浏览器以及老版本的浏览器效率仍然低下
建议通过改变class或者csstext属性集中改变样式
// bad
var left 10;
var top 10;
el.style.left left px;
el.style.top top px;
// good
el.className theclassname;
// good
el.style.cssText ; left: left px; top: top px;;
3. 缓存布局信息
// bad 强制刷新 触发两次重排
div.style.left div.offsetLeft 1 px;
div.style.top div.offsetTop 1 px;// good 缓存布局信息 相当于读写分离
var curLeft div.offsetLeft;
var curTop div.offsetTop;
div.style.left curLeft 1 px;
div.style.top curTop 1 px;4. 离线改变dom 隐藏要操作的dom 在要操作dom之前通过display隐藏dom当操作完成之后才将元素的display属性为可见因为不可见的元素不会触发重排和重绘。 dom.display none
// 修改dom样式
dom.display block通过使用DocumentFragment创建一个dom碎片,在它上面批量操作dom操作完成之后再添加到文档中这样只会触发一次重排。 复制节点在副本上工作然后替换它
5. position属性为absolute或fixed
position属性为absolute或fixed的元素重排开销比较小不用考虑它对其他元素的影响
6. 优化动画 可以把动画效果应用到position属性为absolute或fixed的元素上这样对其他元素影响较小 动画效果还应牺牲一些平滑来换取速度这中间的度自己衡量 比如实现一个动画以1个像素为单位移动这样最平滑但是reflow就会过于频繁大量消耗CPU资源如果以3个像素为单位移动则会好很多。 启用GPPU加速 此部分来自优化CSS重排重绘与浏览器性能 GPU(图像加速器) GPU 硬件加速是指应用 GPU 的图形性能对浏览器中的一些图形操作交给 GPU 来完成因为 GPU 是专门为处理图形而设计所以它在速度和能耗上更有效率。 GPU 加速通常包括以下几个部分Canvas2D布局合成, CSS3转换transitionsCSS3 3D变换transformsWebGL和视频(video)。 这项策略需要慎用得着重考量以牺牲GPU占用率为代价能否换来可期的性能优化毕竟页面中存在太多的渲染层对于GPU而言也是一种不必要的压力通常情况下我们会对动画元素采取硬件加速。 /** 根据上面的结论* 将 2d transform 换成 3d* 就可以强制开启 GPU 加速* 提高动画性能*/
div {transform: translate3d(10px, 10px, 0);
}
7 . visibility: hidden 减小重绘的压力
将没用的元素设为不可见visibility: hidden这样可以减小重绘的压力必要的时候再将元素显示。
8.压缩DOM的深度
一个渲染层内不要有过深的子元素少用DOM完成页面样式多使用伪元素或者box-shadow取代。
9.图片在渲染前指定大小
因为img元素是内联元素所以在加载图片后会改变宽高严重的情况会导致整个页面重排所以最好在渲染前就指定其大小或者让其脱离文档流。
参考https://juejin.im/post/5c15f797f265da61141c7f86
3.JS阻塞性能
脚本带来的问题就是他会阻塞页面的平行下载还会提高进程的CPU占用率。更有甚者现在node.js已经在前端开发中普及稍有不慎我们引发了内存泄漏或者在代码中误写了死循环会直接造成我们的服务器崩溃。
在编程的过程中如果我们使用了闭包后未将相关资源加以释放或者引用了外链后未将其置空比如给某DOM元素绑定了事件回调后来却remove了该元素都会造成内存泄漏的情况发生进而大量占用用户的CPU造成卡顿或死机。我们可以使用chrome提供的JavaScript Profile版块开启方式同Layers等版块
其实浏览器强大的内存回收机制在大多数时候避免了这一情况的发生即便用户发生了死机他只要结束相关进程或关闭浏览器就可以解决这一问题但我们要知道同样的情况还会发生在我们的服务器端也就是我们的node中严重的情况会直接造成我们的服务器宕机网站崩溃。所以更多时候我们都使用JavaScript Profile版块来对我们的node服务进行压力测试搭配node-inspector 插件我们能更有效地检测JS执行时各函数的CPU占用率针对性地进行优化。
PS所以没修炼到一定水平千万别在服务端使用闭包一个是真没啥用我们会有更多优良的解决办法二是真的很容易内存泄漏造成的后果是你无法预期的
js方面的性能优化小建议
①不要使用 with() 语句
和函数类似 with语句会创建自己的作用域因此会增加其中执行的代码的作用域链的长度由于额外的作用域链的查找在with语句中执行的代码肯定会比外面执行的代码要慢在能不使用with语句的时候尽量不要使用with语句。
②避免全局查找
在一个函数中会用到全局对象存储为局部变量来减少全局查找因为访问局部变量的速度要比访问全局变量的速度更快些
function search() {//当我要使用当前页面地址和主机域名alert(window.location.href window.location.host);}//最好的方式是如下这样 先用一个简单变量保存起来function search() {var location window.location;alert(location.href location.host);}
③通过模板元素clone替代createElement
如果文档中存在现成的样板节点应该是用cloneNode()方法因为使用createElement()方法之后你需要设置多次元素的属性使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元素应该先准备一个样板节点。
④在循环时将控制条件和控制变量合并起来
提到性能在循环中需要避免的工作一直是个热门话题因为循环会被重复执行很多次。所以如果有性能优化的需求先对循环开刀有可能会获得最明显的性能提升。
一种优化循环的方法是在定义循环的时候将控制条件和控制变量合并起来下面是一个没有将他们合并起来的例子
for ( var x 0; x 10; x ) {
};当我们要添加什么东西到这个循环之前我们发现有几个操作在每次迭代都会出现。JavaScript引擎需要
#1检查 x 是否存在
#2检查 x 是否小于 0 span stylecolor: #888888;这里可能有笔误/span
#3使 x 增加 1然而如果你只是迭代元素中的一些元素那么你可以使用while循环进行轮转来替代上面这种操作
var x 9;
do { } while( x-- );⑤注意NodeList
最小化访问NodeList的次数可以极大的改进脚本的性能 var images document.getElementsByTagName(img);for (var i 0, len images.length; i len; i) {}编写JavaScript的时候一定要知道何时返回NodeList对象这样可以最小化对它们的访问
进行了对getElementsByTagName()的调用获取了元素的childNodes属性获取了元素的attributes属性访问了特殊的集合如document.forms、document.images等等
要了解了当使用NodeList对象时合理使用会极大的提升代码执行速度
⑥避免与null进行比较
由于JavaScript是弱类型的所以它不会做任何的自动类型检查所以如果看到与null进行比较的代码尝试使用以下技术替换
如果值应为一个引用类型使用instanceof操作符检查其构造函数如果值应为一个基本类型作用typeof检查其类型如果是希望对象包含某个特定的方法名则使用typeof操作符确保指定名字的方法存在于对象上Object.prototype.toString方法检测对象类型
可以通过 toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用传递要检查的对象作为第一个参数称为 thisArg。 var toString Object.prototype.toString;toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
toString.call(hahah); // [object String]//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
⑦循环引用
⑧释放javascript对象
在rich应用中随着实例化对象数量的增加内存消耗会越来越大。所以应当及时释放对对象的引用让GC能够回收这些内存控件。
对象obj null
对象属性delete obj.myproperty
数组item使用数组的splice方法释放数组中不用的item
⑨switch语句相对if较快
通过将case语句按照最可能到最不可能的顺序进行组织 扩展你不知道的Node.js性能优化、Redis【入门】就这一篇、Redis的优势和特点
前端性能优化 24 条建议2020
4.【拓展】负载均衡
我们都知道node的核心是事件驱动通过Event Loop去异步处理用户请求相比于传统的后端服务它们都是将用户的每个请求分配一个进程进行处理推荐大家去看这样一篇博文mp.weixin.qq.com/s?__bizMzA… 。特别生动地讲解了事件驱动的运行机制通俗易懂。事件驱动的最大优势是什么就是在高并发IO时不会造成堵塞对于直播类网站这点是至关重要的我们有成功的先例——快手快手强大的IO高并发究其本质一定能追溯到node。
其实现在的企业级网站都会搭建一层node作为中间层。大概的网站框架如图所示 4.2.pm2实现Node.js“多进程”
我们都知道node的优劣这里分享一份链接找了挺久写的还算详细www.zhihu.com/question/19… 。其实很多都是老套路那些说node不行的都是指着node是单进程这一个软肋开撕告诉你我们有解决方案了——pm2。这是它的官网pm2.keymetrics.io/ 。它是一款node.js进程管理器具体的功能就是能在你的计算机里的每一个内核都启动一个node.js服务也就是说如果你的电脑或者服务器是多核处理器现在也少见单核了吧它就能启动多个node.js服务并且它能够自动控制负载均衡会自动将用户的请求分发至压力小的服务进程上处理。听起来这东西简直就是神器啊而且它的功能远远不止这些这里我就不作过多介绍了大家知道我们在上线的时候需要用到它就行了安装的方法也很简单直接用npm下到全局就可以了$ npm i pm2 -g具体的使用方法还有相关特性可以参照官网。
下面是pm2启动后的效果图 参考 https://www.cnblogs.com/chyingp/p/pm2-documentation.html
4.3.nginx搭建反向代理
在开始搭建工作之前首先得知道什么是反向代理。可能大家对这个名词比较陌生先上一张图 所谓代理就是我们通常所说的中介网站的反向代理就是指那台介于用户和我们真实服务器之间的服务器说的我都拗口了它的作用便是能够将用户的请求分配到压力较小的服务器上其机制是轮询。听完这句话是不是感觉很耳熟没错在我介绍pm2的时候也说过同样的话反向代理起到的作用同pm2一样也是实现负载均衡你现在应该也明白了两者之间的差异反向代理是对服务器实现负载均衡而pm2是对进程实现负载均衡。大家如果想深入了解反向代理的相关知识我推荐知乎的一个贴子www.zhihu.com/question/24… 。
参考https://juejin.im/post/5b0b7d74518825158e173a0c#heading-18 RESTful架构
即Representational State Transfer的缩写。我对这个词组的翻译是表现层状态转化。
要理解RESTful架构最好的方法就是去理解Representational State Transfer这个词组到底是什么意思它的每一个词代表了什么涵义。如果你把这个名称搞懂了也就不难体会REST是一种什么样的设计。
资源Resources
REST的名称表现层状态转化中省略了主语。表现层其实指的是资源Resources的表现层。
所谓资源就是网络上的一个实体或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务总之就是一个具体的实在。你可以用一个URI统一资源定位符指向它每种资源对应一个特定的URI。要获取这个资源访问它的URI就可以因此URI就成了每一个资源的地址或独一无二的识别符。
所谓上网就是与互联网上一系列的资源互动调用它的URI。
表现层Representation
资源是一种信息实体它可以有多种外在表现形式。我们把资源具体呈现出来的形式叫做它的表现层Representation。
比如文本可以用txt格式表现也可以用HTML格式、XML格式、JSON格式表现甚至可以采用二进制格式图片可以用JPG格式表现也可以用PNG格式表现。
URI只代表资源的实体的位置不代表它的形式。严格地说有些网址最后的.html后缀名是不必要的因为这个后缀名表示格式属于表现层范畴而URI应该只代表资源的位置。它的具体表现形式应该在HTTP请求的头信息中用Accept和Content-Type字段指定这两个字段才是对表现层的描述。
状态转化State Transfer
访问一个网站就代表了客户端和服务器的一个互动过程。在这个过程中势必涉及到数据和状态的变化。
互联网通信协议HTTP协议是一个无状态协议。这意味着所有的状态都保存在服务器端。因此如果客户端想要操作服务器必须通过某种手段让服务器端发生状态转化State Transfer。而这种转化是建立在表现层之上的所以就是表现层状态转化。
客户端用到的手段只能是HTTP协议。具体来说就是HTTP协议里面四个表示操作方式的动词GET、POST、PUT、DELETE。它们分别对应四种基本操作GET用来获取资源POST用来新建资源也可以用于更新资源PUT用来更新资源DELETE用来删除资源。
综述
综合上面的解释我们总结一下什么是RESTful架构 1每一个URI代表一种资源 2客户端和服务器之间传递这种资源的所需的外在表现形式类型 3客户端通过四个HTTP动词对服务器端资源进行操作实现表现层状态转化。
误区
RESTful架构有一些典型的设计误区。
最常见的一种设计错误就是URI包含动词。因为资源表示一种实体所以应该是名词URI不应该有动词动词应该放在HTTP协议中。
举例来说某个URI是/posts/show/1其中show是动词这个URI就设计错了正确的写法应该是/posts/1然后用GET方法表示show。
如果某些动作是HTTP动词表示不了的你就应该把动作做成一种资源。比如网上汇款从账户1向账户2汇款500元错误的URI是 POST /accounts/1/transfer/500/to/2 正确的写法是把动词transfer改成名词transaction资源不能是动词但是可以是一种服务 POST /transaction HTTP/1.1Host: 127.0.0.1from1to2amount500.00 另一个设计误区就是在URI中加入版本号 http://www.example.com/app/1.0/foohttp://www.example.com/app/1.1/foohttp://www.example.com/app/2.0/foo 因为不同的版本可以理解成同一种资源的不同表现形式所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分参见Versioning REST Services Accept: vnd.example-com.foojson; version1.0Accept: vnd.example-com.foojson; version1.1Accept: vnd.example-com.foojson; version2.0 参考http://www.ruanyifeng.com/blog/2011/09/restful.html css优先级
下面列表中选择器类型的优先级是递增的
类型选择器例如h1和伪元素例如::before类选择器 (例如.example)属性选择器例如[typeradio]和伪类例如:hoverID 选择器例如#example。给元素添加的内联样式 (例如stylefont-weight:bold) 总会覆盖外部样式表的任何样式 因此可看作是具有最高的优先级。
通配选择符universal selector*关系选择符combinators, , ~, , ||和 否定伪类negation pseudo-class:not()对优先级没有影响。但是在 :not() 内部声明的选择器会影响优先级。
您可以访问 https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance#Specificity_2 或者 https://specifishity.com 来了解更多关于优先级的详细信息。