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 亚州第一视频,国产精品久久久久激情影院,春色www视频在线观看

          整合營銷服務商

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

          免費咨詢熱線:

          不只是前端,后端、產品和測試也需要了解的瀏覽器知識(

          不只是前端,后端、產品和測試也需要了解的瀏覽器知識(二)

          上篇《 不只是前端,后端、產品和測試也需要了解的瀏覽器知識(一)》介紹了瀏覽器的基本情況、發展歷史以及市場占有率。

          本篇文章將介紹瀏覽器基本原理。在掌握基本原理后,通過技術深入,在研發過程中不斷創新,推動產品性能、用戶體驗的提升,來實現業務的增長,創造可持續的價值

          一、 業務系統呈現給用戶的節點

          當用戶訪問我們的業務系統時,瀏覽器和服務器之間會進行一系列復雜的交互過程。瀏覽器整體的導航流程如下:


          ??

          以下是用戶從輸入 URL 到看到業務系統頁面的詳細步驟:

          1. 輸入 URL 并解析

          1.1. 用戶輸入 URL 并按下回車鍵

          用戶在瀏覽器地址欄中輸入業務系統的 URL,例如 https://www.businesssystem.com,并按下回車鍵。

          1.2. 瀏覽器解析 URL

          瀏覽器解析輸入的 URL,確定協議(如 HTTPS)、主機名(如 www.businesssystem.com)、端口號(如果有)、路徑、查詢參數等。

          1.3. DNS 解析:


          ??

          瀏覽器需要將主機名轉換為 IP 地址。這個過程稱為 DNS 解析,通常包括以下步驟:

          ?瀏覽器首先檢查本地 DNS 緩存,看看是否有對應的 IP 地址。

          ?如果本地緩存中沒有,瀏覽器會向操作系統查詢。

          ?操作系統會檢查自己的緩存,并可能向本地的 DNS 服務器發出請求。

          ?本地 DNS 服務器可能會遞歸查詢其他 DNS 服務器,直到找到對應的 IP 地址。

          2. 建立連接、發送請求并接收響應

          2.1. 建立 TCP 連接

          一旦獲得了 IP 地址,瀏覽器會通過 TCP/IP 協議與服務器建立連接。對于 HTTPS,瀏覽器還會進行 SSL/TLS 握手,以建立安全連接。流程如下


          ??

          2.2. 發送 HTTP 請求

          連接建立后,瀏覽器會構建一個 HTTP 請求并發送給服務器。請求包括請求行(例如 GET /index.html HTTP/1.1)、請求頭(如 User-Agent、Accept 等)以及可能的請求體(對于 POST 請求)。

          2.3. 服務器處理請求

          服務器接收到請求后,會根據請求的內容進行處理:

          ?服務器解析請求,確定所需的資源(如 HTML 文件、圖片、數據等)。

          ?服務器可能需要與后端數據庫或其他服務進行交互,以生成響應內容。

          ?服務器構建 HTTP 響應,包括狀態行(如 HTTP/1.1 200 OK)、響應頭(如 Content-Type、Content-Length 等)和響應體(實際的頁面內容)。

          2.4. 發送 HTTP 響應

          服務器將構建好的 HTTP 響應發送回瀏覽器。

          2.5. 瀏覽器接收響應

          瀏覽器接收到服務器的響應后,會根據響應頭的信息處理響應體:

          ?如果響應是重定向(如 301 或 302),瀏覽器會根據 Location 頭再次發起請求。

          ?如果響應包含壓縮內容(如 gzip),瀏覽器會解壓縮。

          ?瀏覽器會根據 Content-Type 頭確定如何處理響應體(如 HTML、CSS、JavaScript、圖片等)。

          發送請求和接受響應流程如下:


          ??

          3. 解析和加載資源、渲染頁面

          3.1. 解析 HTML

          瀏覽器開始解析 HTML 文檔,構建 DOM 樹。解析過程中,瀏覽器會處理各種 HTML 標簽,并根據需要發起其他請求(如 CSS、JavaScript、圖片等)。

          3.2. 加載和執行資源

          ?CSS:瀏覽器解析 CSS 文件并構建 CSSOM 樹,與 DOM 樹結合形成渲染樹。

          ?JavaScript:瀏覽器解析和執行 JavaScript 代碼,可能會修改 DOM 樹或 CSSOM 樹。

          ?圖片和其他資源:瀏覽器會異步加載這些資源,并在加載完成后進行渲染。

          3.3. 渲染頁面

          瀏覽器根據渲染樹計算每個元素的布局(位置和大小),并將頁面繪制到屏幕上。這個過程可能會涉及多次重繪和重排(reflow/repaint),尤其是在 JavaScript 修改 DOM 或 CSS 的情況下。

          頁面渲染流程如下:


          ??

          4. 用戶交互

          頁面加載完成后,用戶可以與頁面進行交互。瀏覽器會響應用戶的操作(如點擊、輸入等),并可能通過 JavaScript 動態更新頁面內容。

          5. 小結

          業務系統的呈現過程主要是:URL解析、與服務器建立連接、服務器處理請求并返回響應、下載和解析響應、頁面渲染。


          二、 系統呈現過程中的技術點

          1. DNS解析優化

          1.1. 啟用 DNS 預解析

          ?DNS 預解析(DNS Prefetching):瀏覽器在用戶點擊鏈接之前,提前解析該鏈接的域名,從而減少等待時間。

          <link rel="dns-prefetch" href="//example.com">

          1.2. DNS緩存優化

          合理設置 DNS 記錄的 TTL(Time-To-Live),使得 DNS 記錄可以在客戶端和中間緩存服務器上保存適當的時間,減少重復解析請求。

          ?對于不經常變化的記錄,可以設置較長的 TTL 值(如 24 小時)。

          ?對于經常變化的記錄,可以設置較短的 TTL 值(如幾分鐘到幾小時)。

          1.3. 負載均衡和冗余

          ?負載均衡:使用 DNS 負載均衡技術,將流量分配到多臺服務器上,防止單點故障。

          ?冗余配置:配置多個權威 DNS 服務器,確保在一個服務器故障時,其他服務器可以繼續提供解析服務。

          1.4. 減少 DNS 查詢次數

          ?合并資源:盡量將資源放在同一個域名下,減少跨域名的 DNS 查詢次數。

          ?減少外部資源:盡量減少頁面中引用的外部資源(如第三方腳本和樣式),以減少額外的 DNS 查詢。

          2. http協議優化

          2.1. 請求方法優化

          ?使用合適的請求方法:確保使用正確的 HTTP 方法(GET、POST、PUT、DELETE 等)來表示操作的意圖。例如,使用 GET 方法獲取數據,使用 POST 方法提交數據。

          ?避免不必要的請求:合并請求,減少頁面中的請求次數。例如,CSS 和 JavaScript 文件可以合并,圖像可以使用精靈圖(sprite)。

          2.2. 狀態碼優化

          ?正確使用狀態碼:確保服務器返回正確的 HTTP 狀態碼。例如,200 表示成功,404 表示資源未找到,500 表示服務器錯誤。

          ?重定向優化:減少重定向次數,避免不必要的 301 或 302 重定向。

          2.3. 請求頭和響應頭優化

          ?壓縮傳輸內容:使用 Gzip 或 Brotli 壓縮傳輸內容,減少傳輸數據量。

          ?緩存控制:使用緩存控制頭(如 Cache-Control、Expires)來緩存靜態資源,減少重復請求。

          ?內容安全策略(CSP):設置內容安全策略頭,防止跨站腳本攻擊(XSS)。

          ?減少頭部大小:刪除不必要的請求和響應頭,減少頭部大小,加快傳輸速度。

          2.4. HTTP/2 和 HTTP/3 優化

          a. 多路復用

          ?啟用 HTTP/2 或 HTTP/3:這些協議支持多路復用,可以在一個 TCP 連接中同時發送多個請求和響應,減少延遲。

          ?減少域名分片:HTTP/2 和 HTTP/3 中,多路復用使得域名分片(將資源分布到多個子域名)不再必要,反而可能降低性能。

          b. 頭部壓縮

          ?使用 HPACK(HTTP/2)或 QPACK(HTTP/3)頭部壓縮:這些協議支持頭部壓縮,減少傳輸的數據量。

          c. 減少延遲

          ?使用優先級和依賴:HTTP/2 和 HTTP/3 支持請求優先級和依賴,確保關鍵資源優先加載。

          ?啟用 QUIC 協議(HTTP/3):QUIC 協議基于 UDP,減少了連接建立的延遲,提供更快的傳輸速度。

          4. CDN優化

          ?使用 CDN:將靜態資源分發到全球各地的節點,減少用戶訪問的延遲。

          ?邊緣計算:利用 CDN 的邊緣計算能力,在靠近用戶的節點上處理部分邏輯,減少服務器負載。

          ?靜態資源托管:將靜態資源(如圖像、CSS、JavaScript)托管在 CDN 上,減少網絡延遲,加快加載速度。


          ??

          5. 頁面渲染時優化

          5.1. HTML 優化

          a. 減少 DOM 復雜度

          ?簡化 HTML 結構:減少嵌套層級,避免過度復雜的 DOM 結構。

          ?刪除不必要的元素:移除無用的 HTML 標簽和注釋。

          b. 延遲加載非關鍵內容

          ?使用 defer 和 async:對非關鍵 JavaScript 文件使用 defer 或 async 屬性,避免阻塞頁面渲染。

          ?懶加載圖像和視頻:使用 loading="lazy" 屬性或 JavaScript 實現懶加載,延遲加載視口外的圖像和視頻。

          5.2. CSS 優化

          a. 減少 CSS 文件大小

          ?壓縮 CSS 文件:使用工具(如 CSSNano、CleanCSS)壓縮 CSS 文件,減少文件大小。

          ?移除未使用的 CSS:使用工具(如 PurgeCSS)移除未使用的 CSS 規則。

          b. 優化 CSS 加載

          ?使用外部樣式表:將 CSS 放在外部樣式表中,而不是內聯樣式,便于緩存和管理。

          ?放置 CSS 在 <head> 中: 確保 CSS 文件在 <head> 中加載,以便盡快渲染頁面。

          ?避免 CSS 阻塞渲染:將關鍵 CSS 內聯到 HTML 中,非關鍵 CSS 異步加載。

          5.3. JavaScript 優化

          a. 減少 JavaScript 文件大小

          ?壓縮和混淆:使用工具(如 UglifyJS、Terser)壓縮和混淆 JavaScript 文件,減少文件大小。

          ?移除未使用的代碼:使用工具(如 Webpack 的 Tree Shaking)移除未使用的代碼。

          b. 優化 JavaScript 加載

          ?分離關鍵和非關鍵腳本:將關鍵腳本放在 <head> 中,非關鍵腳本放在頁面底部或使用 defer 和 async。

          ?代碼分割:使用 Webpack 等工具進行代碼分割,按需加載模塊。

          c. 優化 JavaScript 執行

          ?減少重排和重繪:避免頻繁操作 DOM,使用文檔片段(Document Fragment)或虛擬 DOM 技術。

          ?使用節流和防抖:對高頻率事件(如滾動、輸入)使用節流(throttle)和防抖(debounce)技術,減少不必要的函數調用。

          ?減少 JavaScript 阻塞:避免長時間運行的 JavaScript 任務,使用 Web Workers 將復雜計算移到后臺線程。

          5.4. 圖像優化

          a. 減少圖像文件大小

          ?壓縮圖像:使用工具(如 ImageOptim、TinyPNG)壓縮圖像文件,減少文件大小。

          ?選擇合適的格式:根據圖像內容選擇合適的格式(如 JPEG、PNG、WebP),WebP 通常比 JPEG 和 PNG 更小。

          b. 優化圖像加載

          ?使用響應式圖像:使用 srcset 和 sizes 屬性提供不同分辨率的圖像,適應不同設備。

          ?懶加載圖像:使用 loading="lazy" 屬性或 JavaScript 實現圖像懶加載。

          5.5. 其他優化策略

          a. 優化字體加載

          ?使用字體顯示策略:使用 font-display 屬性控制字體加載行為,避免字體閃爍(FOIT)和不可見文本(FOUT)。

          ?減少字體文件大小:使用子集化工具(如 Google Fonts 的子集化功能)只加載需要的字符集,減少字體文件大小。

          6. 小結

          在實際業務中我們需要針對頁面呈現過程中的每一個節點,去制定不同的優化策略。

          三、總結

          本文主要介紹了業務系統呈現給用戶所經歷的各個節點,以及作為技術人能在各節點中進行優化的點, 通過這些技術優化點,在研發過程中不斷創新,推動產品性能、用戶體驗的提升,來實現業務的增長,創造可持續的價值。

          生 CSS 嵌套(Native CSS nesting)已經被所有現代桌面瀏覽器所支持!,但是請注意,移動端瀏覽器支持的還很有限。

          1.原生 CSS 嵌套

          原生 CSS 嵌套可以像 SASS、LESS 預處理器一樣,將相關的選擇器組合在一起,從而減少需要編寫的規則數量,它可以節省打字時間,并使語法更易于閱讀和維護。您可以將選擇器嵌套到任意深度,但要小心不要超過兩層或三層。嵌套深度沒有技術限制,但它會使代碼更難以閱讀,并且生成的 CSS 可能會變得不必要的冗長。

          .button {
             background-color: red;
          
             &.warning {
               background-color: blue;
             }
          
             & .icon {
                width: 1rem;
                height: 1rem;
             }
          }
          

          雖然原生 CSS 嵌套語法在過去幾年中不斷發展,使大多數 Web 開發人員感到滿意,但不要指望所有 SCSS 代碼都能像您期望的那樣直接工作。

          2.原生 CSS 嵌套規則

          您可以將任何選擇器嵌套在另一個選擇器中,但它必須以符號開頭,例如 &, .(類選擇器)、#(ID選擇器)、@(對于媒體查詢)、:::+~>[。換句話說,它不能是對 HTML 元素的直接引用。下面的代碼是無效的,不會對 input 元素選擇器進行解析:

          .parent {
            color: red;
          
            input {
              margin: 1em;
            }
          }
          /* Invalid, because "input" is an identifier. */
          

          解決此問題的最簡單方法是使用與號 ( &),它以與 Sass 相同的方式引用當前選擇器。

          2.1.& 符號的使用

          .parent {
            color: red;
          
            & input {
              margin: 1em;
            }
          
            /* use pseudo-elements and pseudo-classes */
            &::after {}
          
            &:hover {}
          
            &:target {}
          }
          /* valid, no longer starts with an identifier */
          

          或者,您可以使用其中之一:

          • > input:只對子元素生效
          • :is(input): 將選擇器列表作為參數,并選擇該列表中任意一個選擇器可以選擇的元素
          • :where(input):優先級總是為 0

          它們都可以在這個簡單的示例中工作,但是稍后您可能會遇到更復雜的樣式表的特異性問題。

          它還&允許您在父選擇器上定位偽元素和偽類。例如:

          p.my-element {
          
            &::after {}
          
            &:hover {}
          
            &:target {}
          }
          

          請注意,& 可以在選擇器中的任何位置使用。例如:

          .child1 {
            .parent3 & {
              color: red;
            }
          }
          

          這將轉換為以下非嵌套語法:

          .parent3 .child1 { color: red; }
          

          您甚至可以在選擇器中使用多個 & 符號:

          ul {
            & li & {
              color: blue;
            }
          }
          

          這將以嵌套 <ul> 元素 ( ul li ul) 為目標,但如果您想保持理智,我建議不要使用它!

          2.2.@ 符號的使用

          嵌套媒體查詢示例:

          p {
            color: cyan;
            @media (min-width: 800px) {
              color: purple;
            }
          }
          

          3.原生 CSS 嵌套陷阱

          3.1.場景一:父選擇器包裝在 :is() 中

          原生 CSS 嵌套將父選擇器包裝在 :is() 中,這可能會導致與 Sass 輸出的差異,比如以下嵌套代碼:

          .parent1, #parent2 {
            .child1 {
          
            }
          }
          

          當它在瀏覽器中解析時,它實際上變成以下內容:

          :is(.parent1, #parent2) .child1 {
          
          }
          

          Sass 將相同的代碼編譯為:

          .parent1 .child1,
          #parent2 .child1 {
          
          }
          

          3.2.場景二:& 符號后置

          您可能還會遇到一個更微妙的問題。考慮一下:

          .parent .child {
            .grandparent & {}
          }
          

          原生 CSS 等效項是:

          .grandparent :is(.parent .child) {}
          

          這與以下錯誤排序的 HTML 元素匹配:

          <div class="parent">
            <div class="grandparent">
              <div class="child">MATCH</div>
            </div>
          </div>
          
          

          MATCH變得有樣式是因為 CSS 解析器執行以下操作:

          它會查找所有元素,其所屬類的child祖先也parent為DOM 層次結構中的任何點。

          找到包含MATCH的元素后,解析器會grandparent在 DOM 層次結構中的任何位置再次檢查它是否具有 — 的祖先。它找到一個并相應地設置該元素的樣式。

          Sass 中的情況并非如此,它編譯為:

          .grandparent .parent .child {} 上面的 HTML 沒有樣式化,因為元素類不遵循嚴格的grandparent、parent、 和child順序。

          3.3.場景三:字符串替換

          Sass 使用字符串替換,因此如下所示的聲明是有效的,并且與類的任何元素相匹配 .btn-primary

          .btn {
            &-primary {
              color: blue;
            }
          }
          

          但是原生 CSS 嵌套會忽略&-space選擇器。

          4.CSS 預處理器還需要嗎?

          從短期來看,現有的 CSS 預處理器仍然至關重要。Sass 開發團隊宣布,他們將支持 .css 文件中的原生 CSS 嵌套,并按原樣輸出代碼。他們將一如既往地編譯嵌套 SCSS 代碼,以避免破壞現有代碼庫,但當全球瀏覽器支持率達到 98% 時,他們將開始輸出 :is() 選擇器。

          我猜想,PostCSS 插件等預處理器目前會擴展嵌套代碼,但隨著瀏覽器支持的普及,就會取消這一功能。當然,使用預處理器還有其他很好的理由,比如將部分代碼捆綁到一個文件中,以及對代碼進行精簡。但如果嵌套是你唯一需要的功能,你當然可以考慮在較小的項目中使用原生 CSS。

          總結

          CSS 嵌套是最有用、最實用的預處理器功能之一。瀏覽器供應商努力創造了一個與 CSS 非常相似的原生 CSS 版本,以滿足網絡開發人員的需求。雖然兩者之間存在細微差別,而且在使用(過于)復雜的選擇器時可能會遇到不尋常的特殊性問題,但很少有代碼庫需要進行徹底修改。

          原生嵌套可能會讓你重新考慮是否需要 CSS 預處理器,但它們仍能提供其他好處。Sass 和類似工具仍然是大多數開發者工具包的重要組成部分。

          峰(楚梟) 阿里開發者 2023-05-23 09:01 發表于浙江

          阿里妹導讀


          在日常開發中,遇到非常難以維護的頁面是常態,相信不少同學也為此苦惱過,筆者在業務開發中總結了些經驗希望對大家有所啟發。(后臺回復大數據即可獲得《大數據&AI實戰派》電子書)

          背景

          在日常開發中,遇到非常難以維護的頁面是常態,相信不少同學也為此苦惱過,筆者在業務開發中總結了些經驗希望對大家有所啟發。下圖是一個較為復雜的詳情頁、表單頁,我截取了其中一小部分作為示例:

          隨著需求不斷迭代,這個頁面的代碼變得越來越復雜,代碼達到幾千行,html 標簽嵌套層級非常深,每次想在正確的節點改東西、加元素都非常費眼睛;每次想修改、疊加業務邏輯,看到一堆 useEffect、useState、useRef 令人望而卻步。于是決定重構以改變現狀。

          如何重構,以拆解復雜頁面

          如何重構一個復雜前端頁面?筆者平常主要寫后端,實際工程中后端代碼的腐化很多都來源于 if-else 不斷疊加,要重構一般分幾個層次看:

          • 如果是某一個業務概念比較復雜造成的大量 if-else 嵌套,使用策略模式重構;
          • 如果是多個業務概念交織造成的復雜度,則用責任鏈模式梳理好每個層次;
          • 如果是更高的抽象層次,比如不同的業務身份有不同的執行鏈路,或者不同的業務身份都要做某件事但做法不同,那么最好使用模板方法設計模式定義好高層次的業務抽象和默認邏輯,不同的業務身份進行選擇性的重寫或業務邏輯擴展。

          大的思路如此,具體場景各有各的特殊性,需要靈活應對,這里也不過多展開。總之,后端的復雜度和各個場景的業務邏輯息息相關,垂直縱深很大,但前端呢?私以為前端雖然也有業務邏輯但不深,它的復雜不是來源于垂直縱深,而是水平堆積。一個頁面的內容經常又多又雜,有詳情、有表單、各種區塊、不同標簽頁,里面的內容我不贅述。那么重構的方向呼之欲出:使用組合的思想拆分水平堆積的業務邏輯塊。具體到 React,其實就是拆解業務、封裝組件,是一個組件化的過程。


          組件化

          其實前端整個 React App 說白了可以抽象成一個組件樹,如圖:

          筆者習慣將組件分成:基礎組件和區塊組件。

          • 基礎組件:具有一定業務屬性的小型組件單元。它有業務屬性,但比較弱,強調通用性。
          • 區塊組件:業務知識較重的大組件。它內聚了很多業務知識,通用性弱,甚至可以沒有通用性,強調業務的內聚性。

          按照基礎組件和區塊組件的劃分,我開始重構上圖詳情頁。

          組件封裝實踐

          我將頁面上的展示內容按照業務塊進行了劃分,自頂向上對業務區塊進行了重新的定義,如圖:

          劃分好了就開始封裝,列舉幾個組件的封裝示例。

          基礎組件:AliTalk IM

          AliTalk IM 組件定義

          接收方 IM 身份的 id 作為入參,返回 IM 展示組件,點擊 icon 則喚起聊天彈窗進行聊天操作,完成后可關閉彈窗。至于初始化聊天框、銷毀聊天框的邏輯,以及如何進行聊天,應該在組件內封裝好,外部業務不關心這些,主要代碼:

          type ChatProps={
            uid?: string;
          };
          const Chat: FC<ChatProps>=(data: ChatProps)=> {
            const [showChat, setShowChat]=useState(true);
            useEffect(()=> {
              console.log('init Alitalk: ' + data.uid);
              return ()=> {
                console.log('destroy Alitalk: ' + data.uid);
                setShowChat(false);
          
          
                const aliTalkMessageBox=document.getElementsByClassName('weblite-iframe');
                for (let i=0; i < aliTalkMessageBox.length; i++) {
                  const item=aliTalkMessageBox[i];
                  item.remove();
                }
              };
            }, []);
          
          
            return (
              <div>
                {showChat && (
                  <Alitalk uid={data.uid} pid={'xx'} bizType={1} bizId={'xx'} >
                    <img width={24} height={24}
                      src={
                        'https://img.alicdn.com/imgextra/i2/O1CN01acXzMG1d5JsurHGVR_!!6000000003684-2-tps-200-200.png'
                      }  />
                    <span style={{ marginLeft: '5px', color: '#FF6600' }}>chat now</span>
                  </Alitalk>
                )}
              </div>
            );
          };
          export default Chat;

          AliTalk IM 組件定義組件引用

          直接引入 <Chat> 標簽,一行代碼即可:

          <Descriptions style={{ marginBottom: 24 }} title="買家信息">
          
                <Descriptions.Item label="買家旺旺">
          
                      <Chat uid={detailData?.data?.buyerAliTalkId} />
          
                </Descriptions.Item>
          
          </Descriptions>

          區塊組件:操作欄行動點彈窗

          彈窗組件定義

          以移交服務單為例,點擊按鈕則喚起轉交表單彈窗,填完表單后提交則發起請求,完成后自動關閉彈窗。表單提交的邏輯,操作欄展示區塊并不關心,封裝一個 TransferOrderModalForm 組件內聚這些業務邏輯即可。

          彈窗組件引用

          <Fragment>
          
              <Button.Group>
          
                  <EstimatedQuotationModalForm orderId={detailData?.id} />
          
          
          
          
                  <DomesticWarehouseReceivingModalForm orderId={detailData?.id} />
          
          
          
          
                  <OfficialQuotationModalForm orderId={detailData?.id} />
          
          
          
          
                  <MarkOrderPaidModalForm orderId={detailData?.id} />
          
          
          
          
                  <MarkOrderExceptionModalForm orderId={detailData?.id} />
          
          
          
          
                  <MarkOrderClosedModalForm orderId={detailData?.id} />
          
          
          
          
                  {/* 移交服務單 ModalForm */}
          
                  <TransferOrderModalForm orderId={detailData?.id} />
          
               </Button.Group>
          
          </Fragment>

          按照組件拆分后,主頁面的代碼行數從幾千行降低到 200 行,主頁面僅僅只做了對其他組件的引用和頁面編排,其引用的業務區塊組件如果夠復雜,還能繼續再次拆分組件,整個頁面就成了一個掛載的組件樹,但每個區塊都只關心自己的業務抽象層次,符合 SLAP 原則。

          組件封裝的思考

          關于組件設計思想

          基礎組件應該做成原子能力,不要陷入業務場景中,參數要設計得普適性強一點,這樣設計出來的組件復用性強,比如聊天組件、獲取當前登錄用戶組件、鑒權組件等等,都符合這種情形。

          而業務區塊組件恰恰相反,完全沒必要考慮復用性,目標就是把不同業務抽象層次進行拆分、隔離,使得整個業務層層遞進,每個層次都只關心自己應該關心的業務,這樣設計出來的組件高內聚、易讀、易維護,當然,如果能復用那更好,算是增值收益了,但這不是目標。

          業務區塊組件應該自頂向下設計,開始的時候應該設計得粗粒度一點,隨著業務不斷的迭代可以慢慢下沉,而一開始就想設計精細,想要一步到位,反而會隨著后續的業務迭代不斷要打破進行調整,喪失了靈活性。

          關于前后端思想上的融會貫通

          雖然前端的組件和后端的類要怎么設計、怎么實現,看起來區別很大,但咱們剖析表象看本質,思想其實是一脈相承的,舉幾個例子:

          戰術上,React 現在推行的是函數式組件,給一組入參,返回展示元素,簡單的輸入輸出無副作用;后端也一樣,要盡量避免一個對象參數在不同的方法不同的節點被改來改去,最后改成了啥都不知道,不利于維護,也容易出 bug,所以很多 API 比如 Stream.map() 的設計都提倡不要把對象改來改去,而應該干凈利落的使用純函數。

          戰略上,SLAP 單一抽象層次原則從來都不針對是前端還是后端,前端組件也好,后端類也好,都要搞清楚每個業務層次關心的核心要素是什么,把不該關心的東西丟給其他業務層次完成,不要把編碼變成了一個翻譯業務需求的動作,而應該像畫家作畫一樣,先構圖再落筆。

          《黑客與畫家》中描繪了黑客與畫家的諸多相同點,“畫作永遠沒有完工的一天,你只是不再畫下去而已”。希望追求卓越的你能始終保持那份對設計的熱忱。


          主站蜘蛛池模板: 国产激情无码一区二区三区| 一区二区和激情视频| 精品国产一区AV天美传媒| 正在播放国产一区| 亚洲日韩激情无码一区| 国产伦精品一区二区三区视频小说| 最美女人体内射精一区二区| 国产精品视频一区| 中文字幕人妻无码一区二区三区| 日本无卡码免费一区二区三区| 精品人妻码一区二区三区 | 中文字幕一区在线观看| 欧美日韩一区二区成人午夜电影 | 国产精品一区二区久久沈樵| 亚洲AV无码一区二区二三区软件| 国产一区二区三区日韩精品| 日韩最新视频一区二区三| 水蜜桃av无码一区二区| 上原亚衣一区二区在线观看| 丰满岳妇乱一区二区三区| 无码精品不卡一区二区三区| 蜜臀AV在线播放一区二区三区| 久久国产午夜精品一区二区三区 | 国产成人无码AV一区二区 | 精品一区二区三区四区在线播放 | 成人无码一区二区三区| 精品一区二区三区在线播放 | 夜夜精品无码一区二区三区| 日韩人妻一区二区三区免费| 熟妇人妻系列av无码一区二区| 国产精品美女一区二区| 亚洲欧洲精品一区二区三区| 中文字幕一区二区在线播放| 日本精品一区二区久久久| 日韩在线视频一区| 日本一区二区三区日本免费| 久久精品无码一区二区三区免费 | 国产亚洲无线码一区二区| 久久99精品免费一区二区| 波多野结衣一区二区免费视频| 无码人妻精一区二区三区|