整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          學(xué)成在線 類慕課網(wǎng) 微服務(wù)教育網(wǎng) 第12天-講義-搜

          學(xué)成在線 類慕課網(wǎng) 微服務(wù)教育網(wǎng) 第12天-講義-搜索前端 Nuxt.js

          搜索前端技術(shù)需求

          1.1需求描述

          采用vue.js開發(fā)搜索界面則SEO不友好,需要解決SEO的問題。

          1.2了解SEO

          總結(jié):seo是網(wǎng)站為了提高自已的網(wǎng)站排名,獲得更多的流量,對(duì)網(wǎng)站的結(jié)構(gòu)及內(nèi)容進(jìn)行調(diào)整優(yōu)化,以便搜索引擎

          (百度,google等)更好抓取到更優(yōu)質(zhì)的網(wǎng)站的內(nèi)容。

          下圖是搜索引擎爬取網(wǎng)站頁面的大概流程圖:

          (搜索引擎的工作流程很復(fù)雜,下圖只是簡單概括)

          從上圖可以看到SEO是網(wǎng)站自己為了方便spider抓取網(wǎng)頁而作出的網(wǎng)頁內(nèi)容優(yōu)化,常見的SEO方法比如:

          1)對(duì)url鏈接的規(guī)范化,多用restful風(fēng)格的url,多用靜態(tài)資源url;

          2) 注意title、keywords的設(shè)置。

          3)由于spider對(duì)javascript支持不好,對(duì)于網(wǎng)頁跳轉(zhuǎn)用href標(biāo)簽。

          。。。

          1.3服務(wù)端渲染和客戶端渲染

          采用什么技術(shù)有利于SEO?要解答這個(gè)問題需要理解服務(wù)端渲染和客戶端渲染。什么是服務(wù)端渲染?

          我們用傳統(tǒng)的servlet開發(fā)來舉例:瀏覽器請(qǐng)求servlet,servlet在服務(wù)端生成html響應(yīng)給瀏覽器,瀏覽器展示html 的內(nèi)容,這個(gè)過程就是服務(wù)端渲染,如下圖:

          服務(wù)端渲染的特點(diǎn):

          1)在服務(wù)端生成html網(wǎng)頁的dom元素。

          2)客戶端(瀏覽器)只負(fù)責(zé)顯示dom元素內(nèi)容。

          當(dāng)初隨著web2.0的到來,A JAX技術(shù)興起,出現(xiàn)了客戶端渲染:客戶端(瀏覽器) 使用A JAX向服務(wù)端發(fā)起http請(qǐng)求,獲取到了想要的數(shù)據(jù),客戶端拿著數(shù)據(jù)開始渲染html網(wǎng)頁,生成Dom元素,并最終將網(wǎng)頁內(nèi)容展示給用戶, 如下圖:

          S

          客戶端渲染的特點(diǎn):

          1)在服務(wù)端只是給客戶端響應(yīng)的了數(shù)據(jù),而不是html網(wǎng)頁

          2)客戶端(瀏覽器)負(fù)責(zé)獲取服務(wù)端的數(shù)據(jù)生成Dom元素。

          兩種方式各有什么優(yōu)缺點(diǎn)? 客戶端渲染:

          1)缺點(diǎn)

          不利于網(wǎng)站進(jìn)行SEO,因?yàn)榫W(wǎng)站大量使用javascript技術(shù),不利于spider抓取網(wǎng)頁。

          2)優(yōu)點(diǎn)

          客戶端負(fù)責(zé)渲染,用戶體驗(yàn)性好,服務(wù)端只提供數(shù)據(jù)不用關(guān)心用戶界面的內(nèi)容,有利于提高服務(wù)端的開發(fā)效率。

          3)適用場景

          對(duì)SEO沒有要求的系統(tǒng),比如后臺(tái)管理類的系統(tǒng),如電商后臺(tái)管理,用戶管理等。

          服務(wù)端渲染:

          1)優(yōu)點(diǎn)

          有利于SEO,網(wǎng)站通過href的url將spider直接引到服務(wù)端,服務(wù)端提供優(yōu)質(zhì)的網(wǎng)頁內(nèi)容給spider。

          2)缺點(diǎn)

          服務(wù)端完成一部分客戶端的工作,通常完成一個(gè)需求需要修改客戶端和服務(wù)端的代碼,開發(fā)效率低,不利于系統(tǒng)的 穩(wěn)定性。

          3)適用場景

          對(duì)SEO有要求的系統(tǒng),比如:門戶首頁、商品詳情頁面等。

          2Nuxt.js介紹

          2.1Nuxt.js介紹

          移動(dòng)互聯(lián)網(wǎng)的興起促進(jìn)了web前后端分離開發(fā)模式的發(fā)展,服務(wù)端只專注業(yè)務(wù),前端只專注用戶體驗(yàn),前端大量運(yùn) 用的前端渲染技術(shù),比如流行的vue.js、react框架都實(shí)現(xiàn)了功能強(qiáng)大的前端渲染。

          但是,對(duì)于有SEO需求的網(wǎng)頁如果使用前端渲染技術(shù)去開發(fā)就不利于SEO了,有沒有一種即使用vue.js、react的前 端技術(shù)也實(shí)現(xiàn)服務(wù)端渲染的技術(shù)呢?其實(shí),對(duì)于服務(wù)端渲染的需求,vue.js、react這樣流行的前端框架提供了服務(wù)端渲染的解決方案。

          從上圖可以看到:

          react框架提供next.js實(shí)現(xiàn)服務(wù)端渲染。

          vue.js框架提供Nuxt.js實(shí)現(xiàn)服務(wù)端渲染。

          2.2Nuxt.js工作原理

          下圖展示了從客戶端請(qǐng)求到Nuxt.js進(jìn)行服務(wù)端渲染的整體的工作流程:

          1、用戶打開瀏覽器,輸入網(wǎng)址請(qǐng)求到Node.js

          2、部署在Node.js的應(yīng)用Nuxt.js接收瀏覽器請(qǐng)求,并請(qǐng)求服務(wù)端獲取數(shù)據(jù)

          3、Nuxt.js獲取到數(shù)據(jù)后進(jìn)行服務(wù)端渲染 4、Nuxt.js將html網(wǎng)頁響應(yīng)給瀏覽器

          Nuxt.js使用了哪些技術(shù)?

          Nuxt.js使用Vue.js+webpack+Babel三大技術(shù)框架/組件,如下圖:

          Babel 是一個(gè)js的轉(zhuǎn)碼器,負(fù)責(zé)將ES6的代碼轉(zhuǎn)成瀏覽器識(shí)別的ES5代碼。

          Webpack是一個(gè)前端工程打包工具。Vue.js是一個(gè)優(yōu)秀的前端框架。

          Nuxt.js的特性有哪些?

          基 于 Vue.js

          自動(dòng)代碼分層服務(wù)端渲染

          強(qiáng)大的路由功能,支持異步數(shù)據(jù)靜態(tài)文件服務(wù)

          ES6/ES7 語法支持

          打包和壓縮 JS 和 CSS HTML頭部標(biāo)簽管理本地開發(fā)支持熱加載集成ESLint

          支持各種樣式預(yù)處理器: SASS、LESS、 Stylus等等

          3Nuxt.js基本使用

          3.1創(chuàng)建Nuxt工程

          nuxt.js有標(biāo)準(zhǔn)的目錄結(jié)構(gòu),官方提供了模板工程,可以模板工程快速創(chuàng)建nuxt項(xiàng)目。

          模板工程地址:https://github.com/nuxt-community/starter-template/archive/master.zip

          本項(xiàng)目提供基于Nuxt.js的封裝工程,基于此封裝工程開發(fā)搜索前端,見“資料”–》xc-ui-pc-portal.zip,解壓

          xc-ui-pc-portal.zip到本項(xiàng)目前端工程目錄下。

          本前端工程屬于門戶的一部分,將承載一部分考慮SEO的非靜態(tài)化頁面。

          本工程基于Nuxt.js模板工程構(gòu)建,Nuxt.js使用1.3版本,并加入了今后開發(fā)中所使用的依賴包,直接解壓本工程即 可使用。

          3.2目錄結(jié)構(gòu)

          本工程的目錄結(jié)構(gòu)如下:

          ‐資源目錄
          資源目錄 assets 用于組織未編譯的靜態(tài)資源如 LESS、SASS 或 JavaScript。
          ‐組件目錄
          組件目錄 components 用于組織應(yīng)用的 Vue.js 組件。Nuxt.js 不會(huì)擴(kuò)展增強(qiáng)該目錄下 Vue.js 組件,即這些組件不會(huì)像頁面組件那樣有 asyncData 方法的特性。
          ‐布局目錄
          布局目錄 layouts 用于組織應(yīng)用的布局組件。該目錄名為Nuxt.js保留的,不可更改。
          ‐中間件目錄
          middleware 目錄用于存放應(yīng)用的中間件。
          ‐頁面目錄
          頁面目錄 pages 用于組織應(yīng)用的路由及視圖。Nuxt.js 框架讀取該目錄下所有的 .vue 文件并自動(dòng)生成對(duì)應(yīng)的路由配置。
          該目錄名為Nuxt.js保留的,不可更改。
          ‐插件目錄
          插件目錄 plugins 用于組織那些需要在 根vue.js應(yīng)用 實(shí)例化之前需要運(yùn)行的 Javascript 插件。
          ‐靜態(tài)文件目錄
          靜態(tài)文件目錄 static 用于存放應(yīng)用的靜態(tài)文件,此類文件不會(huì)被 Nuxt.js 調(diào)用 Webpack 進(jìn)行構(gòu)建編譯處理。 服務(wù)器啟動(dòng)的時(shí)候,該目錄下的文件會(huì)映射至應(yīng)用的根路徑 / 下。
          舉個(gè)例子: /static/logo.png 映射至 /logo.png
          該目錄名為Nuxt.js保留的,不可更改。
          ‐Store 目 錄
          store 目錄用于組織應(yīng)用的 Vuex 狀態(tài)樹 文件。 Nuxt.js 框架集成了 Vuex 狀態(tài)樹 的相關(guān)功能配置,在 store 目錄下創(chuàng)建一個(gè) index.js 文件可激活這些配置。
          該目錄名為Nuxt.js保留的,不可更改。
          ‐nuxt.config.js 文 件
          nuxt.config.js 文件用于組織Nuxt.js 應(yīng)用的個(gè)性化配置,以便覆蓋默認(rèn)配置。該文件名為Nuxt.js保留的,不可更改。
          ‐package.json 文 件
          package.json 文件用于描述應(yīng)用的依賴關(guān)系和對(duì)外暴露的腳本接口。
          該文件名為Nuxt.js保留的,不可更改。
          

          nuxt.js 提供了目錄的別名,方便在程序中引用:

          3.3頁面布局

          頁面布局就是頁面內(nèi)容的整體結(jié)構(gòu),通過在layouts目錄下添加布局文件來實(shí)現(xiàn)。在layouts 根目錄下的所有文件都屬于個(gè)性化布局文件,可以在頁面組件中利用 layout 屬性來引用。

          一個(gè)例子:

          1、定義:layouts/test.vue布局文件,如下:

          注意:布局文件中一定要加 <nuxt/> 組件用于顯示頁面內(nèi)容。

          <template>

          <div>

          <div>這里是頭</div>

          <nuxt/>

          <div>這里是尾</div>

          </div>

          </template>

          <script>

          export default {

          }

          </script>

          <style>

          </style>

          2、在pages目錄創(chuàng)建user目錄,并創(chuàng)建index.vue頁面

          在 pages/user/index.vue 頁面里, 可以指定頁面組件使用 test 布局,代碼如下:

          <template>

          <div>

          測試頁面

          </div>

          </template>

          <script>

          export default{ layout:'test'

          }

          </script>

          <style>

          </style>

          3、測試,請(qǐng)求:http://localhost:10000/user,如果如下:

          這里是頭測試頁面這里是尾

          3.4路由

          3.4.1基礎(chǔ)路由

          Nuxt.js 依據(jù)目錄結(jié)構(gòu)自動(dòng)生成 vue-router 模塊的路由配置。

          Nuxt.js根據(jù)pages的目錄結(jié)構(gòu)及頁面名稱定義規(guī)范來生成路由,下邊是一個(gè)基礎(chǔ)路由的例子:

          假設(shè)的目錄結(jié)構(gòu)如下:

          pages/
          ‐‐| user/
          ‐‐‐‐‐| index.vue
          ‐‐‐‐‐| one.vue
          

          那么,Nuxt.js 自動(dòng)生成的路由配置如下:

          router: { routes: [
          {
          name: 'user',
          path: '/user',
          component: 'pages/user/index.vue'
          },
          {
          name: 'user‐one',
          path: '/user/one',
          component: 'pages/user/one.vue'
          }
          ]
          }
          

          index.vue代碼如下:

          <template>
          <div>
          用戶管理首頁
          </div>
          </template>
          <script>
          export default{ layout:"test"
          }
          </script>
          <style>
          </style>
          

          one.vue代碼如下:

          <template>
          <div>
          one頁面
          </div>
          </template>
          <script>
          export default{ layout:"test"
          }
          </script>
          <style>
          </style>
          

          分別訪問如下鏈接進(jìn)行測試:

          http://localhost:10000/user http://localhost:10000/user/one

          3.4.2嵌套路由

          你可以通過 vue-router 的子路由創(chuàng)建 Nuxt.js 應(yīng)用的嵌套路由。

          創(chuàng)建內(nèi)嵌子路由,你需要添加一個(gè) Vue 文件,同時(shí)添加一個(gè)與該文件同名的目錄用來存放子視圖組件。

          別忘了在父級(jí) Vue 文件內(nèi)增加假設(shè)文件結(jié)構(gòu)如:

          pages/
          ‐‐| user/
          ‐‐‐‐‐| _id.vue
          ‐‐‐‐‐| index.vue
          ‐‐| user.vue
          

          Nuxt.js 自動(dòng)生成的路由配置如下:

          router: { routes: [
          {
          path: '/user',
          component: 'pages/user.vue', children: [
          {
          path: '',
          component: 'pages/user/index.vue', name: 'user'
          },
          {
          path: ':id',
          component: 'pages/user/_id.vue', name: 'user‐id'
          }
          ]
          }
          ]
          }
          

          將user.vue文件創(chuàng)建到與user目錄的父目錄下,即和user目錄保持平級(jí)。

          <template>
          <div>
          用戶管理導(dǎo)航,<nuxt‐link :to="'/user/101'">修改</nuxt‐link>
          <nuxt‐child/>
          </div>
          </template>
          <script>
          export default{ layout:"test"
          }
          </script>
          <style>
          </style>
          

          _id.vue頁面實(shí)現(xiàn)了向頁面?zhèn)魅雐d參數(shù),頁面內(nèi)容如下:

          <template>
          <div>
          修改用戶信息{{id}}
          </div>
          </template>
          <script>
          export default{ layout:"test", data(){
          return {
          id:''
          }
          },
          mounted(){
          this.id=this.$route.params.id; console.log(this.id)
          }
          }
          </script>
          <style>
          </style>
          

          測試:http://localhost:10000/user

          點(diǎn)擊修改:

          3.6獲取數(shù)據(jù)

          3.6.1asyncData 方法

          Nuxt.js 擴(kuò)展了 Vue.js,增加了一個(gè)叫 asyncData 的方法,

          方法會(huì)在組件(限于頁面組件)每次加載之前被調(diào)用。它可以在服務(wù)端或路由更新之前被調(diào)用。 在這個(gè)方法被調(diào)用的時(shí)候,第一個(gè)參數(shù)被設(shè)定為當(dāng)前頁面的上下文對(duì)象,你可以利用返回的數(shù)據(jù)一并返回給當(dāng)前組件。方法來獲取數(shù)據(jù),Nuxt.js 會(huì)將返回的數(shù)據(jù)融合組件方法

          注意:由于對(duì)象。

          例子:

          方法是在組件 初始化 前被調(diào)用的,所以在方法內(nèi)是沒有辦法通過來引用組件的實(shí)例

          在上邊例子中的user/_id.vue中添加,頁面代碼如下:

          <template>
          <div>
          修改用戶信息{{id}},名稱:{{name}}
          </div>
          </template>
          <script>
          export default{ layout:'test',
          //根據(jù)id查詢用戶信息
          asyncData(){ console.log("async方法") return {
          name:'黑馬程序員'
          }
          },
          data(){ return {
          id:''
          }
          },
          mounted(){
          this.id=this.$route.params.id;
          }
          }
          </script>
          <style>
          </style>
          

          此方法在服務(wù)端被執(zhí)行,觀察服務(wù)端控制臺(tái)打印輸出“async方法”。

          此方法返回data模型數(shù)據(jù),在服務(wù)端被渲染,最后響應(yīng)給前端,刷新此頁面查看頁面源代碼可以看到name模型數(shù)據(jù)已在頁面源代碼中顯示。

          3.6.2async /await方法

          使用async 和 await配合promise也可以實(shí)現(xiàn)同步調(diào)用,nuxt.js中使用async/await實(shí)現(xiàn)同步調(diào)用效果。1、先測試異步調(diào)用,增加a、b兩個(gè)方法,并在mounted中調(diào)用。

          methods:{
          a(){
          return new Promise(function(resolve,reject){ setTimeout(function () {
          resolve(1)
          },2000)
          })
          }, b(){
          return new Promise(function(resolve,reject){ setTimeout(function () {
          resolve(2)
          },1000)
          })
          }
          },
          mounted(){
          this.a().then(res=>{ alert(res)
          console.log(res)
          })
          this.b().then(res=>{ alert(res) console.log(res)
          })
          }
          

          2、使用async/await完成同步調(diào)用

          async asyncData({ store, route }) { console.log("async方法")
          var a=await new Promise(function (resolve, reject) { setTimeout(function () {
          console.log("1") resolve(1)
          },2000)
          });
          var a=await new Promise(function (resolve, reject) { setTimeout(function () {
          console.log("2") resolve(2)
          },1000)
          });
          return {
          name:'黑馬程序員'
          }
          },
          

          觀察服務(wù)端控制臺(tái)發(fā)現(xiàn)是按照a、b方法的調(diào)用順序輸出1、2,實(shí)現(xiàn)了使用async/await完成同步調(diào)用。

          3搜索前端開發(fā)

          3.1搜索頁面

          3.1.1需求分析

          觀察服務(wù)端控制臺(tái)發(fā)現(xiàn)是按照a、b方法的調(diào)用順序輸出1、2,實(shí)現(xiàn)了使用async/await完成同步調(diào)用。

          3搜索前端開發(fā)

          3.1搜索頁面

          3.1.1需求分析

          <template>
          <div>
          <Header />
          <nuxt/>
          <Footer />
          </div>
          </template>
          <script>
          import Footer from '../components/Footer.vue' import Header from '../components/Header.vue' export default {
          components: { Header, Footer
          }
          }
          </script>
          <style>
          </style>
          

          3.1.3Nginx代理配置

          搜索頁面中以/static開頭的靜態(tài)資源通過nginx解析,如下:

          /static/plugins:指向門戶目錄下的plugins目錄。

          /static/css:指向門戶目錄下的的css目錄

          修改Nginx中www.xuecheng.com虛擬主機(jī)的配置:

          #靜態(tài)資源,包括系統(tǒng)所需要的圖片,js、css等靜態(tài)資源location /static/img/ {
          alias	F:/develop/xc_portal_static/img/;
          }
          location /static/css/ {
          alias	F:/develop/xc_portal_static/css/;
          }
          location /static/js/ {
          alias	F:/develop/xc_portal_static/js/;
          }
          location /static/plugins/ {
          alias	F:/develop/xc_portal_static/plugins/;
          add_header Access‐Control‐Allow‐Origin http://ucenter.xuecheng.com; add_header Access‐Control‐Allow‐Credentials true;
          add_header Access‐Control‐Allow‐Methods GET;
          }
          

          配置搜索Url,下圖是Nginx搜索轉(zhuǎn)發(fā)流程圖:

          用戶請(qǐng)求/course/search時(shí)Nginx將請(qǐng)求轉(zhuǎn)發(fā)到nuxt.js服務(wù),nginx在轉(zhuǎn)發(fā)時(shí)根據(jù)每臺(tái)nuxt服務(wù)的負(fù)載情況進(jìn)行轉(zhuǎn) 發(fā),實(shí)現(xiàn)負(fù)載均衡。

          本教程開發(fā)環(huán)境Nuxt.js服務(wù)和www.xuecheng.com虛擬機(jī)主在同一臺(tái)計(jì)算機(jī),使用同一個(gè)nginx,配置如下:

          #前端門戶課程搜索
          location ^~ /course/search {
          proxy_pass http://dynamic_portal_server_pool;
          }
          #后端搜索服務(wù)
          location /openapi/search/ {
          proxy_pass http://search_server_pool/search/;
          }
          #分類信息
          location /static/category/ {
          proxy_pass http://static_server_pool;
          }
          #前端動(dòng)態(tài)門戶
          upstream dynamic_portal_server_pool{ server 127.0.0.1:10000 weight=10;
          }
          #后臺(tái)搜索(公開api)
          upstream search_server_pool{
          server 127.0.0.1:40100 weight=10;
          }
          

          其它配置:

          #開發(fā)環(huán)境webpack定時(shí)加載此文件location ^~ / webpack_hmr {
          proxy_pass http://dynamic_portal_server_pool/ webpack_hmr;
          }
          #開發(fā)環(huán)境nuxt訪問_nuxt location ^~ /_nuxt/ {
          proxy_pass http://dynamic_portal_server_pool/_nuxt/;
          }
          

          在靜態(tài)虛擬主機(jī)中添加:

          #學(xué)成網(wǎng)靜態(tài)資源server {
          listen	91; server_name localhost;
          #分類信息
          location /static/category/ {
          alias F:/develop/xuecheng/static/category/;
          }
          ...
          

          3.1.4搜索頁面

          創(chuàng)建搜索頁面如下:

          3.1.4搜索頁面

          創(chuàng)建搜索頁面如下:

          //配置文件

          let config=require('~/config/sysConfig') import querystring from 'querystring' import * as courseApi from '~/api/course' export default {

          head() { return {

          title: '傳智播客‐一樣的教育,不一樣的品質(zhì)',

          meta: [

          {charset: 'utf‐8'},

          {name: 'description', content: '傳智播客專注IT培訓(xùn),Java培訓(xùn),Android培訓(xùn),安卓培訓(xùn),PHP培

          訓(xùn),C++培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),平面設(shè)計(jì)培訓(xùn),UI設(shè)計(jì)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),web前端培訓(xùn),云計(jì)算大數(shù)據(jù)培訓(xùn), 全棧工程師培訓(xùn),產(chǎn)品經(jīng)理培訓(xùn)。'},

          {name: 'keywords', content: this.keywords}

          ],

          link: [

          {rel: 'stylesheet', href: '/static/plugins/normalize‐css/normalize.css'},

          {rel: 'stylesheet', href: '/static/plugins/bootstrap/dist/css/bootstrap.css'},

          {rel: 'stylesheet', href: '/static/css/page‐learing‐list.css'}

          ]

          }

          },

          <script>

          //配置文件

          let config=require('~/config/sysConfig') import querystring from 'querystring' import * as courseApi from '~/api/course' export default {

          head() { return {

          title: '傳智播客‐一樣的教育,不一樣的品質(zhì)',

          meta: [

          {charset: 'utf‐8'},

          {name: 'description', content: '傳智播客專注IT培訓(xùn),Java培訓(xùn),Android培訓(xùn),安卓培訓(xùn),PHP培 訓(xùn),C++培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),平面設(shè)計(jì)培訓(xùn),UI設(shè)計(jì)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),web前端培訓(xùn),云計(jì)算大數(shù)據(jù)培訓(xùn), 全棧工程師培訓(xùn),產(chǎn)品經(jīng)理培訓(xùn)。'},

          {name: 'keywords', content: this.keywords}

          ],

          link: [

          {rel: 'stylesheet', href: '/static/plugins/normalize‐css/normalize.css'},

          {rel: 'stylesheet', href: '/static/plugins/bootstrap/dist/css/bootstrap.css'},

          {rel: 'stylesheet', href: '/static/css/page‐learing‐list.css'}

          ]

          }

          },

          async asyncData({ store, route }) { return {

          courselist: {},

          first_category:{},

          second_category:{}, mt:'',

          st:'',

          grade:'',

          keyword:'', total:0,

          imgUrl:config.imgUrl

          }

          },

          data() { return {

          courselist: {}, first_category:{}, second_category:{}, mt:'',

          st:'',

          grade:'',

          keyword:'', imgUrl:config.imgUrl, total:0,//總記錄數(shù)page:1,//頁碼page_size:12//每頁顯示個(gè)數(shù)

          }

          },

          watch:{//路由發(fā)生變化立即搜索search表示search方法'$route':'search'

          },

          methods: {

          //分頁觸發(fā)handleCurrentChange(page) {

          },

          //搜索方法search(){

          //刷新當(dāng)前頁面

          window.location.reload();

          }

          }

          }

          </script>

          3.1.5測試

          重啟Nginx,請(qǐng)求:http://www.xuecheng.com/course/search,頁面效果如下:

          3.2查詢?nèi)?/strong>

          3.2.1需求分析

          初次進(jìn)入頁面,沒有輸入任何查詢條件,默認(rèn)查詢?nèi)空n程,分頁顯示。

          3.2.2API方法

          在api目錄創(chuàng)建本工程所用的api方法類,api方法類使用了public.js等一些抽取類:

          /api/public.js抽取axios 的基礎(chǔ)方法

          /api/util.js工具類

          /con?g/sysCon?g.js系統(tǒng)配置類,配置了系統(tǒng)參數(shù)變量

          創(chuàng)建course.js,作為課程相關(guān)業(yè)務(wù)模塊的api方法類。

          import http from './public' import qs from 'qs'
          let config=require('~/config/sysConfig') let apiURL=config.apiURL
          let staticURL=config.staticURL
          if (typeof window==='undefined') { apiURL=config.backApiURL staticURL=config.backStaticURL
          }
          /*搜索*/
          export const search_course=(page,size,params)=> { let querys=qs.stringify(params);
          return http.requestQuickGet(apiURL+"/search/course/list/"+page+"/"+size+"?"+querys);
          }
          

          3.2.3搜索方法

          實(shí)現(xiàn)思路如下:

          1、用戶請(qǐng)求本頁面到達(dá)node.js

          2、在asyncData方法中向服務(wù)端請(qǐng)求查詢課程 3、asyncData方法執(zhí)行完成開始服務(wù)端渲染

          在asyncData中執(zhí)行搜索,代碼如下:

          async asyncData({ store, route }) {//服務(wù)端調(diào)用方法
          //搜索課程
          let page=route.query.page; if(!page){
          page=1;
          }else{
          page=Number.parseInt(page)
          }
          console.log(page);
          //請(qǐng)求搜索服務(wù),搜索服務(wù)
          let course_data=await courseApi.search_course(page,2,route.query); console.log(course_data)
          if (course_data && course_data.queryResult ) { let keywords=''
          let mt='' let st='' let grade=''
          let keyword=''
          let total=course_data.queryResult.total if( route.query.mt){
          mt=route.query.mt
          }
          if( route.query.st){ st=route.query.st
          }
          if( route.query.grade){ grade=route.query.grade
          }
          if( route.query.keyword){ keyword=route.query.keyword
          }
          return {
          courselist: course_data.queryResult.list,//課程列表keywords:keywords,
          mt:mt,
          st:st,
          grade:grade, keyword:keyword, page:page, total:total, imgUrl:config.imgUrl
          }
          }else{
          return { courselist: {},
          first_category:{}, second_category:{}, mt:'',
          st:'',
          grade:'',
          keyword:'', page:page, total:0,
          imgUrl:config.imgUrl
          }
          }
          }
          

          3.2.5 頁面

          在頁面中展示課程列表。

          <div class="content‐list">
          <div class="recom‐item" v‐for="(course, index) in courselist">
          <nuxt‐link :to="'/course/detail/'+course.id+'.html'" target="_blank">
          <div v‐if="course.pic">
          <p><img :src="imgUrl+'/'+course.pic" width="100%" alt=""></p>
          </div>
          <div v‐else>
          <p><img src="/img/widget‐demo1.png" width="100%" alt=""></p>
          </div>
          <ul >
          <li class="course_title"><span v‐html="course.name"></span></li>
          <li style="float: left"><span v‐if="course.charge=='203001'">免費(fèi)</span>
          <span v‐if="course.charge=='203002'">¥{{course.price | money}}</span>
          <!‐‐ <em> ? </em>‐‐> <!‐‐<em>1125人在學(xué)習(xí)</em>‐‐></li>
          </ul>
          </nuxt‐link>
          </div>
          <li class="clearfix"></li>
          </div>
          

          3.3分頁查詢

          3.3.1服務(wù)端代碼

          ...
          //分頁
          //當(dāng)前頁碼
          if(page<=0){
          page=1;
          }
          //起始記錄下標(biāo)
          int from=(page ‐1) * size; searchSourceBuilder.from(from); searchSourceBuilder.size(size);
          ...
          

          3.3.2前端代碼

          使用Element-UI的el-pagination分頁插件:

          <div style="text‐align: center">
          <el‐pagination background
          layout="prev, pager, next" @current‐change="handleCurrentChange"
          :total="total"
          :page‐size="page_size"
          :current‐page="page" prev‐text="上一頁" next‐text="下一頁">
          </el‐pagination>
          </div>
          

          定義分頁觸發(fā)方法:

          methods:{
          //分頁觸發(fā)handleCurrentChange(page) {
          this.page=page this.$route.query.page=page
          let querys=querystring.stringify(this.$route.query) window.location='/course/search?'+querys;
          }
          ...
          

          3.4按分類搜索

          3.4.1需求分析

          1、通過一級(jí)分類搜索

          2、選擇一級(jí)分類后將顯示下屬的二級(jí)分類

          3、選擇二分類進(jìn)行搜索

          4、選擇一級(jí)分類的全部則表示沒有按照分類搜索

          5、選擇一級(jí)分類的全部時(shí)二級(jí)分類不顯示

          3.4.2API方法

          課程分類將通過頁面靜態(tài)化的方式寫入靜態(tài)資源下,通過/category/category.json可訪問,通過

          www.xuecheng.com/static/category/category.json即可訪問。

          category.json的內(nèi)容如下:

          我們需要定義api方法獲取所有的分類在/api/course.js中添加:

          /*獲取分類*/
          export const sysres_category=()=> {
          return http.requestQuickGet(staticURL+"/static/category/category.json");
          }
          

          3.4.3在asyncData中查詢分類

          進(jìn)入搜索頁面將默認(rèn)顯示所有一級(jí)分類,當(dāng)前如果已選擇一級(jí)分類則要顯示所有一級(jí)分類及該一級(jí)分類下屬的二級(jí) 分類。

          在asyncData方法中實(shí)現(xiàn)上邊的需求,代碼如下:

          async asyncData({ store, route }) {//服務(wù)端調(diào)用方法
          //搜索課程
          let page=route.query.page; if(!page){
          page=1;
          }else{
          page=Number.parseInt(page)
          }
          console.log(page);
          //請(qǐng)求搜索服務(wù),搜索服務(wù)
          let course_data=await courseApi.search_course(page,2,route.query); console.log(course_data)
          //查詢分類
          let category_data=await courseApi.sysres_category() if (course_data && course_data.queryResult ) {
          //全部分類
          let category=category_data.category//分部分類
          let first_category=category[0].children//一級(jí)分類let second_category=[]//二級(jí)分類
          let keywords='' let mt=''
          let st='' let grade=''
          let keyword=''
          let total=course_data.queryResult.total if( route.query.mt){
          mt=route.query.mt
          }
          if( route.query.st){ st=route.query.st
          }
          if( route.query.grade){ grade=route.query.grade
          }
          if( route.query.keyword){ keyword=route.query.keyword
          }
          //遍歷一級(jí)分類
          for(var i in first_category){ keywords+=first_category[i].name+' '
          if(mt!=''&& mt==first_category[i].id){
          //取出二級(jí)分類
          second_category=first_category[i].children;
          // console.log(second_category) break;
          }
          }
          return {
          courselist: course_data.queryResult.list,//課程列表first_category: first_category,
          second_category: second_category, keywords:keywords,
          mt:mt,
          st:st, grade:grade, keyword:keyword, page:page, total:total,
          imgUrl:config.imgUrl
          }
          }else{
          return { courselist: {},
          first_category:{}, second_category:{}, mt:'',
          st:'',
          grade:'',
          keyword:'', page:page, total:0,
          imgUrl:config.imgUrl
          }
          }
          }
          

          3.3.4頁面

          在頁面顯示一級(jí)分類及二級(jí)分類,需要根據(jù)當(dāng)前是否選擇一級(jí)分類、是否選擇二分類顯示頁面內(nèi)容。

          <ul>
          <li>一級(jí)分類:</li>
          <li v‐if="mt!=''"><nuxt‐link class="title‐link" :to="'/course/search? keyword='+keyword+'&grade='+grade">全部</nuxt‐link></li>
          <li class="all" v‐else>全部</li>
          <ol>
          <li v‐for="category_v in first_category">
          <nuxt‐link class="title‐link all" :to="'/course/search?keyword='+keyword+'&mt=' + category_v.id" v‐if="category_v.id==mt">{{category_v.name}}</nuxt‐link>
          <nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + category_v.id" v‐else>{{category_v.name}}</nuxt‐link>
          </li>
          </ol>
          <!‐‐<ol>
          <li>數(shù)據(jù)分析</li>
          <li>機(jī)器學(xué)習(xí)工程</li>
          <li>前端開發(fā)工程</li>
          </ol>‐‐>
          </ul>
          <ul>
          <li>二級(jí)分類:</li>
          <li v‐if="st!=''"><nuxt‐link class="title‐link" :to="'/course/search? keyword='+keyword+'&mt='+mt+'&grade='+grade">全部</nuxt‐link></li>
          <li class="all" v‐else>全部</li>
          <ol v‐if="second_category.length>0">
          <li v‐for="category_v in second_category">
          <nuxt‐link class="title‐link all" :to="'/course/search?keyword='+keyword+'&mt='+mt+'&st='
          + category_v.id" v‐if="category_v.id==st">{{category_v.name}}</nuxt‐link>
          <nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt='+mt+'&st=' + category_v.id" v‐else>{{category_v.name}}</nuxt‐link>
          </li>
          <!‐‐ <li>大數(shù)據(jù)</li>
          <li>云計(jì)算</li>‐‐>
          </ol>
          <!‐‐<a href="#" class="more">更多 ∨</a>‐‐>
          </ul>
          

          當(dāng)用戶點(diǎn)擊分類時(shí)立即執(zhí)行搜索,實(shí)現(xiàn)思路如下:

          1)點(diǎn)擊分類立即更改路由。

          2)通過監(jiān)聽路由,路由更改則刷新頁面。

          1)創(chuàng)建搜索方法

          search(){
          //刷新當(dāng)前頁面window.location.reload();
          }
          

          2)定義watch

          通過vue.js的watch可以實(shí)現(xiàn)監(jiān)視某個(gè)變量,當(dāng)變量值出現(xiàn)變化時(shí)執(zhí)行某個(gè)方法。 實(shí)現(xiàn)思路是:

          1、點(diǎn)擊分類頁面路由更改

          2、通過watch監(jiān)視路由,路由更改觸發(fā)search方法與methods并行定義watch:

          watch:{//路由發(fā)生變化立即搜索search表示search方法'$route':'search'
          },
          

          3.5按難度等級(jí)搜索

          3.5.1需求分析

          用戶選擇不同的課程難度等級(jí)去搜索課程。

          3.5.2API方法

          使用 search_course方法完成搜索。

          3.5.3頁面

          按難度等級(jí)搜索思路如下:

          1)點(diǎn)擊難度等級(jí)立即更改路由。

          2)通過監(jiān)聽路由,路由更改則立即執(zhí)行search搜索方法。按難度等級(jí)搜索頁面代碼如下:

          <ul>
          <li>難度等級(jí):</li>
          <li v‐if="grade!=''">
          <nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade='">全部
          </nuxt‐link>
          </li>
          <li class="all" v‐else>全部</li>
          <ol>
          <li v‐if="grade=='200001'" class="all">初級(jí)</li>
          <li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200001'">初級(jí)</nuxt‐link></li>
          <li v‐if="grade=='200002'" class="all">中級(jí)</li>
          <li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200002'">中級(jí)</nuxt‐link></li>
          <li v‐if="grade=='200003'" class="all">高級(jí)</li>
          <li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200003'">高級(jí)</nuxt‐link></li>
          </ol>
          </ul>
          

          3.6高亮顯示

          3.6.1服務(wù)端代碼

          修改service的搜索方法,添加高亮設(shè)置:

          ...
          //定義高亮
          HighlightBuilder highlightBuilder=new HighlightBuilder(); highlightBuilder.preTags("<font class='eslight'>"); highlightBuilder.postTags("</font>"); highlightBuilder.fields().add(new HighlightBuilder.Field("name")); searchSourceBuilder.highlighter(highlightBuilder);
          ...
          //解析高亮字段
          for(SearchHit hit:searchHits){
          CoursePub coursePub=new CoursePub();
          //源文檔
          Map<String, Object> sourceAsMap=hit.getSourceAsMap();
          //課程id
          String id=(String) sourceAsMap.get("id"); coursePub.setId(id);
          //取出name
          String name=(String) sourceAsMap.get("name");
          //取出高亮字段
          Map<String, HighlightField> highlightFields=hit.getHighlightFields(); if(highlightFields.get("name")!=null){
          HighlightField highlightField=highlightFields.get("name"); Text[] fragments=highlightField.fragments();
          StringBuffer stringBuffer=new StringBuffer(); for(Text text:fragments){
          stringBuffer.append(text);
          }
          name=stringBuffer.toString();
          }
          coursePub.setName(name);
          ....
          

          3.6.2前端代碼

          在search/index.vue中定義eslight樣式:

          <style>
          .eslight{ color: red;
          }
          ...
          

          4集成測試

          4.1需求分析

          本次集成測試的目的如下:

          1、測試課程發(fā)布與CMS接口是否正常。

          2、測試課程發(fā)布與ES接口是否正常。

          3、測試課程從創(chuàng)建到發(fā)布的整個(gè)過程。

          4.2準(zhǔn)備環(huán)境

          1、啟動(dòng)MySQL、MongoDB

          2、啟動(dòng)ElasticSearch、RabbitMQ 3、啟動(dòng)Eureka Server

          4、啟動(dòng)CMS、課程管理服務(wù)、搜索服務(wù)。

          5、啟動(dòng)Nginx、系統(tǒng)管理前端、教學(xué)管理前端、Nuxt.js。

          試中常被問到的問題,此問題包含web開發(fā)中從前端到后端到運(yùn)維的絕大多數(shù)知識(shí),主要考察面試者知識(shí)的廣度。本文會(huì)根據(jù)作者了解的程度增加不斷更新,不足之處歡迎評(píng)論區(qū)補(bǔ)充。

          首先了解一下URL的組成:

          http://www.baidu.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name

          從上面的URL可以看出,一個(gè)完整的URL包括以下幾部分:

          1、協(xié)議部分:該URL的協(xié)議部分為“http:”,這代表網(wǎng)頁使用的是HTTP協(xié)議。在Internet中可以使用多種協(xié)議,如HTTP,F(xiàn)TP等等本例中使用的是HTTP協(xié)議。在"HTTP"后面的“//”為分隔符

          2、域名部分:該URL的域名部分為“www.baidu.com”。一個(gè)URL中,也可以使用IP地址作為域名使用

          3、端口部分:跟在域名后面的是端口,域名和端口之間使用“:”作為分隔符。端口不是一個(gè)URL必須的部分,如果省略端口部分,將采用默認(rèn)端口80

          4、虛擬目錄部分:從域名后的第一個(gè)“/”開始到最后一個(gè)“/”為止,是虛擬目錄部分。虛擬目錄也不是一個(gè)URL必須的部分。本例中的虛擬目錄是“/news/”

          5、文件名部分:從域名后的最后一個(gè)“/”開始到“?”為止,是文件名部分,如果沒有“?”,則是從域名后的最后一個(gè)“/”開始到“#”為止,是文件部分,如果沒有“?”和“#”,那么從域名后的最后一個(gè)“/”開始到結(jié)束,都是文件名部分。本例中的文件名是“index.asp”。文件名部分也不是一個(gè)URL必須的部分,如果省略該部分,則使用默認(rèn)的文件名

          6、錨部分:從“#”開始到最后,都是錨部分。本例中的錨部分是“name”。錨部分也不是一個(gè)URL必須的部分

          7、參數(shù)部分:從“?”開始到“#”為止之間的部分為參數(shù)部分,又稱搜索部分、查詢部分。本例中的參數(shù)部分為“boardID=5&ID=24618&page=1”。參數(shù)可以允許有多個(gè)參數(shù),參數(shù)與參數(shù)之間用“&”作為分隔符。

          URL的輸入到瀏覽器解析的一系列事件

          很多大公司面試喜歡問這樣一道面試題,輸入U(xiǎn)RL到看見頁面發(fā)生了什么?,今天我們來總結(jié)一下。 簡單來說,共有以下幾個(gè)過程

          • DNS解析
          • 發(fā)起TCP連接
          • 發(fā)送HTTP請(qǐng)求
          • 服務(wù)器處理請(qǐng)求并返回HTTP報(bào)文
          • 瀏覽器解析渲染頁面
          • 連接結(jié)束。

          下面我們來看看具體的細(xì)節(jié)

          通過DNS解析域名的實(shí)際IP地址

          發(fā)送至 DNS 服務(wù)器并獲得域名對(duì)應(yīng)的 WEB 服務(wù)器的 ip 地址。
          DNS 解析首先會(huì)從你的瀏覽器的緩存中去尋找是否有這個(gè)網(wǎng)址對(duì)應(yīng)的 IP 地址,如果沒有就向OS系統(tǒng)的 DNS 緩存中尋找,如果沒有就是路由器的 DNS 緩存, 如果沒有就是 ISP 的DNS 緩存中尋找。 所以,緩存的尋找過程就是: 瀏覽器 -> 系統(tǒng) -> 路由器 -> ISP。 如果在某一個(gè)緩存中找到的話,就直接跳到下一步。 如果都沒有找到的話,就會(huì)向 ISP 或者公共的域名解析服務(wù)發(fā)起 DNS 查找請(qǐng)求。這個(gè)查找的過程還是一個(gè)遞歸查詢的過程。

          輸入www.google.com網(wǎng)址后,首先在本地的域名服務(wù)器中查找,沒找到去根域名服務(wù)器查找,沒有再去com頂級(jí)域名服務(wù)器查找,,如此的類推下去,直到找到IP地址,然后把它記錄在本地,供下次使用。大致過程就是. -> .com -> google.com. -> www.google.com.。 (你可能覺得我多寫 .,并木有,這個(gè).對(duì)應(yīng)的就是根域名服務(wù)器,默認(rèn)情況下所有的網(wǎng)址的最后一位都是.,既然是默認(rèn)情況下,為了方便用戶,通常都會(huì)省略,瀏覽器在請(qǐng)求DNS的時(shí)候會(huì)自動(dòng)加上)

          DNS優(yōu)化

          既然已經(jīng)懂得了解析的具體過程,我們可以看到上述一共經(jīng)過了N個(gè)過程,每個(gè)過程有一定的消耗和時(shí)間的等待,因此我們得想辦法解決一下這個(gè)問題!

          DNS緩存

          DNS存在著多級(jí)緩存,從離瀏覽器的距離排序的話,有以下幾種: 瀏覽器緩存,系統(tǒng)緩存,路由器緩存,IPS服務(wù)器緩存,根域名服務(wù)器緩存,頂級(jí)域名服務(wù)器緩存,主域名服務(wù)器緩存。

          在你的chrome瀏覽器中輸入:chrome://dns/,你可以看到chrome瀏覽器的DNS緩存。

          系統(tǒng)緩存主要存在/etc/hosts(Linux系統(tǒng))中

          檢查瀏覽器是否有緩存

          通過Cache-ControlExpires來檢查是否命中強(qiáng)緩存,命中則直接取本地磁盤的html(狀態(tài)碼為200 from disk(or memory) cache,內(nèi)存or磁盤);


          如果沒有命中強(qiáng)緩存,則會(huì)向服務(wù)器發(fā)起請(qǐng)求(先進(jìn)行下一步的TCP連接),服務(wù)器通過
          EtagLast-Modify來與服務(wù)器確認(rèn)返回的響應(yīng)是否被更改(協(xié)商緩存),若無更改則返回狀態(tài)碼(304 Not Modified),瀏覽器取本地緩存;


          若強(qiáng)緩存和協(xié)商緩存都沒有命中則返回請(qǐng)求結(jié)果。

          DNS負(fù)載均衡

          不知道你們有沒有注意這樣一件事,你訪問http://baidu.com的時(shí)候,每次響應(yīng)的并非是同一個(gè)服務(wù)器(IP地址不同),一般大公司都有成百上千臺(tái)服務(wù)器來支撐訪問,假設(shè)只有一個(gè)服務(wù)器,那它的性能和存儲(chǔ)量要多大才能支撐這樣大量的訪問呢?DNS可以返回一個(gè)合適的機(jī)器的IP給用戶,例如可以根據(jù)每臺(tái)機(jī)器的負(fù)載量,該機(jī)器離用戶地理位置的距離等等,這種過程就是DNS負(fù)載均衡

          與 WEB 服務(wù)器建立 TCP 連接。

          TCP 協(xié)議通過三次握手建立連接。

          • 客戶端通過 SYN 報(bào)文段發(fā)送連接請(qǐng)求,確定服務(wù)端是否開啟端口準(zhǔn)備連接。狀態(tài)設(shè)置為 SYN_SEND;
          • 服務(wù)器如果有開著的端口并且決定接受連接,就會(huì)返回一個(gè) SYN+ACK 報(bào)文段給客戶端,狀態(tài)設(shè)置為 SYN_RECV
          • 客戶端收到服務(wù)器的 SYN+ACK 報(bào)文段,向服務(wù)器發(fā)送 ACK 報(bào)文段表示確認(rèn)。此時(shí)客戶端和服務(wù)器都設(shè)置為 ESTABLISHED 狀態(tài)。連接建立,可以開始數(shù)據(jù)傳輸了。

          翻譯成大白話就是:

          1. 客戶端:你能接收到我的消息嗎?
          2. 服務(wù)端:可以的,那你能接收到我的回復(fù)嗎?
          3. 客戶端:可以,那我們開始聊正事吧。

          為什么是3次?:避免歷史連接,確認(rèn)客戶端發(fā)來的請(qǐng)求是這次通信的人。
          為什么不是4次?:3次夠了第四次浪費(fèi)

          建立連接的過程是利用客戶服務(wù)器模式,假設(shè)主機(jī)A為客戶端,主機(jī)B為服務(wù)器端。

          采用三次握手是為了防止失效的連接請(qǐng)求報(bào)文段突然又傳送到主機(jī)B,因而產(chǎn)生錯(cuò)誤。失效的連接請(qǐng)求報(bào)文段是指:主機(jī)A發(fā)出的連接請(qǐng)求沒有收到主機(jī)B的確認(rèn),于是經(jīng)過一段時(shí)間后,主機(jī)A又重新向主機(jī)B發(fā)送連接請(qǐng)求,且建立成功,順序完成數(shù)據(jù)傳輸。考慮這樣一種特殊情況,主機(jī)A第一次發(fā)送的連接請(qǐng)求并沒有丟失,而是因?yàn)榫W(wǎng)絡(luò)節(jié)點(diǎn)導(dǎo)致延遲達(dá)到主機(jī)B,主機(jī)B以為是主機(jī)A又發(fā)起的新連接,于是主機(jī)B同意連接,并向主機(jī)A發(fā)回確認(rèn),但是此時(shí)主機(jī)A根本不會(huì)理會(huì),主機(jī)B就一直在等待主機(jī)A發(fā)送數(shù)據(jù),導(dǎo)致主機(jī)B的資源浪費(fèi)。

          采用兩次握手不行,原因就是上面說的失效的連接請(qǐng)求的特殊情況。而在三次握手中, client和server都有一個(gè)發(fā)syn和收ack的過程, 雙方都是發(fā)后能收, 表明通信則準(zhǔn)備工作OK.

          為什么不是四次握手呢? 大家應(yīng)該知道通信中著名的藍(lán)軍紅軍約定, 這個(gè)例子說明, 通信不可能100%可靠, 而上面的三次握手已經(jīng)做好了通信的準(zhǔn)備工作, 再增加握手, 并不能顯著提高可靠性, 而且也沒有必要。

          三次握手

          第一次握手

          客戶端發(fā)送syn包(Seq=x)到服務(wù)器,并進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器確認(rèn);

          第二次握手:

          服務(wù)器收到syn包,必須確認(rèn)客戶的SYN(ack=x+1),同時(shí)自己也發(fā)送一個(gè)SYN包(Seq=y),即SYN+ACK包,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài);

          第三次握手:

          客戶端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認(rèn)包ACK(ack=y+1),此包發(fā)送完畢,客戶端和服務(wù)器進(jìn)入ESTABLISHED狀態(tài),完成三次握手。

          握手過程中傳送的包里不包含數(shù)據(jù),三次握手完畢后,客戶端與服務(wù)器才正式開始傳送數(shù)據(jù)。理想狀態(tài)下,TCP連接一旦建立,在通信雙方中的任何一方主動(dòng)關(guān)閉連接之前,TCP 連接都將被一直保持下去。

          若協(xié)議是https則會(huì)做加密

          HTTPS=HTTP + 加密 + 認(rèn)證 + 完整性保護(hù)

          要先申請(qǐng)CA證書,并安裝在服務(wù)器上(一個(gè)文件,配置nginx支持監(jiān)聽443端口開啟ssl并設(shè)置證書路徑)

          瀏覽器發(fā)送請(qǐng)求;

          網(wǎng)站從瀏覽器發(fā)過來的加密規(guī)則中選一組自身也支持的加密算法和hash算法,并向?yàn)g覽器發(fā)送帶有公鑰的證書,當(dāng)然證書還包含了很多信息,如網(wǎng)站地址、證書的頒發(fā)機(jī)構(gòu)、過期時(shí)間等。

          瀏覽器解析證書。

          驗(yàn)證證書的合法性。如頒發(fā)機(jī)構(gòu)是否合法、證書中的網(wǎng)站地址是否與訪問的地址一致,若不合法,則瀏覽器提示證書不受信任,若合法,瀏覽器會(huì)顯示一個(gè)小鎖頭。

          若合法,或用戶接受了不合法的證書,瀏覽器會(huì)生成一串隨機(jī)數(shù)的密碼(即密鑰),并用證書中提供的公鑰加密。

          使用約定好的hash計(jì)算握手消息,并使用生成的隨機(jī)數(shù)(即密鑰)對(duì)消息進(jìn)行加密,最后將之前生成的所有消息一并發(fā)送給網(wǎng)站服務(wù)器。

          網(wǎng)站服務(wù)器解析消息。用已有的私鑰將密鑰解密出來,然后用密鑰解密發(fā)過來的握手消息,并驗(yàn)證是否跟瀏覽器傳過來的一致。然后再用密鑰加密一段握手消息,發(fā)送給瀏覽器。

          瀏覽器解密并計(jì)算握手消息的HASH,如果與服務(wù)端發(fā)來的HASH一致,此時(shí)握手過程結(jié)束,之后所有的通信數(shù)據(jù)將由之前瀏覽器生成的隨機(jī)密碼并利用對(duì)稱加密算法進(jìn)行加密。這里瀏覽器與網(wǎng)站互相發(fā)送加密的握手消息并驗(yàn)證,目的是為了保證雙方都獲得了一致的密碼,并且可以正常的加密解密數(shù)據(jù),為后續(xù)真正數(shù)據(jù)的傳輸做一次測試。

          發(fā)送HTTP請(qǐng)求
          首先科補(bǔ)一個(gè)小知識(shí),HTTP的端口為80/8080,而HTTPS的端口為443

          發(fā)送HTTP請(qǐng)求的過程就是構(gòu)建HTTP請(qǐng)求報(bào)文并通過TCP協(xié)議中發(fā)送到服務(wù)器指定端口 請(qǐng)求報(bào)文由
          請(qǐng)求行請(qǐng)求抱頭請(qǐng)求正文組成。
          請(qǐng)求行
          請(qǐng)求行的格式為
          Method Request-URL HTTP-Version CRLF eg: GET index.html HTTP/1.1 常用的方法有: GET, POST, PUT, DELETE, OPTIONS, HEAD
          常見的請(qǐng)求方法區(qū)別
          這里主要展示
          POSTGET的區(qū)別
          常見的區(qū)別

          • GET在瀏覽器回退時(shí)是無害的,而POST會(huì)再次提交請(qǐng)求。
          • GET產(chǎn)生的URL地址可以被Bookmark,而POST不可以。
          • GET請(qǐng)求會(huì)被瀏覽器主動(dòng)cache,而POST不會(huì),除非手動(dòng)設(shè)置。
          • GET請(qǐng)求只能進(jìn)行url編碼,而POST支持多種編碼方式。
          • GET請(qǐng)求參數(shù)會(huì)被完整保留在瀏覽器歷史記錄里,而POST中的參數(shù)不會(huì)被保留。
          • GET請(qǐng)求在URL中傳送的參數(shù)是有長度限制的,而POST么有。
          • 對(duì)參數(shù)的數(shù)據(jù)類型,GET只接受ASCII字符,而POST沒有限制。
          • GET比POST更不安全,因?yàn)閰?shù)直接暴露在URL上,所以不能用來傳遞敏感信息。
          • GET參數(shù)通過URL傳遞,POST放在Request body中。

          注意一點(diǎn)你也可以在GET里面藏body,POST里面帶參數(shù)

          重點(diǎn)區(qū)別

          GET會(huì)產(chǎn)生一個(gè)TCP數(shù)據(jù)包,而POST會(huì)產(chǎn)生兩個(gè)TCP數(shù)據(jù)包。
          詳細(xì)的說就是:

          • 對(duì)于GET方式的請(qǐng)求,瀏覽器會(huì)把http header和data一并發(fā)送出去,服務(wù)器響應(yīng)200(返回?cái)?shù)據(jù));
          • 而對(duì)于POST,瀏覽器先發(fā)送header,服務(wù)器響應(yīng)100 continue,瀏覽器再發(fā)送data,服務(wù)器響應(yīng)200 ok(返回?cái)?shù)據(jù))。

          注意一點(diǎn),并不是所有的瀏覽器都會(huì)發(fā)送兩次數(shù)據(jù)包,F(xiàn)irefox就發(fā)送一次

          請(qǐng)求報(bào)頭
          請(qǐng)求報(bào)頭允許客戶端向服務(wù)器傳遞請(qǐng)求的附加信息和客戶端自身的信息。

          從圖中可以看出,請(qǐng)求報(bào)頭中使用了Accept, Accept-Encoding, Accept-Language, Cache-Control, Connection, Cookie等字段。Accept用于指定客戶端用于接受哪些類型的信息,Accept-Encoding與Accept類似,它用于指定接受的編碼方式。Connection設(shè)置為Keep-alive用于告訴客戶端本次HTTP請(qǐng)求結(jié)束之后并不需要關(guān)閉TCP連接,這樣可以使下次HTTP請(qǐng)求使用相同的TCP通道,節(jié)省TCP連接建立的時(shí)間。

          請(qǐng)求正文
          當(dāng)使用POST, PUT等方法時(shí),通常需要客戶端向服務(wù)器傳遞數(shù)據(jù)。這些數(shù)據(jù)就儲(chǔ)存在請(qǐng)求正文中。在請(qǐng)求包頭中有一些與請(qǐng)求正文相關(guān)的信息,例如: 現(xiàn)在的Web應(yīng)用通常采用Rest架構(gòu),請(qǐng)求的數(shù)據(jù)格式一般為json。這時(shí)就需要設(shè)置
          Content-Type: application/json
          更重要的事情-HTTP緩存
          HTTP屬于客戶端緩存,我們常認(rèn)為瀏覽器有一個(gè)緩存數(shù)據(jù)庫,用來保存一些靜態(tài)文件,下面我們分為以下幾個(gè)方面來簡單介紹HTTP緩存

          • 緩存的規(guī)則
          • 緩存的方案
          • 緩存的優(yōu)點(diǎn)
          • 不同刷新的請(qǐng)求執(zhí)行過程

          緩存的規(guī)則
          緩存規(guī)則分為
          強(qiáng)制緩存協(xié)商緩存


          強(qiáng)制緩存
          當(dāng)緩存數(shù)據(jù)庫中有客戶端需要的數(shù)據(jù),客戶端直接將數(shù)據(jù)從其中拿出來使用(如果數(shù)據(jù)未失效),當(dāng)緩存服務(wù)器沒有需要的數(shù)據(jù)時(shí),客戶端才會(huì)向服務(wù)端請(qǐng)求。

          協(xié)商緩存

          又稱對(duì)比緩存。客戶端會(huì)先從緩存數(shù)據(jù)庫拿到一個(gè)緩存的標(biāo)識(shí),然后向服務(wù)端驗(yàn)證標(biāo)識(shí)是否失效,如果沒有失效服務(wù)端會(huì)返回304,這樣客戶端可以直接去緩存數(shù)據(jù)庫拿出數(shù)據(jù),如果失效,服務(wù)端會(huì)返回新的數(shù)據(jù)

          強(qiáng)制緩存
          對(duì)于強(qiáng)制緩存,服務(wù)器響應(yīng)的header中會(huì)用兩個(gè)字段來表明——Expires和Cache-Control。


          Expires
          Exprires的值為服務(wù)端返回的數(shù)據(jù)到期時(shí)間。當(dāng)再次請(qǐng)求時(shí)的請(qǐng)求時(shí)間小于返回的此時(shí)間,則直接使用緩存數(shù)據(jù)。但由于服務(wù)端時(shí)間和客戶端時(shí)間可能有誤差,這也將導(dǎo)致緩存命中的誤差,另一方面,Expires是HTTP1.0的產(chǎn)物,故現(xiàn)在大多數(shù)使用Cache-Control替代。


          Cache-Control
          Cache-Control有很多屬性,不同的屬性代表的意義也不同。

          • private:客戶端可以緩存
          • public:客戶端和代理服務(wù)器都可以緩存
          • max-age=t:緩存內(nèi)容將在t秒后失效
          • no-cache:需要使用協(xié)商緩存來驗(yàn)證緩存數(shù)據(jù)
          • no-store:所有內(nèi)容都不會(huì)緩存。

          協(xié)商緩存
          協(xié)商緩存需要進(jìn)行對(duì)比判斷是否可以使用緩存。瀏覽器第一次請(qǐng)求數(shù)據(jù)時(shí),服務(wù)器會(huì)將緩存標(biāo)識(shí)與數(shù)據(jù)一起響應(yīng)給客戶端,客戶端將它們備份至緩存中。再次請(qǐng)求時(shí),客戶端會(huì)將緩存中的標(biāo)識(shí)發(fā)送給服務(wù)器,服務(wù)器根據(jù)此標(biāo)識(shí)判斷。若未失效,返回304狀態(tài)碼,瀏覽器拿到此狀態(tài)碼就可以直接使用緩存數(shù)據(jù)了。
          對(duì)于協(xié)商緩存來說,緩存標(biāo)識(shí)我們需要著重理解一下,下面我們將著重介紹它的兩種緩存方案。
          Last-Modified
          Last-Modified:服務(wù)器在響應(yīng)請(qǐng)求時(shí),會(huì)告訴瀏覽器資源的最后修改時(shí)間。

          • if-Modified-Since:瀏覽器再次請(qǐng)求服務(wù)器的時(shí)候,請(qǐng)求頭會(huì)包含此字段,后面跟著在緩存中獲得的最后修改時(shí)間。服務(wù)端收到此請(qǐng)求頭發(fā)現(xiàn)有if-Modified-Since,則與被請(qǐng)求資源的最后修改時(shí)間進(jìn)行對(duì)比,如果一致則返回304和響應(yīng)報(bào)文頭,瀏覽器只需要從緩存中獲取信息即可。

          從字面上看,就是說:從某個(gè)時(shí)間節(jié)點(diǎn)算起,是否文件被修改了

          • 如果真的被修改:那么開始傳輸響應(yīng)一個(gè)整體,服務(wù)器返回:200 OK
          • 如果沒有被修改:那么只需傳輸響應(yīng)header,服務(wù)器返回:304 Not Modified
          • if-Unmodified-Since:從字面上看, 就是說: 從某個(gè)時(shí)間點(diǎn)算起, 是否文件沒有被修改
            • 如果沒有被修改:則開始`繼續(xù)'傳送文件: 服務(wù)器返回: 200 OK
            • 如果文件被修改:則不傳輸,服務(wù)器返回: 412 Precondition failed (預(yù)處理錯(cuò)誤)

          這兩個(gè)的區(qū)別是一個(gè)是修改了才下載一個(gè)是沒修改才下載。
          Last-Modified 說好卻也不是特別好,因?yàn)槿绻诜?wù)器上,一個(gè)資源被修改了,但其實(shí)際內(nèi)容根本沒發(fā)生改變,會(huì)因?yàn)長ast-Modified時(shí)間匹配不上而返回了整個(gè)實(shí)體給客戶端(即使客戶端緩存里有個(gè)一模一樣的資源)。為了解決這個(gè)問題,HTTP1.1推出了Etag。


          Etag
          Etag:服務(wù)器響應(yīng)請(qǐng)求時(shí),通過此字段告訴瀏覽器當(dāng)前資源在服務(wù)器生成的唯一標(biāo)識(shí)(生成規(guī)則由服務(wù)器決定)

          • If-None-Match:再次請(qǐng)求服務(wù)器時(shí),瀏覽器的請(qǐng)求報(bào)文頭部會(huì)包含此字段,后面的值為在緩存中獲取的標(biāo)識(shí)。服務(wù)器接收到次報(bào)文后發(fā)現(xiàn)If-None-Match則與被請(qǐng)求資源的唯一標(biāo)識(shí)進(jìn)行對(duì)比。
            • 不同,說明資源被改動(dòng)過,則響應(yīng)整個(gè)資源內(nèi)容,返回狀態(tài)碼200。
            • 相同,說明資源無心修改,則響應(yīng)header,瀏覽器直接從緩存中獲取數(shù)據(jù)信息。返回狀態(tài)碼304.

          但是實(shí)際應(yīng)用中由于Etag的計(jì)算是使用算法來得出的,而算法會(huì)占用服務(wù)端計(jì)算的資源,所有服務(wù)端的資源都是寶貴的,所以就很少使用Etag了。


          緩存的優(yōu)點(diǎn)

          • 減少了冗余的數(shù)據(jù)傳遞,節(jié)省寬帶流量
          • 減少了服務(wù)器的負(fù)擔(dān),大大提高了網(wǎng)站性能
          • 加快了客戶端加載網(wǎng)頁的速度 這也正是HTTP緩存屬于客戶端緩存的原因。

          不同刷新的請(qǐng)求執(zhí)行過程
          瀏覽器地址欄中寫入U(xiǎn)RL,回車

          • 瀏覽器發(fā)現(xiàn)緩存中有這個(gè)文件了,不用繼續(xù)請(qǐng)求了,直接去緩存拿。(最快)

          F5

          • F5就是告訴瀏覽器,別偷懶,好歹去服務(wù)器看看這個(gè)文件是否有過期了。于是瀏覽器就戰(zhàn)戰(zhàn)兢兢的發(fā)送一個(gè)請(qǐng)求帶上If-Modify-since。

          Ctrl+F5

          • 告訴瀏覽器,你先把你緩存中的這個(gè)文件給我刪了,然后再去服務(wù)器請(qǐng)求個(gè)完整的資源文件下來。于是客戶端就完成了強(qiáng)行更新的操作.

          服務(wù)器處理請(qǐng)求并返回HTTP報(bào)文
          它會(huì)對(duì)TCP連接進(jìn)行處理,對(duì)HTTP協(xié)議進(jìn)行解析,并按照?qǐng)?bào)文格式進(jìn)一步封裝成HTTP Request對(duì)象,供上層使用。這一部分工作一般是由Web服務(wù)器去進(jìn)行,我使用過的Web服務(wù)器有Tomcat, Nginx和Apache等等 HTTP報(bào)文也分成三份,
          狀態(tài)碼響應(yīng)報(bào)頭響應(yīng)報(bào)文


          狀態(tài)碼
          狀態(tài)碼是由3位數(shù)組成,第一個(gè)數(shù)字定義了響應(yīng)的類別,且有五種可能取值:

          • 1xx:指示信息–表示請(qǐng)求已接收,繼續(xù)處理。
          • 2xx:成功–表示請(qǐng)求已被成功接收、理解、接受。
          • 3xx:重定向–要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的操作。
          • 4xx:客戶端錯(cuò)誤–請(qǐng)求有語法錯(cuò)誤或請(qǐng)求無法實(shí)現(xiàn)。
          • 5xx:服務(wù)器端錯(cuò)誤–服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求。

          平時(shí)遇到比較常見的狀態(tài)碼有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500
          常見狀態(tài)碼區(qū)別
          200 成功
          請(qǐng)求成功,通常服務(wù)器提供了需要的資源。
          204 無內(nèi)容
          服務(wù)器成功處理了請(qǐng)求,但沒有返回任何內(nèi)容。
          301 永久移動(dòng)
          請(qǐng)求的網(wǎng)頁已永久移動(dòng)到新位置。 服務(wù)器返回此響應(yīng)(對(duì) GET 或 HEAD 請(qǐng)求的響應(yīng))時(shí),會(huì)自動(dòng)將請(qǐng)求者轉(zhuǎn)到新位置。
          302 臨時(shí)移動(dòng)
          服務(wù)器目前從不同位置的網(wǎng)頁響應(yīng)請(qǐng)求,但請(qǐng)求者應(yīng)繼續(xù)使用原有位置來進(jìn)行以后的請(qǐng)求。
          304 未修改
          自從上次請(qǐng)求后,請(qǐng)求的網(wǎng)頁未修改過。 服務(wù)器返回此響應(yīng)時(shí),不會(huì)返回網(wǎng)頁內(nèi)容。
          400 錯(cuò)誤請(qǐng)求
          服務(wù)器不理解請(qǐng)求的語法。
          401 未授權(quán)
          請(qǐng)求要求身份驗(yàn)證。 對(duì)于需要登錄的網(wǎng)頁,服務(wù)器可能返回此響應(yīng)。
          403 禁止
          服務(wù)器拒絕請(qǐng)求。
          404 未找到
          服務(wù)器找不到請(qǐng)求的網(wǎng)頁。
          422 無法處理
          請(qǐng)求格式正確,但是由于含有語義錯(cuò)誤,無法響應(yīng)
          500 服務(wù)器內(nèi)部錯(cuò)誤
          服務(wù)器遇到錯(cuò)誤,無法完成請(qǐng)求。
          響應(yīng)報(bào)頭
          常見的響應(yīng)報(bào)頭字段有: Server, Connection...。
          響應(yīng)報(bào)文
          你從服務(wù)器請(qǐng)求的HTML,CSS,JS文件就放在這里面

          瀏覽器解析渲染頁面

          就是Webkit解析渲染頁面的過程。

          • 解析HTML形成DOM樹
          • 解析CSS形成CSSOM 樹
          • 合并DOM樹和CSSOM樹形成渲染樹
          • 瀏覽器開始渲染并繪制頁面

          這個(gè)過程涉及兩個(gè)比較重要的概念回流重繪,DOM結(jié)點(diǎn)都是以盒模型形式存在,需要瀏覽器去計(jì)算位置和寬度等,這個(gè)過程就是回流。等到頁面的寬高,大小,顏色等屬性確定下來后,瀏覽器開始繪制內(nèi)容,這個(gè)過程叫做重繪。瀏覽器剛打開頁面一定要經(jīng)過這兩個(gè)過程的,但是這個(gè)過程非常非常非常消耗性能,所以我們應(yīng)該盡量減少頁面的回流和重繪

          瀏覽器解析執(zhí)行js腳本

          這個(gè)過程中可能會(huì)有dom操作、ajax發(fā)起的http網(wǎng)絡(luò)請(qǐng)求等。

          瀏覽器發(fā)起網(wǎng)絡(luò)請(qǐng)求

          web-socket、ajax等,這個(gè)過程通常是為了獲取數(shù)據(jù)

          服務(wù)器響應(yīng)ajax請(qǐng)求

          • ajax請(qǐng)求在到達(dá)真正的server之前,可能還會(huì)經(jīng)過網(wǎng)關(guān)全線校驗(yàn)、消息隊(duì)列或nginx等負(fù)載均衡處理
          • 到達(dá)server后,后端會(huì)解析http請(qǐng)求報(bào)文,得到url、請(qǐng)求參數(shù)、http頭、cookie等等信息
          • 登錄校驗(yàn)、權(quán)限校驗(yàn)(cookie校驗(yàn)、jwt權(quán)限校驗(yàn)等)
          • 可能會(huì)查詢數(shù)據(jù)庫,進(jìn)行常用的CRUD(增刪改查)等操作
          • 返回響應(yīng)數(shù)據(jù)

          瀏覽器處理事件循環(huán)等異步邏輯。

          setTimeout、setInterval、Promise等宏任務(wù)、微任務(wù)隊(duì)列

          性能優(yōu)化之回流重繪

          回流

          當(dāng)Render Tree中部分或全部元素的尺寸、結(jié)構(gòu)、或某些屬性發(fā)生改變時(shí),瀏覽器重新渲染部分或全部文檔的過程稱為回流。

          會(huì)導(dǎo)致回流的操作:

          • 頁面首次渲染
          • 瀏覽器窗口大小發(fā)生改變
          • 元素尺寸或位置發(fā)生改變
          • 元素內(nèi)容變化(文字?jǐn)?shù)量或圖片大小等等)
          • 元素字體大小變化
          • 添加或者刪除可見的DOM元素
          • 激活CSS偽類(例如::hover)
          • 查詢某些屬性或調(diào)用某些方法

          一些常用且會(huì)導(dǎo)致回流的屬性和方法:

          • clientWidth、clientHeight、clientTop、clientLeft
          • offsetWidth、offsetHeight、offsetTop、offsetLeft
          • scrollWidth、scrollHeight、scrollTop、scrollLeft
          • scrollIntoView()、scrollIntoViewIfNeeded()
          • getComputedStyle()
          • getBoundingClientRect()
          • scrollTo()

          重繪

          當(dāng)頁面中元素樣式的改變并不影響它在文檔流中的位置時(shí)(例如:color、background-color、visibility等),瀏覽器會(huì)將新樣式賦予給元素并重新繪制它,這個(gè)過程稱為重繪。

          優(yōu)化

          CSS

          • 避免使用table布局。
          • 盡可能在DOM樹的最末端改變class。
          • 避免設(shè)置多層內(nèi)聯(lián)樣式。
          • 將動(dòng)畫效果應(yīng)用到position屬性為absolute或fixed的元素上。
          • 避免使用CSS表達(dá)式(例如:calc())。

          JavaScript

          • 避免頻繁操作樣式,最好一次性重寫style屬性,或者將樣式列表定義為class并一次性更改class屬性。
          • 避免頻繁操作DOM,創(chuàng)建一個(gè)documentFragment,在它上面應(yīng)用所有DOM操作,最后再把它添加到文檔中。
          • 也可以先為元素設(shè)置display: none,操作結(jié)束后再把它顯示出來。因?yàn)樵赿isplay屬性為none的元素上進(jìn)行的DOM操作不會(huì)引發(fā)回流和重繪。
          • 避免頻繁讀取會(huì)引發(fā)回流/重繪的屬性,如果確實(shí)需要多次使用,就用一個(gè)變量緩存起來。
          • 對(duì)具有復(fù)雜動(dòng)畫的元素使用絕對(duì)定位,使它脫離文檔流,否則會(huì)引起父元素及后續(xù)元素頻繁回流。

          JS的解析

          JS的解析是由瀏覽器的JS引擎完成的。由于JavaScript是單線程運(yùn)行,也就是說一個(gè)時(shí)間只能干一件事,干這件事情時(shí)其他事情都有排隊(duì),但是有些人物比較耗時(shí)(例如IO操作),所以將任務(wù)分為同步任務(wù)異步任務(wù),所有的同步任務(wù)放在主線程上執(zhí)行,形成執(zhí)行棧,而異步任務(wù)等待,當(dāng)執(zhí)行棧被清空時(shí)才去看看異步任務(wù)有沒有東西要搞,有再提取到主線程執(zhí)行,這樣往復(fù)循環(huán)(冤冤相報(bào)何時(shí)了,阿彌陀佛),就形成了Event Loop事件循環(huán),下面來看看大人物

          Event Loop

          先看一段代碼

          setTimeout(function(){
              console.log('定時(shí)器開始啦')
          });
          
          new Promise(function(resolve){
              console.log('馬上執(zhí)行for循環(huán)啦');
              for(var i=0; i < 10000; i++){
                  i==99 && resolve();
              }
          }).then(function(){
              console.log('執(zhí)行then函數(shù)啦')
          });
          
          console.log('代碼執(zhí)行結(jié)束');
          

          結(jié)果我想大家都應(yīng)該知道。主要來介紹JavaScript的解析,至于Promise等下一節(jié)再說

          JavaScript

          JavaScript是一門單線程語言,盡管H5中提出了Web-Worker,能夠模擬實(shí)現(xiàn)多線程,但本質(zhì)上還是單線程,說它是多線程就是扯淡。

          事件循環(huán)

          既然是單線程,每個(gè)事件的執(zhí)行就要有順序,比如你去銀行取錢,前面的人在進(jìn)行,后面的就得等待,要是前面的人弄個(gè)一兩個(gè)小時(shí),估計(jì)后面的人都瘋了,因此,瀏覽器的JS引擎處理JavaScript時(shí)分為同步任務(wù)異步任務(wù)

          這張圖我們可以清楚看到

          • 同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行"場所",同步的進(jìn)入主線程,異步的進(jìn)入Event Table并注冊函數(shù)。
          • 當(dāng)指定的事情完成時(shí),Event Table會(huì)將這個(gè)函數(shù)移入Event Queue。
          • 主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去Event Queue讀取對(duì)應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行。
          • 上述過程會(huì)不斷重復(fù),也就是常說的Event Loop(事件循環(huán))。

          js引擎存在monitoring process進(jìn)程,會(huì)持續(xù)不斷的檢查主線程執(zhí)行棧是否為空,一旦為空,就會(huì)去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)。 估計(jì)看完這些你對(duì)事件循環(huán)有一定的了解,但是事實(shí)上我們看對(duì)的沒這么簡單,通常我們會(huì)看到Promise,setTimeout,process.nextTick(),這個(gè)時(shí)候你和我就懵逼。

          除了同步任務(wù)和異步任務(wù),我們還分為宏任務(wù)和微任務(wù),常見的有以下幾種

          • macro-task(宏任務(wù)):包括整體代碼script,setTimeout,setInterval
          • micro-task(微任務(wù)):Promise,process.nextTick

          不同任務(wù)會(huì)進(jìn)入不同的任務(wù)隊(duì)列來執(zhí)行。 JS引擎開始工作后,先在宏任務(wù)中開始第一次循環(huán)(script里面先執(zhí)行,不過我喜歡把它拎出來,直接稱其進(jìn)入執(zhí)行棧),當(dāng)主線程執(zhí)行棧全部任務(wù)被清空后去微任務(wù)看看,如果有等待執(zhí)行的任務(wù),執(zhí)行全部的微任務(wù)(其實(shí)將其回調(diào)函數(shù)推入執(zhí)行棧來執(zhí)行),再去宏任務(wù)找最先進(jìn)入隊(duì)列的任務(wù)執(zhí)行,執(zhí)行這個(gè)任務(wù)后再去主線程執(zhí)行任務(wù)(例如執(zhí)行```console.log("hello world")這種任務(wù)),執(zhí)行棧被清空后再去微任務(wù),這樣往復(fù)循環(huán)(冤冤相報(bào)何時(shí)了)

          Tip:微任務(wù)會(huì)全部執(zhí)行,而宏任務(wù)會(huì)一個(gè)一個(gè)來執(zhí)行

          下面來看一段代碼

          setTimeout(function() {
              console.log('setTimeout');
          })
          
          new Promise(function(resolve) {
              console.log('promise');
              resolve();
          }).then(function() {
              console.log('then');
          })
          
          console.log('console');
          

          我們看看它的執(zhí)行情況

          • 第一輪
            • 這段代碼進(jìn)入主線程
            • 遇到setTimeout,將其回調(diào)函數(shù)注冊后分發(fā)到宏任務(wù)
          • 第二輪
            • 遇到Promise,new Promise立即執(zhí)行(這個(gè)不解釋,想了解的我后續(xù)文章會(huì)介紹),輸出promise,遇到then,將其分發(fā)到微任務(wù)
          • 第三輪
            • 遇到console.log("console"),直接輸出console
          • 第四輪
            • 主線程執(zhí)行棧已經(jīng)清空,先去微任務(wù)看看,執(zhí)行then函數(shù),輸出then
          • 第五輪
            • 微任務(wù)執(zhí)行完了,看看宏任務(wù),有個(gè)setTimeout,輸出setTimeout,整體執(zhí)行完畢。

          具體的執(zhí)行過程大致就是這樣。

          文本標(biāo)記語言(HTML)是用于在 Internet 上顯示 Web 頁面的主要標(biāo)記語言。換句話說,網(wǎng)頁由 HTML 組成,用于通過 Web 瀏覽器顯示文本,圖像或其他資源。所有 HTML 都是純文本,這意味著它不是編譯的,可以由人類閱讀。HTML 文件的文件擴(kuò)展名為.htm 或.html。

          自1990年以來,HTML就一直被用作WWW的信息表示語言,使用HTML語言描述的文件需要通過WWW瀏覽器顯示出效果。HTML是一種建立網(wǎng)頁文件的語言,通過標(biāo)記式的指令(Tag),將影像、聲音、圖片、文字動(dòng)畫、影視等內(nèi)容顯示出來。事實(shí)上,每一個(gè)HTML文檔都是一種靜態(tài)的網(wǎng)頁文件,這個(gè)文件里面包含了HTML指令代碼,這些指令代碼并不是一種程序語言,只是一種排版網(wǎng)頁中資料顯示位置的標(biāo)記結(jié)構(gòu)語言,易學(xué)易懂,非常簡單。

          HTML的普遍應(yīng)用就是帶來了超文本的技術(shù)―通過單擊鼠標(biāo)從一個(gè)主題跳轉(zhuǎn)到另一個(gè)主題,從一個(gè)頁面跳轉(zhuǎn)到另一個(gè)頁面,與世界各地主機(jī)的文件鏈接超文本傳輸協(xié)議規(guī)定了瀏覽器在運(yùn)行HTML文檔時(shí)所遵循的規(guī)則和進(jìn)行的操作。HTTP協(xié)議的制定使瀏覽器在運(yùn)行超文本時(shí)有了統(tǒng)一的規(guī)則和標(biāo)準(zhǔn)。

          萬維網(wǎng)(world wide web)上的一個(gè)超媒體文檔稱之為一個(gè)頁面(外語:page)。作為一個(gè)組織或者個(gè)人在萬維網(wǎng)上放置開始點(diǎn)的頁面稱為主頁(外語:Homepage)或首頁,主頁中通常包括有指向其他相關(guān)頁面或其他節(jié)點(diǎn)的指針(超級(jí)鏈接),所謂超級(jí)鏈接,就是一種統(tǒng)一資源定位器(Uniform Resource Locator,外語縮寫:URL)指針,通過激活(點(diǎn)擊)它,可使瀏覽器方便地獲取新的網(wǎng)頁。這也是HTML獲得廣泛應(yīng)用的最重要的原因之一。在邏輯上將視為一個(gè)整體的一系列頁面的有機(jī)集合稱為網(wǎng)站(Website或Site)。超級(jí)文本標(biāo)記語言(英文縮寫:HTML)是為“網(wǎng)頁創(chuàng)建和其它可在網(wǎng)頁瀏覽器中看到的信息”設(shè)計(jì)的一種標(biāo)記語言。

          當(dāng) Web 開發(fā)人員構(gòu)建應(yīng)用程序時(shí),工作在服務(wù)器上執(zhí)行,原始 HTML 將發(fā)送給用戶。使用 AJAX 等技術(shù),服務(wù)器端開發(fā)和客戶端開發(fā)之間的界限很模糊。

          HTML 從未被設(shè)計(jì)用于當(dāng)今存在的 Web,因?yàn)樗皇且环N在控制和設(shè)計(jì)方面具有嚴(yán)重限制的標(biāo)記語言。已經(jīng)使用了許多技術(shù)來解決這個(gè)問題 – 最重要的是層疊樣式表(CSS)。

          長期解決方案是(或希望是)HTML5,它是下一代 HTML,允許更多的控制和交互性。與 Web 上的任何開發(fā)一樣,遷移到標(biāo)準(zhǔn)是一個(gè)緩慢而艱巨的過程,Web 開發(fā)人員和設(shè)計(jì)人員必須使用當(dāng)前和支持的技術(shù),這意味著基本 HTML 將繼續(xù)使用一段時(shí)間。


          主站蜘蛛池模板: 日本免费一区二区三区最新| 少妇无码一区二区三区免费| 无码少妇一区二区三区芒果| 丰满岳妇乱一区二区三区| 国产亚洲一区二区三区在线观看| 国产人妖在线观看一区二区| 91精品一区二区三区在线观看| 久久精品免费一区二区三区| 视频一区二区三区在线观看| 视频一区视频二区在线观看| 亚洲综合色一区二区三区| 国产精品香蕉在线一区| 在线播放偷拍一区精品| 亚洲AV无码一区二区二三区软件| 精品国产日产一区二区三区| 波多野结衣中文一区| 国产一区二区三区小向美奈子| 无码国产精品一区二区免费式直播 | 日本精品3d动漫一区二区| 国产成人一区二区三区视频免费| 亚洲日本一区二区一本一道| 色天使亚洲综合一区二区| 一区二区三区四区电影视频在线观看 | 国产福利在线观看一区二区| 人妻av无码一区二区三区| 91午夜精品亚洲一区二区三区 | 狠狠爱无码一区二区三区| 无码人妻一区二区三区兔费| 国产精品分类视频分类一区| 国产综合视频在线观看一区 | 国产一区二区三区不卡在线观看 | 日本一区二区三区中文字幕| 国产suv精品一区二区6| 久夜色精品国产一区二区三区| 在线观看国产一区| 国产无人区一区二区三区| 91国在线啪精品一区| 精品国产AⅤ一区二区三区4区 | 日本中文一区二区三区亚洲| 中文字幕无码一区二区免费| 无码人妻aⅴ一区二区三区有奶水 人妻夜夜爽天天爽一区 |