Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 综合久久久久综合,最近中文字幕视频国语中文字幕,国产成人精品视频2021

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          如何搭建一個高可用的服務端渲染工程

          如何搭建一個高可用的服務端渲染工程

          能大家在看到這個標題的時候,會覺得,只不過又是一篇爛大街的 SSR 從零入門的教程而已。別急,往下看,相信你或多或少會有一些不一樣的收獲呢。

          在落地一種技術的時候,我們首先要想一想:

          1. 是否一定需要引入這種技術呢?他能解決什么問題,或者能帶來什么收益?
          2. 為什么要采用這種技術選型而不是其他的?
          3. 引入了這種技術后,會帶來什么問題嗎(比如額外的開發成本等)?

          上面三個問題思考清楚之后,才能真正地去落地。上面三個問題思考清楚之后,才能真正地去落地。而有贊教育接入服務端渲染,正是為了優化 H5 頁面的首屏內容到達時間,帶來更好的用戶體驗(順便利于 SEO)。

          說了這么多,以下開始正文。

          一、后端模版引擎時代

          在較早時期,前后端的配合模式為:后端負責服務層、業務邏輯層和模版渲染層(表現層);前端只是實現頁面的交互邏輯以及發送 AJAX。比較典型的例子就是 JSP 或 FreeMarker 模板引擎負責渲染出 html 字符串模版,字符串模版里的 js 靜態資源才是真正前端負責的東西。

          而這種形式,就是天然的服務端渲染模式:用戶請求頁面 -> 請求發送到應用服務器 -> 后端根據用戶和請求信息獲取底層服務 -> 根據服務返回的數據進行組裝,同時 JSP 或 FreeMarker 模版引擎根據組裝的數據渲染為 html 字符串 -> 應用服務器講 html 字符串返回給瀏覽器 -> 瀏覽器解析 html 字符串渲染 UI 及加載靜態資源 -> js 靜態資源加載完畢界面可交互。

          那么既然后端模版引擎時代帶來的效果就是我們想要的,那為啥還有以后讓前端發展服務端渲染呢?因為很明顯,這種模式從開發角度來講還有挺多的問題,比如:

          1. 后端需要寫表現層的邏輯,但其實后端更應該注重服務層(和部分業務邏輯層)。當然,其實也可以讓前端寫 JSP 或 FreeMarker,但從體驗上來說,肯定不如寫 JS 來的爽;
          2. 本地開發的時候,需要啟動后端環境,比如 Tomcat,影響開發效率,對前端也不友好;
          3. 所賦予前端的能力太少,使得前端需要的一些功能只能由后端提供,比如路由控制;
          4. 前后端耦合。

          二、SPA 時代

          后來,誕生了 SPA(Single Page Application),解決了上面說的部分問題:

          1. 后端不需要關心表現層的邏輯,只需要注重服務層和業務邏輯層就可以了,暴露出相應的接口供前端調用。這種模式也同時實現了前后端解耦。
          2. 本地開發的時候,前端只需要啟動一個本地服務,如:dev-server 就可以開始開發了。
          3. 賦予了前端更多的能力,比如前端的路由控制和鑒權,比如通過 SPA + 路由懶加載的模式可以帶來更好的用戶體驗。

          但同時,也帶來了一些問題:

          1. 頁面的 DOM 完全由 js 來渲染,使得大部分搜索引擎無法爬取渲染后真實的 DOM,不利于 SEO。
          2. 頁面的首屏內容到達時間強依賴于 js 靜態資源的加載(因為 DOM 的渲染由 js 來執行),使得在網絡越差的情況下,白屏時間大幅上升。

          三、服務端渲染

          正因為 SPA 帶來的一些問題(尤其是首屏白屏的問題),接入服務端渲染顯得尤為必要。// 終于講到服務端渲染這個重點了。

          而正是 Node 的發展和基于 Virtual DOM 的前端框架的出現,使得用 js 實現服務端渲染成為可能。因此在 SPA 的優勢基礎上,我們順便解決了因為 SPA 引入的問題:

          1. 服務端渲染的首屏直出,使得輸出到瀏覽器的就是完備的 html 字符串模板,瀏覽器可以直接解析該字符串模版,因此首屏的內容不再依賴 js 的渲染。
          2. 正是因為服務端渲染輸出到瀏覽器的是完備的 html 字符串,使得搜索引擎能抓取到真實的內容,利于 SEO。
          3. 同時,通過基于 Node 和前端 MVVM 框架結合的服務端渲染,有著比后端模版引擎的服務端渲染更明顯的優勢:可以優雅降級為客戶端渲染(這個后續會講,先占個坑)。

          3.1 實現

          既然服務端渲染能帶來這么多好處,那具體怎么實現呢?從官網給出的原理圖,我們可以清晰地看出:

          • Source 為我們的源代碼區,即工程代碼;
          • Universal Appliation Code 和我們平時的客戶端渲染的代碼組織形式完全一致,只是需要注意這些代碼在 Node 端執行過程觸發的生命周期鉤子不要涉及 DOM 和 BOM 對象即可;
          • 比客戶端渲染多出來的 app.js、Server entry 、Client entry 的主要作用為:app.js 分別給 Server entry 、Client entry 暴露出 createApp() 方法,使得每個請求進來會生成新的 app 實例。而 Server entry 和 Client entry 分別會被 webpack 打包成 vue-ssr-server-bundle.json 和 vue-ssr-client-manifest.json(這兩個 json 文件才是有用的,app.js、Server entry 、Client entry 可以抽離,開發者不感知);
          • Node 端會根據 webpack 打包好的 vue-ssr-server-bundle.json,通過調用 createBundleRenderer 生成 renderer 實例,再通過調用 renderer.renderToString 生成完備的 html 字符串;
          • Node 端將 render 好的 html 字符串返回給 Browser,同時 Node 端根據 vue-ssr-client-manifest.json 生成的 js 會和 html 字符串 hydrate,完成客戶端激活 html,使得頁面可交互。

          3.2 優化

          按照 Vue SSR 官方文檔建立起一個服務端渲染的工程后,是否就可以直接上線了呢?別急,我們先看看是否有什么可以優化的地方。

          3.2.1 路由和代碼分割

          一個大的 SPA,主文件 js 往往很大,通過代碼分割可以將主文件 js 拆分為一個個單獨的路由組件 js 文件,可以很大程度上減小首屏的資源加載體積,其他路由組件可以預加載。

           復制代碼

          // router.js
          constIndex=()=>import(/* webpackChunkName: "index" */'./pages/Index.vue');
          constDetail=()=>import(/* webpackChunkName: "detail" */'./pages/Detail.vue');
          constroutes=[
          {
          path:'/',
          component: Index
          },
          {
          path:'/detail',
          component: Detail
          }
          ];
          constrouter=newRouter({
          mode:'history',
          routes
          });
          

          3.2.2 部分模塊(不需要 SSR 的模塊)客戶端渲染

          因為服務端渲染是 CPU 密集型操作,非首屏的模塊或者不重要的模塊(比如底部的推薦列表)完全可以采用客戶端渲染,只有首屏的核心模塊采用服務端渲染。這樣做的好處是明顯的:1. 較大地節省 CPU 資源;2. 減小了服務端渲染直出的 html 字符串長度,能夠更快地響應給瀏覽器,減小白屏時間。

           復制代碼

          // Index.vue
          asyncData({ store }) {
          returnthis.methods.dispatch(store);// 核心模塊數據預取,服務端渲染
          }
          mounted() {
          this.initOtherModules();// 非核心模塊,客戶端渲染,在 mounted 生命周期鉤子里觸發
          }
          

          3.2 3 頁面緩存 / 組件緩存

          頁面緩存一般適用于狀態無關的靜態頁面,命中緩存直接返回頁面;組件緩存一般適用于純靜態組件,也可以一定程度上提升性能。

           復制代碼

          // page-level caching
          constmicroCache=LRU({
          max:100,
          maxAge:1000// 重要提示:條目在 1 秒后過期。
          })
          server.get('*', (req, res)=> {
          consthit=microCache.get(req.url)
          if(hit) {// 命中緩存,直接返回頁面
          returnres.end(hit)
          }
          // 服務端渲染邏輯
          ...
          })
          

           復制代碼

          // component-level caching
          // server.js
          constLRU=require('lru-cache')
          constrenderer=createRenderer({
          cache: LRU({
          max:10000,
          maxAge: ...
          })
          });
          // component.js
          exportdefault{
          name:'item',// 必填選項
          props: ['item'],
          serverCacheKey:props=>props.item.id,
          render (h) {
          returnh('div',this.item.id)
          }
          };
          

          3.2.4 頁面靜態化

          如果工程中大部分頁面都是狀態相關的,所以技術選型采用了服務端渲染,但有部分頁面是狀態無關的,這個時候用服務端渲染就有點浪費資源了。像這些狀態無關的頁面,完全可以通過 Nginx Proxy Cache 緩存到 Nginx 服務器,可以避免這些流量打到應用服務器集群,同時也能減少響應的時間。

          3.3 降級

          進行優化之后,是否就可以上線了呢?這時我們想一想,萬一服務端渲染出錯了怎么辦?萬一服務器壓力飆升了怎么辦(因為服務端渲染是 CPU 密集型操作,很耗 CPU 資源)?為了保證系統的高可用,我們需要設計一些降級方案來避免這些。具體可以采用的降級方案有:

          • 單個流量降級 – 偶發的服務端渲染失敗降級為客戶端渲染
          • Disconf / Apollo 配置降級 – 分布式配置平臺修改配置主動降級,比如可預見性的大流量情況下(雙十一),可提前通過配置平臺將整個應用集群都降級為客戶端渲染
          • CPU 閾值降級 – 物理機 / Docker 實例 CPU 資源占用達到閾值觸發降級,避免負載均衡服務器在某些情況下給某臺應用服務器導入過多流量,使得單臺應用服務器的 CPU 負載過高
          • 旁路系統降級 – 旁路系統跑定時任務監控應用集群狀態,集群資源占用達到設定閾值將整個集群降級(或觸發集群的自動擴容)
          • 渲染服務集群降級 – 若渲染服務和接口服務是獨立的服務,當渲染服務集群宕機,html 的獲取邏輯回溯到 Nginx 獲取,此時觸發客戶端渲染,通過 ajax 調用接口服務獲取數據

          3.4 上線前準備

          3.4.1 壓測

          壓測可以分為多個階段:本地開發階段、QA 性能測試階段、線上階段。

          • 本地開發階段:當本地的服務端渲染開發完成之后,首先需要用 loadtest 之類的壓測工具壓下性能如何,同時可以根據壓測出來的數據做一些優化,如果有內存泄漏之類的 bug 也可以在這個階段就能被發現。
          • QA 性能測試階段:當通過本地開發階段的壓測之后,我們的代碼已經是經過性能優化且沒有內存泄漏之類嚴重 bug 的。部署到 QA 性能測試環境之后,通過壓真實 QA 環境,和原來的客戶端渲染做對比,看 QPS 會下降多少(因為服務端渲染耗更多的 CPU 資源,所以 QPS 對比客戶端渲染肯定會有下降)。
          • 線上階段:QA 性能測試階段壓測過后,若性能指標達到原來的預期,部署到線上環境,同時可以開啟一定量的壓測,確保服務的可用性。

          3.4.2 日志

          作為生產環境的應用,肯定不能“裸奔”,必須接入日志平臺,將一些報錯信息收集起來,以便之后問題的排查。

          3.4.3 灰度

          如果上線服務端渲染的工程是提供核心服務的應用,應該采用灰度發布的方式,避免全量上線。一般灰度方案可以采用:百分比灰度、白名單灰度、自定義標簽灰度。具體采用哪種灰度方式看場景自由選擇,每隔一段時間觀察灰度集群沒有問題,所以漸漸增大灰度比例 / 覆蓋范圍,直到全量發布。

          3.5 落地

          在有贊電商的服務端渲染的落地場景中,我們抽離了單獨的依賴包,提供各個能力。

          3.6 效果

          從最終的上線效果來看,相同功能的頁面,服務端渲染的首屏內容時間比客戶端渲染提升了 300%+。

          3.7 Q & A

          Q1:為什么服務端渲染就比客戶端渲染快呢?

          A:首先我們明確一點,服務端渲染比客戶端渲染快的是首屏的內容到達時間(而非首屏可交互時間)。至于為什么會更快,我們可以從兩者的 DOM 渲染過程來對比:

          客戶端渲染:瀏覽器發送請求 -> CDN / 應用服務器返回空 html 文件 -> 瀏覽器接收到空 html 文件,加載的 css 和 js 資源 -> 瀏覽器發送 css 和 js 資源請求 -> CDN / 應用服務器返回 css 和 js 文件 -> 瀏覽器解析 css 和 js -> js 中發送 ajax 請求到 Node 應用服務器 -> Node 服務器調用底層服務后返回結果 -> 前端拿到結果 setData 觸發 vue 組件渲染 -> 組件渲染完成

          服務端渲染:瀏覽器發送請求 -> Node 應用服務器匹配路由 -> 數據預取:Node 服務器調用底層服務拿到 asyncData 存入 store -> Node 端根據 store 生成 html 字符串返回給瀏覽器 -> 瀏覽器接收到 html 字符串將其激活:

          我們可以很明顯地看出,客戶端渲染的組件渲染強依賴 js 靜態資源的加載以及 ajax 接口的返回時間,而通常一個 page.js 可能會達到幾十 KB 甚至更多,很大程度上制約了 DOM 生成的時間。而服務端渲染從用戶發出一次頁面 url 請求之后,應用服務器返回的 html 字符串就是完備的計算好的,可以交給瀏覽器直接渲染,使得 DOM 的渲染不再受靜態資源和 ajax 的限制。

          Q2:服務端渲染有哪些限制?

          A:比較常見的限制比如:

          1. 因為渲染過程是在 Node 端,所以沒有 DOM 和 BOM 對象,因此不要在常見的 Vue 的 beforeCreate 和 created 生命周期鉤子里做涉及 DOM 和 BOM 的操作
          2. 對第三方庫的要求比較高,如果想直接在 Node 渲染過程中調用第三方庫,那這個庫必須支持服務端渲染

          Q3:如果我的需求只是生成文案類的靜態頁面,需要用到服務端渲染嗎?

          A:像這些和用戶狀態無關的靜態頁面,完全可以采用預渲染的方式(具體見 Vue SSR 官方指南),服務端渲染適用的更多場景會是狀態相關的(比如用戶信息相關),需要經過 CPU 計算才能輸出完備的 html 字符串,因此服務端渲染是一個 CPU 密集型的操作。而靜態頁面完全不需要涉及任何復雜計算,通過預渲染更快且更節省 CPU 資源。

          、為什么xml

          需要服務器端返回少量的、單一的數據

          用戶名是否可用 1 / 0

          返回兩個數的和 400

          登錄是否成功 true/false

          數據插是否成功 true/false

          如果我們需要從服務器端返回大量、復雜的數據,如何實現?

          xml:服務器端返回xml數據

          json:服務器端返回json數據

          2、格式:

          (1)php解析xml

          $dom=new DOMDocument();
          $dom->loadXML($str);
          $nd=$dom->getElementsByTagName("TagName");
          $value=$nd->item(0)->nodeValue
          $xml=simplexml_load_string($str);
          $first=$xml->first;
          $second=$xml->second;

          (2)js解析xml

          var xml=xmlHttp.responseXML;
          node=xml.getElementsByTagName("TagName");
          node[0].childNodes[0].nodeValue;

          3 、案例1:

          實現兩個數的四則運算

          HTML代碼:

          <script language="javascript" src="public.js"></script>
          <script>
          window.onload=function(){
                  $('btnOk').onclick=function(){
                  var f=$('first').value;
                  var s=$('second').value;
                  var data='first='+f+'&second='+s;
                  var xhr=createxhr();
                  xhr.open('post','demo01.php');
                  xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
                  xhr.onreadystatechange=function(){
                          if(xhr.readyState==4 && xhr.status==200){
                                  //xml --->xml dom對象
                                  var xml=xhr.responseXML;
                                  var str=xml.getElementsByTagName('jia')[0].childNodes[0].nodeValue;
                                  str+='|'+xml.getElementsByTagName('jian')[0].childNodes[0].nodeValue;
                                  str+='|'+xml.getElementsByTagName('cheng')[0].childNodes[0].nodeValue;
                                  str+='|'+xml.getElementsByTagName('chu')[0].childNodes[0].nodeValue;
                                  $('result').innerHTML=str;
                          }
                  };
                  xhr.send(data);
                  };
          };
          </script>
          <input type="text" id="first" /><br>
          <input type="text" id='second' /><br>
          <div id='result'></div>
          <input type="button" id="btnOk" value="計算" />

          理解:

          var xml=xhr.responseXML; 得到ajax返回的xmldom對象

          xml.getElementsByTagName('jia')[0] :是表示獲取jia這個元素

          xml.getElementsByTagName('jia')[0].childNodes:表示獲取jia元素下的所有子節點

          xml.getElementsByTagName('jia')[0].childNodes[0] :表示獲取jia元素下的唯一文本節點

          xml.getElementsByTagName('jia')[0].childNodes[0].nodeValue:文本節點的值

          php代碼:

          <?php
          $first=$_POST['first'];
          $second=$_POST['second'];
          $result1=$first+$second;
          $result2=$first-$second;
          $result3=$first*$second;
          $result4=$first/$second;
          //要想返回xml,首先連接一個xml格式的字符串
          $str='<root>';
          $str.='<jia>'.$result1.'</jia>';
          $str.='<jian>'.$result2.'</jian>';
          $str.='<cheng>'.$result3.'</cheng>';
          $str.='<chu>'.$result4.'</chu>';
          $str.='</root>';
          /*$str=<<<str
          <root>
          <jia>$result1</jia>
          </root>
          str;*/
          header('Content-type:text/xml');
          echo $str;

          理解:

          得到結果后,需要使用字符串連接成一個xml格式的字符串,如:需要一個根元素,下面子元素,最后是具體的值,

          連接時也可以使用<<<str創建xml字符串

          str;

          輸出這個字符串時,默認的響應內容類型:text/html,也就是說客戶端仍把代碼當做html來進行解析,

          ajax對象的responeXML是不能得到一個xmldom對象,必須設置響應頭類型為:text/xml,其代碼:header('Content-type:text/xml');

          public.js:

          function createxhr() {
                  /*var xhr;
                  var str=window.navigator.userAgent;
                  if (str.indexOf('MSIE') > 0) {
                  xhr=new ActiveXObject('Microsoft.XMLHTTP');
                  } else {
                  xhr=new XMLHttpRequest();
                  }
                  return xhr;*/
          
                  try{return new XMLHttpRequest();}catch(e){}
                  try{return new ActiveXObject('Microsoft.XMLHTTP'); }catch(e){}
                  alert('請更換瀏覽器!');
                  }
                  function $(id){
                  return document.getElementById(id);
          }

          4、案例2

          在頁面加載之后,將mysql數據庫goods表中所有數據顯示在表格中

          <root>
          <goods>
          <name>222</name>
          <price>55.00</price>
          </goods>
          <goods>
          <name>諾 E661</name>
          <price>205.00</price>
          </goods>
          <goods>
          <name>諾 E661</name>
          <price>200.00</price>
          </goods>
          </root>

          HTML代碼:

          <style>
          tr{
          background-color:#ffffff;
          height:30px;
          font-size:12px;
          }
          </style>
          <script language="javascript" src='public.js'></script>
          <script>
          window.onload=function(){
                  var xhr=createxhr();
                  xhr.open('post','demo02.php');
                  xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
                  xhr.onreadystatechange=function(){
                          if(xhr.readyState==4 && xhr.status==200)
                                  var xml=xhr.responseXML;
                                  var goods=xml.getElementsByTagName('goods');
                                  for(var i=0;i<goods.length;i++){
                                  //創建行元素
                                  var tr=document.createElement('tr');
                                  //創建序號td元素
                                  var tdID=document.createElement('td');
                                  tdID.innerHTML=i+1;
                                  //創建名稱td元素
                                  var tdName=document.createElement('td');
                                  tdName.innerHTML=goods[i].childNodes[0].childNodes[0].nodeValue;
                                  //創建價格td元素
                                  var tdPrice=document.createElement('td');
                                  tdPrice.innerHTML=goods[i].childNodes[1].childNodes[0].nodeValue;
                                  //將三個td追加到tr元素
                                  tr.appendChild(tdID);
                                  tr.appendChild(tdName);
                                  tr.appendChild(tdPrice);
                                  document.getElementsByTagName('TBODY')[0].appendChild(tr);
                          }
          };
          xhr.send(null);
          }
          </script>
          <table id='tbData' width="800" cellspacing="1" cellpadding="4" bgcolor="#336699">
          <tr>
          <td>序號</td>
          <td>商品名稱</td>
          <td>商品價格</td>
          </tr>
          </table>

          理解:

          創建行元素,

          創建單元格元素

          將單元格元素追加到行元素中

          將行元素追加到表格元素中

          php代碼:

          <?php
                  //查詢goods表中所有數據并返回
                  $sql="select name,price from goods order by id desc";
                  mysql_connect('localhost','root','111111');
                  mysql_select_db('shop');
                  mysql_query('set names gb2312');
                  $result=mysql_query($sql); //發送sql語句
                  $num=mysql_num_rows($result); //總行數
                  $str='<root>';
                  for($i=0;$i<$num;$i++){
                  $row=mysql_fetch_assoc($result);
                  $str.='<goods>';
                  $str.='<name>'.iconv('gb2312','utf-8',$row['name']).'</name>';
                  $str.='<price>'.$row['price'].'</price>';
                  $str.='</goods>';
                  }
                  $str.='</root>';
                  header('Content-Type:text/xml');
                  echo $str;
          ?>

          理解:

          查詢goods表中所有數據

          連接xml格式的字符串

          表中有多少條數據

          xml字符串就有幾對goods標簽

          其中, name字段出現中文,所以需要進行轉碼 gb2312--utf-8

          最后, 輸出xml字符串

          譯 | 核子可樂、Tina

          技術和軟件開發領域存在一種有趣的現象,就是同樣的模式迭起興衰、周而復始。

          htmx 的走紅

          過去Web非常簡單。URL 指向服務器,服務器將數據混合成 html,然后在瀏覽器上呈現該響應。圍繞這種簡單范式,誕生了各種Javascript框架,以前可能需要數月時間完成的一個應用程序基本功能,現在借助這些框架創建相對復雜的項目卻只需要數小時,我們節省了很多時間,從而可以將更多精力花在業務邏輯和應用程序設計上。

          但隨著 Web 不斷地發展,Javascript 失控了。不知何故,我們決定向用戶拋出大量 App,并在使用時發出不斷增加的網絡請求;不知何故,為了生成 html,我們必須使用 JSON,發出數十個網絡請求,丟棄我們在這些請求中獲得的大部分數據,用一個越來越不透明的 JavaScript 框架黑匣子將 JSON 轉換為 html,然后將新的 html 修補到 DOM 中......

          難道大家快忘記了我們可以在服務器上渲染 html 嗎?更快、更一致、更接近應用程序的實際狀態,并且不會向用戶設備發送任何不必要的數據?但是如果沒有 Javascript,我們必須在每次操作時重新加載頁面。

          現在,有一個新的庫出現了,摒棄了定制化的方法,這就是 htmx。作為 Web 開發未來理念的一種實現,它的原理很簡單:

          • 從任何用戶事件發出 AJAX 請求。
          • 讓服務器生成代表該請求的新應用程序狀態的 html。
          • 在響應中發送該 html。
          • 將該元素推到它應該去的 DOM 中。

          htmx 出現在 2020 年,創建者 Carson Gross 說 htmx 來源自他于 2013 年研究的一個項目 intercooler.js。2020 年,他重寫了不依賴 jQuery 的 intercooler.js,并將其重命名為 htmx。然后他驚訝的發現 Django 社區迅速并戲劇性地接受了它!

          圖片來源:https://lp.jetbrains.com/django-developer-survey-2021-486/

          Carson Gross 認為 htmx 設法抓住了開發者對現有 Javascript 框架不滿的浪潮,“這些框架非常復雜,并且經常將 Django 變成一個愚蠢的 JSON 生產者”,而 htmx 與開箱即用的 Django 配合得更好,因為它通過 html 與服務器交互,而 Django 非常擅長生成 html。

          對于 htmx 的迅速走紅,Carson Gross 發出了一聲感嘆:這真是“十年窗下無人問,一舉成名天下知(this is another example of a decade-long overnight success)”。

          htmx 的實際效果

          可以肯定的一點是 htmx 絕對能用,單從理論上講,這個方法確實值得稱道。但軟件問題終究要歸結于實踐效果:效果好嗎,能不能給前端開發帶來改善?

          在 DjangoCon 2022 上,Contexte 的 David Guillot 演示了他們在真實 SaaS 產品上實現了從 React 到 htmx 的遷移,而且效果非常好,堪稱“一切 htmx 演示之母”(視頻地址:https://www.youtube.com/watch?v=3GObi93tjZI)

          Contexte 的項目開始于 2017 年,其后端相當復雜,前端 UI 也非常豐富,但團隊非常小。所以他們在一開始的時候跟隨潮流選擇了 React 來“構建 API 綁定 SPA、實現客戶端狀態管理、前后端狀態分離”等。但實際應用中,因為 API 設計不當,DOM 樹太深,又需要加載很多信息,導致 UI“非常非常緩慢”。在敏捷開發的要求下,團隊里唯一的 Javascript 專家對項目的復雜性表現得一無所措,因此他們決定試試 htmx。

          九大數據提升

          于是我們決定大膽嘗試,花幾個月時間用簡單的 Django 模板和 htmx 替換掉了 SaaS 產品中已經使用兩年的 React UI。這里我們分享了一些相關經驗,公布各項具體指標,希望能幫同樣關注 htmx 的朋友們找到說服 CTO 的理由!

          1. 這項工作共耗費了約 2 個月時間(使用 21K 行代碼庫,主要是 JavaScript)
          2. 不會降低應用程序的用戶體驗(UX)
          3. 將代碼庫體積減小了 67%(由 21500 行削減至 7200 行)
          4. 將 Python 代碼量增加了 140%(由 500 行增加至 1200 行);這對更喜歡 Python 的開發者們應該是好事
          5. 將 JS 總體依賴項減少了 96%(由 255 個減少至 9 個)

          6. 將 Web 構建時間縮短了 88%(由 40 秒縮短至 5 秒)

          7. 首次加載交互時間縮短了 50% 至 60%(由 2 到 6 秒,縮短至 1 到 2 秒)

          8. 使用 htmx 時可以配合更大的數據集,超越 React 的處理極限

          9. Web 應用程序的內存使用量減少了 46%(由 75 MB 降低至 40 MB)

          這些數字令人頗為意外,也反映出 Contexte 應用程序高度契合超媒體的這一客觀結果:這是一款以內容為中心的應用程序,用于顯示大量文本和圖像。很明顯,其他 Web 應用程序在遷移之后恐怕很難有同樣夸張的提升幅度。

          但一些開發者仍然相信,大部分應用程序在采用超媒體 /htmx 方法之后,肯定也迎來顯著的改善,至少在部分系統中大受裨益。

          開發團隊組成

          可能很多朋友沒有注意,移植本身對團隊結構也有直接影響。在 Contexte 使用 React 的時候,后端與前端之間存在硬性割裂,其中兩位開發者全職管理后端,一位開發者單純管理前端,另有一名開發者負責“全棧”。(這里的「全棧」,代表這位開發者能夠輕松接手前端和后端工作,因此能夠在整個「棧」上獨立開發功能。)

          而在移植至 htmx 之后,整個團隊全都成了“全棧”開發人員。于是每位團隊成員都更高效,能夠貢獻出更多價值。這也讓開發變得更有樂趣,因為開發人員自己就能掌握完整功能。最后,轉向 htmx 也讓軟件優化度上了一個臺階,現在開發人員可以在棧內的任意位置進行優化,無需與其他開發者提前協調。

          htmx 是傳統思路的回歸

          如今,單頁應用(SPA)可謂風靡一時:配合 React、Redux 或 Angular 等庫的 JS 或 TS 密集型前端,已經成為創建 Web 應用程序的主流方式。以一個需要轉譯成 JS 的 SPA 應用為例:

          但 htmx 風潮已經襲來,人們開始強調一種“傻瓜客戶端”方法,即由服務器生成 html 本體并發送至客戶端,意味著 UI 事件會被發送至服務器進行處理。

          用這個例子進行前后對比,我們就會看到前者涉及的活動部件更多。從客戶端角度出發,后者其實回避了定制化客戶端技術,采取更簡單的方法將原本只作為數據引擎的服務器變成了視圖引擎。

          后一種方法被稱為 AJAX(異步 JavaScript 與 XML)。這種簡單思路能夠讓 Web 應用程序獲得更高的響應性體驗,同時消除了糟糕的“回發”(postback,即網頁完全刷新),由此回避了極其低效的“viewstate”等.NET 技術。

          htmx 在很多方面都體現出對 AJAX 思路的回歸,最大的區別就是它僅僅作為新的聲明性 html 屬性出現,負責指示觸發條件是什么、要發布到哪個端點等。

          另一個得到簡化的元素是物理應用程序的結構與構建管道。因為不再涉及手工編寫 JS,而且整個應用程序都基于服務器,因此不再對 JS 壓縮器、捆綁器和轉譯器做(即時)要求。就連客戶端項目也能解放出來,一切都由 Web 服務器項目負責完成,所有應用程序代碼都在.NET 之上運行。從這個角度來看,這與高度依賴服務器的 Blazor Server 編程模型倒是頗有異曲同工之妙。

          技術和軟件開發領域存在一種有趣的現象,就是同樣的模式迭起興衰、周而復始。隨著 SPA 的興起,人們一度以為 AJAX 已經過氣了,但其基本思路如今正卷土重來。這其中當然會有不同的權衡,例如更高的服務器負載和網絡流量(畢竟現在我們發送的是數據視圖,而不只是數據),但能讓開發者多個選擇肯定不是壞事。

          雖然不敢確定這種趨勢是否適用于包含豐富用戶體驗的高復雜度應用程序,但毫無疑問,相當一部分 Web 應用程序并不需要完整的 SPA 結構。對于這類用例,簡單的 htmx 應用程序可能就是最好的解決方案。

          參考鏈接:

          https://news.ycombinator.com/item?id=33218439

          https://www.reddit.com/r/django/comments/rxjlc6/htmx_gaining_popularity_rapidly/

          https://mekhami.github.io/2021/03/26/htmx-the-future-of-web/

          https://www.compositional-it.com/news-blog/more-on-htmx-back-to-the-future/

          聲明:本文為InfoQ編譯,未經許可禁止轉載。


          主站蜘蛛池模板: 精品无码av一区二区三区| 久久精品午夜一区二区福利| 日本一区二区三区久久| 一区二区三区视频在线播放| 亚洲一区二区三区国产精华液| 国产精品亚洲一区二区三区久久| 一区二区三区免费在线视频| 精品日本一区二区三区在线观看| 国产一区二区三区亚洲综合 | 国产一区二区三区影院| jazzjazz国产精品一区二区| 精品aⅴ一区二区三区| 亚洲AV无码一区二区二三区入口| 一区二区三区福利视频免费观看| 亚洲欧美日韩国产精品一区| 无码人妻精品一区二区三区99不卡 | 国产电影一区二区| 国产视频一区二区在线观看| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 在线观看精品视频一区二区三区| 内射白浆一区二区在线观看| 久久se精品动漫一区二区三区 | 无码日韩AV一区二区三区| 狠狠色婷婷久久一区二区三区| 久久91精品国产一区二区| 无码一区二区三区免费视频| 一区二区三区电影网| 日韩人妻一区二区三区蜜桃视频| 鲁丝丝国产一区二区| 久久4k岛国高清一区二区| 国产伦精品一区二区三区免费下载| 日韩人妻无码一区二区三区| 精品人妻无码一区二区三区蜜桃一 | 国产一区二区三区在线影院| 波多野结衣中文一区| 成人免费区一区二区三区 | 人妻在线无码一区二区三区| 香蕉免费看一区二区三区| 亚洲国产精品一区二区三区久久| 日韩伦理一区二区| 精品人妻无码一区二区色欲产成人|