taro 多端实践仿微信聊天|taro+react 聊天室

2019-12-16 08:58:39 +08:00
 xiaoyan2017

Taro 开发三端实例 taro-chatroom 仿微信聊天 App 界面 (H5 + 小程序 + App 端)

之前有使用uni-app 多端技术开发过仿抖音短视频 /陌陌直播实例,今天给大家分享的是基于 taro+react+redux+rn 等技术开发的仿微信界面聊天项目,基本实现了消息发送、动图表情、图片预览、长按菜单、红包 /朋友圈等功能。

支持编译到多端 h5/小程序 /app 效果如下:

技术栈:

入口页面 app.jsx

引入公共样式、组件页面及态管理

/**
  * @desc   Taro 入口页面 app.jsx
  * @about  Q:282310962  wx:xy190310
  */

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

自定义导航栏配置非常简单,只需在 window 选项下配置 navigationStyle: 'custom' ,只要不设置 tabbar 参数就可以自定义底部组件了。

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

这里不详细介绍如何实现自定义 Navbar+tabbar,可以去参看:

taro 自定义 Navigation、tabbar 组件

taro 自定义弹窗 taroPop 组件

taro 实现表单登录验证|状态管理|本地存储

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

由于 taro 中 ReactNative 端不支持同步存储,只能使用异步存储实现

/**
 * @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'

import TouchView from '@components/touchView'
import Navigation from '@components/navigation'

// 引入自定义弹窗 taroPop
import TaroPop from '@components/taroPop'

import storage from '@utils/storage'
import util from '@utils/util'

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
            })
			
			...
        }
    }
    // 去注册
    GoToRegister = () => {
        Taro.navigateTo({url: '/pages/auth/register/index'})
    }
    
    render () {
        ...
    }
}

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

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

对于一些兼容样式,不支持编译到 RN 端,则可通过如下代码包裹实现 /*postcss-pxtransform rn eject enable*/ /*postcss-pxtransform rn eject disable*/

滚动聊天信息底部

taro 中实现聊天消息滚动到最底部,由于 RN 端不支持 createSelectorQuery,需另做兼容处理

// 滚动至聊天底部
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()
    }
}

聊天中表情模块则是使用 emoj 表情符,实现起来较简单,这里不介绍了

...

// 点击聊天消息区域
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-native 聊天室|RN 版聊天 App 仿微信实例|RN 仿微信界面

vue 仿微信网页版|vue+web 端聊天室|仿微信客户端 vue 版

3310 次点击
所在节点    推广
3 条回复
evilhero
2019-12-16 09:05:51 +08:00
我就想问问图友和图控还可以进去吗
gunavy
2019-12-16 09:54:35 +08:00
taro 算是狗长里不错的东西了
wuchengqi
2020-01-06 12:52:10 +08:00
楼主有 demo 么

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/629336

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX