opy-Paste 是一件非常有效的開發方式,但是它們一點兒也不適合維護——為了改一個拼寫錯誤,要去修改代碼中的七八個文件,打人的心都有了。
如果萬一我們是要替換這七八個文件的相應代碼,那么就會更加地痛苦。在后端里,我們只需要修改相應的 Java、Go、JavaScript、Python 等語言相關文件的代碼。而在前端我們需要修改 HTML/JavaScript/CSS 文件,而哪怕使用的 React 這樣的框架里,我們也要修改一個文件的多個地方。
于是乎,作為一個專業的程序員,我們都在不斷地尋找方式來復用代碼(PS:復制/粘貼從本質上也是一種復用)。
經驗總結的復用
經驗總結型復用,指的是結合組織和項目的經驗,提取出其中的共同部分,以便于在其它項目中繼續使用。事實上,所有類型的復用都是經驗型復用。因此,這里的經驗總結型復用,專指于用在組織內部的復用。從我的認識來看,有以下四類:
腳手架
腳手架是一種快速創建新應用的方式。在腳手架里,我們會總結出過往經驗中的模式、代碼,將這些模式和代碼融入我們其中。其中特色就是結合常用的各種框架,并將它們結合到一起,如后端的:Spring Boot + Spring Eureka + Feign + Zuul 等,如前端的 React + Redux + React Router 等(PS:Angular 就沒這么復雜)。
市面上的主流框架,本身是提供了相應的腳手架功能。基于此,腳手架可以分為兩類:
兩者都有各自的優缺點。框架官方的腳手架缺少一些團隊、組織特定的因素。而自制的腳手架則需要團隊長期維護。不過,出于種種原因(諸如 KPI),我們都會維護自己的腳手架,你說呢?
組件庫(客戶端)
組件庫,對于每個 Web 項目來說,都是必不可少的元素。它適用于客戶端開發的 UI 復用。組件庫本身分為三個層級:基礎 UI 組件、復合組件、業務組件 。
一般而言,我們會使用第三方的基礎 UI 組件庫。在那的基礎之上,封裝自己的業務組件庫。又或者是,再對基礎 UI 組件庫進行二次封裝,以降低對第三方組件庫的依賴,讓其變成可替換的組件庫。
模式庫
模式庫其本質仍然是一個代碼集,它將我們常用的代碼提取出一個公共的類庫中。按分類上來說,組件庫也是模式庫的一種。為了方便于服務端與客戶端開發區別,我將組件庫獨立出來。
模式庫,是出于共用的目的而提取出來的。在不同的項目中,它的表現形式略有差異:
兩種方式也是各有優缺點。前者維護容易出錯,后者更新不方便。
模板和模板應用
組件庫和模板,實質上是設計系統的一部分。設計系統是一組相互關聯的設計模式與共同實踐的,以連貫組織來達成數字產品的目的。它包含了以下的五部分:
而模板應用,則是在模板的基礎上,進一步地整合而成,用于幫助開發人員快速的構建某一類型的應用。對應于其它類型的應用而言,則要判斷是否會出現相似的應用。
工具
上述的四種方式,是比較常見的方式。而隨著,我們項目數量的變多,開發人員數量的膨脹,它們開始變得麻煩。我們便需要編寫一些工具,以節省大量的人力成本。
CLI
這里的 CLI 是指自制的 CLI,它與我們編寫的一系列自動化代碼工具相互配合,形成自己的解決方案。
其交互諸如于:
choices: [ "React", "Angular", "Vue"] [?] What Framework do you want to do? > React Angular Vue
我們便可以將把配置、組件安裝等一系列的工作自動化。
Schematics
Schematics 來自于 Angular 團隊,其本質上也是 CLI 的一種,只是它相對于 CLI 來說,編程起來更加的簡單。它將我們在編程 CLI 過程中的一些通用模式,整合出來融入了代碼中。換句話來說,它相當于是前端工具中的 Angular、React——只需要編寫業務邏輯,而不需要關注于基礎架構。
它是現代 Web 的工作流程工具; 它可以將修改應用于您的項目,例如創建新組件或更新代碼以修復依賴項中的重大變更(PS:有點類似于后端數據庫腳本的味道)。還可以向現有項目添加新的配置選項或框架。
編程器插件
編程器插件,是一個非常有意思的思路。我們可以編寫一個編輯器插件,在插件中加入我們常見的代碼、模式和模板等。如在 VS Code 中,我們只需要創建對應的:
就此可以用于代碼生成和智能感知。對于一個框架來說,我們只需要定制好框架相應的組件、模式代碼,就可以復用它們。
設計系統與代碼生成
當我們有了一個成體系的設計系統,就可以使用諸如 Storybook 這樣的框架來優化組件的使用。它可以讓我們在查看組件文檔的同時,配置上相應的組件參數,最后我們只需要復制結果代碼,到我們的工程中使用即可。
其與一般的組件庫使用相比,更加的輕便,易于使用。
下一步,我們就是等 AI 來生成代碼了。對于擁有設計系統的項目而言,我們可以直接通過類似于 Sketch2Code 的工具,直接將我們的設計轉換為代碼。但是,實質上這是一種更復雜的模式。對于擁有設計系統的項目來說,我們可以將設計轉換為元數據。
結論
降低程序員的代碼量,就是效率的提升。
者:佚名來源:前端大全
如今依賴 JavaScript 提供交互的網站越來越多。雖然 JavaScript 可以提供愉快的體驗,但同時也帶來了一些負面影響:
鑒于這些缺點,我們可以依靠瀏覽器提供的原生解決方案,這種方式不僅可以降低成本,而且還可以享受社區創建的 Web 標準專業知識帶來的優勢。通常,這些解決方案的代碼量較少,因此還可以減少開發團隊的維護工作(比如無需更新使用的庫)。
在本文中,我們來探討一下部分可供大多數用戶使用的原生解決方案。我會給出一些示例,但不會深入探討所有細節。
在介紹各種技術之前,首先我需要提醒一下使用 JavaScript 的一大缺點:瀏覽器只有一個線程來控制頁面的渲染。在運行 JavaScript 時,瀏覽器會延遲用戶交互事件與界面更新。這就很討厭了,因為你會覺得頁面沒有響應你的操作,或者感覺動畫很卡。
谷歌工程師 Philip Walton 對這方面優化進行了詳細闡述,感興趣可以前往查看:https://calendar.perfplanet.com/2020/html-and-css-techniques-to-reduce-your-javascript/
開發團隊的日常工作比較喜歡功能強大的設備,因此會掩蓋 JavaScript 帶來的負面影響。但不要忘記在功能有限的設備上做定期測試。
以下是用JavaScript實現此操作的兩種方法:
當頁面包含大量需要截斷的文本時,頁面顯示會被延遲。此外,這兩種解決方案會完全截斷文本,有可能會影響到搜索引擎或輔助技術的恢復。頁面中元素的字體大小或寬度也有可能發生變化。考慮所有的情況很麻煩。
-webkit-line-clamp 是一個原生的 CSS 屬性。十年前在 Safari 中引入,如今已被廣泛使用,其他瀏覽器出于兼容性原因也采用了這個屬性,而且已成了標準。你需要一些其他帶前綴的屬性來實現所需的行為。雖然使用帶有前綴的屬性性有點煩人,但是標準中已經詳細描述了該前綴,因此這樣做不會有風險。
除了 IE 和 Firefox 68 之前的版本外,所有瀏覽器都支持該屬性。下面的例子說明了具體的使用方式。
HTML :
<div class="demo-grid">
<div class="demo-card">
<img width="200" height="200" src="https://placekitten.com/200/200?image=1" alt="" class="demo-img">
<h1 class="line-clamp demo-title">
Spill litter box, scratch at owner, destroy all furniture, especially couch
</h1>
</div>
<div class="demo-card">
<img width="200" height="200" src="https://placekitten.com/200/200?image=2" alt="" class="demo-img">
<h1 class="line-clamp demo-title">
Claws in the eye of the beholder
</h1>
</div>
<div class="demo-card">
<img width="200" height="200" src="https://placekitten.com/200/200?image=3" alt="" class="demo-img">
<h1 class="line-clamp demo-title">
Relentlessly pursues moth eat too much then proceed to regurgitate all over living room carpet while humans eat dinner
</h1>
</div>
</div>
<!-- Titles via http://www.catipsum.com/ -->
CSS:
/* By default, truncate the text abruptly */
.line-clamp {
/* Careful computing the max-height, it needs to match n * line-height */
max-height: calc(2 * 1.15 * 1.5rem);
overflow: hidden;
}
/* For capable browsers, truncate with an ellipsis */
/* To simulate a browser without support, you can add a s after clamp in the following line */
@supports (-webkit-line-clamp: 2) {
.line-clamp {
/* Remove the made up max-height */
max-height: none;
/* Those three properties are mandatory, so is overflow: hidden that we defined earlier */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
}
/* Extra code for the look of the demo */
.demo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(13rem,1fr));
gap: 1rem;
margin: 1rem;
}
.demo-card {
background-color: #E2E8F0;
border-radius: 0.5rem;
display: flex;
flex-direction: column;
padding: 0.5rem;
}
.demo-img {
align-self: center;
margin-bottom: 0.5rem;
border-radius: 0.5rem;
}
.demo-title {
font-size: 1.5rem;
margin: 0;
}
該解決方案沒有性能或頁面內容偏移的問題,而且還不會影響搜索引擎或輔助技術。但是,它不適合擁有多個子項的元素。
有時候,你希望頁面的某個部分始終顯示在視野范圍內,比如標題、工具欄或購物車。我經常遇到這種行為,但能夠正確實現的卻很少。
如果想通過 JavaScript 實現這個功能,則必須監聽頻繁觸發的滾動事件。大多數解決方案經常會通過限流或去抖動的技術去除大部分事件。現如今,我們可以使用 IntersectionObserver ,僅在元素進入或離開窗口時接收事件。這樣做的效率更高。
在檢測到元素進入或離開窗口時,我們需要從 position:relative 切換到 position:fixed 。這需要瀏覽器重新計算大量元素的大小和位置(我們稱之為頁面重新布局),這種做法的代價很大。我們需要確保周圍元素不會四處移動,而且不會導致內容跳躍。
如果在元素進入或退出窗口時,渲染被阻塞(如果在滾動的同時協調使用動畫,則很可能發生阻塞),那么切換將被進一步延遲。
CSS 有一個屬性 position:sticky 可以實現這種行為,而且還沒有性能、響應性或內容跳躍的問題:只要瀏覽器可以滾動,它就會將元素準確地定位到你聲明的位置上。你可以利用 top 、 bottom 、 left 或 right 選擇定位。
html
CSS
除了IE和舊版的 Chrome 或 Firefox 之外,所有瀏覽器都支持sticky。對于這些舊版的瀏覽器,元素僅支持默認值 position:static,而且不會處理 top、bottom、left 和 right 的值。如果你需要支持這些瀏覽器,則請記住這一點。舊版的 Safari 需要 -webkit-sticky 前綴。
但是,這個屬性有一個限制:無法根據元素是否sticky來改變元素的外觀,比如使用:stuck之類的偽類。這是 CSS常見的限制。在這種情況下,我建議使用 position:sticky 來設置sticky屬性,同時結合 IntersectionObserver來改變其外觀(注意不要改變大小,以防止內容跳躍)。
如果想在JavaScript中實現這一點,你需要定期執行改變滾動位置的JavaScript。為了動畫能夠流暢地運行,在整個動畫運行過程中,不能有其他JavaScript阻塞渲染。
此外,你還需要選擇一個計時函數。為了看起來很自然,可能需要針對每個操作系統使用不同的計時函數,才能符合該操作系統的常規做法。
CSS有一個屬性 scroll-behavior: smooth 和 {behavior: 'smooth'},可以代替JavaScript的 scroll 、 scrollTo 和 scrollIntoView ,將所有有關計時的決定都交給CSS。這樣可能更符合常用設備的常規做法。
Safari 尚不支持此功能(除非啟用隱藏選項),但大多數情況下,這不是什么大問題。
html
CSS
無論是JavaScript版還是原生版,你都需要注意兩個可訪問性方面的問題:遵循盡可能減少動畫和頁面移動的設置,以及確保焦點正確移動。
通過這種方式,可以創建幻燈片、水平列表,吸附到每張圖片或每一節,讓它們占據整個窗口。
為了創建幻燈片,我們需要監聽:
正確處理所有指針事件(鼠標事件或觸碰事件),并處理鼠標指針離開區域的事件非常需要技巧。如果能正確處理這些事件,就可以相應地移動元素。每次移動都可能導致昂貴的重新布局,破壞顯示效果。
如果每一節都占據整個窗口或遇到水平列表,我們必須監聽所有滾動事件,并用我們需要的滾動處理替換。獲得理想的效果非常困難,因為我們需要完整地控制原生的滾動行為。
不論何種情況,你都需要根據原始的頁面移動速度和距離來確定是否應該移動到下一個項目。如果你的選擇不符合系統的行為,就會給用戶造成困擾。
CSS的滾動吸附功能可以處理該行為。在滾動的容器中,定義 scroll-snap-type 來指示吸附的方向,以及吸附必定發生還是僅在接近吸附點時發生。然后在容器的子元素中定義 scroll-snap-align 來標明吸附點。
下面的演示完全沒有使用JavaScript。它還使用了 scroll-behavior 來提示用戶使用正常的滾動機制。
選中復選框,即可使用 IntersectionObserver 在縮略圖中高亮顯示當前的圖片。
html
CSS
JS
所有現代瀏覽器都支持該行為。還有另一種語法(https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap/Browser_compat)但我不建議使用。它只會增加測試的負擔,而你可以依靠優雅降級來解決該問題。在不支持滾動吸附的瀏覽器中,該功能將會降級為正常的滾動。
不論對于鼠標的情況還是觸摸屏的情況,由于使用了瀏覽器的滾動功能,該方法要比JavaScript的方法流暢許多。
用JavaScript實現該功能需要使用類似于 <img src="..." srcset="..." alt="..."> 的語法。當圖像接近視口時,使用Javascript改變圖像的屬性,以加載并顯示圖片。
這種方法的主要缺點就是,在相應的JavaScript執行之前圖像不會被顯示。而且這種情況發生的頻率遠超你的想象。搜索引擎也很難看到圖像,因為本質上圖像并不存在,而且爬蟲也不會滾動屏幕。
選擇何時觸發加載非常重要。怎樣根據當前的帶寬來決定當圖像距離視口多遠時進行加載?是否應當考慮滾動的速度?
去年,所有瀏覽器(Safari除外)都實現了 <img> 元素的 loading="lazy" 屬性。如果你的網站會加載所有圖像,那么可以嘗試下這個屬性。幾乎不需任何代價,就可以讓網站加載變快。
如果你已經使用了某種延遲加載技術,那么在Safari支持該屬性之前,你需要根據自己的情況做決定。是否值得放棄Safari的延遲加載,來換取更簡單的代碼?
目前,觸發下載的規則由各個瀏覽器決定,可能不是最佳時機。不過有一點可以確定,瀏覽器的決定會越來越理想,而不需要改變任何代碼!
我希望這篇文章可以給你一些啟示,下次在尋找某個 JavaScript 庫來實現某項功能時,可以考慮一下這些技術。此外,你也可以看看其他我沒有提及的 HTML 或 CSS 技術(比如 <details> 和 <summary> ,或 <datalist> )。瀏覽器在不斷發展,會不斷帶來驚喜,用戶也會受益!
少未使用或者正在使用的 JavaScript 代碼是現代 Web 開發者的一個必備技能,也是提升頁面加載速度的一個關鍵因素。
隨著軟件工程的發展,人們總是希望網站速度更快、效率更高、JavaScript 有效載荷更小。未使用的 JavaScript 會給 Web 應用程序增加不必要的負擔,并降低整體性能。
在本文中,您將了解到一系列模式和技巧,這些模式和技巧可用于通過刪除未使用的 JavaScript 來減少這種臃腫,從而幫助您節省時間、優化性能和提高效率。
簡單地說,未使用的 JavaScript(通常稱為Dead Code)是指 Web 應用程序不使用或不需要的任何代碼,但卻存在于您發送給瀏覽器的最終 JavaScript 包中。這些“僵尸代碼”處于休眠狀態,會增加 JavaScript 包的整體大小,從而影響頁面性能。
造成 JavaScript 程序中未使用代碼的原因有很多。最明顯的原因是您可能添加了不再需要的代碼,但在最終的包中忘記刪除它。這些可能是在您的應用程序中不再被執行、調用或使用的函數、類或變量。另一個原因可能是未使用的依賴項。換句話說,您可能在代碼中使用了一個您不需要的第三方JavaScript依賴項。更糟糕的是,這些依賴項可能還帶有它們自己未使用的JavaScript,進一步增加了項目中不必要的龐雜性。
您可以通過幾種方法從 Web 應用程序中移除未使用的 JavaScript。這些提示和模式將幫助您將更強大、更高效的 JavaScript 包發送到頁面上,無論您是使用純 JavaScript 還是任何專用的庫或框架,如 React、SolidJS 或 Vue.js。
代碼拆分是一種將 JavaScript 代碼拆分為更小、更可管理的模塊的技術。然后,您可以按需或并行網絡請求加載這些塊,這意味著您不必每次都加載整個 JavaScript 包,只需加載必要的部分。
想象一下,您有一個像下面這樣的單個JavaScript包:
// 60kb file
<script src="mainbundle.js"></script>
您可以將其分割成小塊,只在需要時下載:
<script async defer src="chunk1.js"></script> // 20 kb file
<script async defer src="chunk2.js"></script> // 20 kb file
<script async defer src="chunk3.js"></script> // 20 kb file
這一策略減少了初始化和下載 JavaScript 腳本的主線程的整體網絡負載。如果您使用的是 Next.js 或 Vue 等 JavaScript 庫或框架,您就不需要手動執行此操作,因為大多數現代 JavaScript 框架都默認支持代碼拆分。
不過,您可以將某些組件僅僅給服務器端使用。這有助于框架 "聰明地" 將 JavaScript 分割成更小的塊,這些塊不應與客戶端 JavaScript 塊捆綁在一起。許多企業都在生產中使用這種策略,這足以說明它的效率。
Tree shaking 是指消除僵尸代碼,即應用程序不需要的 JavaScript。許多流行的捆綁程序(如 webpack、Rollup 或 Vite)在構建 JavaScript 塊并將其發送到瀏覽器時,都會使用Tree shaking方法。
為確保捆綁包中的Tree shaking,請始終在 JavaScript 組件中使用現代 ES6 語法,即導入和導出語法:
// default import
import Navbar from 'Components'
// named import
import { Navbar } from 'Components'
// default export
export default Navbar
// named export
export { Navbar }
現代 ES6 語法可幫助您的捆綁程序識別死代碼,這與 ESLint 如何指出是否存在導入組件但該組件未在任何地方被使用類似。
捆綁包中的 JavaScript 越少,瀏覽器下載捆綁包所需的時間就越短。為確保您的 JavaScript 盡可能精簡,請務必在發布前將其最小化。
對 JavaScript 進行最小化處理,可以去掉代碼中的空白、語法高亮、注釋和其他在最終產品構建中不需要的部分。這些不必要的代碼段會占用捆綁包中的空間,成為死代碼。
即使是看似簡單的 JavaScript 代碼,也可以進行壓縮和修改。下面是一個精簡前的簡單 JavaScript 代碼示例:
// add function
const add=(a, b)=> {
return a + b
}
// call add function
add(3, 4)
壓縮之后的代碼:
const add=(d,a)=>d+a;add(3,4);
想象一下對 JavaScript 進行最小化會對大型捆綁包產生怎樣的影響!
JavaScript 壓縮比想象中的要容易得多。您可以從 Terser、Ugligy、babel-minify 等許多在線 JavaScript 壓縮工具中進行選擇。
一個可以節省大量網絡帶寬時間的小竅門是始終異步加載 JavaScript。
其中一種方法是在 JavaScript 腳本中添加 async 和 defer。這將自動處理 JavaScript 的下載,并且在加載 JavaScript 時不會延遲或阻止 HTML 的解析或呈現。
async 和 defer 屬性處理 JavaScript 腳本下載和執行的順序略有不同。您可以選擇最適合自己項目的方式。
async JavaScript 腳本的工作原理如圖所示:
defer JavaScript 腳本的工作原理如圖所示:
在許多情況下,同時添加 async 和 defer 就能正確完成工作。下面是一個如何編寫的示例:
<script async defer src="bundle.js"></script>
由于有了 ES6 模塊,現在可以在純 JavaScript 中實現動態導入。當您想按條件地加載 JavaScipt 模塊或腳本時,動態導入尤其有用。下面是動態導入的代碼示例:
import('./utility.js')
.then((module)=> {
// use utility code here
})
.catch((error)=> {
// catch errors
});
與在文件頂部導入每個 JavaScript 組件相比,這種策略使我們能夠在滿足特定條件后請求 JavaScript 捆綁程序。請看下面的代碼片段,其中只有在附加事件監聽器后才會加載模塊:
const $dashboard=document.getElementById('dashboard');
$dashboard.addEventListener('click', ()=> {
import('./utility.js')
.then((module)=> {
// Use utility module APIs
module.callSomeFunction()
})
// catch unexpected error here
.catch((error)=> {
console.error("Oops! An error has occurred");
});
});
JavaScript 中的懶加載是一種簡單但相當有用的模式。如果操作得當,懶加載可以幫助你節省網絡帶寬。基本規則是只加載當前需要的 JavaScript 模塊。
您可以遵循的一種模式是始終按照視口高度加載 JavaScript。假設您有一個非常龐大的用戶列表。您可能不想加載第 300 個用戶的信息,因為這些信息在當前視口中并不需要,甚至不可見。
針對這種特殊的使用情況,有一些非常出色的庫,它們委托 JavaScript 模塊僅在指定用戶(如第 300 個用戶)到達當前視口時加載信息。
懶加載不僅限于列表。圖片、視頻、大量節點等資產也可以懶加載。例如,你可以在瀏覽器下載實際圖片及其相關 JavaScript 之前放置一個占位符。
這些模式不僅可以幫助您減少 JavaScript 代碼,還能極大地改善用戶體驗。
隨著 JavaScript 生態系統的不斷完善,您的代碼和使用的庫也應跟上任何變化。這將有助于使您的代碼在未來的版本中更加健壯和安全。
如果您在代碼中使用任何第三方庫,請確保始終使用最新版本,并檢查它們是否已被廢棄。許多庫,尤其是由志愿者維護的小型開源庫,可能會意外過時或被廢棄。
在基于 npm 或 Node.js 的開發環境中,您可以運行以下代碼來檢查庫是否被棄用:
npx depcheck
外部依賴關系或第三方庫的選擇會影響 JavaScript 的傳輸量。請務必確保您為項目選擇的任何庫都得到了積極維護,使用了最新的 ECMAScript 版本,最重要的是,它們都是輕量級的。
在本文中,我們討論了可以用來減少 JavaScript 的交付量并提高項目性能的八種方法。因每個項目都不盡相同,使用的架構和模式也不盡相同,本文提到的很多技巧對您的項目可能都適用,但有些可能不適用,在使用時需要根據實際環境和情況靈活選擇。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。