·

【机器学习】ChatTTS:开源文本转语音(text-to-speech)大模型天花板

发布时间:2024-08-27 23:14:40阅读量:228
转载请注明来源

一、引言

我很愿意推荐一些小而美、高实用模型,比如之前写的YOLOv10霸榜百度词条,很多人搜索,仅需100M就可以完成毫秒级图像识别与目标检测,相关的专栏也是CSDN付费专栏中排行最靠前的。今天介绍有一个小而美、高实用性的模型:ChatTTS。

二、TTS(text-to-speech)模型原理

2.1 VITS 模型架构

由于ChatTTS还没有公布论文,我们也不好对ChatTTS的底层原理进行武断。这里对另一个TTS里程碑模型VITS原理进行简要介绍,让大家对TTS模型原理有多认知。VITS详细论文见链接

VITS论文对训练和推理两个环节分别进行讲述:

2.2 VITS 模型训练

VITS模型训练:在训练阶段,音素(Phonemes)可以被简单理解为文字对应的拼音或音标。它们经过文本编码(Text Encode)和映射(Projection)后,生成了文本的表示形式。左侧的线性谱(Linear Sepctrogram)是从用于训练的音频中提取的 wav 文件的音频特征。这些特征通过后验编码器(Posteritor)生成音频的表示,然后通过训练对齐这两者(在模块 A 中)。节奏也是表达的重要因素,因此还加入了一个随机持续时间预测器(Stochasitic Duration Predictor)模块,根据音素和对齐结果对输出音频长度进行调整。

2.3 VITS 模型推理

VITS模型推理:在推理过程中,输入是文本对应的音素。将映射和对长度采样输入模型,将其转换为语音表示流,然后通过解码器将其转换为音频格式。

根据论文中描述的逻辑,文本数据被转换为音素(即词的拼音)并输入模型。模型学习了音素与音频之间的关系,包括说话者的音质、音高、口音和发音习惯等。

三、ChatTTS 模型实战

3.1 ChatTTS 简介

ChatTTS 是一款专门为对话场景(例如 LLM 助手)设计的文本转语音模型。

3.2 ChatTTS 亮点

1. 对话式 TTS: ChatTTS 针对对话式任务进行了优化,能够实现自然且富有表现力的合成语音。它支持多个说话者,便于生成互动式对话。
2. 精细的控制: 该模型可以预测和控制精细的韵律特征,包括笑声、停顿和插入语。
3. 更好的韵律: ChatTTS 在韵律方面超越了大多数开源 TTS 模型。我们提供预训练模型以支持进一步的研究和开发。

3.3 ChatTTS 数据集

1. 主模型使用了 100,000+ 小时的中文和英文音频数据进行训练。
2. HuggingFace 上的开源版本是一个在 40,000 小时数据上进行无监督微调的预训练模型。

3.4 ChatTTS 部署

3.4.1 创建conda环境

conda create -n chattts
conda activate chattts

3.4.2 拉取源代码

git clone https://github.com/2noise/ChatTTS
cd ChatTTS

3.4.3 安装环境依赖

pip install -r requirements.txt

3.4.4 启动WebUI

export CUDA_VISIBLE_DEVICES=3 #指定显卡
nohup   python examples/web/webui.py --server_name 0.0.0.0 --server_port 8888 > chattts_20240624.out 2>&1 & #后台运行

执行后会自动跳转出webui,地址为server_name:server_port

3.4.5 WebUI推理

个人感觉:其中夹杂着“那个”、“然后”、“嗯...”等口头禅,学的太逼真了,人类说话不就是这样么。。

1. [uv_break]、[laugh]等符号进行断句、微笑等声音控制。
2. Audio Seed:用于初始化随机数生成器的种子值。设置相同的 Audio Seed 可以确保重复生成一致的语音,便于实验和调试。推荐 Seed: 3798-知性女、462-大舌头女、2424-低沉男。
3. Text Seed:类似于 Audio Seed,在文本生成阶段用于初始化随机数生成器的种子值。
4. Refine Text:勾选此选项可以对输入文本进行优化或修改,提升语音的自然度和可理解性。
5. Audio Temperature️:控制输出的随机性。数值越高,生成的语音越可能包含意外变化;数值较低则趋向于更平稳的输出。
6. Top_P:核采样策略,定义概率累积值,模型将只从这个累积概率覆盖的最可能的词中选择下一个词。
7. Top_K:限制模型考虑的可能词汇数量,设置为一个具体数值,模型将只从这最可能的 K 个词中选择下一个词。

