一、專案介紹
前幾天有開發過一個react版仿微信手機端聊天室
這次使用react+react-dom+react-router-dom+redux+react-redux+webpack2.0+nodejs等技術開發的仿微信web端聊天,實現了聊天記錄右鍵選單、傳送訊息、表情(動圖),圖片、視訊預覽,瀏覽器截圖貼上傳送等功能。
二、技術選型
- MVVM框架:react / react-dom
- 狀態管理:redux / react-redux
- 頁面路由:react-router-dom
- 彈窗外掛:wcPop
- 打包工具:webpack 2.0
- 環境配置:node.js + cnpm
- 圖片預覽:react-photoswipe
- 輪播滑動:swiper
/* * 依賴安裝package */ { "name": "react-webchat", "version": "0.1.0", "private": true, "dependencies": { "react": "^16.8.6", "react-dom": "^16.8.6", "react-redux": "^7.1.0", "react-router-dom": "^5.0.1", "react-scripts": "0.9.x", "redux": "^4.0.1", "redux-thunk": "^2.3.0" }, "devDependencies": { "jquery": "^2.2.3", "react-custom-scrollbars": "^4.2.1", "react-photoswipe": "^1.3.0", "swiper": "^4.5.0" }, "scripts": { "start": "set HOST=localhost&& set PORT=3003 && react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } }
// 引入頁面元件
import Login from '../views/auth/login'
import Register from '../views/auth/register'
import Index from '../views/index'
import Contact from '../views/contact'
import Uinfo from '../views/contact/uinfo'
import NewFriend from '../views/contact/new-friends'
import Ucenter from '../views/ucenter'
import News from '../views/news'
import NewsDetail from '../views/news/detail'
export default [
{
path: '/login', name: 'Login', component: Login,
meta: { hideSideBar: true },
},
{
path: '/register', name: 'Register', component: Register,
meta: { hideSideBar: true },
},
{
path: '/index', name: 'App', component: Index,
meta: { requireAuth: true },
},
{
path: '/contact', name: 'Contact', component: Contact,
meta: { requireAuth: true },
},
{
path: '/contact/uinfo', name: 'Uinfo', component: Uinfo,
},
{
path: '/contact/new-friends', name: 'NewFriend', component: NewFriend,
meta: { requireAuth: true },
},
{
path: '/news', name: 'News', component: News,
},
{
path: '/news/detail', name: 'NewsDetail', component: NewsDetail,
},
{
path: '/ucenter', name: 'Ucenter', component: Ucenter,
meta: { requireAuth: true },
},
]
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 引入狀態管理
import { Provider } from 'react-redux'
import { store } from './store'
// 匯入公共樣式
import './assets/fonts/iconfont.css'
import './assets/css/reset.css'
import './assets/css/layout.css'
// 引入wcPop彈窗樣式
import './assets/js/wcPop/skin/wcPop.css'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
render() {
let token = this.props.token
return (
<Router>
<div className="vChat-wrapper flexbox flex-alignc">
<div className="vChat-panel" /*style={{ backgroundImage: `url(${require("./assets/img/placeholder/vchat__panel-bg02.jpg")})` }}*/ >
<div className="vChat-inner flexbox">
{/* //頂部(最大、最小、關閉) */}
<Switch>
<WinBar />
</Switch>
{/* //側邊欄 */}
<Switch>
<SideBar />
</Switch>
{/* //主頁面 */}
<div className="flex1 flexbox">
{/* 路由容器 */}
<Switch>
{
routers.map((item, index) => {
return <Route key={index} path={item.path} exact render={props => (
!item.meta || !item.meta.requireAuth ? (<item.component {...props} />) : (
token ? <item.component {...props} /> : <Redirect to={{pathname: '/login', state: {from: props.location}}} />
)
)} />
})
}
{/* 初始化頁面跳轉 */}
<Redirect push to="/index" />
</Switch>
</div>
</div>
</div>
</div>
</Router>
);
}
// 【編輯器+表情處理模組】------------------------------------------
function surrounds() {
setTimeout(function () { //chrome
var sel = window.getSelection();
var anchorNode = sel.anchorNode;
if (!anchorNode) return;
if (sel.anchorNode === $(".J__wcEditor")[0] ||
(sel.anchorNode.nodeType === 3 && sel.anchorNode.parentNode === $(".J__wcEditor")[0])) {
var range = sel.getRangeAt(0);
var p = document.createElement("p");
range.surroundContents(p);
range.selectNodeContents(p);
range.insertNode(document.createElement("br")); //chrome
sel.collapse(p, 0);
(function clearBr() {
var elems = [].slice.call($(".J__wcEditor")[0].children);
for (var i = 0, len = elems.length; i < len; i++) {
var el = elems[i];
if (el.tagName.toLowerCase() == "br") {
$(".J__wcEditor")[0].removeChild(el);
}
}
elems.length = 0;
})();
}
}, 10);
}
// 定義最後游標位置
var _lastRange = null, _sel = window.getSelection && window.getSelection();
var _rng = {
getRange: function () {
if (_sel && _sel.rangeCount > 0) {
return _sel.getRangeAt(0);
}
},
addRange: function () {
if (_lastRange) {
_sel.removeAllRanges();
_sel.addRange(_lastRange);
}
}
}
// 格式化編輯器包含標籤
$("body").on("click", ".J__wcEditor", function(){
$(".wc__choose-panel").hide();
_lastRange = _rng.getRange();
});
$("body").on("focus", ".J__wcEditor", function(){
surrounds();
_lastRange = _rng.getRange();
});
$("body").on("input", ".J__wcEditor", function(){
surrounds();
_lastRange = _rng.getRange();
});