国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

React 同構(gòu)實(shí)踐與思考

MageekChiu / 2820人閱讀

摘要:后面會利用這個框架來做實(shí)踐。接下來就是我們要繼續(xù)探討的同構(gòu)同構(gòu)數(shù)據(jù)處理的探討我們都知道,瀏覽器端獲取數(shù)據(jù)需要發(fā)起請求,實(shí)際上發(fā)起的請求就是對應(yīng)服務(wù)端一個路由控制器。是有生命周期的,官方給我們指出的綁定,應(yīng)該在里來進(jìn)行。

眾所周知,目前的 WEB 應(yīng)用,用戶體驗(yàn)要求越來越高,WEB 交互變得越來越豐富!前端可以做的事越來越多,去年 Node 引領(lǐng)了前后端分層的浪潮,而 React 的出現(xiàn)讓分層思想可以更多徹底的執(zhí)行,尤其是 React 同構(gòu) (Universal or Isomorphic) 這個黑科技到底是怎么實(shí)現(xiàn)的,我們來一探究竟。

React 服務(wù)端方法

如果熟悉 React 開發(fā),那么一定對 ReactDOM.render 方法不陌生,這是 React 渲染到 DOM 中的方法。

現(xiàn)有的任何開發(fā)模式都離不開 DOM 樹,如圖:

服務(wù)端渲染就要稍作改動,如圖:

比較兩張圖可以看出,服務(wù)端渲染需要把 React 的初次渲染放到服務(wù)端,讓 React 幫我們把業(yè)務(wù) component 翻譯成 string 類型的 DOM 樹,再通過后端語言的 IO 流輸出至瀏覽器。

我們來看 React 官方給我們提供的服務(wù)端渲染的API:

React.renderToString 是把 React 元素轉(zhuǎn)成一個 HTML 字符串,因?yàn)榉?wù)端渲染已經(jīng)標(biāo)識了 reactid,所以在瀏覽器端再次渲染,React 只是做事件綁定,而不會將所有的 DOM 樹重新渲染,這樣能帶來高性能的頁面首次加載!同構(gòu)黑魔法主要從這個 API 而來。

React.renderToStaticMarkup,這個 API 相當(dāng)于一個簡化版的 renderToString,如果你的應(yīng)用基本上是靜態(tài)文本,建議用這個方法,少了一大批的 reactid,DOM 樹自然精簡了,在 IO 流傳輸上節(jié)省一部分流量。

配合 renderToStringrenderToStaticMarkup 使用,createElement 返回的 ReactElement 作為參數(shù)傳遞給前面兩個方法。

React 玩轉(zhuǎn) Node

有了解決方案,我們就可以動手在 Node 來做一些事了。后面會利用 KOA 這個 Node 框架來做實(shí)踐。

我們新建應(yīng)用,目錄結(jié)構(gòu)如下,

react-server-koa-simple
├── app
│   ├── assets
│   │   ├── build
│   │   ├── src
│   │   │    ├── img
│   │   │    ├── js
│   │   │    └── css
│   │   ├── package.json
│   │   └── webpack.config.js
│   ├── middleware
│   │   └── static.js(前端靜態(tài)資源托管中間件)
│   ├── plugin
│   │   └── reactview(reactview 插件)
│   └── views
│       ├── layout
│       │    └── Default.js
│       ├── Device.js
│       └── Home.js
├── .babelrc
├── .gitgnore
├── app.js
├── package.json
└── README.md

首先,我們需要實(shí)現(xiàn)一個 KOA 插件,用來實(shí)現(xiàn) React 作為服務(wù)端模板的渲染工作,方法是將 render 方法插入到 app 上下文中,目的是在 controller 層中調(diào)用,this.render(viewFileName, props, children) 并通過 this.body 輸出文檔流至瀏覽器端。

/*
 * koa-react-view.js
 * 提供 react server render 功能
 * {
 *   options : {
 *     viewpath: viewpath,                 // the root directory of view files
 *     doctype: "",
 *     extname: ".js",                     // view層直接渲染文件名后綴
 *     writeResp: true,                    // 是否需要在view層直接輸出
 *   }
 * }
 */
