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

資訊專欄INFORMATION COLUMN

基于react+react-router+redux+socket.io+koa開發(fā)一個聊天室

NusterCache / 1389人閱讀

摘要:最近練手開發(fā)了一個項目,是一個聊天室應(yīng)用。由于我們的項目是一個單頁面應(yīng)用,因此只需要統(tǒng)一打包出一個和一個。而就是基于實現(xiàn)的一套基于事件訂閱與發(fā)布的通信庫。比如說,某一個端口了,而如果端口訂閱了,那么在端,對應(yīng)的回調(diào)函數(shù)就會被執(zhí)行。

最近練手開發(fā)了一個項目,是一個聊天室應(yīng)用。項目雖不大,但是使用到了react, react-router, redux, socket.io,后端開發(fā)使用了koa,算是一個比較綜合性的案例,很多概念和技巧在開發(fā)的過程中都有所涉及,非常有必要再來鞏固一下。

項目目前部署在heroku平臺上,在線演示地址: online demo, 因為是國外的平臺速度可能有點慢,點進(jìn)去耐心等一會兒就能加載好了。

加載好之后,首先出現(xiàn)的頁面是讓用戶起一個昵稱:

輸入昵稱之后,就會進(jìn)入聊天頁面,左邊是進(jìn)入聊天室的在線用戶,右邊則是聊天區(qū)域,下圖是三個在線用戶聊天的情形:

項目源碼的github地址: 源碼地址, 有興趣的同學(xué)歡迎關(guān)注學(xué)習(xí)~

下面就來分析一下項目的整體架構(gòu),以及一下值得注意的技巧和知識點。

1. 整體結(jié)構(gòu)

項目的目錄如下:

├── README.md
├── node_modules
├── dist
│?? ├── bundle.css
│?? ├── bundle.js
│?? └── resource
│??     ├── background.jpeg
│??     └── preview.png
├── package.json
├── server.js
├── src
│?? ├── action
│?? │?? └── index.js
│?? ├── components
│?? │?? ├── chatall
│?? │?? │?? ├── index.js
│?? │?? │?? └── index.less
│?? │?? ├── login
│?? │?? │?? ├── index.js
│?? │?? │?? └── index.less
│?? │?? ├── msgshow
│?? │?? │?? ├── index.js
│?? │?? │?? └── index.less
│?? │?? ├── namelist
│?? │?? │?? ├── index.js
│?? │?? │?? └── index.less
│?? │?? ├── nav
│?? │?? │?? ├── index.js
│?? │?? │?? └── index.less
│?? │?? └── typein
│?? │??     ├── index.js
│?? │??     └── index.less
│?? ├── container
│?? │?? ├── chatAll.js
│?? │?? └── login.js
│?? ├── index.ejs
│?? ├── index.js
│?? ├── index.less
│?? ├── index2.js
│?? ├── reducer
│?? │?? └── index.js
│?? ├── redux_middleware
│?? │?? └── index.js
│?? └── resource
│??     ├── background.jpeg
│??     └── preview.png
└── webpack.config.js

其中src當(dāng)中是前端部分的源代碼。項目使用webpack進(jìn)行打包,打包后的代碼在dist目錄當(dāng)中。由于我們的項目是一個單頁面應(yīng)用,因此只需要統(tǒng)一打包出一個bundle.js和一個bundle.css。而后端使用了koa框架,由于代碼相對比較少,都集中在了server.js這一個文件當(dāng)中。

開發(fā)過程中,由于要webpack打包,一般我們會配合webpack-dev-server來使用。webpack-dev-server運行的時候自身就會開啟一個server,而在我們的項目當(dāng)中,后端koa也是一個server,因此為了簡單起見,我們可以使用koa-webpack-dev-middleware來在koa當(dāng)中開啟webpack-dev-server。