3.5 ChatTTS 代码

import os, sys
 
if sys.platform == "darwin":
    os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
 
now_dir = os.getcwd()
sys.path.append(now_dir)
 
import random
import argparse
 
import torch
import gradio as gr
import numpy as np
 
from dotenv import load_dotenv
load_dotenv("sha256.env")
 
import ChatTTS
 
# 音色选项:用于预置合适的音色
voices = {
    "默认": {"seed": 2},
    "音色1": {"seed": 1111},
    "音色2": {"seed": 2222},
    "音色3": {"seed": 3333},
    "音色4": {"seed": 4444},
    "音色5": {"seed": 5555},
    "音色6": {"seed": 6666},
    "音色7": {"seed": 7777},
    "音色8": {"seed": 8888},
    "音色9": {"seed": 9999},
    "音色10": {"seed": 11111},
}
 
def generate_seed():
    new_seed = random.randint(1, 100000000)
    return {
        "__type__": "update",
        "value": new_seed
        }
 
# 返回选择音色对应的seed
def on_voice_change(vocie_selection):
    return voices.get(vocie_selection)['seed']
 
def generate_audio(text, temperature, top_P, top_K, audio_seed_input, text_seed_input, refine_text_flag):
 
    torch.manual_seed(audio_seed_input)
    rand_spk = chat.sample_random_speaker()
    params_infer_code = {
        'spk_emb': rand_spk,
        'temperature': temperature,
        'top_P': top_P,
        'top_K': top_K,
        }
    params_refine_text = {'prompt': '[oral_2][laugh_0][break_6]'}
 
    torch.manual_seed(text_seed_input)
 
    if refine_text_flag:
        text = chat.infer(text,
                          skip_refine_text=False,
                          refine_text_only=True,
                          params_refine_text=params_refine_text,
                          params_infer_code=params_infer_code
                          )
 
    wav = chat.infer(text,
                     skip_refine_text=True,
                     params_refine_text=params_refine_text,
                     params_infer_code=params_infer_code
                     )
 
    audio_data = np.array(wav[0]).flatten()
    sample_rate = 24000
    text_data = text[0] if isinstance(text, list) else text
 
    return [(sample_rate, audio_data), text_data]
 
 
