V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nanxiaobei
V2EX  ›  React

2022 年,我们再来谈谈 React 状态管理

  •  1
     
  •   nanxiaobei ·
    nanxiaobei · 178 天前 · 4892 次点击
    这是一个创建于 178 天前的主题,其中的信息可能已经有所发展或是发生改变。

    1. 什么是 "状态"?

    jQuery 时代,JS 代码中混杂 DOM 结构,各个流程庞杂交织时,就形成面条式代码,当使用发布订阅模型时,调试会一团乱麻。

    jQuery 是针对 "过程" 的命令式编程,而那么多命令,最终都是为了更新 UI 中的 "数据",为什么不直接去改数据呢?

    北京 → 上海,把 city="北京" 变为 city="上海" 就行。不管飞机火车步行抛锚,也不管路上会不会遇到王宝强,

    现代前端框架的意义,就是问题解决思路的革新,把对 "过程" 的各种命令,变为了对 "状态" 的描述。

    什么是状态?状态就是 UI 中的动态数据。

    2. React 中的状态

    2013 年 5 月 React 诞生。但 2015 年之前,大概都是 jQuery 的天下。2015 年 3 月 React 0.13.0 发布,带来了 class 组件写法。

    在 React class 组件时代,状态就是 this.state,使用 this.setState 更新。

    为避免一团乱麻,React 引入了 "组件" 和 "单向数据流" 的理念。有了状态与组件,自然就有了状态在组件间的传递,一般称为 "通信"。

    父子通信较简单,而深层级、远距离组件的通信,则依赖于 "状态提升" + props 层层传递。

    于是,React 引入了 Context ,一个用于解决组件 "跨级" 通信的官方方案。

    但 Context 其实相当于 "状态提升",并没有额外的性能优化,且写起来比较啰嗦。

    为优化性能,一般会添加多个 Context ,写起来就更啰嗦。在项目没那么复杂时,还不如层层传递简单。

    3. 什么是 "状态管理"?

    实用主义来说,"状态管理" 就是为了解决组件间的 "跨级" 通信。

    当然,在使用状态管理库时,其会带来一些衍生的思维模式,比如如何组织 state ,如何拆分公共逻辑、业务逻辑、组件逻辑等,但归根结底,这些都不是核心缘由。

    核心就是为了解决实际问题 —— 为了通信。其它的各种概念与哲学,都不是必要的。

    Context 没那么好用,React 官方也没什么最佳实践,于是一个个社区库就诞生了。

    4. class 时代的状态管理

    React class 组件时代,就是 Redux (及其相关衍生库)与 MobX 的故事。

    Redux 是符合 React 理念的实现。而 MobX 这种 "监听" 的模式,特点是 "不够 React",但用起来简单。

    Redux 的利弊已讨论太多,简单来说,开发者关心的是 "使用",而 Redux 关心的是 "哲学"。

    之前开玩笑说,其实 Redux 用一行代码就可以表示,却写出了论文规格昏昏欲睡的文档:

    createStore = (reducer, state) => ({ dispatch: action => state = reducer(state, action) });
    

    而几乎所有 React 状态管理器的原理,其实都很简单,一个 "观察者模式" 的实现:

    在各个组件中订阅 listener ,state 更新时,再把 listener 都调用一遍,从而触发组件更新。

    5. 为什么是 Hooks ?

    React class 组件存在以下问题:

    1. this.state 是一个对象,每次更新局部,更新时也可新加 state 进去,这就让 state 整体比较混沌。
    2. 使用高阶组件等模式时,会造成 this.props 中的数据来源不透明,同样混沌。
    3. 因为 this 魔法指针的存在,很容易挂一大堆东西上去,互相随意调用,就会让逻辑缠绕。

    为了解决以上问题,React 引入了 Hooks:

    1. 将混沌的 state 打散为一个个元数据。
    2. 提供逻辑共享,以替代高阶组件。
    3. 组件中不再存在 this

    这是一种开发理念与组织理念的革新,Hooks 带有强烈的 3 个特点:primitive 、decentralization 、algebraic effects 。

    1. primitive 。元数据化,从最底层构建,让数据结构更清晰。同时也是一种工程趋势,比如 Tailwind CSS 便是将 CSS 元数据化。
    2. decentralization 。去中心化,class 时代普遍是一种 "顶层下发" 的理念,但 Hooks 带来强烈的 "组件自治" 的理念(比如 Provider 不再必须,组件请求自行处理)。同时,在其他领域,去中心化也是一个大的流行概念。
    3. algebraic effects 。代数效应,归根结底,Hooks 可以理解为一根管道,直通 React 核心能力,将内部机器暴露给了开发者。

    6. Hooks 时代的状态管理

    Hooks 出现之后,社区还没有一个像 Redux 一样曾经一统江湖的状态管理器。

    Redux 添加了一些 useSelector 、useDispatch 、useStore 之类的能力,而 Facebook 自己也开源了 Recoil 这样的库。

    但 Redux 终究老气沉沉,且早期给人留下的阴影太大,很多人的思维被格式化,随便一写就是云里雾里,只为实现一个简单功能,

    而 Recoil 的写法则看起来有些别扭、有些啰嗦,发展也不温不火。

    // Recoil
    atom({ key: 'textState', default: '' });
    useRecoilState(textState);
    

    而在 Hooks 时代,一个神秘组织异军突起,一口气贡献了 3 个状态管理库。

    它就是 pmndrs ,pmndrs for Poimandres 。pmnd.rs

    说是 "组织",其实主要开发者应该是一个人,就是这位大师,Daishi Kato 。github.com/dai-shi

    这三个库分别是 zustand 、jotai 、valtio 。有趣的是,这三个词其实都是 "状态" 的意思。

    zustand 🇩🇪 德语 "状态",jotai 🇯🇵 日语 "状态"、valtio 🇫🇮 芬兰语 "状态"。

    简单看一下用法:

    // zustand 🇩🇪 - Redux 理念,旧时代精神,中心化逻辑
    
    const useStore = create((set) => ({
      bears: 0,
      removeBears: () => set({ bears: 0 }),
    }));
    const bears = useStore((state) => state.bears);
    
    // jotai 🇯🇵 - primitive 理念,用法略啰嗦,但符合 Hooks 精神
    
    const countAtom = atom(0);
    const [count, setCount] = useAtom(countAtom);
    
    // valtio 🇫🇮 - proxy 理念,"不太 React",但用起来简单
    
    const state = proxy({ count: 0, text: 'hello' });
    const snap = useSnapshot(state);
    

    7. 贪婪更新 vs 惰性更新?

    如之前提及 MobX 时所说,使用 proxy "监听" 的方案,虽然不够 React ,但确实用起来简单,且最符合直觉。

    本质上来说,React 是一种 "贪婪更新" 的策略,全量 re-render 然后 diff 。

    而 proxy 是一种 "惰性更新" 的策略,可以精准知道是哪个变量更新。所以利用 proxy ,可以做一些 re-render 的性能优化。

    而 React conf 上介绍的 React Forget ,代表 React 自身也并不排斥在 "惰性更新" 的思路上做一些优化。

    注意上面的 "贪婪更新" 和 "惰性更新" 是我自创的词,参考了正则中的贪婪和惰性概念。

    8. React 状态管理思路的变迁

    1. 所有 state 在一个大对象里 → 分割为元数据
    2. 数据不透明 → 数据透明
    3. 顶层请求,下发数据 → 组件自身处理请求
    4. 状态提升 → 组件自治
    5. Provider & Container components → just Hooks
    6. 混沌集合 → 透明解耦
    7. 贪婪更新 → 惰性更新
    8. 大而全、强概念、DX ❌ → 更清晰、更简单、DX ✅
    9. 少一些概念,多一些直觉
    10. 少一些规则,多一些自动化

    总的来说,这虽是状态管理思路的变迁,但更是 React 社区开发思路的变迁,一种对最佳实践的不断探索:

    1. 中心化 → 去中心化
    2. 数据集合 → 元数据
    3. Build a structure, completely from the ground up

    9. 隆重介绍 resso ,可能是最简单的 React 状态管理器

    我一直在思索怎样的一个 React 状态管理器用起来最简单,不断去探索一个自己用起来最舒服的工具。

    之前曾经开发过 Retalk ( Redux 最佳实践)、flooks ( Hooks 状态管理),但随着新思路的出现,现在将最新的一些灵感集中在了 resso 这个状态管理库里。

    下面是 resso 的使用方式:

    import resso from 'resso';
    
    const snap = resso({ count: 0, text: 'hello' });
    
    function App() {
      return (
        <>
          {snap.count}
          <button onClick={() => snap.count++}>+</button>
        </>
      );
    }
    

    GitHub 地址:github.com/nanxiaobei/resso

    注意它与很简单的 valtio 相比,写法也更简单一些,应该没法更简单了,如果有,请告诉我。

    更重要的是,resso 会自动优化 re-render ,绝不因为数据在同一个对象里,就触发额外的 re-render 。

    其实状态管理本是个很简单的东西,但 Redux 等工具追加了太多的复杂上去。人们用一个工具的原始目的,就是解决问题,如何而已。

    所以,简单,清晰,让工具回归工具。我们了解一个锤子的方式,就是拿起来用。

    希望 resso 会让有此需要的人喜欢。

    10. Invest the future

    但是这一切,又有什么用呢?

    在新东西不断涌来时,人们不免会发出疑问:class 组件又不是不能用,Redux 又不是不能用,或者更彻底一些,jQuery 又不是不能用,为什么一定要去追逐这些新的东西呢?

    一个抽象的解释:我们应该不断投资未来。

    这不只是在开发中,在工作中,更是在任何领域 —— “在新赛道中以不断细分的形式,用第一的身份换取资源。”

    旧世界的轨道上挤满了辛苦的赶路者,虽然新大陆海市蜃楼,但只有新大陆才会跃升一切。

    48 条回复    2022-04-12 00:59:02 +08:00
    Hanggi
        1
    Hanggi  
       178 天前
    状态管理只需要 hook 和 recoil 就能绝大部分需求。
    ospider
        2
    ospider  
       178 天前   ❤️ 5
    useSWR + useReducer 才是未来。redux 的问题在于把前端的数据 cache 作为了状态的一部分,完全没必要啊。useSWR 来管理数据,useReducer 来管理真正的 UI 状态,这样感觉比较清晰。
    KuroNekoFan
        3
    KuroNekoFan  
       178 天前
    @Hanggi recoil 还是 experimental ,真的敢用吗....而且体积也很大
    986244073
        4
    986244073  
       178 天前
    hooks 到底用啥呢 我是小白
    Hanggi
        5
    Hanggi  
       178 天前
    @KuroNekoFan Recoil? 体积大?你是怎么测算它的体积的?
    况且 Recoil 都 1 万 5 星了,在各种环境下均运行良好。(当然主要还是为了 cross component share state 使用,页面的大部分内容还是用 hook 写。)
    Hanggi
        6
    Hanggi  
       178 天前
    @986244073 你是问 hook 是啥?还是说 hooks 用啥实现的?还是 hook 怎么用?
    KuroNekoFan
        7
    KuroNekoFan  
       178 天前
    @Hanggi node_modules/recoil/es 或者 node_modules/recoil/cjs 里面的文件 均超过 100k....
    KuroNekoFan
        8
    KuroNekoFan  
       178 天前
    @Hanggi 并且我期望应用的场景在基于 taro react 的小程序上,所以文件尺寸真的会成为一个问题
    (当然我本人是讨厌小程序这个玩意的,没办法,要恰饭的嘛)
    guoliim
        9
    guoliim  
       178 天前   ❤️ 1
    @ospider 确实在加上 react server component 整个状态逻辑已经进入新的阶段了
    jguo
        10
    jguo  
       178 天前   ❤️ 1
    就像上面说的,你漏了 swr 和 react query 这类把后端数据也当成状态的广义状态管理
    nanxiaobei
        11
    nanxiaobei  
    OP
       178 天前
    @KuroNekoFan #8 https://github.com/nanxiaobei/resso 绝对能满足你对尺寸的要求,一共 0.88 kB (是的,888B ),而 Recoil 是 76.5 kB
    Hanggi
        12
    Hanggi  
       177 天前
    @KuroNekoFan
    em.... recoil 项目包确实内容挺多的,但是不知道实际打包出来会多出来多少。
    但是跟以往的一些状态控制相比,Recoil 真的很简单高效,关键是兼容 hook 语法。

    至于 taro ,不太清楚他与实际 React 兼容性如何,如果是纯 React 项目应该没啥问题。

    好久没弄过小程序了,但是这年头网站动不动都好几 MB ,加载也不会太慢,小程序多出来几百 KB 加载速度会慢很多吗?
    ragnaroks
        13
    ragnaroks  
       177 天前
    这个库如何跨文件组件共享状态?
    sjhhjx0122
        14
    sjhhjx0122  
       177 天前
    @Hanggi 小程序主要还是大小有限制吧,我记得只能 3mb 最大,recoil 确实算大的了
    Hanggi
        15
    Hanggi  
       177 天前
    @sjhhjx0122 如果是这种限制方式,那可选项很少啊
    Obrigado0815
        16
    Obrigado0815  
       177 天前   ❤️ 1
    耐心看完了,up 主讲的很好,对于 react 初学者的我,表示受益匪浅
    mywaiting
        17
    mywaiting  
       177 天前
    页面内多层级的组件相互强关联的时候,感觉啥状态管理都不好使,代码最后都会跟面条差不多
    SolidZORO
        18
    SolidZORO  
       177 天前 via iPhone
    Taro 小程序选 jotai ,体积非常小且兼容性非常好。实际上 jotai 可以用在任何地方,包括 Taro 和 RN 等一切 React Base 的地方。recoil 是很奇怪,在 Taro 上用不了,体积相对 jotai 也大了非常多,不知道有什么黑魔法。

    我已经用 jotai 替代 mobx 已经半年了,感觉全局 state 这块的心智负担已降至 0 。但 jotai 有个缺点,就是 jotai 只能在 「 React 内」使用。这个作者也比较头疼,宣称用户想要和 「 React 外」通信只好借助 jotai-zustand 解决。不过我是接受不了这个方式,所以所有非 React 代码比如 utils 工具都要以 hooks 的方式出现,习惯后发现这样也挺好。
    fernandoxu
        19
    fernandoxu  
       177 天前
    用 jotai
    1016
        20
    1016  
       177 天前
    redux-alita 这个也挺好用的
    IvanLi127
        21
    IvanLi127  
       177 天前 via Android
    就我一个人一直只用 react 自带的 context 吗😂
    nanxiaobei
        22
    nanxiaobei  
    OP
       177 天前
    @ragnaroks #13 这个库就是用于组件共享状态的,直接 import snap from './snap.js' 到不同的组件使用就行
    nanxiaobei
        23
    nanxiaobei  
    OP
       177 天前
    @SolidZORO #18 jotai 写起来太啰嗦,试试 resso ,最简单 https://github.com/nanxiaobei/resso
    nanxiaobei
        24
    nanxiaobei  
    OP
       177 天前
    @fernandoxu #19 试试 resso ,用起来更简单,直接更新数据更符合直觉
    nanxiaobei
        25
    nanxiaobei  
    OP
       177 天前
    @Hanggi #12 recoil 用起来太啰嗦,也一直不温不火,试试 resso ,或者 valtio
    Hanggi
        26
    Hanggi  
       177 天前
    @nanxiaobei
    首先并没有觉得 Recoil 太罗嗦,至少跟 Redux 比简直是极简。
    其次 Recoil 15k 星说不温不火就有点。。。
    而且,没有明白一点是,为啥要出 3 个库?现在选择困难症患者很多啊。
    nanxiaobei
        27
    nanxiaobei  
    OP
       177 天前
    @Hanggi #26 上面的文章里有基本介绍,你可以看一下文章再来讨论的 ...
    vZexc0m
        28
    vZexc0m  
       177 天前
    所以图片上的英文字体是什么呢
    nanxiaobei
        29
    nanxiaobei  
    OP
       177 天前
    @vZexc0m #28 macOS 自带 Klee
    daysv
        30
    daysv  
       177 天前   ❤️ 2
    喜欢往 FP 哲学上走, 不太喜欢 proxy 这类实现
    useReducer + useContext 挺好.
    daysv
        31
    daysv  
       177 天前
    Redux 的这种函数式哲学, 我认为很长一段时间依然是霸主, 整个社区都是这个方向, 整出 hooks 也是为了这个哲学. 毕竟 Facebook 那帮人好像搞 ocaml 的很多.
    chairuosen
        32
    chairuosen  
       177 天前
    感觉和 mobx 一样呀,就是把 mobx 的 class 写法改成了对象更方便了
    nanxiaobei
        33
    nanxiaobei  
    OP
       177 天前
    @chairuosen #32 "更方便" 就是目标所在
    sweetcola
        34
    sweetcola  
       177 天前
    现在一个现象就是,很多人在批判 React Redux 的啰嗦和繁琐,但是 React Redux 却一直在统治地位(用户量)。我粗略看了对比了一下各 React 状态库的 GitHub 中的 used by 和 npm 的下载量,发现 React Redux 要比其他加起来还要多。是不是说明了,啰嗦和繁琐对于其他开发者来说并不是一个缺点呢?

    当然,这也可能是一个历史遗留问题,因为 Redux 是 React 团队里的人开发的,虽然现在已经转移了,也和 FB 没什么关系了。在原本没有 hook 的环境下,Redux 的写法确实是十分啰嗦,但随着 hook 的出现这种啰嗦也随之消失了。与其他以简洁为主题的状态库似乎也就是现在仍然需要多写一段 const dispatch = useDispatch(); ,但我觉得这并不算什么问题。

    再就是 Redux 的一个配置问题,可以说是十分繁琐了,各种 actions 和 reducers ,虽然 @reduxjs/toolkit 的出现把 actions 和 reducers 整合成 slices 了(还有完整的 TypeScript 支持),依然不能使部分简洁爱好者动心(可以理解,真的还是挺繁琐的)。

    再就是某方面的心智负担问题吧,我觉得 Redux 做得挺好的,比如 React 18 中引入的 Concurrent mode 和面向非内部的状态管理 useSyncExternalStore 等等的新概念,在面临如此大的更新的情况下如果要我选择状态管理库,那我只会选择 Redux 了,当然这只是我的看法而已。
    lxzxl
        35
    lxzxl  
       177 天前
    用的 jotai, API 设计的比 Recoil 简单
    daief
        36
    daief  
       177 天前
    在 hooks 之后,在社区里看到很多结合订阅模式实现的状态共享方案,实现很简单,也不会有其他依赖,我现在自己也喜欢使用新的方式,在 web 、taro 中使用下来的体验都还可以。不久前也总结过一下 React 的状态管理,大家有兴趣可以看看: https://daief.tech/post/react-state-management-based-on-react-hooks
    nanxiaobei
        37
    nanxiaobei  
    OP
       177 天前
    @sweetcola #34 我主要还是在意好不好用,不太在意 "统治地位" 之类。另外 Redux 不是 React 团队开发的,是出名之后,Dan Abramov 加入了 React 团队。
    chairuosen
        38
    chairuosen  
       177 天前
    @nanxiaobei #33 我跟你一样,我也喜欢用 mobx 的形式,不喜欢 redux 。但是对于 mobx 不方便的地方,我的处理方法不同,你相当于写了一个新轮子来替换 mobx ,我的想法是,把 mobx 封一层,把不方便的地方不暴露给使用者。 两种方式使用起来的表现基本都一样:暴露唯一的一个纯 js 的入口方法,传入初始 state 和改变 state 的方法,输出一个完整的 state 。
    nanxiaobei
        39
    nanxiaobei  
    OP
       177 天前
    @chairuosen #38 Hooks 出来以后,这些东西其实都很简单(当然其实本来也没那么简单,只是人为的搞复杂了),自己做一个,总共才 461 B 的代码
    ztcaoll222
        40
    ztcaoll222  
       177 天前   ❤️ 1
    redux-toolkit 挺好用的啊
    lupkcd
        41
    lupkcd  
       177 天前
    这么多年了 React 还是纠结状态管理库的选择
    secondwtq
        42
    secondwtq  
       177 天前   ❤️ 2
    对我这个已经多年不搞前端的人来说,楼主的文章提供了 React 状态管理现状不错的 overview 。

    虽然 Web 前端暂时怎么不搞了,但广义的前端还是一直在关注的。补充一下,楼主“自创”的“贪婪更新”和“惰性更新”已经是有点年头的概念了,这东西一般叫“incremental computation”。所以大概可以叫“增量渲染”和“全量重渲染”。

    Incremental Computation 的关键是构建一个 dependency graph ,当数据发生改变时程序只会运行其中 dirty 的部分。它的用途很广泛,是指令乱序执行、编译器优化、Spreadsheet 、包管理器、构建系统以及许多类似的任务系统的核心。

    类似的方法在其他 UI 框架中早就有应用,比如 Cocoa 有 KVO ,WPF 有 INotifyPropertyChanged ,GTK 和 Qt 等框架也有一点 MVC 的意思,他们的列表会有个 model ,接受变化的通知。
    但是可能是由于建立在 imperative 语言的基础上,这些较传统的框架并不适合操作复杂的 dependency graph 。给我的感觉是,它们“incremental”的地方主要集中在“界面 - 代码的接口”上,使用简单的数据绑定实现。界面本身不是 incremental 的,incremental 的方法也很难渗透到代码逻辑里面。数据绑定可以解决一些数据和简单属性的更新,但是“UI 操作”依然需要额外手写,而这一过程依然非常麻烦并且容易出错。

    React 的流行普及了一种新方法,React 鼓励用户尽可能用数据表示 UI ,淘汰“UI 操作”的概念,以“数据变换”来取代。这并不是性能上最优的方式,原始的 React 并不是很重视 incremental ,基本仅仅体现在一个偶尔用一下的 shouldComponentUpdate 和 virtual DOM diffing 上。后者基本上也就相当于“React - Renderer 接口”,不用 shouldComponentUpdate 的话,React 里面这一层其实是有意设计得 brute force 的,但是确实很大程度上简化了问题。

    React 这一步的一个副作用是,由于数据变换可以以更简单的纯函数来表示,现在 UI 全链路都可以 incremental 了。我不知道这一点当年 React 的设计者们有没有想到,但是后来者有不少想到了。更新的框架不少都是以 Declarative + Incremental 的思路设计的。比如 Web 这里比较典型的是 Svelte ,作为 React 竞品直接说 virtual DOM diffing “eat into your frame budget and tax the garbage collector”,而 SwiftUI 里面干脆就有个 dependency graph 模块,React 的状态管理也在往这个方向走。

    上面有人说原始 React 更加“函数式”,实际上搞 incremental 的人不少也搞 FP——在一个 dependency graph 中,明确能具有“状态”的基本只有叶子节点,从叶子节点到最后要求的值,依然是一个纯函数。非要说的话只能说一个是比较“粗放”的函数式,一个是比较“精细”的函数式。要说起来的话 FB (现在应该叫 Meta ?)粗放不是第一次了,FB 用 OCaml 是吧? OCaml 自己是不支持共享内存并行的(即将发布的 OCaml 5.0 将会开始完善这一能力),结果 FB 自己 hack 出来一个库给自己项目用 ...
    我其实是从 Jane Street 那边了解的 Incremental Computation ,在 OCaml 社区这是与 FB 齐名的公司。他们自己做了个库就叫 Incremental ,需要注意的是在这个下面他们还自己实现了一套 Virtual DOM 解决 Incremental 不太好处理的问题,也就是说他们是两个都在用,和现在 React 有点类似。关于这个他们发了一堆的博客:
    blog.janestreet.com/introducing-incremental Jane Street Tech Blog - Introducing Incremental
    blog.janestreet.com/incrementality-and-the-web Jane Street Tech Blog - Incremental computation and the web
    blog.janestreet.com/self-adjusting-dom Jane Street Tech Blog - Self Adjusting DOM
    blog.janestreet.com/self-adjusting-dom-and-diffable-data Jane Street Tech Blog - Self Adjusting DOM and Diffable Data
    blog.janestreet.com/seven-implementations-of-incremental Jane Street Tech Blog - Seven Implementations of Incremental
    mizuhashi
        43
    mizuhashi  
       176 天前 via iPhone
    其实 imba 语言或者说 mithril.js 的状态管理更自然
    zhwithsweet
        44
    zhwithsweet  
       176 天前
    那肯定是 mbox 或者 @vue/reactivity 怎么缝合怎么来
    charlie21
        45
    charlie21  
       160 天前
    支持南老师
    charlie21
        46
    charlie21  
       159 天前
    一个类似总结的文章 各流派 React 状态管理对比和原理实现
    https://zhuanlan.zhihu.com/p/394106764
    jingcoco
        47
    jingcoco  
       146 天前
    xstate
    everettjf
        48
    everettjf  
       118 天前
    感谢分享
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1180 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 23:16 · PVG 07:16 · LAX 16:16 · JFK 19:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.