<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

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

      早幾年前端還處于刀耕火種、JQuery獨樹一幟的時代,前后端代碼的耦合度很高,一個web頁面文件的代碼可能是這樣的:

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

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

      前端框架熱潮

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

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

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

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

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

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

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

      弊端

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

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

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

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

      另外使用前端框架的頁面也不利于SEO,其實應該說不利于國內(nèi)這些渣搜索引擎的SEO,谷歌早已能從內(nèi)存中去抓數(shù)據(jù)(客戶端渲染后的DOM數(shù)據(jù))。

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

      直出和同構(gòu)

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

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

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

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

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

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

      示例

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

      項目的目錄結(jié)構(gòu)如下:

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

      1. node 端 jsx 解析處理

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

      //app.js
      require('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);
      View Code

      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'),  //取首屏數(shù)據(jù)
              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,  //將取到的首屏數(shù)據(jù)注入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 class="wrap" id="wrap"><%-reactHtml%></div>

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

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

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

      ejs上注入直出數(shù)據(jù):

        <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 來做轉(zhuǎn)換處理,在 gulp 和 webpack 中都有使用到,具體可參考它們的配置。

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

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

      4. issue

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

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

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

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

      5. 其它

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

      require('css/component/List');

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

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

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

      所以走直出的話,建議把首屏的樣式抽離出來內(nèi)聯(lián)到頭部去。

       

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

      donate

       

      posted @ 2015-12-27 15:08  vajoy  閱讀(10425)  評論(6)    收藏  舉報
      Copyright © 2014 - 2022 VaJoy Studio
      主站蜘蛛池模板: 性男女做视频观看网站| 色一伊人区二区亚洲最大| 亚洲另类丝袜综合网| 一本久道久久综合狠狠躁av| 国产精品普通话国语对白露脸| 肥大bbwbbw高潮抽搐| 国产精品成人久久电影| 久久精品人人看人人爽| 丝袜美腿诱惑之亚洲综合网| 亚洲欧洲日韩国内高清| 久久精品天天中文字幕人妻| 国产不卡免费一区二区| 国产福利视频区一区二区| 国产精品视频第一第二区| 亚洲国产一区二区精品专| 免费AV片在线观看网址| 免费区欧美一级猛片| 中文字幕日韩一区二区不卡| 国产精品一二三区蜜臀av| 免费a级黄毛片| 国产sm调教折磨视频| 强伦人妻一区二区三区| 国产成人8X人网站视频| 国精产品999国精产| 中文字幕国产日韩精品| 国产偷自视频区视频| 国产中文字幕一区二区| 青青青青国产免费线在线观看| 国产普通话对白刺激| 国产一区精品在线免费看| 亚洲最大成人在线播放| 情欲少妇人妻100篇| 乱人伦人妻中文字幕无码久久网| 99久久夜色精品国产亚洲| 蜜桃久久精品成人无码av| 日韩V欧美V中文在线| 她也色tayese在线视频| 国产午夜亚洲精品国产成人 | 日本高清一区免费中文视频| 无码三级av电影在线观看| 成人福利一区二区视频在线|