def main():
 
    with gr.Blocks() as demo:
        gr.Markdown("# ChatTTS Webui")
        gr.Markdown("ChatTTS Model: [2noise/ChatTTS](https://github.com/2noise/ChatTTS)")
 
        default_text = "四川美食确实以辣闻名,但也有不辣的选择。[uv_break]比如甜水面、赖汤圆、蛋烘糕、叶儿粑等,这些小吃口味温和,甜而不腻,也很受欢迎。[laugh]"
        text_input = gr.Textbox(label="Input Text", lines=4, placeholder="Please Input Text...", value=default_text)
 
        with gr.Row():
            refine_text_checkbox = gr.Checkbox(label="Refine text", value=True)
            temperature_slider = gr.Slider(minimum=0.00001, maximum=1.0, step=0.00001, value=0.3, label="Audio temperature")
            top_p_slider = gr.Slider(minimum=0.1, maximum=0.9, step=0.05, value=0.7, label="top_P")
            top_k_slider = gr.Slider(minimum=1, maximum=20, step=1, value=20, label="top_K")
 
        with gr.Row():
            voice_options = {}
            voice_selection = gr.Dropdown(label="音色", choices=voices.keys(), value='默认')
            audio_seed_input = gr.Number(value=2, label="Audio Seed")
            generate_audio_seed = gr.Button("\U0001F3B2")
            text_seed_input = gr.Number(value=42, label="Text Seed")
            generate_text_seed = gr.Button("\U0001F3B2")
 
        generate_button = gr.Button("Generate")
 
        text_output = gr.Textbox(label="Output Text", interactive=False)
        audio_output = gr.Audio(label="Output Audio")
 
        # 使用Gradio的回调功能来更新数值输入框
        voice_selection.change(fn=on_voice_change, inputs=voice_selection, outputs=audio_seed_input)
 
        generate_audio_seed.click(generate_seed,
                                  inputs=[],
                                  outputs=audio_seed_input)
 
        generate_text_seed.click(generate_seed,
                                 inputs=[],
                                 outputs=text_seed_input)
 
        generate_button.click(generate_audio,
                              inputs=[text_input, temperature_slider, top_p_slider, top_k_slider, audio_seed_input, text_seed_input, refine_text_checkbox],
                              outputs=[audio_output, text_output])
 
        gr.Examples(
            examples=[
                ["四川美食确实以辣闻名,但也有不辣的选择。比如甜水面、赖汤圆、蛋烘糕、叶儿粑等,这些小吃口味温和,甜而不腻,也很受欢迎。", 0.3, 0.7, 20, 2, 42, True],
                ["What is [uv_break]your favorite english food?[laugh][lbreak]", 0.5, 0.5, 10, 245, 531, True],
                ["chat T T S is a text to speech model designed for dialogue applications. [uv_break]it supports mixed language input [uv_break]and offers multi speaker capabilities with precise control over prosodic elements [laugh]like like [uv_break]laughter[laugh], [uv_break]pauses, [uv_break]and intonation. [uv_break]it delivers natural and expressive speech,[uv_break]so please[uv_break] use the project responsibly at your own risk.[uv_break]", 0.2, 0.6, 15, 67, 165, True],
            ],
            inputs=[text_input, temperature_slider, top_p_slider, top_k_slider, audio_seed_input, text_seed_input, refine_text_checkbox],
        )
    
    parser = argparse.ArgumentParser(description='ChatTTS demo Launch')
    parser.add_argument('--server_name', type=str, default='0.0.0.0', help='Server name')
    parser.add_argument('--server_port', type=int, default=8080, help='Server port')
    parser.add_argument('--root_path', type=str, default=None, help='Root Path')
    parser.add_argument('--custom_path', type=str, default=None, help='the custom model path')
    args = parser.parse_args()
 
    print("loading ChatTTS model...")
    global chat
    chat = ChatTTS.Chat()
 
    if args.custom_path == None:
        chat.load_models()
    else:
        print('local model path:', args.custom_path)
        chat.load_models('custom', custom_path=args.custom_path)
 
    demo.launch(server_name=args.server_name, server_port=args.server_port, root_path=args.root_path, inbrowser=True)
 
 
if __name__ == '__main__':
    main()

通过import ChatTTS和chat = ChatTTS.chat()以及chat.infer对ChatTTS类进行引用,通过装载多个配置项进行不同语音类型的生成。

四、总结

本文首先以VITS为例,对TTS基本原理进行简要讲解,让大家对TTS模型有基本的认知,其次对ChatTTS模型进行step by step实战教学,个人感觉4万小时语音数据开源版本还是被阉割的很严重,可能担心合规问题吧。其次就是没有特定的角色与种子值对应关系,需要人工去归类,期待更多相关的工作诞生。

实用性上来讲,对于语音聊天助手,确实是一种技术上的升级,不需要特别多的GPU资源就可以搭建语音聊天服务,比LLM聊天上升了一个档次。最近好忙,主要在做一个人工智能助手,3天涨了1.3万粉丝。最近计划把ChatTTS应用于这个人工智能助手(微博:面子小行家)的私信回复中,涉及到音频文件与业务相结合。期待我的成果吧!


原文链接:https://blog.csdn.net/weixin_48007632/article/details/139929395

0 人喜欢

评论区

暂无评论,来发布第一条评论吧!

弦圈热门内容

创意总会有枯竭的那天,但创新不会,唯有创新才有可能源源不断、永无止境

