10 月,开发者不可错过的开源大数据大会-2021 WeDataSphere 社区大会深圳站 了解详情
写点什么

基于 React Native 的跨三端应用架构实践

2018 年 12 月 29 日

基于React Native的跨三端应用架构实践

一次编写,到处运行”(Write once, run anywhere )是很多前端团队孜孜以求的目标。实现这个目标,不但能以最快的速度,将应用推广到各个渠道,而且还能节省大量人力物力。


React Native 的推出,为跨平台的开发带来了新的曙光。 虽然 Facebook 官方 blog 的说法 React Native 支持“Learn once, write anywhere.”。但经过开源社区的不断努力,React Native 已经可以达到“一次编写,到处运行”的目标。可以说超过了 Facebook 的预期。作者在最近的几个项目中,运用 React Native 技术,成功实现跨越 iOS,Android,Web 三端的前端架构。这里将使用到的技术和过程中遇到的困难和问题揭示出来,供读者探讨。


技术选型

我们的目标是希望一套代码同时支持 iOS,Android App 和微信公众号内的网页(同时保留将来支持桌面浏览器的能力)。在开始重构之前,我们盘点了目前可用的一些技术:



① SPA:single page web application,就是只有一张 html 页面的应用。仅在该 Web 页面初始化时加载相应的 HTML、JavaScript、CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转,而是利用 JavaScript 动态的变换 HTML(采用的是 div 切换显示和隐藏),从而实现 UI 与用户的交互。


② MPA: multipage web application, 相对于 SPA,MPA 有多个 html 页面。页面间跳转刷新所有资源,公共资源(js、css 等)需选择性重新加载。


本人于 2012 年开始接触 Cordova & Ionic,应该说 Cordova 在 React-Native 出现之前确实是跨平台的主流技术。但是现在是 2018 年,Cordova 在性能上肯定达不到我们的要求,首先被 pass 掉。


Vue.js 也是我们团队的备选前端框架,主要用于桌面浏览器展示的项目。缺乏原生移动解决方案,以及实际用下来感觉 template 表现力比不上 JSX。另外我们用到了蚂蚁金服优秀的前端控件库 ant design mobile, 暂时不支持 Vue。


2018 年 7 月份我们对 Flutter(0.5.1) 和 React-Native(0.51.0)进行了一次性能比较测试。我们在 Android 上用 Flutter 和 React-Native 分别实现了一个含图文的新闻客户端,比较了页面加载,图片加载,页面跳转等关键性能。实测下来 Flutter 在 List 加载,跳转到详情页时都有明显掉帧。另外代码无法移植到 web 上。这些原因导致我们放弃了 Flutter。


最终我们选择了 React-Native 作为我们项目的实现技术,除了上述的一些优点之外,我们在如下一些方面收益颇多。


项目架构

我们在项目中用到的前端整体架构如下图:



以下对上图中一些技术点进行介绍:


应用支持层

作为应用和后台服务 &原生 App 之间的桥梁,应用支持层需要处理诸如端到端通讯,数据加密解密,数据缓存,数据拦截,原生应用功能访问等基础服务。最大限度的屏蔽掉平台间差异,让位于其上的层尽量做到平台无关。


原生模块封装

React-Native 可以方便的封装原生应用模块。对于有 UI 的原生模块,既支持在一个新的 ViewController(Activity)中展示, 也支持将其封装成一个 View,嵌入到 React-Native 的上下文中。 这也是 React-Native 最接地气的特性,远超 Cordova。在一些场景下需要等待原生模块中的事件,诸如用户操作等异步事件之后才能返回,这时需要用到 Promise 作为原生模块的参数。


比如通过调用手机摄像头,对银行卡进行扫描,这时会调用原生第三发控件的 ScanCardViewController 进行扫描,扫描结果通过代理函数回调。整个调用和回调的流程无法直接在一个函数中完成,这时可以用 React native 的 Promise 实现对 JS 端 Promise 的无缝对接。


@protocol RCTBankCardScannerDelegate <NSObject>-(void)onScanCardResult:(NSDictionary *) result;@end @interface RCTBankCardScanner()<RCTBankCardScannerDelegate>@property(nonatomic, strong) RCTPromiseResolveBlock resolveBlock;@property(nonatomic, strong) RCTPromiseRejectBlock rejectBlock;@end @implementation RCTBankCardScannerRCT_EXPORT_MODULE();RCT_REMAP_METHOD(scan, resolver:(RCTPromiseResolveBlock)resolve                 rejecter:(RCTPromiseRejectBlock)reject){  //异步调用,函数本体不返回,需要保留resolve,和reject函数指针  self.resolveBlock = resolve;  self.rejectBlock = reject;  //跳转到扫描银行卡控件的ViewController  ScanCardViewController * viewController = [ScanCardViewController new];  UIViewController *rootViewController = RCTPresentedViewController();  [rootViewController presentViewController:viewController animated:YES completion:nil];} #pragma mark RCTBankCardScannerDelegate-(void)onScanCardResult:(NSDictionary *) result{  // 在原生ViewController回调处,再返回Promise的处理结果  if(result != nil && [[result objectForKey:@"code"] isEqualToString:@"0"]){    if(self.resolveBlock != nil){      self.resolveBlock(result);    }  }else if(result != nil){    if(self.rejectBlock != nil){      self.rejectBlock([result objectForKey:@"code"], @"failed", nil);    }  }else{    if(self.rejectBlock != nil){      self.rejectBlock(@"-100", @"invaild response", nil);    }  }}
复制代码


上述代码实现了银行卡扫描控件的封装。调用 scan 函数的时候会新启动摄像头,完成身份证扫描识别之后将结果传回 JavaScript.在 JavaScript 中,可以通过


import {NativeModules} from 'react-native'const BankCardScanner = NativeModules. BankCardScannerconst { code, no } = await BankCardScanner.scan()

复制代码


实现对原生层的异步调用,并等待 ScanCardViewController 完成并回调。


后台接口封装

到服务器的端到端访问通过继承 BaseService 类实现.BaseService 负责处理跟服务端交互,加密,解密,错误处理等。


import BaseService from '../common/base-service'import Page from './Page'export default class DemoService extends BaseService {  constructor(props) {    super(props)    this.page = new Page(this.getDemoList.bind(this))  }  /**   * 获取示例列表详情   */  async getDemoList (params) {    const res = await this.postJson('getDemoList', params)    return res  }}
复制代码


Page 类实现了对分页数据的加载和存储封装,使其与页面解除耦合。通过指定支持分页的方法,可以实现分页加载。


PaginationHoc 则封装了需要暴露给页面的分页相关方法,包括获取设置支持分页的 Service,获取分页对象,加载下一页数据,设置搜索参数等。


一个包含分页的页面例子如下:


@Pagination@Loadingexport default class DemoPage extends Component {  constructor(props) {    super(props);    this.props.setService(new DemoService(this.props));  }
async componentDidMount() { await this.props.loadMore(); }
render() { return ( <View> <FlatListView style={styles.list} data={this.props.getPage().list} renderItem={this.renderRow.bind(this)} hasMore={this.props.hasMore()} onEndReached={this.props.loadMore.bind(this)} /> </View> ); }}
复制代码


全局异常捕获

在 web 开发中,可以使用 window.onerror = function(){message, source, …} 来捕获未处理的 JavaScript 错误。但是对于一个遍布异步调用的复杂应用来说,window.onerror 没太大用。通常需要捕获的是未处理的异步调用异常,即 unhandled rejection。


在 web 中,unhandled rejection 可以通过收听’unhandledrejection’事件来处理。


window.addEventListener('unhandledrejection', function(event) {  const error = event.reason  handleErrors(error);})
复制代码


增加了全局’unhandledrejection’事件监听之后,依然可以通过 try catch 实现对某个异常的自定义处理,这时全局’unhandledrejection’事件监听就不会被调用到。如:


 try{    await this.service.getDemoList(); } catch (error) {    Modal.alert(‘数据获取异常’) }
复制代码


Promise 目前在 WebKit 系的浏览器支持的比较好,如果需要在非 Webkit 内核浏览器上使用,通常需要添加 polyfill。这里需要注意的是项目不能采用 promise-polyfill。因为 promise-polyfill 的实现没有考虑到’unhandledrejection’,并且会覆盖浏览器原生的 Promise 实现。我们选用的是 es6-promise-promise 库作为 Promise 的 polyfill 方案。


对于 react-native。异步异常捕获未见于其官方文档。但 react-native 的 Promise 模块引用的是Then Promise 。Then Promise 对于’unhandledrejection’,提供了处理钩子函数:


require('promise/lib/rejection-tracking').enable({  allRejections: true,  onUnhandled: function(id, error){    ...  }});
复制代码


需要注意的是 Then Promise 对 onUnhandle 的默认定义是: 2 秒钟内没有被处理的 Promise rejection,因此错误处理时一定要考虑到这 2 秒钟的等待时间。


应用状态层

相信本文读者应该多少了解通过 Flux、 Redux、VueX 来管理前端应用状态的意义了。严格说来, 前端应用就是一个通过渲染层,将状态渲染出来,并通过响应事件来修改状态的单向数据流模型。对于状态管理库的选择和应用场景,我们在前后几个项目中经历了多次尝试。最开始我们使用 Redux,尝试按照单向数据流的原教旨主义,通过 Redux 管理应用的全部状态,效果不理想,主要问题有以下几点:


1. 跟后台的异步交互所获得的数据,如果全部通过 Redux Store 管理,写法太繁琐。


2. 同一个页面组件在不同场景(路由)下,访问同一个 Store。数据到底是清空呢,还是不清空呢?这是一个视具体情况而定的问题。


3. 需要多次异步请求才能完成的操作,需要用 Saga 之类的中间件处理,比较麻烦。


后面的项目中我们试图完全不用状态管理库,回到依赖 React 组件的 State 来管理状态,实操下来发现难以为继,特别是有主页面和承接页面的情况下,如果承接页的交互,会反映到主页面的情况下,很难通过纯粹的页面内 State 来实现。


经过摸索,我们最后在架构中采用了 MobX 来作为应用全局状态管理器。同时相对弱化了 Store 的地位,仅仅在一些需要采用 Store 的地方利用 Store。经验看来以下场景中利用 Store 是比较好的设计模式:


1. 管理会话状态,处理用户登录,登出状态时,通过 Action & Store 隔绝视图层和后台服务调用,视图层不需要处理登录后跳转到具体页面,会话超时需要调转到登录页等具体而繁琐的逻辑。只需要通过 Action 来调用封装好的方法即可。


2. 主页面跳转到承接页,承接页进行交互之后,需要主页面 UI 进行更新的场景。比如主页面是一个待录入的产品列表,其中有一项“生产厂商”需要跳转到承接页面中选择,选择完成之后回到主页面,并把选中的厂商名字显示在主界面上。可以在承接页面中通过 Action 修改 Store,主页面中监听 Store 的变更实现。


3. 不希望频繁从服务器获取的数据,比如产品列表数据,错误类型数据字典,也可以存入 Store。


虚拟 Dom 层

以往手机浏览器中复杂页面的性能优化往往要付出巨大的代价。究其原因是因为手机浏览器 DOM 渲染的性能远远落后于 JavaScript 执行引擎的性能。而且不同层次(layer)的 Dom 结构和属性变化,会导致浏览器的重绘 (redraw)和重排(reflow),需要付出高昂的性能代价。这也是为什么基于 Cordova 的混合应用,受其性能影响,不适合做有复杂用户交互,且重视用户体验的应用的深度原因。


而 React 创造性的用虚拟 Dom 解决的这个问题。虚拟 DOM,以及其高效的 Diff 算法。这让我们在大部分情况下直接让页面重绘,而不用担心性能问题,由虚拟 DOM 来确保只对界面上真正变化的部分进行实际的 DOM 操作。


虚拟 Dom 带来的另一个好处是构建了超越平台的 Dom 语言(JSX),使得原来浏览器界用于描述界面结构的 Dom 语言,能够以最小代价适用于其他各种原生应用平台。在这个领域已经涌现出了部分优秀的开源框架。


经过对比,我们选用 react-native-web 作为 react-native 在 Web 上的实现。 react-native-web 是一个通过将 react-native 的组件和 APIs 在 Web 上重新实现,使得 react-native 应用经过少量更改,可以在浏览器上运行的开源项目。官方宣称支持到 react-native 0.55, 但是我们实测下来,兼容 react-native 最新版 (截止项目结束时) 0.57.4 没什么问题。


公共模块层

选择了 react,我们就拥有了大量成熟的开源库,包括 UI 组件和工具类库。但是前端的技术迭代周期是非常快的,今年流行的库,明年说不定就 out 了。


架构设计时必须要考虑前端页面跟具体控件解除耦合。我们的做法是设计出一套标准的控件 IDL(接口描述语言),作为媒介沟通页面跟具体组件实现。比如我们用到了某一个开源的 UI 组件,我们会根据实际业务抽象出一份标准接口,对开源组件进行二次封装之后再调用。这样即使后续需要更换其他组件,也不需要对页面进行改动。


所有的 UI 组件,不论是我们自己造轮子写的,还是开源的,都是按照:1.定义 IDL -> 2.进行封装 -> 3.实现并上传 cnpm 服务器 -> 4.项目 depencency 中引用来自 cnpm 的组件 IDL。 这样的流程来进行引用。


高阶组件层

在函数式编程的中,Hoc(高阶组件)被广泛的用于组件中公共功能的复用,以及函数式编程的方式实现组件的扩展。我觉得讲 Hoc 讲的比较好的一篇文章是:《React Higher Order Components in depth》, 把 Hoc 的几种应用场景都讲的比较透,而且还有 github 代码直接可以拿来用。


这里结合我们项目中用到 Hoc 的场景,稍微展开一下。比如大家都知道 React 不像 Vue 提供了 v-model 的语法糖实现双向数据绑定(MVVM)。如果一定要双向绑定怎么办呢?可以利用 Input-Hoc 实现:


export default function InputHoc(OriginalComponent) { return class ComposedComponent extends Component {  constructor(props){   super(props);   this.state = {    inputs: {    }   }  }  getInput(name, params){   if(!this.state.inputs[name]){    this.state.inputs[name] = {        value: '',       onChange: value => this.state.inputs[name].value = value    }   }
return { value: this.state.inputs[name].value, onChange: this.state.inputs[name].onChange } }
render() { const props = {...this.props, ...{ getInput: this.getInput.bind(this) }}; return ( <OriginalComponent {...props} /> ); } }}
复制代码


需要双向绑定的时候,对页面进行 Hoc 扩展。


@Inputexport default class Login extends Component {   render() {      return (<TextInput {...this.props.getInput('username')} />)   }}
复制代码


统一路由层

在跨平台实现的过程中,最让人感到头疼就是页面路由的实现。因为 iOS,Android,H5 各自有其导航方式和偏好。难点在于设计一套通用的路由规则,适用于三个平台。


首先我们考查了 react-router。react-router 分 react-router-dom 和 react-router-native,支持 react.js 和 react native。实测下来 react-router-native 的页面导航方式看起来跟 SPA 一样,只是页面内容的替换,不支持原生导航的堆栈。这种导航方式的用户体验非常糟糕。比如用在 App 上,通常可以用拖动屏幕的手势返回到前一屏。在这个手势转场动画中,当前页面和上一页面都可以展示出来。如下图:



手势转场动画形象的揭示了在原生应用中,页面是以堆栈的方式存放的。在这个前提下,我们选用了 react-navigation 作为我们原生底层实现。


原生导航的问题解决后,另一个 react-router 的常见问题开始困扰我们,即 H5 上页面保持的问题,比如上图中:客户列表是一个支持下拉分页,加载更多的列表页。


点击一行,可以进入客户详情页。这时从客户详情返回到客户列表页,需要保持客户列表页之前的选中行位置。在原生应用中,由于页面堆栈的存在,这不是一个问题。但是在 H5 中使用 react-router 从客户详情返回到客户列表。客户列表组件是需要重新创建,重新渲染的。


最终我们改用 react-keeper 作为我们 web 层的底层实现,解决了上述问题。并基于 react-keeper 的路由风格,结合原生的特性,打造了跨平台的统一路由组件,有效解决了上述的几个问题。统一路由的设计如下图:



编译系统

为了同时支持 react-native 和 web,项目打包分别采用了 metro-bundler 和 webpack 对代码和资源进行编译。在 web 项目中,为了提升页面加载速度,对生成代码进行了分 chunk,实现按需加载。



webpack 目前提供了一个很好用的插件 webpack-bundle-analyzer,直接在编译的时候做代码分析,并生成分析报告。



可以通过分析 bundle 中打入了哪些模块。对应于一些非必须的模块, 可以用 require.ensure 进行动态加载,或者移到 common bundle 里面去。这样一点点的优化主 bundle 的大小。最后我们成功的控制了首页加载小于 1MB。



其他问题

MobX 实践中遇到的问题

经过各种尝试,最终我们在项目中采用了 MobX 4.3.1 版。 在实际使用过程中,发现几个需要注意的地方:


1) 只有通过 render 函数体,或者其子组件所渲染的内容,才能响应 observer 的变化。如果传递一个 render 回调函数给子组件,其内容不会自动响应 observer 的变化。