var webpackDev = require("koa-webpack-dev-middleware");
var webpackConf = require("./webpack.config.js");
var compiler = webpack(webpackConf);
app.use(webpackDev(compiler, {
  contentBase: webpackConf.output.path,
  publicPath: webpackConf.output.publicPath,
  hot: true
}));
2. 項目布局: flexbox實踐

在這個項目中我們有意識的使用了flex布局,作為面向未來的一種新的布局方式,實踐一下還是很有必要的!沒有學(xué)習(xí)郭flexbox的同學(xué)可以參考這篇來學(xué)一下:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html

以聊天界面為例進(jìn)行分析,使用flex布局的話,可以非常方便,下圖就是對界面的一個簡單的切分:

整個聊天框最外層紅框框起來的部分display設(shè)置為flex,并且flex-direction設(shè)置為column,這樣它里面的兩個元素(即粉框和藍(lán)框部分)就會豎直方向排列,同時粉框的flex設(shè)置為0 0 90px,代表該框不可伸縮,固定高度90px,而對于藍(lán)框,則設(shè)置flex為1,代表伸展系數(shù)為1,這樣,藍(lán)框的高度就會占滿除了粉框以外的全部空間。

而于此同時,粉框和藍(lán)框本身又分別設(shè)置display為flex。對粉框而言,內(nèi)部一共有歡迎標(biāo)簽和退出button兩個元素,分列兩側(cè),因此只需要設(shè)置justify-content為space-between即可做到這一點。而對藍(lán)框而言,內(nèi)部有在線用戶列表以及聊天區(qū)域兩個元素。這里在線用戶列表(即黃色框)需要設(shè)置固定寬度,因此類似于剛才粉框的設(shè)置,flex: 0 0 240px,而聊天區(qū)域(即綠色框)則設(shè)置flex為1,這樣會自適應(yīng)占滿剩余寬度。

最后,聊天區(qū)域內(nèi)部又分為信息展示區(qū)以及打字區(qū),因此聊天區(qū)域自身又是一個flexbox,設(shè)置方式類似,就不再具體分析了。

可以看出,使用flexbox,相比使用float以及position等等而言,更加的規(guī)整,使用這種思路,整個頁面就像庖丁解牛一般,布局格外清晰。

3. 設(shè)計頁面的數(shù)據(jù)結(jié)構(gòu)

項目中使用了redux作為數(shù)據(jù)流管理工具,配合react,能夠讓頁面組件同頁面數(shù)據(jù)形成規(guī)律的映射。

分析我們的聊天頁面,可以看出,主要的數(shù)據(jù)就是目前在線的用戶昵稱列表,以及消息記錄,此外我們還需要記錄自己的用戶昵稱,方便消息發(fā)送時候取用。因此,整個應(yīng)用的數(shù)據(jù)結(jié)構(gòu)如下, 也就是redux中的store的數(shù)據(jù)結(jié)構(gòu)如下:

{
  "nickName": "your nickname",
  "nameList": ["user A","user B","user C","...."],
  "msgList": [
    {
    "nickName": "some user",
    "msg": "some string"
  },{
    "nickName": "another user",
    "msg": "another string"
  },
  ]
}

有了這個總體的數(shù)據(jù)結(jié)構(gòu),我們就可以根據(jù)該結(jié)構(gòu)設(shè)計具體的action,reducer等等部分了。這里整個程序的模塊拆分遵循了redux官方實例當(dāng)中的拆分方法,action文件夾當(dāng)中定義action creators,reducer文件夾中定義reducer函數(shù),component文件夾中定義一些通用的組件,container文件夾當(dāng)中則是將通用組件取出,定義store中的數(shù)據(jù)同組件如何映射,以及組件中的事件如何dispatch action,從而引起store數(shù)據(jù)的改變。

以component/namelist中的組件為例,該組件用于顯示在線用戶昵稱列表,因此它接受一個數(shù)組,也就是store中的nameList作為參數(shù),因此其通用組件的寫法也很簡單:

class NameList extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    var {nameList} = this.props;
    return (
      
  • 在線用戶:
  • {nameList.map((item, index) => (
  • {item}
  • ))}
) } } export default NameList

