本文将从浏览器输入url到页面加载完成中经历的各个阶段来探讨web前端性能能够优化的点css
无优化点html
传送门:https://www.cnblogs.com/chenq...前端
服务器对静态资源设置浏览器缓存信息,浏览器在有缓存的状况下直接从本地读取资源。vue
地址栏输入的域名并非最后资源所在的真实位置,域名只是与IP地址的一个映射。网络服务器的IP地址那么多,咱们不可能去记一串串的数字,所以域名就产生了,域名解析的过程实际是将域名还原为IP地址的过程。
顺便介绍下,无优化点react
DNS解析后获得了服务器的ip,接下来就是和服务器创建起链接,这经过TCP的三次握手完成。
具体为:
浏览器:你好~你在吗~我能和你聊会天吗
服务器:嗯!我在~咱们聊会吧~
浏览器:好的,那咱们开始咯webpack
顺便介绍下,无优化点,顺便再说下四次告别es6
TCP链接后数据的传输是双向的,那么当浏览器不想说话了,就会发出停止链接的信号标志,服务器收到后会回一个确认标志给浏览器,这时浏览器就不能再向服务器传输数据了,可是还能够发送信号。为了对服务器的尊重,等服务器把话说完后才会发一个结束标志给浏览器,这时浏览器知道服务器也向本身传输完数据了,回一个确认标志过去,才真正结束此次TCP链接。web
完整的HTTP请求包含请求起始行、请求头部、请求主体三部分。缓存信息是存储在请求头中,在阶段二上的链接有介绍。json
服务器在收到浏览器发送的HTTP请求以后,会将收到的HTTP报文封装成HTTP的Request对象,并经过不一样的Web服务器进行处理,处理完的结果以HTTP的Response对象返回,主要包括状态码,响应头,响应报文三个部分。响应体为服务器返回给浏览器的信息,主要由HTML,css,js,图片文件组成。
传送门: https://www.cnblogs.com/tootw...segmentfault
js是会阻塞页面渲染的,那么解决方法有不少,能够把js放在body的底部,或者是异步加载js。
同步异步加载详解传送门:https://blog.csdn.net/qq_3498...
浅谈script标签的defer和async传送门:https://segmentfault.com/a/11...
前端页面渲染时会根据DOM结构生成一个DOM树,而后加上CSS样式生成渲染树。若是CSS文件放在<head>标签中,则CSS RuleTree会先于DOM树完成构建,以后浏览器就能够边构建DOM树边完成渲染;反之,CSS文件放在全部页面标签以后,好比<body/>以前,那么当DOM树构建完成了,渲染树才构建,浏览器不得再也不从新渲染整个页面,这样形成了资源的浪费。并且页面还可能会出现闪跳的感受,或者白屏或者布局混乱或者样式很丑,直到CSS加载完成,页面重绘才能恢复正常。所以,通常来说,css标签应放在标签之间。但若是css文件较大,会让首页白屏时间更长,因此并非说把css都放顶部是一个完美的方法。权衡利弊,应该把必须的css(js)放顶部,把不那么重要的css(js)放底部。--------------------- 做者:JiajiaAz 来源:CSDN 原文:https://blog.csdn.net/qq_3265...
概念:
访问页面时,先把img元素的背景图片src替换成一张占位图,这样只需请求一次,当图片出如今浏览器的可视区域内时,再设置图片的真实路径,显示图片。方法:
页面中的img元素,若没有src属性,浏览器就不会发出请求去下载图片,只有经过Javascript设置了图片路径,浏览器才会发送请求。
1)懒加载先在页面中把须要延迟加载的图片统一使用一张占位图进行占位,把真正的路径存在元素“data-url”属性里。
2)页面加载完成后,经过scrollTop判断图片是否在用户的视野,若是在,则将 data-url的值取出来存放到src中。
在vue或者react的项目下,怎么解决页面渲染问题呢,毕竟初始的html就是一个空白页面,页面渲染全靠js的render,若是这个入口js过大,必然会致使页面白屏时间过长。
若是能够对入口的js进行代码分割,把后期才会用到的js先独立出来,等用时再引入那么就能够大大减小初始js的体积了,首屏页面渲染起来天然也快。
<button>click to load js</button> <script> document.querySelector('button').onclick = () => { let loadJs = document.createElement('script') loadJs.src = './load.js' document.body.appendChild(loadJs) loadJs.onload = () => { alert("finish loading!") } } </script>
嗯简单来讲原理就是这么实现的,设置个触发点,触发后建立个src为须要引进js路径的script Dom,而后设置下加载后的回调函数..
写在主入口main.js中👇
document.querySelector('button').onclick = () => { require.ensure([], ()=> { // 引入异步加载的js let loadJS = require('./asyncJS') alert(loadJS.flag) }, 'asyncJS') }
webpack的配置文件👇
output: { path: path.resolve(__dirname, './dist'), filename: '[name].bundle.js', publicPath: '../dist/', chunkFilename: 'chunks/[name]-[hash].js' }
webpack 在编译时,会静态地解析代码中的require.ensure(),同时将模块添加到一个分开的 chunk 当中。这个新的 chunk 会被 webpack 经过 jsonp来按需加载。语法以下:
require.ensure(dependencies: String[], callback: function(require), chunkName: String)
依赖 - dependencies
这是一个字符串数组,经过这个参数,在全部的回调函数的代码被执行前,咱们能够将全部须要用到的模块进行声明。回调 - callback
当全部的依赖都加载完成后,webpack会执行这个回调函数。require对象的一个实现会做为一个参数传递给这个回调函数。所以,咱们能够进一步 require() 依赖和其它模块提供下一步的执行。chunk名称 - chunkName
chunkName 是提供给这个特定的 require.ensure() 的 chunk的名称。经过提供 require.ensure() 不一样执行点相同的名称,咱们能够保证全部的依赖都会一块儿放进相同的 文件束(bundle)。
打包出来的目录👇
其中chunks文件夹里的内容就是从main.js中分离出来的,会在被须要时再引入到项目中。
页面加载时是这样👇
点了按钮就是这样了👇
顺便介绍下require.ensure() 的坑点
空数组做为参数 require.ensure([], function(require){
require('./a.js'); }); 以上代码保证了拆分点被建立,并且 a.js 被 webpack 分开打包。依赖做为参数 require.ensure(['./a.js'], function(require) {
require('./b.js'); }); 上面代码, a.js 和 b.js 都被打包到一块儿,并且从主文件束中拆分出来。但只有 b.js 的内容被执行。a.js 的内容仅仅是可被使用,但并无被输出。想去执行 a.js,咱们须要异步地引用它,如 require('./a.js'),让它的 JavaScritp 被执行。
说了这么多,绕回来本题中心,这里以vue为例子来实现按需异步加载功能。按需加载什么呢?固然是组件啦!
异步组件 - 官网解释
在大型应用中,咱们可能须要将应用分割成小一些的代码块,而且只在须要的时候才从服务器加载一个模块。为了简化,Vue
容许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue
只有在这个组件须要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供将来重渲染。例如:
Vue.component('async-example', function (resolve, reject) { setTimeout(function () { // 向 `resolve` 回调传递组件定义 resolve({ template: '<div>I am async!</div>' }) }, 1000) })
如你所见,这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器获得组件定义的时候被调用。你也能够调用 reject(reason) 来表示加载失败。
结合前面讲的直接上例子,基于异步组件咱们能够实现vue的异步路由👇
export default new Router({ routes: [ { path: '/', name: 'HelloWorld', component: resolve => { require.ensure([], () => { resolve(require('@/components/HelloWorld.vue')) }) } }, { path: '/other', name: 'other', component: resolve => { require.ensure([], () => { resolve(require('@/components/Other.vue')) }) } } ] })
固然require.ensure这种写法比较旧,就是对webpack的兼容性会好点,如今结合es6的写法会更加简洁
你也能够在工厂函数中返回一个 Promise,因此把 webpack 2 和 ES2015 语法加在一块儿,咱们能够写成这样:
Vue.component( 'async-webpack-example', // 这个 `import` 函数会返回一个 `Promise` 对象。 () => import('./my-async-component') )
export default new Router({ routes: [ { path: '/', name: 'HelloWorld', component: () => import('@/components/HelloWorld.vue') }, { path: '/other', name: 'Other', component: () => import('@/components/Other.vue') } ] })
嗯这样就好了
顺便也提下如何处理加载状态
这里的异步组件工厂函数也能够返回一个以下格式的对象:
const AsyncComponent = () => ({ // 须要加载的组件 (应该是一个 `Promise` 对象) component: import('./MyComponent.vue'), // 异步组件加载时使用的组件 loading: LoadingComponent, // 加载失败时使用的组件 error: ErrorComponent, // 展现加载时组件的延时时间。默认值是 200 (毫秒) delay: 200, // 若是提供了超时时间且组件加载也超时了, // 则使用加载失败时使用的组件。默认值是:`Infinity` timeout: 3000 })