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

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

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

      react18-webchat網頁聊天實例|React Hooks+Arco Design仿微信桌面端

      React18 Hooks+Arco-Design+Zustand仿微信客戶端聊天ReactWebchat

      react18-webchat基于react18+vite4.x+arco-design+zustand等技術開發的一款仿制微信網頁版聊天實戰項目。實現發送帶有emoj消息文本、圖片/視頻預覽、紅包/朋友圈、局部模塊化刷新/美化滾動條等功能。

      使用技術

      • 編輯器:vscode
      • 技術棧:react18+vite4+react-router-dom+zustand+sass
      • 組件庫:@arco-design/web-react (字節跳動react組件庫)
      • 狀態管理:zustand^4.4.1
      • 路由管理:react-router-dom^6.15.0
      • className拼合:clsx^2.0.0
      • 對話框組件:rdialog (基于react18 hooks自定義桌面端彈窗組件)
      • 預處理樣式:sass^1.66.1

      項目目錄結構

      使用vite4.x創建react18項目,目錄線性結構如下。

      react-webchat 項目全部采用react18 hooks規范編碼開發,使用到的對話框及虛擬滾動條均是自研組件實現功能效果。

      react18 hooks自定義對話框+美化滾動條

      大家看到的彈窗及滾動條組件都是自定義組件實現功能場景。

      // 引入對話框組件
      import RDialog, { rdialog } from '@/components/rdialog'
      
      // 組件式調用
      <RDialog
          visible={confirmVisible}
          title="標題信息"
          content="對話框內容信息"
          closeable
          shadeClose={false}
          zIndex="2050"
          dragOut
          maxmin
          btns={[
              {text: '取消', click: () => setConfirmVisible(false)},
              {text: '確定', click: handleInfo}
          ]}
          onClose={()=>setConfirmVisible(false)}
      />
      
      // 函數式調用
      rdialog({
          title: '標題信息',
          content: '對話框內容信息',
          closeable: true,
          shadeClose: false,
          zIndex: 2050,
          dragOut: true,
          maxmin: true,
          btns: [
              {text: '取消', click: rdialog.close()},
              {text: '確定', click: handleInfo}
          ]
      })

      react-scrollbar美化系統滾動條調用非常簡單,需要包裹需要滾動的內容塊即可快速生成一個虛擬滾動條。

      // 引入滾動條組件
      import RScroll from '@/components/rscroll'
      
      <RScroll autohide maxHeight={100}>
          包裹需要滾動的內容塊。。。
      </RScroll>

      main.jsx入口配置

      import React from 'react'
      import ReactDOM from 'react-dom/client'
      import App from './App.jsx'
      import '@arco-design/web-react/dist/css/arco.css'
      import './style.scss'
      
      ReactDOM.createRoot(document.getElementById('root')).render(<App />)

      App.jsx配置

      import { HashRouter } from 'react-router-dom'
      
      // 引入useRoutes集中式路由配置文件
      import Router from './router'
      
      function App() {
          return (
              <>
                  <HashRouter>
                    <Router />
                  </HashRouter>
              </>
          )
      }
      
      export default App

      react18-router-dom配置

      自定義路由占位模板,有點類似vue里面的router-view占位。

      // 路由占位模板(類似vue中router-view)
      const RouterLayout = () => {
          const authState = authStore()
          return (
              <div className="rc__container flexbox flex-alignc flex-justifyc" style={{'--themeSkin': authState.skin}}>
                  <div className="rc__layout flexbox flex-col">
                      {/* <div className="rc__layout-header">頂部欄</div> */}
                      <div className="rc__layout-body flex1 flexbox">
                          {/* 菜單欄 */}
                          <Menu />
      
                          {/* 中間欄 */}
                          <Aside />
      
                          {/* 主內容區 */}
                          <div className="rc__layout-main flex1 flexbox flex-col">
                              { lazyload(<Outlet />) }
                          </div>
                      </div>
                  </div>
              </div>
          )
      }

      路由配置文件

      /**
       * react-router路由配置 by HS Q:282310962
      */
      
      import { lazy, Suspense } from 'react'
      import { useRoutes, Outlet, Navigate } from 'react-router-dom'
      import { Spin } from '@arco-design/web-react'
      
      import { authStore } from '@/store/auth'
      
      // 引入路由頁面
      import Login from '@views/auth/login'
      import Register from '@views/auth/register'
      const Index = lazy(() => import('@views/index'))
      const Contact = lazy(() => import('@views/contact'))
      const Uinfo = lazy(() => import('@views/contact/uinfo'))
      const NewFriend = lazy(() => import('@views/contact/newfriend'))
      const Chat = lazy(() => import('@views/chat/chat'))
      const ChatInfo = lazy(() => import('@views/chat/info'))
      const RedPacket = lazy(() => import('@views/chat/redpacket'))
      const Fzone = lazy(() => import('@views/my/fzone'))
      const Favorite = lazy(() => import('@views/my/favorite'))
      const Setting = lazy(() => import('@views/my/setting'))
      const Error = lazy(() => import('@views/404'))
      
      import Menu from '@/layouts/menu'
      import Aside from '@/layouts/aside'
      
      // 加載提示
      const SpinLoading = () => {
          return (
              <div className="rcLoading">
                  <Spin size="20" tip='loading...' />
              </div>
          )
      }
      
      // 延遲加載
      const lazyload = children => {
          // React 16.6 新增了<Suspense>組件,讓你可以“等待”目標代碼加載,并且可以直接指定一個加載的界面,讓它在用戶等待的時候顯示
          // 路由懶加載報錯:react-dom.development.js:19055 Uncaught Error: A component suspended while responding to synchronous input.
          // 懶加載的模式需要我們給他加上一層 Loading的提示加載組件
          return <Suspense fallback={<SpinLoading />}>{children}</Suspense>
      }
      
      // 路由鑒權驗證
      const RouterAuth = ({ children }) => {
          const authState = authStore()
      
          return authState.isLogged ? (
              children
          ) : (
              <Navigate to="/login" replace={true} />
          )
      }
      
      export const routerConfig = [
          {
              path: '/',
              element: <RouterAuth><RouterLayout /></RouterAuth>,
              children: [
                  // 首頁
                  { index: true, element: <Index /> },
      
                  // 通訊錄模塊
                  { path: '/contact', element: <Contact /> },
                  { path: '/uinfo', element: <Uinfo /> },
                  { path: '/newfriend', element: <NewFriend /> },
      
                  // 聊天模塊
                  { path: '/chat', element: <Chat /> },
                  { path: '/chatinfo', element: <ChatInfo /> },
                  { path: '/redpacket', element: <RedPacket /> },
      
                  // 我的模塊
                  { path: '/fzone', element: <Fzone /> },
                  { path: '/favorite', element: <Favorite /> },
                  { path: '/setting', element: <Setting /> },
      
                  // 404模塊 path="*"不能省略
                  { path: '*', element: <Error /> }
              ]
          },
          // 登錄/注冊
          { path: '/login', element: <Login /> },
          { path: '/register', element: <Register /> }
      ]
      
      const Router = () => useRoutes(routerConfig)
      
      export default Router

      react新狀態管理器Zustand

      這次開發react項目沒有使用redux作為狀態管理,而是使用支持react18 hooks新一代狀態管理庫Zustand。

      // NPM
      npm install zustand
      
      // Yarn
      yarn add zustand

      zustand提供了內置的persist本地持久化存儲管理。

      /**
       * react18狀態管理庫Zustand
      */
      import { create } from 'zustand'
      import { persist, createJSONStorage } from 'zustand/middleware'
      
      export const authStore = create(
          persist(
              (set, get) => ({
                  isLogged: false,
                  token: null,
                  // 折疊側邊欄
                  collapse: false,
                  // 個性換膚
                  skin: null,
                  // 登錄數據
                  loggedData: (data) => set({isLogged: data.isLogged, token: data.token}),
                  setCollapse: (v) => set({collapse: v}),
                  setSkin: (v) => set({skin: v})
              }),
              {
                  name: 'authState',
                  // name: 'auth-store', // name of the item in the storage (must be unique)
                  // storage: createJSONStorage(() => sessionStorage), // by default, 'localStorage'
              }
          )
      )

      react18-chat聊天模塊

      聊天區域支持拖拽發送圖片、編輯框支持多行文本、光標處插入emoj表情符。

      return (
          <div
              {...rest}
              ref={editorRef}
              className={clsx('editor', className)}
              contentEditable
              onClick={handleClick}
              onInput={handleInput}
              onFocus={handleFocus}
              onBlur={handleBlur}
              style={{'userSelect': 'text', 'WebkitUserSelect': 'text'}}
          />
      )

      獲取輸入光標位置

      // 獲取光標最后位置
      const getLastCursor = () => {
          let sel = window.getSelection()
          if(sel && sel.rangeCount > 0) {
              return sel.getRangeAt(0)
          }
      }

      光標處插入內容

      // 光標處插入emoj表情符內容
      const insertHtmlAtCursor = (html) => {
          let sel, range
          if(!editorRef.current.childNodes.length) {
              editorRef.current.focus()
          }
      
          if(window.getSelection) {
              // IE9及其它瀏覽器
              sel = window.getSelection()
      
              // ##注意:判斷最后光標位置
              if(lastCursor.current) {
                  sel.removeAllRanges()
                  sel.addRange(lastCursor.current)
              }
      
              if(sel.getRangeAt && sel.rangeCount) {
                  range = sel.getRangeAt(0)
                  range.deleteContents()
                  let el = document.createElement('div')
                  el.appendChild(html)
                  var frag = document.createDocumentFragment(), node, lastNode
                  while ((node = el.firstChild)) {
                      lastNode = frag.appendChild(node)
                  }
                  range.insertNode(frag)
                  if(lastNode) {
                      range = range.cloneRange()
                      range.setStartAfter(lastNode)
                      range.collapse(true)
                      sel.removeAllRanges()
                      sel.addRange(range)
                  }
              }
          } else if(document.selection && document.selection.type != 'Control') {
              // IE < 9
              document.selection.createRange().pasteHTML(html)
          }
      
          // 執行輸入操作
          handleInput()
      }

      okay,基于react18 hooks+arco開發網頁聊天項目就分享到這里,希望對大家有些幫助哈~

      最后附上兩個最新uniapp/tauri跨端實例項目

      http://www.rzrgm.cn/xiaoyan2017/p/17507581.html

      http://www.rzrgm.cn/xiaoyan2017/p/17552562.html

       

      posted @ 2023-09-12 09:43  xiaoyan2017  閱讀(876)  評論(0)    收藏  舉報
      友情鏈接: UP主小店B站
      主站蜘蛛池模板: 亚洲AV成人无码精品电影在线| 亚洲中文字幕久久精品码| 国产精品视频第一第二区| 国产男女猛烈无遮挡免费视频| 欧美亚洲另类自拍偷在线拍| 熟妇人妻久久精品一区二区| 日本道不卡一二三区视频| 毛片内射久久久一区| 色猫咪av在线观看| 亚洲精品男男一区二区| 久青草久青草视频在线观看| av在线播放国产一区| 欧美成人黄在线观看| gogogo高清在线观看视频中文| 男女啪啪18禁无遮挡激烈| 国产三级精品三级在专区| 达拉特旗| 大庆市| 亚洲 成人 无码 在线观看| 国产偷国产偷亚洲清高| 国产永久免费高清在线| 国产精品毛片在线完整版| 中国女人高潮hd| 精品不卡一区二区三区| 九九热精品免费视频| 国产特级毛片aaaaaa毛片| 韩国无码AV片午夜福利| 亚洲欧美人成人综合在线播放| 最近中文国语字幕在线播放| 亚洲乱妇老熟女爽到高潮的片| 东京热人妻无码一区二区av| 国产成人一区二区免av| 国内不卡一区二区三区| 精品国产三级在线观看| 亚洲日韩性欧美中文字幕| 亚洲国产日韩精品一区二区三区| 精品国模一区二区三区| 久久精品国产亚洲AⅤ无码| 亚洲第一极品精品无码久久| 精品亚洲没码中文字幕| 久久这里有精品国产电影网 |