而在container當(dāng)中,只需要將store中的nameList賦值到該組件的props上面即可。其他組件也是類似的寫法。

可以看出,在redux的思想下,我們可以對整個應(yīng)用抽象出一個總體的數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu)的改變,會引發(fā)各個組件的改變,而組件當(dāng)中的各種事件,又會反過來修改數(shù)據(jù)結(jié)構(gòu),從而再次引起頁面的改變,這是一種單向的數(shù)據(jù)流,總體的數(shù)據(jù)都在store這個對象中進(jìn)行維護(hù),從而讓整個應(yīng)用開發(fā)變得更加有規(guī)律。redux的這種程序架構(gòu)是對react提出的flux架構(gòu)的一種消化和改良,下圖是flux架構(gòu)的示意圖:

4. socket.io的使用

由于是一個即時聊天應(yīng)用,websocket協(xié)議自然是首選。而socket.io就是基于websocket實現(xiàn)的一套基于事件訂閱與發(fā)布的js通信庫。

在socket.io中,主要有server端和client端。創(chuàng)建一個server和client都非常容易,對于server端,配合koa,只需要如下代碼:

var app=require("koa")();
var server = require("http").Server(app.callback());
var io = require("socket.io")(server);

client端更加簡單:

var io=require("socket.io-client");
var socket = io();

一旦連接建立,client和server即可通過時間訂閱與發(fā)布來彼此通信,socket.io提供的api非常類似于nodejs中的event對象的使用,對于server端:

io.on("connection",function(socket){
  socket.on("some event",function(data){
    //do something here....
    socket.emit("another event",{some data here});
  });
});

對于client端,同樣通過socket.on以及socket.emit來訂閱和發(fā)布事件。比如說,某一個client端口emit了event A,而如果server端口訂閱了event A,那么在server端,對應(yīng)的回調(diào)函數(shù)就會被執(zhí)行。通過這種方式,可以方便的編寫即時通信程序。

5. 一些值得注意的實現(xiàn)細(xì)節(jié)

下面對程序中涉及的一些我認(rèn)為值得注意的細(xì)節(jié)和技巧進(jìn)行一下簡要分析。

1. socket.io同redux的結(jié)合方案:redux中間件的運用

在程序編寫過程當(dāng)中,我遇到一個難題,就是如何將socket.io的client實例結(jié)合到redux當(dāng)中。

socket.io的client類似于一個全局的對象,它不屬于任何一個react組件,它訂閱到的任何消息都可能更改整個應(yīng)用的數(shù)據(jù)結(jié)構(gòu),而這種更改在redux當(dāng)中又只能通過dispatch來實現(xiàn)。思考之后,我覺得編寫一個redux中間件來處理socket.io相關(guān)的事件是一個很好的選擇。

關(guān)于redux中間件,簡單來說,就是在redux真正出發(fā)dispatch之前,中間件可以首先捕獲到react組件出發(fā)的action,并針對不同action做一些處理,然后再調(diào)用dispatch。中間件的寫法,在redux的官方文檔當(dāng)中寫的非常詳細(xì),有興趣的可以參考一下: http://redux.js.org/docs/advanced/Middleware.html , 后續(xù)我也會出一些系列文章,深入分析redux包括react-redux的原理,其中就會提到中間件的原理,盡請期待~

知道了redux中間件是怎么一回事之后,我們就可以發(fā)現(xiàn),socket.io相關(guān)的事件非常適合通過寫一個中間件來處理。我們程序當(dāng)中中間件如下所示:

import { message_update, guest_update } from "../action"

