基於 Taro+react 多端仿微信聊天室|taro 聊天例項分享

xiaoyan2015發表於2019-12-16

基於Taro+react多端仿微信聊天室|taro聊天例項分享

TaroChatroom多端聊天室是基於taro+react+redux+reactNative+taroPop等技術開發的仿微信App聊天室例項,支援編譯到多端(h5+小程式+RN端)。

這是第二個多端實踐專案,不過這次使用的是react技術,上次分享的uniapp+vue仿抖音短視訊/陌陌直播聊天室是基於vue技術開發的多端專案。

基於vue+uniapp直播專案|uni-app仿抖音/陌陌直播室

  • 編碼器/技術:Vscode + react/taro/redux/react-native
  • 字型圖示:阿里iconfont字型圖示庫
  • 自定義導航欄 + 底部Tabbar
  • 彈窗元件:taroPop(Taro封裝自定義Modal框)
  • 支援編譯:H5端 + 小程式 + app端

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

基於Taro+react多端仿微信聊天室|taro聊天例項分享

/**
  * @desc   Taro入口頁面 app.jsx
  */

import Taro, { Component } from '@tarojs/taro'
import Index from './pages/index'

// 引入狀態管理redux
import { Provider } from '@tarojs/redux'
import { store } from './store'

// 引入樣式
import './app.scss'
import './styles/fonts/iconfont.css'
import './styles/reset.scss'

class App extends Component {
  config = {
    pages: [
      'pages/auth/login/index',
      'pages/auth/register/index',
      'pages/index/index',
      ...
    ],
    window: {
      backgroundTextStyle: 'light',
      navigationBarBackgroundColor: '#fff',
      navigationBarTitleText: 'TaroChat',
      navigationBarTextStyle: 'black',
      navigationStyle: 'custom'
    }
  }

  // 在 App 類中的 render() 函式沒有實際作用
  // 請勿修改此函式
  render () {
    return (
      <Provider store={store}>
        <Index />
      </Provider>
    )
  }
}

Taro.render(<App />, document.getElementById('app'))

專案中為了三端統一效果,頂部導航條及底部tabbar均採用自定義元件模式,由於之前有分享文章,這裡不詳細介紹了。

Taro多端自定義導航欄Navbar+Tabbar例項

Taro自定義Modal對話方塊元件|taro仿微信、android彈窗

在taro中獲取多個表單值,方法還是比較簡單的,像下面的方法就能簡單獲取多個input值了

<Input placeholder="請輸入手機號/暱稱" onInput={this.handleInput.bind(this, 'tel')} />
<Input placeholder="請輸入密碼" password onInput={this.handleInput.bind(this, 'pwd')} />

this.state = {
    tel: '',
    pwd: '',
}

handleInput = (key, e) => {
    this.setState({ [key]: e.detail.value })
}

下面就詳細展示如何獲取表單並驗證、本地儲存處理

return (
    <View className="taro__container flexDC bg-eef1f5">
        <Navigation background='#eef1f5' fixed />

        <ScrollView className="taro__scrollview flex1" scrollY>
            <View className="auth-lgreg">
                {/* logo */}
                <View className="auth-lgreg__slogan">
                    <View className="auth-lgreg__slogan-logo">
                        <Image className="auth-lgreg__slogan-logo__img" src={require('../../../assets/taro.png')} mode="aspectFit" />
                    </View>
                    <Text className="auth-lgreg__slogan-text">歡迎來到Taro-Chatroom</Text>
                </View>
                {/* 表單 */}
                <View className="auth-lgreg__forms">
                    <View className="auth-lgreg__forms-wrap">
                        <View className="auth-lgreg__forms-item">
                            <Input className="auth-lgreg__forms-iptxt flex1" placeholder="請輸入手機號/暱稱" onInput={this.handleInput.bind(this, 'tel')} />
                        </View>
                        <View className="auth-lgreg__forms-item">
                            <Input className="auth-lgreg__forms-iptxt flex1" placeholder="請輸入密碼" password onInput={this.handleInput.bind(this, 'pwd')} />
                        </View>
                    </View>
                    <View className="auth-lgreg__forms-action">
                        <TouchView onClick={this.handleSubmit}><Text className="auth-lgreg__forms-action__btn">登入</Text></TouchView>
                    </View>
                    <View className="auth-lgreg__forms-link">
                        <Text className="auth-lgreg__forms-link__nav">忘記密碼</Text>
                        <Text className="auth-lgreg__forms-link__nav" onClick={this.GoToRegister}>註冊賬號</Text>
                    </View>
                </View>
            </View>
        </ScrollView>

        <TaroPop ref="taroPop" />
    </View>
)
/**
 * @tpl 登入模組
 */

