成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

拒絕Redux文檔“毒害” 一個(gè)項(xiàng)目告訴你Redux最新真正哲學(xué)

YuboonaZhang / 1572人閱讀

摘要:之前分享過(guò)幾篇關(guān)于技術(shù)棧的原創(chuàng)文章解析前端架構(gòu)學(xué)習(xí)復(fù)雜場(chǎng)景數(shù)據(jù)設(shè)計(jì)干貨總結(jié)打造單頁(yè)應(yīng)用一個(gè)項(xiàng)目理解最前沿技術(shù)棧真諦一個(gè)工程實(shí)例今天進(jìn)一步剖析一個(gè)實(shí)際案例移動(dòng)網(wǎng)頁(yè)版。目前面臨的問(wèn)題在于提高產(chǎn)品的各方面性能體驗(yàn)。

之前分享過(guò)幾篇關(guān)于React技術(shù)棧的原創(chuàng)文章:

解析Twitter前端架構(gòu) 學(xué)習(xí)復(fù)雜場(chǎng)景數(shù)據(jù)設(shè)計(jì)

React Conf 2017 干貨總結(jié)1: React + ES next = ?

React+Redux打造“NEWS EARLY”單頁(yè)應(yīng)用 一個(gè)項(xiàng)目理解最前沿技術(shù)棧真諦

一個(gè)react+redux工程實(shí)例

......

今天進(jìn)一步剖析一個(gè)實(shí)際案例:Uber APP 移動(dòng)網(wǎng)頁(yè)版。

如果你對(duì)React技術(shù)棧沒有多大興趣,或者不是很了解,也沒有關(guān)系。因?yàn)樽x下來(lái),你會(huì)發(fā)現(xiàn),這篇文章的真諦其實(shí)在于性能優(yōu)化上。

本文靈感和主體內(nèi)容翻譯自Narendra N Shetty的文章:How I built a super fast Uber clone for mobile web,同時(shí)進(jìn)行了大量擴(kuò)充以及深挖。

出發(fā)點(diǎn)和產(chǎn)品雛形

很早以來(lái),相信大家都會(huì)認(rèn)同一個(gè)觀點(diǎn):移動(dòng)端流量超越PC端是不爭(zhēng)的事實(shí)。對(duì)于前端開發(fā)者來(lái)說(shuō),移動(dòng)端web的開發(fā)同樣非常有趣,也充滿挑戰(zhàn)。

這不,Uber最近發(fā)布了最新版本APP,全新樣式,體驗(yàn)超棒。于是,筆者決定使用React來(lái)從零開始構(gòu)建一個(gè)新的屬于自己的Uber。

開發(fā)期間,筆者花費(fèi)了很多時(shí)間在基礎(chǔ)組件和樣式搭建上。這環(huán)節(jié)中,主要應(yīng)用了Uber官方開放的React地圖庫(kù),并在地圖上“目的地”和“起始點(diǎn)”之間采用svg-overlay和html-overlay去繪制路線。

最終的基本交互可以參考下面Gif圖:

走上優(yōu)化之路

現(xiàn)在,我們有基本的產(chǎn)品形態(tài)了。目前面臨的問(wèn)題在于提高產(chǎn)品的各方面性能體驗(yàn)。我使用了Chrome Lighthouse去檢驗(yàn)產(chǎn)品的性能表現(xiàn)。最終得到的結(jié)果為:

wow...
第一次繪制時(shí)間就已經(jīng)接近2秒,后面的時(shí)間慘不忍睹就不要看了吧。
想象一下,一個(gè)用戶拿出手機(jī),企圖叫車。主屏?xí)r間的繪制就超過(guò)了19189.9ms,這是極其不能忍受的。

接下來(lái),什么也不說(shuō)了,擼起袖子,想辦法去優(yōu)化吧。

優(yōu)化方法1-代碼分離(Code Splitting)