function createSocketMiddleware(socket) {
  var eventFlag = false;
  return store => next => action => {
    //如果中間件第一次被調(diào)用,則首先綁定一些socket訂閱事件
    if (!eventFlag) {
      eventFlag = true;
      socket.on("guest update", function(data) {
        next(guest_update(data));
      });
      socket.on("msg from server", function(data) {
        next(message_update(data));
      });
      socket.on("self logout", function() {
        window.location.reload();
      });
      setInterval(function() {
        socket.emit("heart beat");
      }, 10000);
    }
    //捕獲action,如果是和發(fā)送相關(guān)的事件,則調(diào)用socket對應(yīng)的發(fā)布函數(shù)
    if (action.type == "MSG_UPDATE") {
      socket.emit("msg from client", action.msg);
    } else if (action.type == "NICKNAME_GET") {
      socket.emit("guest come", action.nickName);
    } else if (action.type == "NICKNAME_FORGET") {
      socket.emit("guest leave", store.getState().nickName);
    }
    return next(action);
  }
}

export default createSocketMiddleware

這段代碼是一個socket middleware的創(chuàng)建函數(shù),從中我們可以看出,這個中間件如果第一次調(diào)用的話(eventFlag),會首先綁定一些訂閱主題和對應(yīng)的回調(diào)函數(shù),主要是訂閱了消息到達(dá)、新用戶來到、用戶離開等等事件。同時,中間件會在真正dispatch函數(shù)調(diào)用之前,首先捕獲action,然后分析action的type。如果是和發(fā)送事件相關(guān)的,就會調(diào)用socket.emit來發(fā)布對應(yīng)的事件和數(shù)據(jù)。比如說,在我們的應(yīng)用中,點擊“發(fā)送”按鈕會觸發(fā)一個type為"MSG_UPDATE"的事件,這個事件首先被中間件捕獲,那么這時候就會出發(fā)socket.emit("msg from client")來將消息發(fā)送給server。

2. 權(quán)限驗證: 單頁面應(yīng)用中的頁面跳轉(zhuǎn)

整個應(yīng)用使用react-router,做成了一個單頁面應(yīng)用,其中前端路由的層級非常簡單:

render(
  
    
      
      
    
  
  ,
  document.getElementById("test"));

可以看出,主要是兩條路徑: "/"和"/login",其中"/"是我們的聊天界面,而"/login"則是起昵稱界面。

由于應(yīng)用的邏輯是,只有用戶起了昵稱才可以進(jìn)入聊天界面,因此我們需要做一些權(quán)限驗證,對于沒有起昵稱就進(jìn)入"/"路徑的用戶,需要跳轉(zhuǎn)到"/login"。在傳統(tǒng)多頁面web應(yīng)用中,我們對于跳轉(zhuǎn)非常熟悉,無非是服務(wù)器發(fā)送一個重定向請求,瀏覽器就會重定向到新的頁面。然而在單頁面中,由于始終只有一頁,服務(wù)器又能夠讓瀏覽器跳轉(zhuǎn)到哪里去呢?也就是說,服務(wù)器重定向的方法是行不通的。

因此,我們換一種思路,頁面跳轉(zhuǎn)的邏輯需要在瀏覽器端執(zhí)行,在react-router的框架下,執(zhí)行跳轉(zhuǎn)也非常簡單,只需要使用其中的hashHistory對象,通過hashHistory.push("path"),即可讓應(yīng)用跳轉(zhuǎn)到指定路徑對應(yīng)的界面。有了這個認(rèn)知,那么我們下面要解決的,就是何時控制單頁面的跳轉(zhuǎn)?

我的思路是,將用戶的昵稱通過一定的加密和編碼,保存在cookie當(dāng)中。當(dāng)用戶訪問"/"的時候,在對于界面的組件掛載之前,首先會向服務(wù)器發(fā)送一個認(rèn)證請求,服務(wù)器會從請求中讀取cookie,如果cookie當(dāng)中沒有用戶名存在,那么服務(wù)器返回的參數(shù)當(dāng)中有一個"permit"字段,設(shè)置為false,當(dāng)應(yīng)用解析到該字段后,就會調(diào)用hashHistory.push("/login")來讓頁面跳轉(zhuǎn)到起昵稱界面下。這部分對應(yīng)的邏輯主要在container/chatAll.js文件當(dāng)中實現(xiàn)。