import Taro from '@tarojs/taro'
import { View, Text, ScrollView, Image, Input, Button } from '@tarojs/components'

import './index.scss'

import { connect } from '@tarojs/redux'
import * as actions from '../../../store/action'...

class Login extends Taro.Component {
    config = {
        navigationBarTitleText: '登入'
    }
    constructor(props) {
        super(props)
        this.state = {
            tel: '',
            pwd: '',
        }
    }
    componentWillMount() {
        // 判斷是否登入
        storage.get('hasLogin').then(res => {
            if(res && res.hasLogin) {
                Taro.navigateTo({url: '/pages/index/index'})
            }
        })
    }
    // 提交表單
    handleSubmit = () => {
        let taroPop = this.refs.taroPop
        let { tel, pwd } = this.state

        if(!tel) {
            taroPop.show({content: '手機號不能為空', time: 2})
        }else if(!util.checkTel(tel)) {
            taroPop.show({content: '手機號格式有誤', time: 2})
        }else if(!pwd) {
            taroPop.show({content: '密碼不能為空', time: 2})
        }else {
            // ...介面資料
            ...

            storage.set('hasLogin', { hasLogin: true })
            storage.set('user', { username: tel })
            storage.set('token', { token: util.setToken() })

            taroPop.show({
                skin: 'toast',
                content: '登入成功',
                icon: 'success',
                time: 2
            })

            ...
        }
    }

    render () {
        ...
    }
}

const mapStateToProps = (state) => {
    return {...state.auth}
}

export default connect(mapStateToProps, {
    ...actions
})(Login)

在開發中需要注意官網提供的api支援性,由於taro中rn端不支援同步儲存,只能改為setStorageSync非同步儲存

基於Taro+react多端仿微信聊天室|taro聊天例項分享

/**
 * @desc Taro本地儲存
 */

import Taro from '@tarojs/taro'

export default class Storage {
    static get(key) {
        return Taro.getStorage({ key }).then(res => res.data).catch(() => '')
    }

    static set(key, data){
        return Taro.setStorage({key: key, data: data}).then(res => res)
    }

    static del(key){
        Taro.removeStorage({key: key}).then(res => res)
    }

    static clear(){
        Taro.clearStorage()
    }
}

在開發中對於一些不相容rn端樣式,不希望編譯到rn端,則可通過如下程式碼包裹即可

/*postcss-pxtransform rn eject enable*/ 
/*postcss-pxtransform rn eject disable*/

下面整理了一些rn端相容樣式處理,對於不相容樣式,可通過@mixins統一處理下

/**
 * RN 不支援針對某一邊設定 style,即 border-bottom-style 會報錯
 * 那麼 border-bottom: 1px 就需要寫成如下形式: border: 0 style color; border-bottom-width: 1px;
 */
@mixin border($dir, $width, $style, $color) {
    border: 0 $style $color;
    @each $d in $dir {
        #{border-#{$d}-width}: $width;
    }
}

/**
 * NOTE RN 無法通過 text-overflow 實現省略號,這些程式碼不會編譯到 RN 中
 */
@mixin ellipsis {
    /*postcss-pxtransform rn eject enable*/
    overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
    /*postcss-pxtransform rn eject disable*/
}

/**
 * NOTE 實現多行文字省略,RN 用 Text 標籤的 numberOfLines={2},H5/小程式用 -webkit-line-clamp
 */
