摘要:當我們在瀏覽器端進行切換切換的時候,頁面是不刷新的,通過請求獲取到數據,重新渲染結構。如果想在實際開發中使用,有幾個問題不得不面對對開發者的要求高,至少要熟悉,,特別是組件的構建,如何提高復用率這些都是要在前期思考的。
概述
一直想用React做些東西,苦于沒有實際項目練手,所以一直都是自己在搞些小玩意兒,做過用React Router構建的內部訂餐系統,是個SPA,也在社區分享過。由于一個人做全棧開發,數據庫(mongodb)全靠自己設,需求全靠自己編,頁面全靠自己扯,心好累,感覺不會在愛了!
SPA用來構建內部的系統完全沒問題,但是用來做門戶、做電商網站就不行了,為啥?因為SEO,很多的MVVM,MV*框架不能用、不敢用都是基于這個原因(當然也可能因為我不會用)。
最近拿CNode的API做了個React服務器端渲染的例子,這里跟大家分享下這個項目的構建過程和代碼組織,未必好,主要提供一個思路。
整體項目目錄如上,這里作個說明,附上代碼地址,上面有說明怎么使用。
component 我們的組件目錄,這里放置了view、ui等組件
lib 后端代碼,如過濾器等
node_modules 依賴包
public 靜態資源
routes 路由
瀏覽器端和服務器端的代碼我們沒必要完全獨立,實際上有時候代碼是可以復用的。舉個例子
表單異步提交的時候,后端返回一個state狀態告知是否成功,相信大部分的人的第一反應都是抽出常量
constants.js
module.exports = { state: { SUCCESS: 10000 } };
當然了,瀏覽器端也是要判斷這個state的,為了提高代碼的復用性,這里同樣抽出
constants.js
module.exports = { state: { SUCCESS: 10000 } };
雖然內容相同,實際上這是兩個不同的js,分處不同的目錄,oh shit。我的開發理念一般是這樣的
相同的代碼堅決不寫第二遍,特殊情況除外!
采用React后端渲染,我用了webpack打包,實際上就避免了這個問題,寫一份constants.js,打包到瀏覽器端去,NICE!
編碼既然是后端渲染,首先得選擇一個模板引擎,這里我采用的|90ee772881e409df0a8a3bb9717d59483|,具體配置和使用可以參考文檔,這里我就不贅述了。既然是構建SPA必不可少得要個路由管理,這里我選擇的react-router,react-engine也是兼容react-router的,真棒!拿首頁的編碼舉個例子
route路由我這里用的自己的路由組織express-mapping,看首頁的代碼
routes/index.js
var constants = require("../lib/constants"); var request = require("superagent"); var queryString = require("query-string"); module.exports = { get: { "/": function (req, res) { request .get("http://cnodejs.org/api/v1/topics?" + queryString.stringify(req.query)) .end(function (err, response) { if (err) { throw err; } res.render(req.url, { state: constants.state.SUCCESS, data: response.body.data, title: "CNode:Node.js專業中文社區" }); }); } } };
實際上,res.render方法被我重寫了,根據發的請求是不是ajax返回不同的內容
lib/filter.js
/** * 區分ajax請求與普通請求 */ req.isXmlHttpRequest = (function () { var xRequestedWith = req.headers["x-requested-with"]; return xRequestedWith && xRequestedWith.toLowerCase() === "xmlhttprequest"; })(); /** * 重寫res.render方法 */ var render = res.render; res.render = function (view, data) { var response = _.extend({session: req.session}, data); req.isXmlHttpRequest ? res.json(response) : render.call(res, view, response); };
這樣我們又做到了接口的復用!
組件來看看我們打包的入口
component/index.jsvar React = require("react"); var Router = require("react-router"); var $ = require("jquery"); var Routes = require("./routes.jsx"); var CLIENT_VARIABLENAME = "__REACT_ENGINE__"; var _window; var _document; if (typeof window !== "undefined" && typeof document !== "undefined") { _window = window; _document = document; } document.addEventListener("DOMContentLoaded", function onLoad() { Router.run(Routes, Router.HistoryLocation, function onRouterRun(Root, state) { var props = _window[CLIENT_VARIABLENAME]; if (props) { var componentInstance = React.createElement(Root, props); React.render(componentInstance, _document); _window[CLIENT_VARIABLENAME] = null; } else { $.get(state.path).then(function (data) { var componentInstance = React.createElement(Root, data); React.render(componentInstance, _document); }); } }); });
后端渲染的原理是這樣的,當我們第一訪問的時候,node端返回React渲染好的HTML結構,并通過script標簽將數據傳遞到前端,然后在瀏覽器端獲取到傳遞的數據再渲染一次,總共渲染了兩次。當我們在瀏覽器端進行切換切換的時候,頁面是不刷新的,通過ajax請求獲取到數據,重新渲染DOM結構。
component/routes.jsx再來看看路由,不熟悉React Router的最好熟悉下,會用到
var React = require("react"); var Router = require("react-router"); var Route = Router.Route; var DefaultRoute = Router.DefaultRoute; var App = require("./app.jsx"); var Index = require("./views/index.jsx"); var TopicDetail = require("./views/topic/detail.jsx"); var UserDetail = require("./views/user/detail.jsx"); var routes = (); module.exports = routes;
都是些基本的路由配置
component/app.jsx再來看下入口組件
var React = require("react"); var Router = require("react-router"); var Layout = require("./views/layouts/default.jsx"); var RouteHandler = Router.RouteHandler; module.exports = React.createClass({ render: function () { var data = this.props.data; return () } });
Layout就是我們的布局了,相同的代碼總要抽出來的。
var React = require("react"); var constants=require("../../../lib/constants"); var Footer=require("../partials/footer.jsx"); module.exports = React.createClass({ render: function render() { return (component/views/index.jsx{this.props.title} {this.props.children} ); } });
這里就是業務代碼了
var React = require("react"); var Router = require("react-router"); var $ = require("jquery"); var Navbar = require("./partials/navbar.jsx"); var queryString = require("query-string"); var utils=require("../component/utils"); var Link = Router.Link; var Label = React.createClass({ render: function () { var tab = this.props.tab; var data = this.props.data; if (data.top) { return ; } if (data.good) { return ; } if (!tab || tab === "all") { if (data.tab === "share") { return ; } if (data.tab === "ask") { return ; } if (data.tab === "job") { return ; } } return null; } }); module.exports = React.createClass({ getInitialState: function () { return { data: this.props.data || [], page: 1 } }, componentWillReceiveProps: function (nextProps) { this.setState({ data: nextProps.data, page: 1 }); }, componentDidMount: function () { var loading = false; $(window).on("scroll", function () { var fromBottom = $(document).height() - $(window).height() - $(window).scrollTop(); if (fromBottom <= 10 && !loading) { loading = true; var query = queryString.parse(location.search); query.page = this.state.page + 1; $.get(location.pathname + "?" + queryString.stringify(query), function (response) { this.setState({ data: this.state.data.concat(response.data), page: this.state.page + 1 }, function () { loading = false; }); }.bind(this)); } }.bind(this)); }, render: function () { var tab = this.props.query.tab; return () } });{this.state.data.map(function (item) { return (
- 全部
- 精華
- 分享
- 問答
- 招聘
) }.bind(this))}
{item.visit_count} {item.reply_count} 發表于{utils.getPubDate(item.create_at)}
看個效果
總體來說開發流程還是比較順利,當然了因為這里沒有涉及到登錄問題。如果想在實際開發中使用React,有幾個問題不得不面對
對開發者的要求高,至少要熟悉React,React Router,特別是組件的構建,如何提高復用率?這些都是要在前期思考的。多人開發協作下,這個問題尤其尖銳,一個不好就是一鍋粥!
React的第三方組件不夠成熟,如果是后端渲染,很多組件不能用,以為它們在代碼里直接使用的window、document對象!
程序是為業務服務的!
就算這樣,我還是想還成為那個吃桃子的人!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85983.html
摘要:如果你不想這樣每次改完代碼都要手動編譯,而且自己啟動,那么就要用到這個玩意。 通過配置webpack-dev-server啟動一個webserver,自動編譯,自動刷新瀏覽器的功能,我們開啟React基礎速過 接上一篇:構建React開發環境 使用webpack-dev-server 按照上篇文章構建好React開發環境后,我們發現每次寫完代碼還需要手動編譯,并且需要自己啟動一個web...
摘要:從零開始搭建同構應用三配置這篇文章來講解來配置,我們先從最簡單的方法開始,用的方式模擬實現。影響生產環境下執行效率。最后權衡下,還是決定使用現在多一套編譯配置的方案。新建,寫入以下內容以為例,注意不能少。 從零開始搭建React同構應用(三):配置SSR 這篇文章來講解來配置server side render,我們先從最簡單的方法開始,用cli的方式模擬實現SSR。 demo在這里 ...
摘要:入門與實戰組件虛擬的概念這是性能高效的核心算法為此引入了虛擬的機制。這個過程是自動完成的。實際上是改變了樣式文件中類的名稱,使其唯一。如果希望使用達到的效果,則需要做件事情服務器支持。 React 入門與實戰 react組件 虛擬DOM的概念 這是React性能高效的核心算法 React為此引入了虛擬DOM(Virtual DOM)的機制。基于React進行開發時所有的DOM構造都是通...
摘要:入門與實戰組件虛擬的概念這是性能高效的核心算法為此引入了虛擬的機制。這個過程是自動完成的。實際上是改變了樣式文件中類的名稱,使其唯一。如果希望使用達到的效果,則需要做件事情服務器支持。 React 入門與實戰 react組件 虛擬DOM的概念 這是React性能高效的核心算法 React為此引入了虛擬DOM(Virtual DOM)的機制。基于React進行開發時所有的DOM構造都是通...
摘要:不過這時的控制臺會拋出這樣一則警告提醒我們在服務端渲染時用來取代,并警告我們在時將不能用去混合服務端渲染出來的標簽。綜上所述,服務端和客戶端都是需要路由體現的。我們畫一下重點,意思很明確,就是為了服務端渲染而打造的。 拋磚引玉 在早幾年前,jquery算是一個前端工程師必備的技能。當時很多公司采用的是java結合像velocity或者freemarker這種模板引擎的開發模式,頁面渲染...
閱讀 2538·2023-04-26 00:57
閱讀 911·2021-11-25 09:43
閱讀 2221·2021-11-11 16:55
閱讀 2207·2019-08-30 15:53
閱讀 3592·2019-08-30 15:52
閱讀 1459·2019-08-30 14:10
閱讀 3379·2019-08-30 13:22
閱讀 1209·2019-08-29 11:18