module.exports = function(app) {
  const opts = app.config.reactview || {};
  assert(opts && opts.viewpath && util.isString(opts.viewpath), "[reactview] viewpath is required, please check config!");
  const options = Object.assign({}, defaultOpts, opts);

  app.context.render = function(filename, _locals, children) {
    let filepath = path.join(options.viewpath, filename);

    let render = opts.internals
      ? ReactDOMServer.renderToString
      : ReactDOMServer.renderToStaticMarkup;

    // merge koa state
    let props = Object.assign({}, this.state, _locals);
    let markup = options.doctype || "";

    try {
      let component = require(filepath);
      // Transpiled ES6 may export components as { default: Component }
      component = component.default || component;
      markup += render(React.createElement(component, props, children));
    } catch (err) {
      err.code = "REACT";
      throw err;
    }
    if (options.writeResp) {
      this.type = "html";
      this.body = markup;
    }
    return markup;
  };
};

然后,我們來寫用 React 實(shí)現(xiàn)的服務(wù)端的 Components,

/*
 * react-server-koa-simple - app/views/Home.js
 * home模板
 */

render() {
  let { microdata, mydata } = this.props;
  let homeJs = `${microdata.styleDomain}/build/${microdata.styleVersion}/js/home.js`;
  let scriptUrls = [homeJs];

  return (
    
      
); }

這里做了幾件事,初始化 DOM 樹,用 data 屬性作服務(wù)端數(shù)據(jù)埋點(diǎn),渲染前后端公共 Content 模塊,引用前端模塊

而客戶端,我們就可以很方便地拿到了服務(wù)端的數(shù)據(jù),可以直接拿來使用,

import ReactDOM from "react-dom";
import Content from "./components/Content.js";

const microdata = JSON.parse(appEle.getAttribute("data-microdata"));
const mydata = JSON.parse(appEle.getAttribute("data-mydata"));

ReactDOM.render(
  ,
  document.getElementById("demoApp")
);

然后,到了啟動一個簡單的 koa 應(yīng)用的時候,完善入口 app.js 來驗(yàn)證我們的想法,

const koa = require("koa");
const koaRouter = require("koa-router");
const path = require("path");
const reactview = require("./app/plugin/reactview/app.js");
const Static = require("./app/middleware/static.js");

const App = ()=> {
  let app = koa();
  let router = koaRouter();

  // 初始化 /home 路由 dispatch 的 generator
  router.get("/home", function*() {
    // 執(zhí)行view插件
    this.body = this.render("Home", {
      microdata: {
        domain: "http://localhost:3000"
      },
      mydata: {
        nick: "server render body"
      }
    });
  });
  app.use(router.routes()).use(router.allowedMethods());

  // 注入 reactview
  const viewpath = path.join(__dirname, "app/views");
  app.config = {
    reactview: {
      viewpath: viewpath,                 // the root directory of view files
      doctype: "",
      extname: ".js",                     // view層直接渲染文件名后綴
      beautify: true,                     // 是否需要對dom結(jié)構(gòu)進(jìn)行格式化
      writeResp: false,                    // 是否需要在view層直接輸出
    }
  }
  reactview(app);

  return app;
};

const createApp = ()=> {
  const app = App();

  // http服務(wù)端口監(jiān)聽
  app.listen(3000, ()=> {
    console.log("3000 is listening!");
  });

  return app;
};
createApp();

現(xiàn)在,訪問上面預(yù)先設(shè)置好的路由,http://localhost:3000/home 來驗(yàn)證 server render,

服務(wù)端:

瀏覽器端:

react-router 和 koa-router 統(tǒng)一

我們已經(jīng)建立了服務(wù)端渲染的基礎(chǔ)了,接著再考慮下如何把后端和前端的路由做統(tǒng)一。

假設(shè)我們的路由設(shè)置成 /device/:deviceID 這種形式,
那么服務(wù)端是這么來實(shí)現(xiàn)的,

// 初始化 device/:deviceID 路由 dispatch 的 generator
router.get("/device/:deviceID", function*() {
  // 執(zhí)行view插件
  let deviceID = this.params.deviceID;
  this.body = this.render("Device", {
    isServer: true,
    microdata: microdata,
    mydata: {
      path: this.path,
      deviceID: deviceID,
    }
  });
});

以及服務(wù)端 View 模板,

render() {
  const { microdata, mydata, isServer } = this.props;
  const deviceJs = `${microdata.styleDomain}/build/${microdata.styleVersion}/js/device.js`;
  const scriptUrls = [deviceJs];

  return (
    
      
); }

前端 app 入口:app.js

function getServerData(key) {
  return JSON.parse(appEle.getAttribute(`data-${key}`));
};

// 從服務(wù)端埋點(diǎn)處 
獲取 microdata, mydata let microdata = getServerData("microdata"); let mydata = getServerData("mydata"); ReactDOM.render( , document.getElementById("demoApp"));