根据网上查到的资料,创意这个词是创新的子集:创意是创造意识或创新意识的简称,亦作“剙意”。它是指对现实存在事物的理解以及认知,所衍生出的一种新的抽象思维和行为潜能。但是我认为从实践中讲,更准确地,应该这样定义创意。假设创新是一个集合$A$,那么创意就是任意一个单射$f: B\rightarrow A$且满足$f(B)\subsetneqq A$。By abuse of notation,我们直接将其记作$B$。显然,此定义推广了创意的文字定义。怎么理解这个定义呢?首先两个定义的共同之处是——创意小,创新大。在生产实践中,创意的例子比比皆是,比如说一个商品的包装、一个产品的界面和logo、相同食材的不同煮法等等。这些创意有些是有限的,而有些看似无限其实也是有其上确界。我们可以将这个说法写成一个命题。命题/定义1. 任意一个创意$B$,都存在一个最小实数$M\in\mathbb{R}_{\geq0}$使得$\|B\| \leq M$。此数被称为创意$B$的上确界,并记作$\sup(B)$。为什么说创意是有限的?从生产实践中考虑,绝大多数有创意的产品,经过激烈的商业竞争,在不断的 产生新创意 ...

回顾经典 - 使命召唤5僵尸模式mod 海绵宝宝

使命召唤5虽然是2008年发行的游戏,却是COD系列中最为经典的一个。可以说它是很多玩家的童年回忆,相较于COD的其他版本,COD5可以说拥有最强大的MOD功能,啥都能做成被做成COD5的MOD,即便是COD新版本的僵尸模式也能被做成MOD回到COD5中。得益于此,COD5的僵尸模式至今仍保持着一定热度。曾经COD5的僵尸模式非常火爆,很多MOD如雨后春笋一般涌现,其中不乏一些优秀有趣的MOD,而我今天要介绍的海绵宝宝MOD便是其中之一。这张地图的面积挺大,可玩区域包括:比奇堡小镇水母田音乐会区域高菲高伯冰淇淋船蟹堡王餐厅海绵宝宝的家章鱼哥的房子珊迪的圆顶树屋这种地图包含紧张的跑酷场景,并且有多种饮料,还有特殊的武器。话不多说,直接上图。更多细节介绍、视频,以及下载地址见:Spongebob, Battle for Bikini Bottom [V1.1] LINK UPSpongebob, Battle for Bikini Bottom [V1.1]

(✔已修复)弦圈APP下载附件功能存在问题,目前暂时无法修复。如若需要下载附件,请先用Web端

今天有粉丝反馈,弦圈APP里下载的附件并不能打开。接着我马上打开APP测试,发现文件确实是下载了,但是却找不到下载的文件,这也是当初测试的一个疏漏😢。不过令人沮丧的是,我目前找不到解决这个问题的办法😭。目前这个问题的原因已经查明,就是下载路径的问题。文件下载成功后所放的位置file:///data/user/0/com.sinering.manitori/files,在手机里是打不开的,里面的文件对于用户而言不可见。用户下载的文件相当于存到APP的数据里了,我目前也不清楚如何在手机访问这些文件,开发的时候可以通过Android Studio查看,但是这是真机。由于这是我第一次写APP,自己的技术水平有限,而手机端APP与Web端相比,同样的功能没那么好实现,复杂很多。目前这个问题,我暂时找不到解决办法,其实就是一行代码有问题需要修改。import { File, Paths } from "expo-file-system/next"; const new_file = new File(Paths.document, new_filename);就是上面这行代码里的Paths.do ...

如何创建你的第一个React.js+Vite项目?

最近弦圈APP第一个正式版上线了,在下载弦圈APP这个页面中,GitHub Page的下载页面就是直接用React.js+Vite写的:https://ricciflows.github.io/xianquan-app-download/。那么,对于新人小白而言,如何创建第一个React.js+Vite项目,并写出这样一个简单的页面呢?本文将手把手教你如何实现。首先,你需要安装并配置好node.js环境,具体见Node.js安装与更新教程 - Windows版,并确保node版本是18+或者20+。接着win+R并输入cmd打开控制台(如果你想要选择项目的位置,如D:\Reactjs,则分别输入D:和cd Reactjs)然后输入命令npm create vite@latest如果输出以下结果,则输入y然后按enter键接着输入项目名称,如vite-test按方向键↓,选择React,然后enter接着,根据自己的需要选择。这里我们选择第一个然后根据提示,分别输入三条命令。第一个命令是指进入项目文件vite-test,第二个命令则是安装所有npm依赖,第三个命令则是运行测试模式。注:输 ...

