日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
?未來全??蚣軙淼姆较?/div>

大家好,我卡頌。

創(chuàng)新互聯(lián)公司2013年成立,先為策勒等服務(wù)建站,策勒等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為策勒企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

從全球web發(fā)展角度看,框架競爭已經(jīng)從第一階段的前端框架之爭(比如Vue、React、Angular等),過渡到第二階段的全??蚣苤疇帲ū热鏝ext、Nuxt、Remix等)。

這里為什么說全球,是因為國內(nèi)web發(fā)展方向主要是更封閉的小程序生態(tài)

在第一階段的前端框架之爭中,不管爭論的主題是「性能」還是「使用體驗」,最終都會落實到框架底層實現(xiàn)上。

不同框架底層實現(xiàn)的區(qū)別,可以概括為「更新粒度的區(qū)別」,比如:

  • Svelte更新粒度最細(xì),粒度對應(yīng)到每個狀態(tài)
  • Vue更新粒度中等,粒度對應(yīng)到每個組件
  • React更新粒度最粗,粒度對應(yīng)到整個應(yīng)用

那么,進(jìn)入第二階段的全??蚣苤疇幒?,最終會落實到什么的競爭上呢?

我認(rèn)為,會落實到「業(yè)務(wù)邏輯的拆分粒度」上,這也是各大全棧框架未來會卷的方向。

本文會從「實現(xiàn)原理」的角度聊聊業(yè)務(wù)邏輯的拆分粒度。

邏輯拆分意味著什么

「性能」永遠(yuǎn)是最硬核的指標(biāo)。在前端框架時期,性能通常指「前端的運行時性能」。

為了優(yōu)化性能,框架們都在優(yōu)化各自的運行時流程,比如:

  • 更好的虛擬DOM算法。
  • 更優(yōu)秀的AOT編譯時技術(shù)。

在web中,最基礎(chǔ),也是最重要的性能指標(biāo)之一是FCP(First Contentful Paint 首次內(nèi)容繪制),他測量了頁面從開始加載到頁面內(nèi)容的任何部分在屏幕上完成渲染的時間。

對于傳統(tǒng)前端框架,由于渲染頁面需要完成4個步驟:

  1. 加載HTML。
  2. 加載框架運行時代碼。
  3. 加載業(yè)務(wù)代碼。
  4. 渲染頁面(此時統(tǒng)計FCP)。

框架能夠優(yōu)化的,只有步驟2、3,所以FCP指標(biāo)不會特別好。

SSR的出現(xiàn)改善了這一情況。對于傳統(tǒng)的SSR,需要完成:

  1. 加載帶內(nèi)容的HTML(此時統(tǒng)計FCP)。
  2. 加載框架運行時代碼。
  3. 加載業(yè)務(wù)代碼。
  4. hydrate頁面。

在第一步就能統(tǒng)計FCP,所以FCP指標(biāo)優(yōu)化空間更大。

除此之外,SSR還有其他優(yōu)勢(比如更好的SEO支持),這就是近幾年全棧框架盛行的一大原因。

既然大家都是全??蚣?,那不同框架該如何突出自己的特點呢?

我們會發(fā)現(xiàn),在SSR場景下,業(yè)務(wù)代碼既可以寫在前端,也能寫在后端。按照業(yè)務(wù)代碼在后端的比例從0~100%來看:

  • 0%邏輯在后端,對應(yīng)純前端框架渲染的應(yīng)用。
  • 100%邏輯在后端,對應(yīng)PHP時代純后端渲染的頁面。

合理調(diào)整框架的這個比例,就能做到差異化競爭。

按照這個思路改進(jìn)框架,就需要回答一個問題:一段業(yè)務(wù)邏輯,到底應(yīng)該放在前端還是后端呢?

這就是本文開篇說的「邏輯拆分」問題。我們可以用「邏輯拆分的粒度」區(qū)分不同的全??蚣?。

下述內(nèi)容參考了文章wtf-is-code-extraction。

粗粒度

在Next.js中,文件路徑與后端路由一一對應(yīng),比如文件路徑pages/posts/hello.tsx就對應(yīng)了路由http(s)://域名/posts/hello。

開發(fā)者可以在hello.tsx文件中同時書寫前端、后端邏輯,比如如下代碼中:

  • Post組件對應(yīng)代碼會在前端執(zhí)行,用于渲染組件視圖。
  • getStaticProps方法會在代碼編譯時在后端執(zhí)行,執(zhí)行的結(jié)果會在Post組件渲染時作為props傳遞給它。