前后端公用的 Iso.js 模塊,前端路由同樣設(shè)置成 /device/:deviceID

class Iso extends Component {
  static propTypes = {
    // ...
  };

  // 包裹 Route 的 Component,目的是注入服務(wù)端傳入的 props
  wrapComponent(Component) {
    const { microdata, mydata } = this.props;

    return React.createClass({
      render() {
        return React.createElement(Component, {
          microdata: microdata,
          mydata: mydata
        }, this.props.children);
      }
    });
  }

  // LayoutView 為路由的布局; DeviceView 為參數(shù)處理模塊
  render() {
    const { isServer, mydata } = this.props;

    return (
      
        
          
          
        
      
    );
  }
}

這樣我就實(shí)現(xiàn)了服務(wù)端和前端路由的同構(gòu)!

無論你是初次訪問這些資源路徑: /device/all, /device/pc, /device/wireless,還是在頁面手動切換這些資源路徑效果都是一樣的,既保證了初次渲染有符合預(yù)期的 DOM 輸出的用戶體驗(yàn),又保證了代碼的簡潔性,最重要的是前后端代碼是一套,并且由一位工程師開發(fā),有沒有覺得很棒?

其中注意幾點(diǎn):

Iso 的 render 模塊需要判斷isServer,服務(wù)端用createMemoryHistory,前端用browserHistory;

react-router 的 component 如果需要注入 props 必須對其進(jìn)行包裹 wrapComponent。因?yàn)榉?wù)端渲染的數(shù)據(jù)需要通過傳 props 的方式,而react-router-route 只提供了 component,并不支持繼續(xù)追加 props。截取 Route 的源碼,

propTypes: {
  path: string,
  component: _PropTypes.component,
  components: _PropTypes.components,
  getComponent: func,
  getComponents: func
},

為什么服務(wù)端獲取數(shù)據(jù)不和前端保持一致,在 Component 里作數(shù)據(jù)綁定,使用 fetchData 和數(shù)據(jù)綁定!只能說,你可以大膽的假設(shè)。接下來就是我們要繼續(xù)探討的同構(gòu)model!

同構(gòu)數(shù)據(jù)處理的探討

我們都知道,瀏覽器端獲取數(shù)據(jù)需要發(fā)起 ajax 請求,實(shí)際上發(fā)起的請求 URL 就是對應(yīng)服務(wù)端一個路由控制器。

React 是有生命周期的,官方給我們指出的綁定 Model,fetchData 應(yīng)該在 componentDidMount 里來進(jìn)行。在服務(wù)端,React 是不會去執(zhí)行componentDidMount 方法的,因?yàn)?,React 的 renderTranscation 分成兩塊: ReactReconcileTransactionReactServerRenderingTransaction,其在服務(wù)端的實(shí)現(xiàn)移除掉了在瀏覽器端的一些特定方法。

而服務(wù)端處理數(shù)據(jù)是線性的,是不可逆的,發(fā)起請求 > 去數(shù)據(jù)庫獲取數(shù)據(jù) > 業(yè)務(wù)邏輯處理 > 組裝成 html-> IO流輸出給瀏覽器。顯然,服務(wù)端和瀏覽器端是矛盾的!

實(shí)驗(yàn)的方案

你或許會想到利用 ReactClass 提供的 statics 來做點(diǎn)文章,React 確實(shí)提供了入口,不僅能包裹靜態(tài)屬性,還能包裹靜態(tài)方法,并且能 DEFINE_MANY:

/**
 * An object containing properties and methods that should be defined on
 * the component"s constructor instead of its prototype (static methods).
 *
 * @type {object}
 * @optional
 */
statics: SpecPolicy.DEFINE_MANY,

利用 statics 把我們的組件擴(kuò)展成這樣,

class ContentView extends Component {
  statics: {
    fetchData: function (callback) {
      ContentData.fetch().then((data)=> {
        callback(data);
      });
    }
  };
  // 瀏覽器端這樣獲取數(shù)據(jù)
  componentDidMount() {
    this.constructor.fetchData((data)=> {
      this.setState({
        data: data
      });
    });
  }
  ...
});

ContentData.fetch() 需要實(shí)現(xiàn)兩套:

服務(wù)端:封裝服務(wù)端service層方法

瀏覽器端:封裝ajax或Fetch方法

服務(wù)端調(diào)用:

require("ContentView").fetchData((data)=> {
  this.body = this.render("Device", {
    isServer: true,
    microdata: microdata,
    mydata: data
  });
});

