亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 課堂 > FAQ問答 > 正文

降低首屏時間 “直出”是個什么概念-

2020-03-22 16:28:41
字體:
來源:轉載
供稿:網友
  • 早幾年前端還處于刀耕火種、JQuery獨樹一幟的時代,前后端代碼的耦合度很高,一個web頁面文件的代碼可能是這樣的:

    這意味著后端的工程師往往得負責一部分修改HTML、編寫腳本的工作,而前端開發者也得了解頁面上存在的服務端代碼含義。

    有時候某處頁面邏輯的變動,鑒于代碼的混搭,可能都不確定應該請后端還是前端來改動(即使他們都能處理)。

    前端框架熱潮

    有句俗話說的好——“人啊,要是擅于開口‘關我屁事’和‘關你屁事’這倆句,可以節省人生中的大部分時間”。

    隨著這兩年被 angular 牽頭帶起的各種前端MV*框架的風靡,后端可以毋須再于靜態頁面耗費心思,只需要專心開發數據接口供前端使用即可。得益于此,前后端終于可以安心地互相道一聲“關我屁事”或“關你屁事”了。

    以 avalon 為例,前端只需要在頁面加載時發送個ajax請求取得數據綁定到vm,然后做view層渲染即可:

    var vm = avalon.define({    $id: 'wrap',    list: []});fetch('data/list.php')   //向后端接口發出請求    .then(res => res.json())    .then(json => {        vm.list = json; //數據注入vm        avalon.scan();  //渲染view層    });

    靜態頁面的代碼也由前端一手掌握,原本服務端的代碼換成了 avalaon 的專用屬性與插值表達式:

    <ul ms-controller='wrap'>    <li ms-repeat='list'>{el.name}</li></ul>

    前后端代碼隔離的形式大大提升了項目的可維護性和開發效率,已經成為一種web開發的主流模式。它解放了后端程序員的雙手,也將更多的控制權轉移給前端人員(當然前端也因此需要多學習一些框架知識)。

    弊端

    前后端隔離的模式雖然給開發帶來了便利,但相比水乳交融的舊模式,頁面首屏的數據需要在加載的時候向服務端發去請求才能取得,多了請求等候的時間(RTT)。

    這意味著用戶訪問頁面的時候,這段“等待后端返回數據”的時延會處于白屏狀態,如果用戶網速差,那么這段首屏等候時間會是很糟糕的體驗。

    當然拉到數據后,還得做 view 層渲染(客戶端引擎的處理還是很快的,忽略渲染的時間),這又依賴于框架本身,即框架要先被下載下來才能處理這些視圖渲染操作。那么好家伙,一個 angular.min.js 就達到了 120 多KB,用著渣信號的用戶得多等上一兩秒來下載它。

    這么看來,單純前后端隔離的形式存在首屏時間較長的問題,除非未來平均網速達到上G/s,不然都是不理想的體驗。

    so 怎么辦?相信很多朋友猜到了——用 node 來助陣。

    直出和同構

    直出說白了其實就是“服務端渲染并輸出”,跟起初我們提及的前后端水乳交融的開發模式基本類似,只是后端語言我們換成了 node 。

    09年開始冒頭的 node 現在成了當紅炸子雞,包含阿里、騰訊在內的各大公司都廣泛地把 node 用到項目上,前后端整而為一,如果 node 的特性適用于你的項目,那么何樂而不為呢。

    我們在這邊也提及了一個“同構”的概念,即前后端(這里的“后端”指的是直出端,數據接口不一定由node開發)使用同一套代碼方案,方便維護。

    當前 node 在服務端有著許多主流抑或非主流的框架,包括 express、koa、thinkjs 等,能夠較快上手,利用各種中間件得以進行敏捷開發。

    另外諸如 ejs、jade 這樣的渲染模板能讓我們輕松地把首屏內容(數據或渲染好的DOM樹)注入頁面中。

    這樣用戶訪問到的便是已經帶有首屏內容的頁面,大大降低了等候時間,提升了體驗。

    示例

    在這里我們以 koa + ejs + React 的服務端渲染為例,來看看一個簡單的“直出”方案是怎樣實現的。該示例也可以在我的github上下載到。

    項目的目錄結構如下:

    +---data   //模擬數據接口,放了一個.json文件+---dist  //文件構建后(gulp/webpack)存放處|   +---css|   |   +---common|   |   ---page|   +---js|   |   +---component|   |   ---page|   ---views|       +---common|       ---home+---modules  //一些自行封裝的通用業務模塊+---routes  //路由配置---src  //未構建的文件夾    +---css     |   +---common    |   +---component    |   ---page    +---js    |   +---component //React組件    |   ---page //頁面入口文件    ---views  //ejs模板        +---common        ---home

    1. node 端 jsx 解析處理

    node 端是不會自己識別 React 的jsx 語法的,故我們需要在項目文件中引入node-jsx,即使現在可以安裝babel-cli 后(并添加預設)使用 babel-node 命令替代 node,但后者用起來總會出問題,故暫時還是采納 node-jsx 方案:

    //app.jsrequire('node-jsx').install({  //讓node端能解析jsx    extension: '.js'});var fs = require('fs'),    koa = require('koa'),    compress = require('koa-compress'),    render = require('koa-ejs'),    mime = require('mime-types'),    r_home = require('./routes/home'),    limit = require('koa-better-ratelimit'),    getData = require('./modules/getData');var app = koa();app.use(limit({ duration: 1000*10 ,     max: 500, accessLimited : '您的請求太過頻繁,請稍后重試'}));app.use(compress({    threshold: 50,     flush: require('zlib').Z_SYNC_FLUSH}));render(app, {  //ejs渲染配置    root: './dist/views',    layout: false ,    viewExt: 'ejs',    cache: false,    debug: true});getData(app);//首頁路由r_home(app);app.use(function*(next){    var p = this.path;    this.type = mime.lookup(p);    this.body = fs.createReadStream('.'+p);});app.listen(3300);

    2. 首頁路由('./routes/home')配置

    var router = require('koa-router'),    getHost = require('../modules/getHost'),    apiRouter = new router();var React = require('react/lib/ReactElement'),    ReactDOMServer = require('react-dom/server');var List = React.createFactory(require('../dist/js/component/List'));module.exports = function (app) {    var data = this.getDataSync('../data/names.json'),  //取首屏數據        json = JSON.parse(data);    var lis = json.map(function(item, i){       return (           <li>{item.name}</li>       )    }),        props = {color: 'red'};    apiRouter.get('/', function *() {  //首頁        yield this.render('home/index', {            title: 'serverRender',            syncData: {                names: json,  //將取到的首屏數據注入ejs模板                props: props            },            reactHtml:  ReactDOMServer.renderToString(List(props, lis)),            dirpath: getHost(this)        });    });    app.use(apiRouter.routes());};

    注意這里我們使用了ReactDOMServer.renderToString 來渲染 React 組件為純 HTML 字符串,注意 List(props, lis) ,我們還傳入了 props 和 children。

    其在 ejs 模板中的應用為:

    <div html' target='_blank'>class='wrap' id='wrap'><%-reactHtml%></div>

    就這么簡單地完成了服務端渲染的處理,但還有一處問題,如果組件中綁定了事件,客戶端不會感知。

    所以在客戶端我們也需要再做一次與服務端一致的渲染操作,鑒于服務端生成的DOM會被打上 data-react-id 標志,故在客戶端渲染的話,react 會通過該標志位的對比來避免冗余的render,并綁定上相應的事件。

    這也是我們把所要注入組件中的數據(syncData)傳入 ejs 的原因,我們將把它作為客戶端的一個全局變量來使用,方便客戶端掛載組件的時候用上:

    ejs上注入直出數據:

      <script>    syncData = JSON.parse('<%- JSON.stringify(syncData) %>');  </script>

    頁面入口文件(js/page/home.js)掛載組件:

    import React from 'react';import ReactDOM from 'react-dom';var List = require('../component/List');var lis = syncData.names.map(function(item, i){      return (        <li>{item.name}</li>    )});ReactDOM.render(    <List {...syncData.props}>        {lis}    </List>,    document.getElementById('wrap'));

    3. 輔助工具

    為了玩鮮,在部分模塊里寫了 es2015 的語法,然后使用 babel 來做轉換處理,在 gulp 和 webpack 中都有使用到,具體可參考它們的配置。

    另外鑒于服務端對 es2015 的特性支持不完整,配合 babel-core/register 或者使用 babel-node 命令都存在兼容問題,故針對所有需要在服務端引入到的模塊(比如React組件),在koa運行前先做gulp處理轉為es5(這些構建模塊僅在服務端會用到,客戶端走webpack直接引用未轉換模塊即可)。

    ejs文件中樣式或腳本的內聯處理我使用了自己開發的 gulp-embed ,有興趣的朋友可以玩一玩。

    4. issue

    說實話 React 的服務端渲染處理整體開發是沒問題的,就是開發體驗不夠好,主要原因還是各方面對 es2015 支持不到位導致的。

    雖然在服務端運行前,我們在gulp中使用babel對相關模塊進行轉換,但像export default XXX 這樣的語法轉換后還是無法被服務端支持,只能降級寫為module.exports = XXX。但這么寫,在其它模塊就沒法 import XXX from 'X' 了(改為 require('X')代替),總之不爽快。只能期待后續 node(其實應該說V8) 再迭代一些版本能更好地支持 es2015 的特性。

    另外如果 React 組件涉及列表項,常規我們會加上 key 的props特性來提升渲染效率,但即使前后端傳入相同的key值,最終 React 渲染出來的 key 值是不一致的,會導致客戶端掛載組件時再做一次渲染處理。

    對于這點我個人建議是,如果是靜態的列表,那么統一都不加 key ,如果是動態的,那么就加吧,客戶端再渲染一遍感覺也沒多大點事。(或者你有更好方案請留言哈~)

    5. 其它

    有時候服務端引入的模塊里面,有些東西是僅僅需要在客戶端使用到的,我們以這個示例中的組件 component/List 為例,里面的樣式文件

    require('css/component/List');

    不應當在服務端執行的時候使用到,但鑒于同構,前后端用的一套東西,這個怎么解決呢?其實很好辦,通過 window 對象來判斷即可(只要沒有什么中間件給你在服務端也加了window接口)

    var isNode = typeof window === 'undefined';if(!isNode){    require('css/component/List');}

    不過請注意,這里我通過 webpack 把組件的樣式也打包進了客戶端的頁面入口文件,其實不妥當。因為通過直出,頁面在響應的時候就已經把組件的DOM樹都先顯示出來了,但這個時候是還沒有取到樣式的(樣式打包到入口腳本了),需要等到入口腳本加載的時候才能看到正確的樣式,這個過程會有一個閃動的過程,是種不舒服的體驗。

    所以走直出的話,建議把首屏的樣式抽離出來內聯到頭部去。

    嘮嘮磕磕就說了這么多,歡迎討論交流,共勉~

    PHP編程

    鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

  • 發表評論 共有條評論
    用戶名: 密碼:
    驗證碼: 匿名發表
    亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
    丝袜情趣国产精品| 自拍视频国产精品| 精品国产视频在线| 日本精品视频在线观看| 国产欧美日韩中文| 日韩精品亚洲视频| 午夜精品久久久久久99热软件| 超碰97人人做人人爱少妇| 久久精品中文字幕电影| 色99之美女主播在线视频| 国产视频久久久久久久| 中文字幕国产日韩| 日日噜噜噜夜夜爽亚洲精品| 日韩亚洲国产中文字幕| 国产成人精品一区二区在线| 国产精品ⅴa在线观看h| 国产综合香蕉五月婷在线| 亚洲成人a**站| 性欧美办公室18xxxxhd| 久久精品人人做人人爽| 国产精品高潮视频| 精品magnet| 欧美日韩综合视频网址| 中文字幕日韩欧美| 日韩精品视频免费专区在线播放| 欧美日韩国产在线播放| 国内外成人免费激情在线视频| 992tv在线成人免费观看| 91在线中文字幕| 色爱精品视频一区| 亚洲人成电影在线观看天堂色| 亚洲字幕在线观看| 久久久综合av| 亚洲欧美国产视频| 精品在线欧美视频| 久久婷婷国产麻豆91天堂| 91精品久久久久久久| 国产精品成av人在线视午夜片| 欧美裸身视频免费观看| 在线播放日韩专区| 亚洲黄一区二区| 日本精品中文字幕| 亚洲品质视频自拍网| 国产一区二区美女视频| 国产精品日韩电影| 欧美日韩一区二区免费在线观看| 日韩一区二区精品视频| 国产精品午夜视频| 日韩精品极品在线观看播放免费视频| 成人精品久久久| 久久夜色撩人精品| 色香阁99久久精品久久久| 91欧美日韩一区| 日韩有码在线播放| 国模精品视频一区二区三区| 欧美精品一区二区三区国产精品| 人妖精品videosex性欧美| 中文字幕日韩精品有码视频| 亚洲成年人在线| 国产一区二区美女视频| 欧美成年人视频网站欧美| 大荫蒂欧美视频另类xxxx| 亚洲第一在线视频| 欧美一区二区大胆人体摄影专业网站| 欧美精品生活片| 亚洲欧美精品在线| 久久久中文字幕| 亚洲高清在线观看| 色哟哟亚洲精品一区二区| 成人免费激情视频| 97视频在线观看亚洲| 色阁综合伊人av| 亚洲美女在线观看| 欧美一级大片在线免费观看| 精品国产91久久久久久老师| 富二代精品短视频| 成人a视频在线观看| 久久成人精品一区二区三区| 亚洲第一av网| 一区二区日韩精品| 91国产精品电影| 欧美自拍视频在线观看| 成人在线免费观看视视频| 欧美网站在线观看| 欧美极品少妇与黑人| 国产日韩精品电影| 国产一区二区三区视频在线观看| 在线亚洲男人天堂| 亚洲精品91美女久久久久久久| 欧美国产日韩一区二区| 日韩在线小视频| 日韩精品在线免费播放| 欧美一级黑人aaaaaaa做受| 日韩经典一区二区三区| 精品视频在线观看日韩| 欧美中文字幕在线播放| 日韩高清有码在线| 欧美日韩亚洲视频| 国产精品国产亚洲伊人久久| 久久成人这里只有精品| 国产亚洲欧美一区| 亚洲欧美另类中文字幕| 欧美一区亚洲一区| 日韩精品中文字幕有码专区| 中文字幕日韩欧美在线视频| 欧美激情一级精品国产| 91高清视频免费观看| 久久中文字幕国产| 国产精品va在线播放我和闺蜜| 精品国产1区2区| 国产精品久久久久久网站| 亚洲天天在线日亚洲洲精| 欧美性极品xxxx娇小| 好吊成人免视频| 亚洲最新在线视频| 亚洲成av人影院在线观看| 亚洲精品乱码久久久久久金桔影视| 久久国产精品影视| 欧美成人黑人xx视频免费观看| 欧美成人午夜剧场免费观看| 国产精品中文字幕在线观看| 成人免费高清完整版在线观看| 久久久国产影院| 国产精品视频公开费视频| 亚洲第一中文字幕| 91精品国产91久久久久久| 麻豆国产va免费精品高清在线| 欧美区二区三区| 成人午夜激情免费视频| 成人网欧美在线视频| 欧美区在线播放| 自拍偷拍亚洲区| 91久久在线播放| 亚洲欧美中文字幕在线一区| 欧美最近摘花xxxx摘花| 国产成人综合一区二区三区| 欧美在线视频在线播放完整版免费观看| 国产精品香蕉国产| 伊人久久免费视频| 清纯唯美亚洲综合| 91免费看视频.| 欧美日韩精品国产| 在线观看欧美www| 久久夜色精品亚洲噜噜国产mv| 亚洲一级黄色av| 国产aⅴ夜夜欢一区二区三区| 91麻豆国产语对白在线观看| 日韩精品一二三四区| 久久久久久久成人| 亚洲国产精品va在看黑人| 亚洲一区二区三区乱码aⅴ蜜桃女| 亚洲有声小说3d| 久久久精品一区二区三区| 亚洲第一av在线| 成人免费淫片aa视频免费| 精品视频—区二区三区免费| 欧美午夜精品久久久久久浪潮| 国产精品 欧美在线| 欧美乱妇40p| 国产精品1234| 亚洲精品第一页| 国产不卡在线观看| 成人午夜一级二级三级| 国产一区二区三区丝袜|