比如下面的情况,this.store 是 observable 对象。在 this.store.text 发生变化时,render 方法不会重绘。


const TabBars = ({content}) => { {this.props.content}}@inject(‘store’)@observerclass Page extends Component{render() {     return <TabBars content={<this.content/>} />  }  content = () => {    return <Text>{this.store.text}</Text>  }}
复制代码


  1. 如果采用 mobx-react 修饰页面,最好保证 @observer 是需要渲染 observable 对象的组件外的第一层 Hoc,这样可以有效避免内层 Hoc 不是直接将原始组件在 render 函数,或者其子组件中渲染出来的问题。比如:




React-native 对 Base64 图片的支持问题

react-native 的 Image 组件允许使用 base64 编码的图片作为 uri。 但是实测下来发现在 iOS 平台上有时候 base64 的图片显示不出来。具体场景是使用安全键盘时,从服务器获取乱序的字母数字 Base64 编码后的图片显示在 iOS 平台上,如果直接使用 \<Image source={{uri: base64Image}} />,在多次大小写键盘,数字和字母键盘之间切换后,部分键盘按钮图片会显示不出来:



但是通过 XCode 的截屏工具连手机截图,截取出来的图片又是正确的。经排查发现是 react-native 的 Image 组件绘制的问题,其 github 的 issue list 里面也承认,并推荐升级到0.57版本。但是升级 0.57.4 之后,并没有解决这个问题。