這樣可以解決數(shù)據(jù)層的同構(gòu)!但我并不認(rèn)為這是一個好的方法,好像回到 JSP 時代。

我們團(tuán)隊(duì)現(xiàn)在使用的方法:

參考資料

本文完整運(yùn)行的 例子

https://facebook.github.io/react/docs/getting-started.html

https://github.com/facebook/react

https://github.com/rackt/react-router

https://github.com/koajs/koa

https://github.com/alexmingoia/koa-router

https://github.com/koajs/react-view

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

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/79019.html

相關(guān)文章

  • React同構(gòu)直出優(yōu)化總結(jié)

    摘要:同構(gòu)的關(guān)鍵要素完善的屬性及生命周期與客戶端的時機(jī)是同構(gòu)的關(guān)鍵。的一致性在前后端渲染相同的,將輸出一致的結(jié)構(gòu)。以上便是在同構(gòu)服務(wù)端渲染的提供的基礎(chǔ)條件??梢詫⒎庋b至的中,在服務(wù)端上生成隨機(jī)數(shù)并傳入到這個中,從而保證隨機(jī)數(shù)在客戶端和服務(wù)端一致。 原文地址 React 的實(shí)踐從去年在 PC QQ家校群開始,由于 PC 上的網(wǎng)絡(luò)及環(huán)境都相當(dāng)好,所以在使用時可謂一帆風(fēng)順,偶爾遇到點(diǎn)小磕絆,也能夠...

    alaege 評論0 收藏0
  • koa 實(shí)現(xiàn) react-view 原理

    摘要:今天,其實(shí)講的是在實(shí)現(xiàn)同構(gòu)過程中看到過,可能非常容易被忽視更小的一個點(diǎn)。每一個架構(gòu)的框架都會涉及到層的展現(xiàn),也不例外。這種說法即對也不對??偨Y(jié)其實(shí),實(shí)現(xiàn)非常簡單,我們也從一些維度看到了設(shè)計(jì)一個的一般方法。 在之前我們有過一篇『React 同構(gòu)實(shí)踐與思考』的專欄文章,給讀者實(shí)踐了用 React 怎么實(shí)現(xiàn)同構(gòu)。今天,其實(shí)講的是在實(shí)現(xiàn)同構(gòu)過程中看到過,可能非常容易被忽視更小的一個點(diǎn) —— R...

    zxhaaa 評論0 收藏0
  • 前端每周清單:Node.js 微服務(wù)實(shí)踐,Vue.js GraphQL,Angular 組件技巧

    摘要:前端每周清單第期微服務(wù)實(shí)踐,與,組件技巧,攻防作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。 前端每周清單第 26 期:Node.js 微服務(wù)實(shí)踐,Vue.js 與 GraphQL,Angular 組件技巧,HeadlessChrome 攻防 作者:王下邀月熊 編輯:徐川...

    wall2flower 評論0 收藏0
  • React 進(jìn)階設(shè)計(jì)控制權(quán)問題

    摘要:盤點(diǎn)一下,模式反應(yīng)了典型的控制權(quán)問題。異步狀態(tài)管理與控制權(quán)提到控制權(quán)話題,怎能少得了這樣的狀態(tài)管理工具。狀態(tài)管理中的控制主義和極簡主義了解了異步狀態(tài)中的控制權(quán)問題,我們再從全局角度進(jìn)行分析。 控制權(quán)——這個概念在編程中至關(guān)重要。比如,輪子封裝層與業(yè)務(wù)消費(fèi)層對于控制權(quán)的爭奪,就是一個很有意思的話題。這在 React 世界里也不例外。表面上看,我們當(dāng)然希望輪子掌控的事情越多越好:因?yàn)槌橄髮?..

    superw 評論0 收藏0
  • React 進(jìn)階設(shè)計(jì)控制權(quán)問題

    摘要:盤點(diǎn)一下,模式反應(yīng)了典型的控制權(quán)問題。異步狀態(tài)管理與控制權(quán)提到控制權(quán)話題,怎能少得了這樣的狀態(tài)管理工具。狀態(tài)管理中的控制主義和極簡主義了解了異步狀態(tài)中的控制權(quán)問題,我們再從全局角度進(jìn)行分析。 控制權(quán)——這個概念在編程中至關(guān)重要。比如,輪子封裝層與業(yè)務(wù)消費(fèi)層對于控制權(quán)的爭奪,就是一個很有意思的話題。這在 React 世界里也不例外。表面上看,我們當(dāng)然希望輪子掌控的事情越多越好:因?yàn)槌橄髮?..

    rubyshen 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<