我最開始想到并使用的方法就是:Code Splitting(代碼分離),正好我們可以借助webpack來(lái)實(shí)現(xiàn)這項(xiàng)技術(shù)。
什么是webpack code splitting呢? 您可以參考這里,如果英語(yǔ)閱讀吃力,可以參考下面引文:

code splitting就是指將文件分割為塊(chunk),webpack使我們可以定義一些分割點(diǎn)(split point),根據(jù)這些分割點(diǎn)對(duì)文件進(jìn)行分塊,并實(shí)現(xiàn)按需加載。

因?yàn)楣P者使用了React技術(shù)棧,并采用了react-router,所以代碼的劃分(split)就可以按照路由和加載時(shí)機(jī)進(jìn)行。具體操作可以使用react-router的getComponent api來(lái)實(shí)現(xiàn):

 {
    require.ensure([], (require) => {
        cb(null, require("../components/Home").default);
    }, "HomeView");
}}> 

只有當(dāng)對(duì)應(yīng)路由被請(qǐng)求時(shí),相應(yīng)的組件才會(huì)被加載呈現(xiàn)。

同時(shí),筆者使用了webpack的CommonChunkPlugin插件提取第三方代碼。這是出于什么考慮呢?

細(xì)心的讀者可能會(huì)發(fā)現(xiàn)上面的code splitting也許會(huì)存在一個(gè)問(wèn)題:
按需(按路由)引入資源后,這些資源可能存在大量重復(fù)代碼。尤其是我們使用的第三方資源。
想明白這個(gè)問(wèn)題,這時(shí)候,你應(yīng)該就會(huì)明白CommonChunkPlugin這個(gè)插件的意義了。關(guān)于這個(gè)插件配置方法有多種,這里我們采用了:有選擇性的提取(對(duì)象方式傳參):

{
    "entry": {
        "app": "./src/index.js",
        "vendor": [
            "react",
            "react-redux",
            "redux",
            "react-router",
            "redux-thunk"
        ]
    },
    "output": {
        "path": path.resolve(__dirname, "./dist"),
        "publicPath": "/",
        "filename": "static/js/[name].[hash].js",
        "chunkFilename": "static/js/[name].[hash].js"
    },
    "plugins": [
        new webpack.optimize.CommonsChunkPlugin({
            name: ["vendor"], // 公共塊的塊名稱
            minChunks: Infinity, // 最小被引用次數(shù),最小是2。傳遞Infinity只是創(chuàng)建公共塊,但不移動(dòng)模塊。 
            filename: "static/js/[name].[hash].js", // 公共塊的文件名
        }),
    ]
}

這樣子,我們把公共代碼(react、react-redux、redux、react-router、redux-thunk)專門抽取到vendor模塊中。

通過(guò)上述方法,筆者欣喜地發(fā)現(xiàn):
First meaningful paint時(shí)間由19189.9ms縮短到4584.3ms:

這無(wú)疑是激動(dòng)人心的。

優(yōu)化方法2-Server side rendering(服務(wù)端直出)

也許你一直在聽說(shuō)過(guò)“服務(wù)端渲染”或者“服務(wù)端直出”這樣的名詞。但是從未實(shí)踐過(guò),也從來(lái)沒有了解過(guò)他的意義。好吧,這里我先描述一下,到底什么是服務(wù)端直出。

服務(wù)端直出,其實(shí)簡(jiǎn)單總結(jié)為服務(wù)器在接到來(lái)自瀏覽器第一次請(qǐng)求時(shí),便返回一個(gè)“初步最終”HTML文檔。這個(gè)HTML文檔已經(jīng)進(jìn)行了數(shù)據(jù)拼接。這樣用戶能以最快的時(shí)間看到首屏的效果,當(dāng)然這個(gè)效果是“閹割版”的,非最終版本。

這種方式主要是針對(duì)“前后分離”的傳統(tǒng)模式。傳統(tǒng)模式中,服務(wù)器返回HTML文檔,之后瀏覽器解析文檔標(biāo)簽,拉取CSS,之后拉取JS文件。JS文件加載完成之后,執(zhí)行JS內(nèi)容,并發(fā)送請(qǐng)求獲取數(shù)據(jù)。最終,將數(shù)據(jù)渲染在頁(yè)面上。

由此,Server side rendering方式將JS請(qǐng)求數(shù)據(jù)的過(guò)程放在了服務(wù)器上,甚至對(duì)于數(shù)據(jù)與HTML結(jié)合處理也可以在服務(wù)器上做。

這樣一來(lái),主要就是加快了首屏渲染時(shí)間。當(dāng)然,使用服務(wù)端渲染,還能夠優(yōu)化前端渲染難以克服的SEO問(wèn)題。

理論理解起來(lái)很簡(jiǎn)單,難處就在于服務(wù)器端環(huán)境的前端腳本如何處理,如何與客戶端保持一致。

在這個(gè)項(xiàng)目中,我使用了Express作為nodeJS框架,結(jié)合react-router完成:

server.use((req, res)=> {
    match({
    "routes": routes,
    "location": req.url
    }, (error, redirectLocation, renderProps) => {
        if (error) {
            res.status(500).send(error.message);
        } 
        else if (redirectLocation) {
            res.redirect(302, redirectLocation.pathname + redirectLocation.search);
        } 
        else if (renderProps) {
            // Create a new Redux store instance
            const store = configureStore();

            // Render the component to a string
            const html = renderToString();

            const preloadedState = store.getState();

            fs.readFile("./dist/index.html", "utf8", function (err, file) {
                if (err) {
                    return console.log(err);
                }
                let document = file.replace(/
/, `
${html}
`); document = document.replace(/"preloadedState"/, `"${JSON.stringify(preloadedState)}"`); res.setHeader("Cache-Control", "public, max-age=31536000"); res.setHeader("Expires", new Date(Date.now() + 2592000000).toUTCString()); res.send(document); }); } else { res.status(404).send("Not found") } }); });

通過(guò)上述方法,我們欣喜地發(fā)現(xiàn):
First meaningful paint時(shí)間已經(jīng)縮短到921.5ms:

這無(wú)疑是令人振奮的。

優(yōu)化方法3-Compressed static assets(壓縮靜態(tài)文件)

壓縮文件,當(dāng)然是一個(gè)容易想到而且行之有效的措施。為此,我使用了webpack的CompressionPlugin插件:

{
    "plugins": [
        new CompressionPlugin({
            test: /.js$|.css$|.html$/
        })
    ]
}

同時(shí),使用express-static-gzip來(lái)對(duì)服務(wù)端進(jìn)行配置:

server.use("/static", expressStaticGzip("./dist/static", {
    "maxAge": 31536000,
    setHeaders: function(res, path, stat) {
    res.setHeader("Expires", new Date(Date.now() + 2592000000).toUTCString());
        return res;
    }
}));

express-static-gzip是一個(gè)處于express.static之上的中間件。如果對(duì)于指定路徑的文件沒有找到壓縮版本,就使用為壓縮版本進(jìn)行返回。

經(jīng)過(guò)此處理,我們縮短了400ms時(shí)間,OK,現(xiàn)在First meaningful paint時(shí)間為546.6ms.

優(yōu)化方法4-Caching(緩存)

截止到此,我們已經(jīng)從最初的19189.9ms已經(jīng)優(yōu)化到546ms,我們當(dāng)然繼續(xù)可以在客戶端進(jìn)行靜態(tài)文件緩存來(lái)使得加載時(shí)間變得更短。

筆者使用了sw-toolbox搭配service workers進(jìn)行。

sw-toolbox:A collection of service worker tools for offlining runtime requests.

Service Worker Toolbox provides some simple helpers for use in creating your own service workers. Specifically, it provides common caching strategies for dynamic content, such as API calls, third-party resources, and large or infrequently used local resources that you don"t want precached.

