【资料图】
React Native特点跨平台使用js写出页面组件代码被React框架统一转成Virtual DOM树,Virtual DOM树是UI结构的一层抽象,可以被转换成任何支持端的UI视图。ReactNative框架将Virtual DOM 转成原APP的UIView树。热修复ReactNative的产物bundle包,bundle包中包含的为RN业务所需要的所有资源,包括代码和资源。bundle的加载方式是APP启动时从后台下载,然后通过js虚拟机执行的。所以可以将每次业务迭代修改后的代码上传到服务,进行用户无感知版本更新。注意:bundle中的业务代码不能修改APP现有的原生行为,不能调用私有API,不然禁止上架。bundle包中的js是经过babel转义后的普通js,而非jsx语法糖。JS与Native交互的基本原理JS引擎iOS侧使用的JavaScriptCore作为bundle产物的js执行引擎。JS与Native交互的基本原理很简单,就是在JS的全局上下文添加成员变量。原生调用JS是JS在JS上下文中添加方法成员变量,然后原生调用。JS调用原生是原生往JS上下文中添加方法成员变量,然后JS调用。JS调用原生通过将block对象赋值给js全局上下文中的全局变量,js内部调用这个全局方法进行执行。ctx[@"NativeMethod"] = ^(NSString *name) { // do something return someResult}原生调用JS先创建一个JS上下文对象,在上下文中添加方法的全局变量。原生获取上下文的全局变量Value, 然后调用,执行这个JS方法。
// 创建一个ctx的JS上下文JSContent *ctx = [[JSContent alloc] init];// 创建一个变量name[ctx evaluateScript:@"var name = "jack""];// 创建一个方法[ctx evaluateScript:@"var sayHi = function(name) { return "hello " + name }"];// 通过ctx上下文对象,获取到hello方法JSValue *sayHiUnction = ctx[@"sayHi"];// 运行js方法JSValue *greetings = [sayHiUnction callWithArguments:@[@"jack"]; // hello jackReactNative框架中原生与JS的调用基本思路也是这种,只不过考虑到大量的Native对象注册会污染js引擎中的上下文,增加了一层Bridge。原生和JS之间的交互都是通过Bridge这个通道,通过里面的几个基础方法进行交互。原生与JS的交互是异步的。另外,Facebook为了提升RN框架中JS的执行效率,专门推出了一个JS引擎 Hermes, 在关键指标上,相比于JSCore,V8提升了不少,比较易于RN框架集成。
ReactNative核心知识
RCTBridge:ReactNative中原生与JS交互的通道RCTBridge用于给js引擎提供原生扩展接口。将原生功能如定位,3D等通过Bridge将其封装成JS接口,然后注入到js引擎的上下文中。RN框架启动的简单流程为:首先将js代码加载到内存,然后创建RCTBridge实例,然后创建RCTRootContentView内容展示的容器视图,然后调用JS上下文中AppRegistry对象的runApplication方法,并将@[moduleName, appParameters]组件名,参数传递给JS。// RCTRootView.m- (void)javaScriptDidLoad:(NSNotification *)notification{ RCTAssertMainQueue(); RCTBridge *bridge = notification.userInfo[@"bridge"]; if (bridge != _contentView.bridge) { [self bundleFinishedLoading:bridge]; }}- (void)bundleFinishedLoading:(RCTBridge *)bridge{ // 省略创建RCTRootContentView... [self runApplication:bridge]; // 省略添加一个RCTRootContentView...}- (void)runApplication:(RCTBridge *)bridge{ NSString *moduleName = _moduleName ?: @""; // 这里是@"NewProject" NSDictionary *appParameters = @{ @"rootTag": _contentView.reactTag, @"initialProps": _appProperties ?: @{}, }; [bridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[moduleName, appParameters] completion:NULL];}原生调用JS在JS上下文中,调用JS的方式是通过方法:global.batchedBridge.callFunctionReturnFlushedQueue所以RN在原生侧的的JS引擎的封装对象中使用成员变量保存了这个JS的函数指针,原生调用JS时,通过传递参数 moduleid 和 methodid 完成方法的调用。
void JSIExecutor::bindBridge() { std::call_once(bindFlag_, [this] { SystraceSection s("JSIExecutor::bindBridge (once)"); Value batchedBridgeValue = runtime_->global().getProperty(*runtime_, "__fbBatchedBridge"); if (batchedBridgeValue.isUndefined() || !batchedBridgeValue.isObject()) { throw JSINativeException( "Could not get BatchedBridge, make sure your bundle is packaged correctly"); } Object batchedBridge = batchedBridgeValue.asObject(*runtime_); callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction( *runtime_, "callFunctionReturnFlushedQueue"); invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction( *runtime_, "invokeCallbackAndReturnFlushedQueue"); flushedQueue_ = batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue"); });}JS调用原生JS调用原生通常是通过原生主动处理_eventQueue中的事件,特殊情况会直接调用原生注册给JS的nativeFlushQueueImmediate方法, 并传递moduleName 、methodName、callback 参数给这个方法完成调用。
void JSIExecutor::initializeRuntime() { SystraceSection s("JSIExecutor::initializeRuntime"); runtime_->global().setProperty( *runtime_, "nativeModuleProxy", Object::createFromHostObject( *runtime_, std::make_sharedVirtual DOM 虚拟DOM虚拟DOM的特点1.用于描述页面的UI结构:在作用上虚拟DOM和普通的DOM是一样的。2.平台无关性:虚拟DOM表示的UI结构是对UI的一层抽象,它是平台无关的。具体的UI渲染是交个具体的平台渲染引擎进行的,如iOS,安卓自身的渲染引擎。虚拟DOM对标签的定义虚拟DOM把标签分为2类:原子型标签,组合型标签。原子型标签是平台支持型的基础标签,如果RCTView, RCTText。对应浏览器页面中,原子型标签有h1,li,div等。组合型标签是用户自定义的组件,它在虚拟DOM中对应的是自定义标签构造器函数,页面渲染时调用这个构造函数,创建一个实例,然后调用实例的render方法,组合型标签的render方法内会把组合标签进行拆解,最后拆解成基本的原子型标签。(nativeModules_))); runtime_->global().setProperty( *runtime_, "nativeFlushQueueImmediate", Function::createFromHostFunction( *runtime_, PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"), 1, [this]( jsi::Runtime &, const jsi::Value &, const jsi::Value *args, size_t count) { if (count != 1) { throw std::invalid_argument( "nativeFlushQueueImmediate arg count must be 1"); } callNativeModules(args[0], false); return Value::undefined(); })); runtime_->global().setProperty( *runtime_, "nativeCallSyncHook", Function::createFromHostFunction( *runtime_, PropNameID::forAscii(*runtime_, "nativeCallSyncHook"), 1, [this]( jsi::Runtime &, const jsi::Value &, const jsi::Value *args, size_t count) { return nativeCallSyncHook(args, count); })); runtime_->global().setProperty( *runtime_, "globalEvalWithSourceUrl", Function::createFromHostFunction( *runtime_, PropNameID::forAscii(*runtime_, "globalEvalWithSourceUrl"), 1, [this]( jsi::Runtime &, const jsi::Value &, const jsi::Value *args, size_t count) { return globalEvalWithSourceUrl(args, count); })); if (runtimeInstaller_) { runtimeInstaller_(*runtime_); } bool hasLogger(ReactMarker::logTaggedMarker); if (hasLogger) { ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP); }}
var ele = { ... type: type, // 元素的类型 key: key, // 元素key标示 ref: ref, // 元素的引用 props: props, // 元素的参数,包含children ...}// example 1UI渲染RN框架与浏览器的对比:在浏览器中,JS通过调用DOM API创建UI视图。在RN中,JS通过调用RCTUIManager来创建iOS,Android移动端的UI视图。RN的UI渲染是基于虚拟DOM的,通过根据不同的平台调用不同平台的Bridge, Brideg再调用不同平台的的RCTUIManager进行UI的创建。hello// 会被描述为{type: "div", props: { children: ["hello"] }}// example 2// 会被描述为{ type: CustomerComponents}
其他
三条线程RN内部有三条线程在同时运行着:Shadow Thread, JS Thread, UI Thread。JS Thread:JS线程,负责JS与原生的交互,它们的交互是异步的,每次调用都是将block放入队列中,等js代码执行完后,读取事件队列进行处理。UI Thread:UI主线程,负责页面的交互与渲染, 由RCTUIManager使用。Shadow Thread: 负责将flex布局转成Native的布局,由yago引擎使用。三个队列RN框架内,原生与JS的交换类型分两种:UI和事件,这2这种事件的处理都是异步的,它们都是将事件顺序放置到队列中,在合适的时机被调用。事件的处理在RCTBridge中处理,UI的处理在RCTUIManager中处理。JS调用原生异步事件队列:_eventQueue队列原生调用JS异步事件队列:_pendingCalls队列UI更新异步事件处理队列:_pendingUIBlocks队列JSIjavascript interface js虚拟机通用接口层,是针对JS引擎封装的上层API框架,使用JSI做JS引擎调用的优点:1.底部可以任意替换JS引擎而不影响上层JS引擎的使用。如:可以任意替换JavaScript Core, V8等。2.通过JSI,JavaScript可以持有C++宿主对象的引用,所以可以直接调用原生方法(UIView, NativeModule),它与现在统一使用Bridge这个通道和消息异步调用比起来,提高了消息发送的及时性,避免了消息队列执行的等待。React Native核心知识在框架中的使用React Native核心功能在RN项目启动时会进行各自的初始化,生成bundle运行上下文。在类型上可以分为2类:1.JS与原生的事件处理:创建RCTBridge桥接通道。2.UI交互与更新的事件处理:创建RCTRootView容器视图。APP启动,React Native运行环境初始化。- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ if (!self.bridge) { self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions]; } NSDictionary *initProps = [self prepareInitialProps]; UIView *rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps]; if (@available(iOS 13.0, *)) { rootView.backgroundColor = [UIColor systemBackgroundColor]; } else { rootView.backgroundColor = [UIColor whiteColor]; } self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [self createRootViewController]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; return YES;}JS与原生的事件处理:创建RCTBridge桥接通道RCTBridge的主要逻辑是在batchedBridge中,主要初始化流程为:1.初始化Native Modules2.创建Native Modules配置表3.准备JS引擎工厂,创建JS引擎4.将Modules配置信息注册到JS引擎中5.加载boundle代码6.执行boundle代码
- (void)setUp{ RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil); //_performanceLogger日志工具初始化 //_bundleURL获取 //batchedBridge创建 self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self]; [self.batchedBridge start]; RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");}batchedBridge是RCTCXXBridge,它的初始化方法如下:- (instancetype)initWithParentBridge:(RCTBridge *)bridge{ RCTAssertParam(bridge); if ((self = [super initWithDelegate:bridge.delegate bundleURL:bridge.bundleURL moduleProvider:bridge.moduleProvider launchOptions:bridge.launchOptions])) { _parentBridge = bridge; _performanceLogger = [bridge performanceLogger]; registerPerformanceLoggerHooks(_performanceLogger); /** * Set Initial State */ _valid = YES; _loading = YES; _moduleRegistryCreated = NO; _pendingCalls = [NSMutableArray new]; _displayLink = [RCTDisplayLink new]; _moduleDataByName = [NSMutableDictionary new]; _moduleClassesByID = [NSMutableArray new]; _moduleDataByID = [NSMutableArray new]; _objCModuleRegistry = [RCTModuleRegistry new]; [_objCModuleRegistry setBridge:self]; _bundleManager = [RCTBundleManager new]; [_bundleManager setBridge:self]; _viewRegistry_DEPRECATED = [RCTViewRegistry new]; [_viewRegistry_DEPRECATED setBridge:self]; _callableJSModules = [RCTCallableJSModules new]; [_callableJSModules setBridge:self]; [RCTBridge setCurrentBridge:self]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; RCTLogSetBridgeModuleRegistry(_objCModuleRegistry); RCTLogSetBridgeCallableJSModules(_callableJSModules); } return self;}- (void)start{ RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge start]", nil); [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification object:_parentBridge userInfo:@{@"bridge" : self}]; //启动JS线程 _jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil]; _jsThread.name = RCTJSThreadName; _jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;#if RCT_DEBUG _jsThread.stackSize *= 2;#endif [_jsThread start]; dispatch_group_t prepareBridge = dispatch_group_create(); [_performanceLogger markStartForTag:RCTPLNativeModuleInit]; //1.初始化Native Modules [self registerExtraModules]; //2.创建Native Modules配置表 // Initialize all native modules that cannot be loaded lazily (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO]; [self registerExtraLazyModules]; [_performanceLogger markStopForTag:RCTPLNativeModuleInit]; // This doesnt really do anything. The real work happens in initializeBridge. _reactInstance.reset(new Instance); __weak RCTCxxBridge *weakSelf = self; // 3.准备JS引擎工厂,创建JS引擎 std::shared_ptr初始化Native Modules与创建Native Modules配置表把本地的RN模块都收集起来,包括RN框架自带的和用户自定义的,将模块信息保存到Bridge的变量中,用于与JS交换。_moduleDataByName = [NSMutableDictionary new];_moduleClassesByID = [NSMutableArray new];_moduleDataByID = [NSMutableArray new];JS发送消息到Native时,通过- (id)moduleForName:(const char *)moduleName;查询到模块详情,进行模块调用。_objCModuleRegistry = [RCTModuleRegistry new];[_objCModuleRegistry setBridge:self];准备JS引擎工厂,创建JS引擎与将Modules配置信息注册到JS引擎中RN将Native Modules信息收集完成后保存到成员变量中,这个成员变量是一个数组。使用moduleConfig保存模块的模块名,方法名。然后将这些数据注入到JS引擎中。JS调用原生时,通过模块名,方法名,参数调用原生方法。在原生调用JS时,会将调用放入_pendingCalls队列中,进行异步执行。而JS调原生是将调用放入到_eventQueue队列中,进行异步执行。JS可以通过方法nativeFlushQueueImmediate直接调用Native,但是一般JS不会这样做,而是等原生自己去_eventQueue队列中自己去取任务做处理。executorFactory; if (!self.executorClass) { if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) { id cxxDelegate = (id )self.delegate; executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self]; } // 4.将Modules配置信息注册到JS引擎中 if (!executorFactory) { auto installBindings = RCTJSIExecutorRuntimeInstaller(nullptr);#if RCT_USE_HERMES executorFactory = std::make_shared (installBindings);#else executorFactory = std::make_shared (installBindings);#endif } } else { id objcExecutor = [self moduleForClass:self.executorClass]; executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) { if (error) { [weakSelf handleError:error]; } })); } //_turboModuleRegistry是一个TurboModule注册表,TurboModule是JS在RN中的一种优化方式,将常用的JS代码编译成可执行代码,提高执行速度。 /** * id jsExecutorFactory may create and assign an id object to * RCTCxxBridge If id is assigned by this time, eagerly initialize all TurboModules */ if (_turboModuleRegistry && RCTTurboModuleEagerInitEnabled()) { for (NSString *moduleName in [_turboModuleRegistry eagerInitModuleNames]) { [_turboModuleRegistry moduleForName:[moduleName UTF8String]]; } for (NSString *moduleName in [_turboModuleRegistry eagerInitMainQueueModuleNames]) { if (RCTIsMainQueue()) { [_turboModuleRegistry moduleForName:[moduleName UTF8String]]; } else { id turboModuleRegistry = _turboModuleRegistry; dispatch_group_async(prepareBridge, dispatch_get_main_queue(), ^{ [turboModuleRegistry moduleForName:[moduleName UTF8String]]; }); } } } // Dispatch the instance initialization as soon as the initial module metadata has // been collected (see initModules) dispatch_group_enter(prepareBridge); [self ensureOnJavaScriptThread:^{ [weakSelf _initializeBridge:executorFactory]; dispatch_group_leave(prepareBridge); }]; // 5.加载boundle代码 // Load the source asynchronously, then store it for later execution. dispatch_group_enter(prepareBridge); __block NSData *sourceCode; [self loadSource:^(NSError *error, RCTSource *source) { if (error) { [weakSelf handleError:error]; } sourceCode = source.data; dispatch_group_leave(prepareBridge); } onProgress:^(RCTLoadingProgress *progressData) { }]; // 模块和js代码加载完成后,执行js代码 // Wait for both the modules and source code to have finished loading dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ RCTCxxBridge *strongSelf = weakSelf; if (sourceCode && strongSelf.loading) { // 6.执行boundle代码 [strongSelf executeSourceCode:sourceCode sync:NO]; } }); RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");}
// js thread only (which surprisingly can be the main thread, depends on used JS executor)- (void)flushEventsQueue{ [_eventQueueLock lock]; NSDictionary *events = _events; _events = [NSMutableDictionary new]; NSMutableArray *eventQueue = _eventQueue; _eventQueue = [NSMutableArray new]; _eventsDispatchScheduled = NO; [_eventQueueLock unlock]; for (NSNumber *eventId in eventQueue) { [self dispatchEvent:events[eventId]]; }}UI交互与更新的事件处理:创建RCTRootView容器视图RCTRootView为RN页面的入口,在RCTRootView初始化过程中,会创建RCTRootContentView作为内容视图放置在RCTRootView的底部作为根视图。RCTRootContentView的初始化方法中,在uiManager中将RCTRootContentView注册成根视图。
- (instancetype)initWithFrame:(CGRect)frame bridge:(RCTBridge *)bridge reactTag:(NSNumber *)reactTag sizeFlexiblity:(RCTRootViewSizeFlexibility)sizeFlexibility{ if ((self = [super initWithFrame:frame])) { _bridge = bridge; self.reactTag = reactTag; _sizeFlexibility = sizeFlexibility; _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge]; [_touchHandler attachToView:self]; [_bridge.uiManager registerRootView:self]; } return self;}RCTUIManager是RN中UI的管理者,它负责处理所有与UI相关的事情,如:注入到JS中的创建View方法。在createView方法中可以看到,RN对View的操作都是双份的,分别作用在RCTShadowView和UIView上。RCTShadowView和UIView的关系类似于虚拟DOM和DOM的关系。RCTShadowView是一个虚拟DOM树,是一个结构体,用于描述视图的样式和事件,比较轻量级。在RN中当调用setState更新组件状态时,就会生成一个新的虚拟DOM,然后RN将新的虚拟DOM与旧的虚拟DOM进行Diff对比,生成差异对象,然后遍历差异对象,将所有的改动更新到UI上。而更新到了Native是先更新到RCTShadowView上,等合适的时候再统一更新到UI上。UI更新操作也是异步的,更新任务被放置在_pendingUIBlocks队列上,在UI变化时或Bridge批出来结束时刷新这个队列。
RCT_EXPORT_METHOD(createView : (nonnull NSNumber *)reactTag viewName : (NSString *)viewName rootTag : (nonnull NSNumber *)rootTag props : (NSDictionary *)props){ RCTComponentData *componentData = _componentDataByName[viewName]; if (componentData == nil) { RCTLogError(@"No component found for view with name \"%@\"", viewName); } // Register shadow view RCTShadowView *shadowView = [componentData createShadowViewWithTag:reactTag]; if (shadowView) { [componentData setProps:props forShadowView:shadowView]; _shadowViewRegistry[reactTag] = shadowView; RCTShadowView *rootView = _shadowViewRegistry[rootTag]; RCTAssert( [rootView isKindOfClass:[RCTRootShadowView class]] || [rootView isKindOfClass:[RCTSurfaceRootShadowView class]], @"Given `rootTag` (%@) does not correspond to a valid root shadow view instance.", rootTag); shadowView.rootView = (RCTRootShadowView *)rootView; } // Dispatch view creation directly to the main thread instead of adding to // UIBlocks array. This way, it doesnt get deferred until after layout. __block UIView *preliminaryCreatedView = nil; void (^createViewBlock)(void) = ^{ // Do nothing on the second run. if (preliminaryCreatedView) { return; } preliminaryCreatedView = [componentData createViewWithTag:reactTag rootTag:rootTag]; if (preliminaryCreatedView) { self->_viewRegistry[reactTag] = preliminaryCreatedView; } }; // We cannot guarantee that asynchronously scheduled block will be executed // *before* a block is added to the regular mounting process (simply because // mounting process can be managed externally while the main queue is // locked). // So, we positively dispatch it asynchronously and double check inside // the regular mounting block. RCTExecuteOnMainQueue(createViewBlock); [self addUIBlock:^(__unused RCTUIManager *uiManager, __unused NSDictionary参考文章https://juejin.cn/post/6916452544956858382#heading-11https://juejin.cn/post/6844904184542822408https://juejin.cn/post/6844904184500715527*viewRegistry) { createViewBlock(); if (preliminaryCreatedView) { [componentData setProps:props forView:preliminaryCreatedView]; } }]; [self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];}
标签:
ReactNative特点跨平台使用js写出页面组件代码被React框架统一转成Virt
近日, "第三支箭 "再迎来重要进展。随着招商蛇口的定增并购事项...
新亚电子(SH605277,收盘价:元)6月21日发布公告称,2023年7月10日14
克劳斯6月21日收上交所年报信披监管工作函,公司被要求补充披露2022年
6月21日电,三星医疗公告,下属子公司宁波奥克斯智能科技股份有限公司
近日,海南澄迈警方通过大量走访调查和海量数据比对,发挥多警种协同联
原标题:载人潜水器在“泰坦尼克”号残骸探险中失踪船员生还希望渺...
对于普通老百姓而言,养老金是退休老年人重要的经济来源。因此,我国每
1、孝义镇中心小学“营养餐工程” 实施方案 为改善全镇在园幼...
川润股份(002272)6月21日披露投资者关系活动记录表显示,在储能液冷
惠民生、暖民心!青岛市首笔跨机构“带押过户”业务成功落地
女排坏消息,女排迎来失利!蔡斌再受巨大争议这次失利给中国女排上了一
天眼查App显示,6月20日,梅赛德斯一奔驰(上海)数字技术有限公司成立
腾讯控股(00700)发布公告,于2023年6月20日斥资约4 01亿港元回购股份
对于午时三刻是现在的几点这个问题感兴趣的朋友应该很多,这个也是目前
“为做好今年生源地助学贷款帮扶工作,今年高考期间,我们组织人员...
《假面骑士》是很多粉丝非常喜欢的特摄剧作品,总共分为了昭和系,平成
初级经济师《财政税收》每日一练(2023 06 21),由经济师考试频道提供,
国家药监局网站今天发布关于15批次药品不符合规定的通告。经广州市药品
1、《真实魔鬼游戏4》是安里麻里执导的一部生存冒险电影,由荒井敦史、
从盈趣科技获悉,日前,中国电子商会智慧健康养老产业专业委员会第一届
00:466月19日,青岛电影学院颁奖典礼现场,一名获三等奖的女生上台后夺
为进一步提升群众防骗意识,中央宣传部、公安部近日联合启动“全民...
凌云光(688400)06月20日在投资者关系平台上答复了投资者关心的问题。
6月19日,敕勒川乳业开发区新材料装备制造9个项目集中开工仪式举行,让
精研医术待患如亲他做居民健康的守护者
炎炎夏季,西瓜无疑是大家首选的消暑佳品。近日,开封市祥符区朱仙镇木
公积金还商业贷款有两种方式:提取还贷和委托冲贷,具体来看就是:一、
品玩6月20日讯,据界面新闻报道,现代汽车集团在美国佐治亚州建设的电
本文内容是由小编为大家搜集关于常州信用卡,以及的资料,整理后发布的