// hello.tsx

export async function getStaticProps() {
  const postData = await getPostData();
  return {
    props: {
      postData,
    },
  };
}

export default function Post({ postData }) {
  return (
    
      {postData.title}
      
{postData.id}
{postData.date}
); }

通過以上方式,在同一個文件中(hello.tsx),就能拆分出前端邏輯(Post組件邏輯)與后端邏輯(getStaticProps方法)。

雖然以上方式可以分離前端/后端邏輯,但一個組件文件只能定義一個getStaticProps方法。

如果我們還想定義一個執(zhí)行時機(jī)類似getStaticProps的getXXXData方法,就不行了。

所以,通過這種方式拆分前/后端邏輯,屬于比較粗的粒度。

中粒度

我們可以在此基礎(chǔ)上修改,改變拆分的粒度。

首先,我們需要改變之前約定的「前/后端代碼拆分方式」,不再通過具體的方法名(比如getStaticProps)顯式拆分,而是按需拆分方法。

修改后的調(diào)用方式如下:

// 修改后的 hello.tsx
export async function getStaticProps() {
  const postData = await getPostData();
  return {
    props: {
      postData,
    },
  };
}

export default function Post() {
  const postData = getStaticProps();
  return (
    
      {postData.title}
      
{postData.id}
{postData.date}
); }

現(xiàn)在,我們可以增加多個后端方法了,比如下面的getXXXData:

export async function getXXXData() {
  // ...省略
}

export default function Post() {
  const postData = getStaticProps();
  const xxxData = getXXXData();
  
  // ...省略
}

但是,Post組件是在前端執(zhí)行,getStaticProps、getXXXData是后端方法,如果不做任何處理,這兩個方法會隨著Post組件代碼一起打包到前端bundle文件中,如何將他們分離開呢?

這時候,我們需要借助編譯技術(shù),上述代碼經(jīng)編譯后會變?yōu)轭愃葡旅娴拇a:

// 編譯后代碼
/*#__PURE__*/ SERVER_REGISTER('ID_1', getStaticProps);
/*#__PURE__*/ SERVER_REGISTER('ID_2', getXXXData);

export const method1 = SERVER_PROXY('ID_1');
export const method2 = SERVER_PROXY('ID_2');

export const MyComponent = () => {
  const postData = method1();
  const xxxData = method2();

  // ...省略
}

讓我們來解釋下其中的細(xì)節(jié)。

首先,這段編譯后代碼可以直接在后端執(zhí)行,執(zhí)行時會通過框架提供的SERVER_REGISTER方法注冊后端方法(比如ID為ID_1的getStaticProps)。

由于SERVER_REGISTER方法前加了/*#__PURE__*/標(biāo)記,這個文件在打包客戶端bundle時,SERVER_REGISTER會被tree-shaking掉。

也就是說,打包后的客戶端代碼類似如下:

export const method1 = SERVER_PROXY('ID_1');
export const method2 = SERVER_PROXY('ID_2');

export const MyComponent = () => {
  const postData = method1();
  const xxxData = method2();

  // ...省略
}

當(dāng)以上客戶端代碼執(zhí)行時,在前端,SERVER_PROXY方法會根據(jù)id請求對應(yīng)的后端邏輯,比如:

  • 發(fā)起id為ID_1的請求,后端會執(zhí)行g(shù)etStaticProps并返回結(jié)果。
  • 發(fā)起id為ID_2的請求,后端會執(zhí)行g(shù)etXXXData并返回結(jié)果。

實際上,通過這種方式,可以將任何函數(shù)作用域內(nèi)的邏輯從前端移到后端。

比如在下面的代碼中,我們在按鈕的點擊回調(diào)中訪問了數(shù)據(jù)庫并做后續(xù)處理:

export function Button() {
  return (
    
  );
}

這個「按鈕點擊邏輯」顯然無法在前端執(zhí)行(前端不能直接訪問數(shù)據(jù)庫)。但我們可以通過上述方式將代碼編譯為下面的形式:

import {SERVER_REGISTER, SERVER_PROXY} from 'xxx-framework';

/*#__PURE__*/ SERVER_REGISTER('ID_123', () => {
  // 訪問數(shù)據(jù)庫
  const post = await db.posts.find('xxx');
  // ...后續(xù)處理
});

export function Button() {
  return (
    
  );
}