Node.js安装与更新教程 - Windows版

Node.js环境是前端开发的必备环境,无论哪一个前端框架都需要用到node.js,本文将会教你如何安装配置node.js环境。如果你已经安装过node.js,但是想更新,那你也只需要按照安装的步骤直接覆盖原目录即可。首先,打开node.js的官网https://nodejs.org/zh-cn,然后下载node.js的Windows安装包。注:无特别需求,直接安装LTS(long term support)版就可以了。如果觉得在官网下载速度太慢,可以选择镜像网站下载:https://mirrors.aliyun.com/nodejs-release/v22.14.0/?spm=a2c6h.25603864.0.0.4b507621PbOVxm。然后根据自己电脑的配置选择安装包,下载完后直接打开,选Next勾同意协议,然后选Next设置你的安装路径,然后选Next接下来,这里不用管直接选Next继续选Next,注:这里说的是某些npm库安装时需要从C/C++中编译出来,如果你想要能安装这些库,就勾选此项。现在直接选Install,开始安装等待安装完成(如果这时候弹出请求管理员权限,点是 ...

Flutter、Tauri、React Native、Android原生的四次开发经历,为何最后我选择了React Native?

Flutter、Tauri、React Native都是目前移动端流行的跨平台开发框架,并且他们还能胜任全平台开发。React Native是最早开源的跨平台框架,而Flutter紧跟其后,并且Flutter最近几年超越React Native成为当前世界上最流行的跨平台框架。Tauri则是最近几年诞生的新跨平台框架,跟其他框架显著不同的一点是,它允许你使用任何前端框架,即你能够自由使用整个Web生态进行跨平台开发。Flutter、Tauri、React Native、Android原生我都尝试过,接下来我说一下我分别使用他们的开发经历。首先,我第一个使用的跨平台框架是Tauri,当时Tauri V2.0已经发布,我看它能够使用Nuxt.js或者Next.js进行开发,觉得蛮不错的。毕竟我有两个网站,一个是Nuxt写的,另一个是Next写的,这样我就只需要在原有代码基础上修改一下就行了。于是很快我就栽跟头了,首先是Nuxt的桌面端应用,我在dev模式下,没有发现任何问题,$fetch请求也能正常发送。结果build以后,发现所有的请求都废了,全是404,将url改为完整url,结果就是 ...

中国当前的教育最缺少什么?

在我看来,目前国内教育最大的问题之一,就是不告诉学生这个社会残酷的真相,只会不断给学生灌输好好读书,以后勤奋工作才能出人头地、赚钱的错误观念。这导致大学里一个非常奇怪的现象,很多人明明没有赚钱的本事,却拿着父母赚的辛苦钱无所事事,在学校享受人生,俨然一副天堂的样子。因为到了大学,好好读书这个意念也随着高考结束而逐渐淡化,没有人再管你的学习,你只需要期末考试及格,干啥都可以,甚至不及格还能补考。然而殊不知这样的学生出来以后,大概率很快会被社会毒打。被当成螺丝钉是小事,最怕的是连当螺丝钉的资格都没有。目前国内教育的目的就是为了产生好的螺丝钉。但是讽刺的是,学校即便连螺丝钉都不能好好培养出来。因为学校教育与社会现实严重脱轨,以计算机编程类的课程为例,很多课里面的内容还停留在十几年前,完全没有与时俱进,也没考虑生产实际。这也意味着,在学校哪怕你认真上好每一节,学好所有的知识,也并不能在社会的竞争中脱颖而出,你必须自学加量,疯狂内卷,在自我内耗中度过。学校只是个没有感情的流水线工厂,只会机械性的给你灌输教材的内容,不会教你如何赚钱、如何社交、如何对待感情等等更加重要的问题,最后导致很多人遇到感情 ...

是否存在人类大脑永远无法理解的数学结构?