因此我们只能想办法绕过 base64,既然 react-native Image 对普通 url 的图片显示没有问题,只对 base64 编码的支持有问题。那么就需要将 base64 转换成普通图片 url 方式。我们采用的是修改原生层代码,直接在原生层将 base64 转换成二进制图片格式,react-native 的 js 代码通过特定的 url 去访问这个图片。


首先需要拦截 React-Native 的网络请求,这就是原生 AOP 服务所做的事情。以 iOS 为例,我们找到 react-native iOS 源代码的RCTHTTPRequestHandler.mm.有这么一段:


- (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request                         withDelegate:(id<RCTURLRequestDelegate>)delegate{  // Lazy setup  if (!_session && [self isValid]) {    NSOperationQueue *callbackQueue = [NSOperationQueue new];    callbackQueue.maxConcurrentOperationCount = 1;    callbackQueue.underlyingQueue = [[_bridge networking] methodQueue];    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];    [configuration setHTTPShouldSetCookies:YES];

复制代码


可以通过替换掉 defaultSessionConfiguration,来达到对 http 请求进行拦截的目的。当然可以直接修改 react-native 的代码,不过我偏向于利用 Objective-C 的 method swizzling:


@implementation NSURLSessionConfiguration (extend)+(void)load {  static dispatch_once_t onceToken;  dispatch_once(&onceToken, ^{    [self swizzleClassMethod:@selector(defaultSessionConfiguration)  withMethod:@selector(aopDefaultSessionConfiguration)];  });}
+(NSURLSessionConfiguration *) aopDefaultSessionConfiguration{ NSURLSessionConfiguration * instance = [self aopDefaultSessionConfiguration]; Class secureKeyboardURLProtocol = NSClassFromString(@"AOPURLProtocol"); if (secureKeyboardURLProtocol){ instance.protocolClasses = @[AOPURLProtocol]; }return instance;}@end
复制代码


然后我们就可以定义自己的 NSURLProtocol 来对特殊 url 的请求进行拦截了。


@implementation AOPProtocol+ (BOOL)canInitWithRequest:(NSURLRequest *)request {  if (request != nil) {    NSURL* url = [request URL];    if(url.scheme != nil &&  [url.scheme isEqualToString:@"demo"]){      return YES;    }  }  return NO;}- (void)startLoading{  NSURL *url = [self.request URL];  NSString * path = [url.absoluteString stringByReplacingOccurrencesOfString:@"demo://" withString: @""];  NSData * imgData = [SecureImage imageWithPath: path];  NSDictionary * headersDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%ld", [imgData length]], @"Content-Length",@"image/png",@"Content-Type",nil];    NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[self.request URL] statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headersDict];    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];    [self.client URLProtocol: self didLoadData:imgData];    [self.client URLProtocolDidFinishLoading: self];  }}
复制代码


这样,在前端通过请求 demo://开头的,按一定规则索引的 url,就可以返回对应的 png 图片,顺利绕过 base64 图片的问题。


RN 对中文输入的支持问题

在 react-native 0.57 之前,如果像这样写:


<TextInput value={this.state.value} onChange={val => this.setState({value: val})} />  
复制代码


会面临中文输入时无法输入的问题,解决办法是不做 value 绑定,而是通过 ref 来获取值。当然这样 input-hoc 也没法用了。


好在 react-native0.57 之后,Facebook 修复了这个问题。


WebView 相关问题

虽然在绝大部分的常见,React-Native 的性能都要超过 WebView。但是由于 React-Native 上目前还缺乏可以媲美 highbharts, e-charts 的报表组件,所以需要绘制报表的时候,还是需要通过 WebView 内嵌 html 的方式实现。


在使用 WebView 时,遇到的问题有两个:


1.viewport:  页面指定 viewport 为 device-width 的话,会按屏幕宽度来展现页面内容。 如果希望 webview 内容不按整个屏幕宽度显示,则需要计算好 viewport 的宽度,并传入 webview 里面的 html 中。


2.Android :  android 上 webview 不支持 require 方式加载的 html 资源文件。比如<WebView source={require(’…/…/components/charts/charts.html’)} />


在 iOS 上没问题,但是在 Android 上实际加载不了。解决的办法是要么把 html 文件放进 android 的 assets 目录,要么通过网络加载。


如:


<WebView source={Platform.OS === 'android' ? 'file:///android_asset/charts/charts.html' :    require('../../components/charts/charts.html')} />    
复制代码


总结

本文介绍了我们基于 React-Native 构建跨平台的前端应用架构中的一些实践经验,以及期间踩的一些坑。希望通过开放的描述我们的技术实现,抛砖引玉供大家探讨,得到有益的改进意见和建议。


作者简介:

陈子涵,7 年以上前端 &移动架构,跨平台应用架构设计和开发经验。曾在 SAP Labs,远景能源负责移动和云产品相关设计和开发工作。


2018 年 12 月 29 日 14:295911

评论 5 条评论

发布
用户头像
还是离不开框架的堆栈
2019 年 01 月 11 日 11:46
回复
用户头像
干货满满,看后都想按你这样子去搞了。
2019 年 01 月 09 日 09:04
回复
用户头像
能提供一个Demo学习一下吗?
2019 年 01 月 03 日 15:33
回复
没戏的
2019 年 01 月 08 日 16:50
回复
没有更多了
发现更多内容

酷家乐 UI 自动化测试平台实践

CPPAlien

测试框架 selenium BDD UI测试 活文档

开放原子全球开发者峰会「开源治理」论坛预告(更新中)

开放原子开源基金会

简化IT运维工作,就要学会使用自动化运维工具!

行云管家

运维 云服务 IT运维

云资源生命周期管理怎么做?用什么软件好?

行云管家

云计算 云服务 云资源 生命周期管理

代理服务器转发消息时的相关头部(qbit)

qbit

正向代理与反向代理 Proxy

mongoDB

刘帅强

统信软件张磊:国产操作系统如何获得大众市场的认可?

Jessie

开源 最佳实践 新基建 企业动态 文化 & 方法

淘宝网 Java 千亿级并发系统架构设计笔记(全彩版小册开源)

Java 架构 面试 后端 高并发

吊打面试官必备-阿里内部性能优化实战手册

Java架构师迁哥

Alibaba内部713页Java程序性能优化实战手册首次开放!大受好评

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

张宏江出席HICOOL 2021全球创业者峰会,阐述AI研究与创业新机遇

硬科技星球

人工智能 大模型时代 悟道2.0

爬虫初探: 一次爬虫的编写尝试

程序员架构进阶

实战问题 个人思考 9月日更 spider 搜索结果

Tensorflow保存神经网络参数有妙招:Saver和Restore

华为云开发者社区

神经网络 tensorflow 变量 Saver Restore

小游戏如何应对大流量?Shopee Shake 的大促实践

Shopee技术团队

后端 高并发 游戏 电商大促 Shopee

「TEG+系列」破局者 - 腾讯金融级数据库TDSQL

腾讯云数据库

数据库 tdsql

TDSQL是什么:腾讯如何打造一款金融级分布式数据库

腾讯云数据库

数据库 tdsql

TDSQL核心架构

腾讯云数据库

数据库 tdsql

SpringBoot 如何进行对象复制,老鸟们都这么玩的

Java 架构 后端 计算机 框架

华为云与北大BIOPIC联合发布蛋白质多序列比对开源数据集

华为云开发者社区

华为云 AI模型 蛋白质 多序列 开源数据集

快速提升Golang编程能力:那就一起用Go做项目吧

博文视点Broadview

GraphQL 快速入门【2】环境设置

信码由缰

Rest graphql

TDSQL 全时态数据库系统 -- 典型案例

腾讯云数据库

数据库 tdsql

分布式TDSQL的实践

腾讯云数据库

数据库 tdsql

TDSQL数据同步和备份

腾讯云数据库

数据库 tdsql

史诗级放水…字节3-2大牛分享350道Java岗真题,刷完获阿里offer

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

TDSQL inside之路

腾讯云数据库

数据库 tdsql

MYSQL离线迁移至分布式TDSQL

腾讯云数据库

数据库 tdsql

TDSQL核心特性:极具挑战的“四高”服务与安全可运维

腾讯云数据库

数据库 tdsql

TDSQL金融级特性之:数据强一致性保障

腾讯云数据库

数据库 tdsql

马某兵VIP资料惨遭泄露,Java大厂面试手册,21年高频题版

Java架构师迁哥

Java并发学习笔记:ReentrantReadWriteLock(良心之作)

不想秃头

Java 程序员

基于React Native的跨三端应用架构实践-InfoQ