編譯后的代碼可以在后端直接執(zhí)行(并訪問數(shù)據(jù)庫)。對于前端,我們再打包一個bundletree-shaking掉后端代碼),類似下面這樣:

import {SERVER_PROXY} from 'xxx-framework';

export function Button() {
  return (
    
  );
}

相比于粗粒度的邏輯分離方式(文件級別粒度),這種方式的粒度更細(xì)(函數(shù)級別粒度)。

細(xì)粒度

中粒度的方式有個缺點 —— 分離的方法中不能存在客戶端狀態(tài)。比如下面的例子,點擊回調(diào)依賴了id狀態(tài):

export function Button() {
  const [id] = useStore();
  return (
    
  );
}

如果遵循之前的分離方式,后端取不到id的值:

import {SERVER_REGISTER, SERVER_PROXY} from 'xxx-framework';

/*#__PURE__*/ SERVER_REGISTER('ID_123', () => {
  // 獲取不到id的值
  const post = await db.posts.find(id);
  // ...后續(xù)處理
});

export function Button() {
  const [id] = useStore();
  return (
    
  );
}

為了解決這個問題,我們需要進(jìn)一步降低邏輯分離的粒度,使粒度達(dá)到狀態(tài)級。

首先,相比于中粒度中將內(nèi)聯(lián)方法提取到模塊頂層(并標(biāo)記/*#__PURE__*/)的方式,我們可以將方法提取到新文件中。

對于如下代碼,如果想將onClick回調(diào)提取為后端方法:

import {callXXX} from 'xxx';

export function() {
  return (
    
  );
}

可以將其提取到新文件中:

// hash1.js
import {callXXX} from 'xxx';
export const id1 = () => callXXX();

原文件則編譯為:

import {SERVER_PROXY} from 'xxx-framework';

export function() {
  return (
    
  );
}

這種方式比中粒度中提到的分離方式更靈活,因為:

  • 省去了標(biāo)記/*#__PURE__*/。
  • 省去了先在后端注冊方法(SERVER_REGISTER)。

當(dāng)考慮前端狀態(tài)時,可以將狀態(tài)作為參數(shù)一并傳給SERVER_PROXY。

比如對于上面提過的代碼:

export function Button() {
  const [id] = useStore();
  return (
    
  );
}

會編譯為單獨的文件:

// hash1.js
import {lazyLexicalScope} from 'xxx-framework';

export const id1 = () => {
  const [id] = lazyLexicalScope();
  const post = await db.posts.find(id);
  // ...后續(xù)處理
};

與前端代碼:

import {SERVER_PROXY} from 'xxx-framework';

export function Button() {
  const [id] = useStore();
  return (
    
  );
}

其中前端傳入的[id]參數(shù)在后端方法中可以通過lazyLexicalScope方法獲取。

通過這種方式,可以做到狀態(tài)級別的邏輯分離。

總結(jié)

類似前端框架的更新粒度,全??蚣芤泊嬖诓煌6龋@就是邏輯分離粒度。

按照邏輯分離到后端的粒度劃分:

  • 粗粒度:以文件作為前/后端邏輯分離的粒度,比如Next.js。
  • 中粒度:以方法作為前/后端邏輯分離的粒度。
  • 細(xì)粒度:以狀態(tài)作為前/后端邏輯分離的粒度,比如Qwik。

在粗粒度與中粒度之間,還存在一種方案 —— 將組件作為劃分粒度的單元,這就是React的Server Component。

「劃分粒度」的本質(zhì),也是性能的權(quán)衡 —— 如果將盡可能多的邏輯放到后端,那么前端頁面需要加載的JS代碼(邏輯對應(yīng)的代碼)就越少,那么前端花在加載JS資源上的時間就越少。

但是另一方面,如果劃分的粒度太細(xì)(比如中或細(xì)粒度),可能意味著:

  • 更大的后端運行時壓力(畢竟很多原本前端執(zhí)行的邏輯放到了后端)。
  • 降低部分前端交互的響應(yīng)速度(有些前端交互還得先去后端請求回交互對應(yīng)代碼再執(zhí)行)。

所以,具體什么粒度才是最合適的,還有待開發(fā)者與框架作者一起探索。

未來,這也會是全棧框架一個主意的競爭方向。


網(wǎng)頁名稱:?未來全??蚣軙淼姆较?
瀏覽路徑:http://www.5511xx.com/article/ccisoho.html