@mixin clamp($line) {
    /*postcss-pxtransform rn eject enable*/
    display: -webkit-box;
    overflow: hidden;
    -webkit-line-clamp:$line;
    /* autoprefixer: ignore next */
    -webkit-box-orient: vertical;
    /*postcss-pxtransform rn eject disable*/
}

/**
 * 對於不能打包到 RN 的樣式,可以用 postcss 方式引入
 */
 @mixin eject($attr, $value) {
    /*postcss-pxtransform rn eject enable*/
    #{$attr}: $value;
    /*postcss-pxtransform rn eject disable*/
}

基於Taro+react多端仿微信聊天室|taro聊天例項分享

在taro中實現聊天資訊滾動到最底部,在H5/小程式端則可通過獲取createSelectorQuery 來實現滾動到聊天底部,由於RN端不支援createSelectorQuery,則只能另外相容處理

基於Taro+react多端仿微信聊天室|taro聊天例項分享

// 滾動聊天底部
scrollMsgBottom = () => {
    let query = Taro.createSelectorQuery()
    query.select('#scrollview').boundingClientRect()
    query.select('#msglistview').boundingClientRect()
    query.exec((res) => {
        // console.log(res)
        if(res[1].height > res[0].height) {
            this.setState({ scrollTop: res[1].height - res[0].height })
        }
    })
}
scrollMsgBottomRN = (t) => {
    let that = this
    this._timer = setTimeout(() => {
        that.refs.ScrollViewRN.scrollToEnd({animated: false})
    }, t ? 16 : 0)
}
componentDidMount() {
    if(process.env.TARO_ENV === 'rn') {
        this.scrollMsgBottomRN()
    }else {
        this.scrollMsgBottom()
    }
}

另外在開發中需要特別注意rn端flex佈局和h5、小程式端差異。。

constructor(props) {
    super(props)
    this.state = {
        scrollTop: 0,

        showFootToolbar: false,
        showFootViewIndex: 0,

        editorText: '',
        editorLastCursor: 0,

        // 訊息記錄
        msgJson: msgList,

        // 表情json
        emotionJson: emotionList,

        // 預覽圖片陣列
        previewImgArray: [],
    }
}

componentDidMount() {
    if(process.env.TARO_ENV === 'rn') {
        this.scrollMsgBottomRN()
    }else {
        this.scrollMsgBottom()
    }
}

...

// 點選聊天訊息區域
msgPanelClicked = () => {
    if(!this.state.showFootToolbar) return
    this.setState({ showFootToolbar: false })
}

// 表情、選擇區切換
swtEmojChooseView = (index) => {
    this.setState({ showFootToolbar: true, showFootViewIndex: index })
}

// 底部表情tab切換
swtEmojTab = (index) => {
    let lists = this.state.emotionJson
    for(var i = 0, len = lists.length; i < len; i++) {
        lists[i].selected = false
    }
    lists[index].selected = true
    this.setState({ emotionJson: lists })
}

bindEditorInput = (e) => {
    this.setState({
        editorText: e.detail.value,
        editorLastCursor: e.detail.cursor
    })
}
bindEditorFocus = (e) => {
    this.setState({ editorLastCursor: e.detail.cursor })
}
bindEditorBlur = (e) => {
    this.setState({ editorLastCursor: e.detail.cursor })
}

handleEmotionTaped = (emoj) => {
    if(emoj == 'del') return
    // 在游標處插入表情
    let { editorText, editorLastCursor } = this.state
    let lastCursor = editorLastCursor ? editorLastCursor : editorText.length
    let startStr = editorText.substr(0, lastCursor)
    let endStr = editorText.substr(lastCursor)
    this.setState({
        editorText: startStr + `${emoj} ` + endStr
    })
}

好了,以上就是taro+react開發聊天室例項分享介紹,希望能有點點幫助~~??

最後分享兩個最近例項專案,希望能喜歡。

react-native聊天室|RN版聊天App仿微信例項|RN仿微信介面

vue仿微信網頁版|vue+web端聊天室|仿微信客戶端vue版

基於Taro+react多端仿微信聊天室|taro聊天例項分享

本文為原創文章,未經作者允許不得轉載,歡迎大家一起交流 QQ(282310962) wx(xy190310)

相關文章