Taro+react+RN仿微信app聊天室|taro聊天实例



  • TaroChat聊天实例项目是一个基于taro+react+redux+ReactNative技术开发的仿微信界面聊天App多端应用实践。实现了支持编译到多端(h5+小程序+App端)

    编译到H5+小程序+RN端 截图如下:
    未标题-2.png

    使用技术:

    • 编码/技术:Vscode + react/taro/redux/react-native
    • iconfont图标:阿里字体图标库
    • 自定义导航栏Navigation + 底部Tabbar
    • 弹窗组件:taroPop(基于Taro封装自定义模态框)
    • 支持编译:H5端 + 小程序 + RN端

    000360截图20191213022736497.png

    001360截图20191213022929441.png

    003360截图20191212175802429.png

    006360截图20191212180004356.png

    007360截图20191212180541309.png

    009360截图20191212180914028.png

    010360截图20191212180948684.png

    012360截图20191212181108837.png

    014360截图20191212181204893.png

    015360截图20191212181349261.png

    017360截图20191212181758949.png

    018360截图20191212181916509.png

    021360截图20191212182208773.png

    022360截图20191212182238468.png

    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'))
    

    表单登录验证|taro本地存储
    <Input placeholder="请输入手机号/昵称" onInput={this.handleInput.bind(this, 'tel')} />

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

    taro通过以上方法即可简单获取input值。更完整表单验证看下面的介绍

    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)
    

    需要注意是 taro中rn端不支持同步存储,只能改为setStorageSync异步存储

    对于一些样式不希望编译到rn端,则可如下包裹处理
    /*postcss-pxtransform rn eject enable*/
    /*postcss-pxtransform rn eject disable*/

    /**
     * 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*/
    }
    
    ...
    
    // 点击聊天消息区域
    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
    	})
    }
    
    ...
    

    okay 这次先介绍到这里吧,后续会继续分享一些实例项目 😴😴
    最后分享个uniapp多端实例项目:
    vue+uniapp仿陌陌直播项目|uni-app仿抖音短视频



  • 感谢UP分享~
    ~我是字数补丁


Log in to reply