3. 文本輸入的細(xì)節(jié)處理: xss的預(yù)防,以及組合鍵的識別

在我們的聊天應(yīng)用中,如果不對用戶的輸入進(jìn)行一些處理,就有可能導(dǎo)致xss漏洞。舉個例子,比如說有一個用戶輸入了"",如果不進(jìn)行一些防范,輸入到消息顯示界面,這段文字就直接被解析成為了一段js代碼。為了防范這類攻擊,這里我們需要做一些簡單的預(yù)防:

var regLeft = //g;
value = value.replace(regLeft, "<");
value = value.replace(regRight, ">");

這段代碼在components/typein組件當(dāng)中。

此外,為了方便用戶快速發(fā)送消息,在消息輸入框中,我們設(shè)置了"enter"按鍵為之間發(fā)送按鍵。那么,為了讓用戶能夠打出換行,我們模仿微信,約定用戶輸入ctrl+enter組合鍵的時候是換行,這樣,在消息輸入框中,就需要監(jiān)聽組合鍵。在js的鍵盤事件中,event對象有一個ctrlKey屬性,用于判斷ctrl按鍵是否按下:

someDom.onkeydown=function(e){
  if(e.keyCode==13&&e.ctrlKey){
    //組合鍵被按下
  }
}

這就是組合鍵監(jiān)聽的原理。

以上就是對于這個項目的概述以及一些細(xì)節(jié)的講解。最后安利一下我的博客 http://mly-zju.github.io/,會不定期更新我的原創(chuàng)技術(shù)文章和學(xué)習(xí)感悟,歡迎大家關(guān)注~

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

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

相關(guān)文章

  • node技術(shù)棧 - 收藏集 - 掘金

    摘要:異步最佳實踐避免回調(diào)地獄前端掘金本文涵蓋了處理異步操作的一些工具和技術(shù)和異步函數(shù)。 Nodejs 連接各種數(shù)據(jù)庫集合例子 - 后端 - 掘金Cassandra Module: cassandra-driver Installation ... 編寫 Node.js Rest API 的 10 個最佳實踐 - 前端 - 掘金全文共 6953 字,讀完需 8 分鐘,速讀需 2 分鐘。翻譯自...

    王偉廷 評論0 收藏0
  • koa-cola:只需一個react組件,同時支持單頁應(yīng)用(SPA)和服務(wù)器渲染(SSR)

    摘要:是一個基于和的服務(wù)器端和瀏覽器端的的前后端全棧應(yīng)用框架。是的組件,并且會進(jìn)行數(shù)據(jù)初始化不但可以支持的數(shù)據(jù)初始化,還可以合并和的,使用同一個,和的無縫結(jié)合。 koa-cola是一個基于koa和react的服務(wù)器端SSR(server side render)和瀏覽器端的SPA(single page application)的web前后端全棧應(yīng)用框架。 koa-cola使用typescr...

    XGBCCC 評論0 收藏0
  • 一些基于React、Vue、Node.js、MongoDB技術(shù)棧的實踐項目

    摘要:利用中間件實現(xiàn)異步請求,實現(xiàn)兩個用戶角色實時通信。目前還未深入了解的一些概念。往后會寫更多的前后臺聯(lián)通的項目。刪除分組會連同組內(nèi)的所有圖片一起刪除。算是對自己上次用寫后臺的一個強化,項目文章在這里。后來一直沒動,前些日子才把后續(xù)的完善。 歡迎訪問我的個人網(wǎng)站:http://www.neroht.com/? 剛學(xué)vue和react時,利用業(yè)余時間寫的關(guān)于這兩個框架的訓(xùn)練,都相對簡單,有的...

    tangr206 評論0 收藏0
  • 前端文檔收集

    摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個最重要的技術(shù)點常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實現(xiàn)的大排序算法一怪對象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術(shù)點 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...

    jsbintask 評論0 收藏0

發(fā)表評論

0條評論

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