是否存在人类大脑永远无法理解的数学结构?答案是存在也不存在。这个问题重点在于“理解”这个词,怎么样才算是理解?本文中,我们就将理解分为直观理解和抽象理解吧。所谓直观理解,指的是能够通过五官直接感受到。基于这个定义,数学从线性代数中最基础的n维线性空间开始,就不是人脑能够直观理解的了,毕竟人脑只能理解四维以下的空间,即只能理解三维的空间,不能理解处在第四维度的时间。到目前为止,四维时空是否存在都还存在争议,因为并没有直接证据表明四维时空真实存在。因此,从物理世界来看,人脑从四维线性空间开始就无法直观理解了。抛开四维空间是否真实存在的物理争议,考虑纯粹数学上的定义,四维空间是存在的。那么有可能通过作图的方式来直观理解高维空间呢?不能。那些所谓画出四维及以上空间的图,其实是通过投影等方法实现降维,将高维空间的东西通过三维的形式表现出来,并不是真正的高维空间。既然存在那么多大脑无法理解的数学结构,这时数学就派上用场了。数学正是人类用于理解人脑无法直观理解的工具,因为人脑有个很强大的功能——抽象化,既然你无法想象、也无法理解,那干脆就将它抽象化为一个数学对象来研究,即抽象理解。人类对于高维空间的 ...

数学与物理公式可以精准简洁地描述自然现象,究竟是世界本就如此巧妙,还是科学家努力简化后的结果?

这个问题有点像数学究竟是人发明的,还是人发现的?每个人基于不同理念、哲学观,会有不同的答案。而如今这个问题,可以引申出几个类似的问题。世界的底层运行规律究竟是复杂的,还是简洁的?物理定律究竟是真理,还是人类为解释宇宙而创造的?(类似于数学是否人造?)数学定理或者物理定律是绝对真理吗?或者说存在绝对真理的数学定理或者物理定律吗?这些问题都涉及到一种哲学观,没有标准答案,只是你观念的不同。回到这个问题,我是持爱因斯坦的那种观点,认为宇宙能够由简洁而优美的数学所描述,因为宇宙的底层规律本身就是足够简单的,只是人类未曾发现。换句话说,这就有点像线性空间的基底一样,只需要几条简单的定律,就可以通过线性组合,不断复杂化,最终产生如今的宇宙。这里又涉及到一个问题,即这个线性空间到底是有限维的,还是无穷维的?不过基于世界本质的简单性,从审美角度出发,我更倾向于假设这个线性空间是有限维的。因此,从这个角度看,如果数学或物理公式不够简洁和美妙,那么其本身所蕴含的奥秘也就越浅显,并且距离世界的本质就更远,即引用高斯的话“距离神更远”。故而简洁的数学或物理公式,更多的是科学家们发现的结果,是自然的,而不是刻意 ...

国内曾经出现过很多的数学论坛,但是为什么如今大多数都访问不了了?

今天我在知乎宣传弦圈的时候,回答了一个问题有哪些数学论坛值得推荐?,结果发现有好几个回答里的数学网站已经访问不了了。这些回答里的几乎所有数学网站,我都未曾听说过(正如弦圈很多人不知道一样),这证明国内曾经也出现过很多数学论坛,有些或许曾经也辉煌过,但是最后都坚持不下去了。我做数学的时候,用的数学论坛基本上都是国外的MathStackExchange和Mathoverflow,知乎也很少用。可以说国内目前除了知乎,就没有高人气的数学论坛。毕竟本来纯数学就是一种非常小众的文化,而数学这种严肃的内容,也注定不会有高活跃、高互动的用户。因此可以看到很多国内的数学网站都已经不能访问了,有些还“活”着的,其实也是半死不活,空有用户量,但活跃度却低得可怜。而知乎的数学也早就变味了,彻底娱乐化了,真正有营养的内容已经没多少,真正有实力的大佬也相继退乎,回答都删得干干净净的。似乎中文互联网中已经没有太多数学文化的栖息之地了。国外虽然也好不到哪里去,但却跟国内天差地别,最大的MathStackExchange和Mathoverflow两个数学论坛,虽然也是不能盈利,纯粹靠捐赠维持生计,但是却能保持纯粹的数 ...

前端跨平台开发框架对比:Flutter vs Tauri vs React Native