簡(jiǎn)單翻譯下:
Service Worker實(shí)現(xiàn)常見運(yùn)行時(shí)緩存模式,例如動(dòng)態(tài)內(nèi)容、API調(diào)用以及第三方資源,實(shí)現(xiàn)方法就像編寫README一樣簡(jiǎn)單。

也許到這里你一頭霧水,沒關(guān)系,我們從最初開始,了解一下什么是service worker:

在2014年,W3C公布了service worker的草案,service worker提供了很多新的能力,使得web app擁有與native app相同的離線體驗(yàn)、消息推送體驗(yàn)。

service worker是一段腳本,與web worker一樣,也是在后臺(tái)運(yùn)行。
作為一個(gè)獨(dú)立的線程,運(yùn)行環(huán)境與普通腳本不同,所以不能直接參與web交互行為。native app可以做到離線使用、消息推送、后臺(tái)自動(dòng)更新,service worker的出現(xiàn)是正是為了使得web app也可以具有類似的能力。

而sw-toolbox,顧名思義,就是service worker一個(gè)toolbox,具體我們看代碼:

toolbox.router.get("(.*).js", toolbox.fastest, {
    "origin":/.herokuapp.com|localhost|maps.googleapis.com/,
    "mode":"cors",
    "cache": {
        "name": `js-assets-${VERSION}`,
        "maxEntries": 50,
        "maxAgeSeconds": 2592e3
    }
});

上面代碼的意思是,我們對(duì)于get類型的請(qǐng)求,當(dāng)請(qǐng)求內(nèi)容為js腳本時(shí),應(yīng)用toolbox.fastest handler處理。
toolbox.fastest指示:對(duì)于這個(gè)請(qǐng)求,我們既從緩存中獲取,也同時(shí)通過(guò)正常的請(qǐng)求network獲取。這兩種方式哪個(gè)返回快,就應(yīng)用哪一個(gè)。
另外,toolbox.router.get的第三個(gè)參數(shù)表示配置項(xiàng)。

考慮周到的讀者可能會(huì)想,上面是對(duì)于支持Service worker的瀏覽器,那么對(duì)于不支持的瀏覽器呢?我們干脆設(shè)置:

res.setHeader("Expires", new Date(Date.now() + 2592000000).toUTCString());

通過(guò)這樣處理,我們來(lái)直觀感受一下頁(yè)面加載瀑布流:

優(yōu)化方法5-Preload and then load(預(yù)加載/延后加載)

如果你還沒聽說(shuō)過(guò)“Preload”,不要緊。我們這就來(lái)了解一下:

Preload作為一個(gè)新的web標(biāo)準(zhǔn),旨在提高性能和為web開發(fā)人員提供更細(xì)粒度的加載控制。Preload使開發(fā)者能夠自定義資源的加載邏輯,且無(wú)需忍受基于腳本的資源加載器帶來(lái)的性能損失。

換成你能聽明白的話來(lái)說(shuō):
preload建議允許始終預(yù)加載某些資源,瀏覽器必須請(qǐng)求preload標(biāo)記的資源。

這樣子,究竟有什么意義呢?
舉個(gè)例子:比如一些隱藏在CSS和Javascript中的資源。
當(dāng)瀏覽器發(fā)現(xiàn)自己需要這些資源時(shí)已經(jīng)為時(shí)已晚,所以大多數(shù)情況,這些資源的加載都會(huì)對(duì)頁(yè)面渲染造成延遲。

preload的出現(xiàn)就是為了優(yōu)化這個(gè)過(guò)程。
對(duì)于preload的兼容性,可以參考這里。

對(duì)于不支持preload的瀏覽器,筆者使用了prefetch來(lái)處理。
但于preload不同,prefetch的作用是告訴瀏覽器加載下一頁(yè)面可能會(huì)用到的資源,注意,是下一頁(yè)面,而不是當(dāng)前頁(yè)面。因此該方法的加載優(yōu)先級(jí)非常低。

這些新標(biāo)準(zhǔn)其實(shí)很有意思,里面的內(nèi)容遠(yuǎn)不止這些。有興趣的同學(xué)可以自行了解,也歡迎與我討論。

回到正題,我在head標(biāo)簽中使用:


最終優(yōu)化的結(jié)果如圖:

總結(jié)

其實(shí),使用React+Webpack做出一個(gè)Uber已經(jīng)不是重點(diǎn)了。真正激動(dòng)人心的是整套流程的優(yōu)化之路。我們使用了大量成熟的、未成熟(新技術(shù)),希望對(duì)讀者有所啟發(fā)!

Happy Coding!

PS: 作者Github倉(cāng)庫(kù),歡迎通過(guò)代碼各種形式交流。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91700.html

相關(guān)文章

  • 2016-JavaScript之星

    摘要:在,是當(dāng)之無(wú)愧的王者,贏得了與之間的戰(zhàn)爭(zhēng),攻陷了的城池。于月發(fā)布了版本,這一版本為了更好的表現(xiàn)加入了渲染方式。前端框架這個(gè)前端框架清單可能是年疲勞的元兇之一。的創(chuàng)建者,目前在工作為尋找構(gòu)建簡(jiǎn)單性和自主配置性之間的平衡做了很大的貢獻(xiàn)。 春節(jié)后的第一篇就從這個(gè)開始吧~本文已在前端早讀課公眾號(hào)上首發(fā) 原文鏈接 JavasScript社區(qū)在創(chuàng)新的道路上開足了馬力,曾經(jīng)流行過(guò)的也許一個(gè)月之后就過(guò)...

    Binguner 評(píng)論0 收藏0
  • 重新設(shè)計(jì) Redux

    摘要:相關(guān)狀態(tài)父組件傳遞給子組件的狀態(tài)。外部狀態(tài)狀態(tài)是可以從視圖庫(kù)中移出來(lái)的,然后可以使用提供者消費(fèi)者模式把狀態(tài)重新連接回視圖庫(kù)。重新設(shè)計(jì)在我看來(lái),重寫是有其必要性的,至少有以下個(gè)方面可以改進(jìn)得更友好。 Redux 學(xué)習(xí)起來(lái)很困難?寫起代碼來(lái)很啰嗦?一起來(lái)看看 Rematch 的作者對(duì) Redux 的思考和簡(jiǎn)化吧~ 原文:《Redesigning Redux》, Shawn McKay 都過(guò)...

    kidsamong 評(píng)論0 收藏0
  • Taro 優(yōu)秀學(xué)習(xí)資源匯總

    摘要:多端統(tǒng)一開發(fā)框架優(yōu)秀學(xué)習(xí)資源匯總官方資源項(xiàng)目倉(cāng)庫(kù)官方文檔項(xiàng)目倉(cāng)庫(kù)官方文檔微信小程序官方文檔百度智能小程序官方文檔支付寶小程序官方文檔字節(jié)跳動(dòng)小程序官方文檔文章教程不敢閱讀包源碼帶你揭秘背后的哲學(xué)從到構(gòu)建適配不同端微信小程序等的應(yīng)用小程序最 Awesome Taro 多端統(tǒng)一開發(fā)框架 Taro 優(yōu)秀學(xué)習(xí)資源匯總 showImg(https://segmentfault.com/img/r...

    toddmark 評(píng)論0 收藏0
  • 解析Twitter前端架構(gòu) 學(xué)習(xí)復(fù)雜場(chǎng)景數(shù)據(jù)設(shè)計(jì)

    摘要:總結(jié)本文分析了在采用架構(gòu)下的數(shù)據(jù)設(shè)計(jì)結(jié)構(gòu),在一個(gè)復(fù)雜的場(chǎng)景下,希望引起讀者對(duì)能有一個(gè)更深入的認(rèn)識(shí)。 前幾天刷Twitter,發(fā)現(xiàn)Nicolas(Engineering at @twitter. Technical Lead for Twitter Lite)發(fā)布了這么一條推文: showImg(https://segmentfault.com/img/remote/1460000009...

    csRyan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<