传统移动端开发往往需要同时兼顾Android和IOS的开发,而桌面端开发又需要同时兼顾Windows、MacOS、Linux系统。如果你想要全平台覆盖,不仅意味着要同时维护多套完全不同的代码(极大提高了维护成本),并且代码和逻辑还可能不能复用,这意味着高昂的开发成本(极低开发效率),每个平台都得从零开始写。现在国内还多出个鸿蒙系统,这意味着你要同时开发和维护更多套代码,哪怕补贴钱,这成本也不是小企业能够负担得起的。于是,跨平台框架应运而生,Facebook开源的React Native,曾经是最流行的框架,不过近几年被Flutter超越。它不仅能让你使用React语言同时开发Android和IOS APP,甚至还能进行Windows桌面端开发。而谷歌开源的Flutter,是目前最流行的跨平台框架,略微领先React Native。它能让你使用dart语言开发移动端与桌面端应用。而Tauri则允许你使用任何前端框架进行全平台开发,不过也需要你懂得一些Rust语言。我们先从开发体验出发来对比这三个跨平台框架。首先,React Native能够让你完全用JSX语言来进行跨平台开发,这对于本身 ...

给Web开发者写的React Native简介,React Native与React的区别与对比(2)

本文我们继续之前的话题给Web开发者写的React Native简介,React Native与React的区别与对比(1),在上文中我们讲到在React Native想要写<p>或者<span>需要用Text组件。除了展示文本,还有一个很重要的东西就是展示图片。在React Native中你无法使用HTML的<img>,而要用React Native提供的Image组件。处理图片可以说是React Native中的一个难点,因为在React Native中无论是什么图片都需要你设置一个宽度和高度,见实例:import React from 'react'; import {Image} from 'react-native'; import {SafeAreaView, SafeAreaProvider} from 'react-native-safe-area-context'; const DisplayAnImage = () => ( <SafeAreaProvider> <SafeAreaView s ...

弦圈登录功能完成更新,之后只要登录一次便可长期保持登录

原标题:弦圈登录功能完成更新,之后只要登录一次便可长期保持登录。目前该功能仍在测试阶段不稳定,如果发现有登录后掉线问题,可以试试清空cookie。这几天,我对弦圈的登录功能进行了更新,换了目前最新的OAuth2技术,取代以前的session登录。基于OAuth2的登录功能有很多好处,首先第一个就是能够长时间的保持登录状态,现在大家上网,无论是哪个平台,你都会发现自己只要登录一次,哪怕过了很久再打开,仍然是登录状态。第二个好处就是,token是无状态的,因此会占用更少的服务器资源,这意味着弦圈负荷更小、访问更顺畅。旧登录功能基于session是有状态的,如果人多起来,服务器负荷直线上升,这或许也是之前卡的原因之一吧。由于我是第一次在Web端使用OAuth2实现登录功能,因此刚开始更新的时候,网站还是有很多bug。比如说最大的一个bug就是,关闭浏览器后重新打开,需要重新登录,这显然问题很大。而这个bug今天经过我整整一天的艰难调试,终于是修好了。别小看一个简单的登录功能,尤其是OAuth2,前后端实现真的挺复杂。最后虽然网站代码已经更新好了,但是用户浏览器里的cookie是不会因此自动删 ...

给Web开发者写的React Native简介,React Native与React的区别与对比(1)

React Native是React下的一个跨平台框架,能让你用熟悉的React JSX语法来进行跨平台开发。所谓的跨平台开发是如今的一种趋势,即用同一种语言来同时进行Web端、手机端安卓与IOS、桌面端Windows、MacOS、Linux的开发。这样不仅能极大的提高开发效率,同时大大增加了代码的可维护性,节省了大量的成本。然而React Native虽然带个React,用的也是JSX语言,却跟React有很多不一样的地方。因为React Native不仅面向网页端,还面向手机端APP,而React Native的代码会直接编译为native原生代码。在本文中,我将会列举说明几个React Native的不同之处。首先,在React Native中我们不能像React那样直接使用HTML语言,因为无论是Android还是IOS,都无法编译HTML语言。因此,我们需要使用React Native提供的组件。在React Native中,如果你想要写<div>,则需要换成<View>。View组件在Web端会被编译成<div>,而在Android和IO ...