Узнайте больше о Weex

внешний интерфейс исходный код JavaScript Weex

Weex

предыдущий постОн сказал, что гибридное приложение простое, эта статьяWeexВ качестве примера для анализа гибридного приложения эта статья не является введением.WeexКак он используется, если вы хотите знать, как его использовать, лучше знатьErosРешение, в основном хочу проанализироватьWeexПринцип, понялWeexрабочий механизм.

Почему выбирают Векс

Прежде всего, я хочу поговорить о том, почему мы выбираемWeex. Предыдущая статьяКонечная параWeexа такжеReactNativeБыл проведен краткий анализ в контексте нашего технического выбораRNЭто лучшее решение в любом случае, и можно провести больше сравнений.Сравнение weex и ReactNativeПослушайте, при выборе технологий я все время спрашиваю, почему? В конце концов, относительно хороший выбор был получен, вероятно, из следующих аспектов.

Плюсы и минусы Weex

Прежде всего, вы должны посмотреть на преимущества и недостатки.Преимущества используются, чтобы судить о том, подходит ли ваша сцена для этой технологии.Недостатки заключаются в том, будет ли ваша сцена ограничена, и есть ли способ решить или обойти это.

преимущество:

  • js может писать бизнес, кроссплатформенное, горячее обновление
  • Weex может использовать структуру Vue, близкую к нашему стеку технологий.
  • Weex легче, чем RN, может быть передан субподрядчикам, а один экземпляр на страницу имеет лучшую производительность.
  • Weex решает некоторые проблемы, уже существующие в Rn, и развиваются на RN.
  • У него хорошая расширяемость, и лучше расширять новый компонент и модуль.

недостаток:

  • Документация неполная, информации мало, коммьюнити почти нет, задачи накапливаются, а способ фоновых задач изменен на JIRA, что многие разработчики не понимают
  • Есть много ошибок, нестабильность и много обновлений, похожих на обрывы.
  • Компонента и модуля недостаточно для переопределения функциональности

По сути, резюмируя, это отечественный продукт с поздним стартом, и преимущества не повторятся. В основном это зависит от того, будут ли недостатки ограничивать бизнес-сценарий и есть ли соответствующее решение.

Соответствующей информации относительно мало, поэтому вы можете увидеть исходный код.Если у вас есть исходный код, вы сможете продолжить, вы можете увидеть исходный код, и проблема не решена, в основном, чтобы найти его.WeexПредоставляет множество хороших свойств и методов, которые не описаны в документации.

Проект стартовал относительно поздно.bugИх много, и обновление тоже обрывистое.Мы, наконец, приняли метод интеграции исходного кода и обнаружили, что естьbugПросто восстановите исходный код и дайте официальное упоминаниеPR, наша команда упомянула многоPRОн также был официально принят, в основном потому, что это пустая трата времени на обновление каждой версии.diffЕсли чиновник был зафиксирован, чтобы удалить наш собственный патч. Это действительно пустая трата времени, ноRNЕсли вы хотите расширить себя, вам нужно пройти через эту боль.

который предоставилComponentа такжеModuleЭтого недостаточно для удовлетворения потребностей бизнеса.Конечно, официальная также предоставляет способ расширения соответствующего плагина.Быстрее попробовать расширить несколько плагинов с помощью нативных знаний, и мы решили использовать официальную как можно меньше с самого начала.Module, настолько далеко, насколько возможноModuleВсе продлеваются самим нашим клиентом, с одной стороны, на них не будет распространяться официальнаяModule bugИли влияние, когда он не имеет обратной совместимости, с другой стороны, при расширении собственногоModuleВ то же время мы можем понять его механизм, а также позволить расширенноеModuleВсе в соответствии с нашим бизнесом.

Стоимость доступа и стоимость обучения

Наш основной технологический стек вращается вокругVueОн был установлен, и я сам сделал унифицированные леса, и он был адаптирован для серверной системы, паблик-аккаунта WeChat, апплета, машины самообслуживания и других многоцелевых проектов.APPрешение, если вы можете использоватьVueЧтобы получить доступ на основе фонда, он улучшит всю технологическую цепочку переднего плана, сотрудничает сVueСтоимость переключения между проектами синтаксической инфраструктуры будет очень низкой, эффективность развития будет высокой.

на основеVueСтек технологий позволяет нашим бизнес-студентам быстро адаптироваться, разделять компоненты,widgetплагин,mixinsЭти родственные применения можно использовать напрямую, и единственное, чему осталось научиться, этоWeexизComponentа такжеModuleиспользовать иcssПоддержка, мы также напрямую поддерживаем после подключения лесовsass/less/styule, весь процесс позволяет новым студентам приступить к работе, и требуется полдня, чтобы увидеть, что они могут построить полныйdemoСтраницы, начните скорее. В общем, цена для нас большой плюс.

Опыт разработки и пользовательский опыт

Вышеприведенная картинка - это то, что мы наконец дали, улучшивErosПлан развития представляет собой модель развития, основу которой составляют строительные леса.

Опыт разработки основывается наVueметод, все виды грамматик были сглажены на слое лесов, разработка в основном такая же, как и в предыдущем режиме разработки, способ разработки и отладкиWeexОбеспечивает независимую поддержку модулей.Поняв принцип, мы быстро реализовали функцию сохранения и обновления, плюс себяWeex debugкоторый предоставилdebugстраница,jsЕго также можно отлаживать, и клиент также поддерживает вывод журнала.Общий опыт разработки относительно гладкий, но он не так хорош, какwebРазработка настолько естественна, но мы значительно улучшили опыт разработки, изменив структуру, поддержав функцию горячего обновления для клиента и предоставив некоторые собственные инструменты.

Общее сравнение производительности с точки зрения пользовательского опытаRNС улучшением стояRNНа плече клиента это действительно решило много проблем с производительностью.Для первого белого экрана мы использовали встроенный пакет, и с нашим механизмом горячего обновления мы можем гарантировать, что при открытии клиента должен быть соответствующий контент, нет необходимости загружать дополнительные ресурсы, а также гарантируется время белого экрана. При переключении страниц мы используем многостраничный подход для достиженияWeex, с нашим собственным расширенным механизмом маршрутизации, каждая страница представляет собой отдельныйWeexНапример, поэтому производительность и эффективность рендеринга каждой страницы в отдельности лучше, и мы занимаемся предварительной загрузкой решений, хотя эффект повышения производительности не очевиден, но каждый маленький шаг может уменьшить время переключения между страницами белого экрана.

Мониторинг производительности и аварийное восстановление

WeexМы сами провели много мониторинга производительности. Нам нужно только подключить данные о производительности к нашей системе мониторинга, чтобы отобразить соответствующие данные о производительности. В настоящее время это реализовано с помощью эффекта мониторинга.WeexПриверженность производительности.

Обработка аварийного восстановления используется для обработкиjsBundleВ случае отказа доступа,WeexЕсли у вас есть решение для аварийного восстановления, вам нужно, чтобы разработчик выполнил преобразование для обработки перехода на более раннюю версию.При отображении страницы клиент загрузит соответствующий, если клиент загружаетjs bundleНе удалось включитьwebViewпосетить, показатьHTMLТем не менее, опыт будет очень плохим.Мы используем встроенный механизм пакет+горячее обновление, чтобы гарантировать, что у нас не будет проблем с ошибкой разбора пакета или недоступностью.Если есть проблема с выпущенным пакетом, его можно выпустить срочно , и пользователи получат его немедленно. Чтобы обновить и сообщить пользователю, следует ли обновлять немедленно в соответствии с конфигурацией, если вы хотите сделать лучше, вы можете сохранить стабильную версию пакета в мобильном телефоне пользователя. В результате, пакет относительно большой.Если требуется стабильная обработка аварийного восстановления, это можно рассмотреть.

После завершения программы исследований и простыхdemoТест, мы начинаем приземляться, околоWeexМы также проделали большую работу по строительству окружающей среды, например, преобразовали существующие строительные леса в опорные.Weexразработка, как построить механизм горячего обновления, какая поддержка нужна в нижней части клиента, как сделать расширения для отвязки от исходного кода и т.д.

Вернемся к делу, поговорим оWeexобщая архитектура.

Общая архитектура Weex

Из приведенного выше рисунка видно, чтоWeexОбщий принцип работы, вот общее введение в процесс, а каждый шаг будет подробно описан позже.

Weexпредоставлять разныеframeworkразбор, вы можете использовать.weа также.vueбизнес по написанию файлов, а затем передатьwebpackСкомпилируйте и сгенерируйтеjs bundle, в основном используется в процессе компиляцииweexСвязанныйloader,Erosк упакованномуjs bundleгенерируетсяzippackage, а также генерирует логику для дифференциальных пакетов. Какой бы файл ни был сгенерирован, в итоге он будетjs bundleразвернуть на сервере илиCDNна узле.

При запуске клиента обнаруживается, что введениеWeex sdk, сначала инициализирует среду и некоторый мониторинг, а затем запускает локальныйmain.jsкоторыйjs framework,js frameworkинициализирует некоторую среду, когдаjs frameworkПосле того, как клиент и клиент готовы, начните ждать, пока клиент отобразит страницу.

Когда страница должна быть отображена, клиент инициализируетWeexэкземпляр, то естьWXSDKInstance,WeexЭкземпляр загрузит соответствующийjs bundleфайл, весьjs bundleФайл передается в виде строки вjs framework, а также передать некоторые параметры среды.js frameworkначать сJavaScript Coreвыполнить вjs bundle,Будуjs bundleвыполнить перевод наvirtual DOM, готовый к двойной привязке данных, и в то же времяvDOMВыполните глубокий обход и проанализируйте вvNode, в соответствии с инструкциями рендеринга один за другим черезjs Coreпередано клиенту.

js frameworkпередачаWeex SDKготов к инициализацииcallNative,addElementи другие методы, передайте инструкцию нативной и найдите соответствующую инструкциюWeex ComponentВыполните рендеринг и рисование, показывая по одному для каждого визуализируемого компонента,WeexУзким местом в производительности является процесс передачи компонентов один за другим, вызовmoduleЭто немного сложнее, что будет подробно объяснено позже, и привязка событий также будет подробно объяснена позже. В этот момент отображается страница.

Weex SDK

Выше мы проанализировали грубыйWeexАрхитектура также кратко представляет запущенный процесс.ErosИсходный код, чтобы подробно увидеть, как выполняется каждый шаг,Erosосновывается наWeexВторая инкапсуляция клиента, первая часть работы клиента — инициализацияWeexизsdk.

инициализацияWeex sdkВ основном выполните следующие четыре вещи:

  • Ключевые узлы записывают информацию мониторинга
  • Инициализируйте среду SDK, загрузите и запустите фреймворк js.
  • Регистрация компонентов, модулей, обработчиков
  • Если эмулятор инициализирован в среде разработки, попробуйте подключиться к локальному серверу

ErosсуществуетWeexМногие расширения были сделаны на основеWeexОсновной процесс описан выше,ErosОсновной поток кода выглядит следующим образом.

+ (void)configDefaultData
{
    /* 启动网络变化监控 */
    AFNetworkReachabilityManager *reachability = [AFNetworkReachabilityManager sharedManager];
    [reachability startMonitoring];
    
    /** 初始化Weex */
    [BMConfigManager initWeexSDK];
    
    BMPlatformModel *platformInfo = TK_PlatformInfo();
    
    /** 设置sdimage减小内存占用 */
    [[SDImageCache sharedImageCache] setShouldDecompressImages:NO];
    [[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];
    [[SDImageCache sharedImageCache] setShouldCacheImagesInMemory:NO];
    
    /** 设置统一请求url */
    [[YTKNetworkConfig sharedConfig] setBaseUrl:platformInfo.url.request];
    [[YTKNetworkConfig sharedConfig] setCdnUrl:platformInfo.url.image];
    
    /** 应用最新js资源文件 */
    [[BMResourceManager sharedInstance] compareVersion];
    
    /** 初始化数据库 */
    [[BMDB DB] configDB];
    
    /** 设置 HUD */
    [BMConfigManager configProgressHUD];

    /* 监听截屏事件 */
    // [[BMScreenshotEventManager shareInstance] monitorScreenshotEvent];
}

Инициализировать записи мониторинга

WeexОдним из преимуществ является то, что у него есть собственный мониторинг, и он будет фиксировать простые показатели производительности, такие как инициализацияSDKвремя, запросить успех и неудачу,jsСообщается об ошибках, эта информация будет автоматически записана вWXMonitorсередина.

WeexОшибки делятся на две категории, однаglobal, классinstance. существуетiOSсерединаWXSDKInstanceПеред инициализацией все глобальныеglobalоперации будут размещены наWXMonitorизglobalPerformanceDictсередина. когдаWXSDKInstanceПосле инициализации WXPerformanceTaginstance以下的所有操作都会放在instance.performanceDict`.

globalмониторинг

  • SDKINITTIME: мониторинг инициализации SDK
  • SDKINITINVOKETIME: мониторинг вызовов инициализации SDK
  • JSLIBINITTIME: мониторинг инициализации ресурсов js

instanceмонитор

  • NETWORKTIME: мониторинг сетевых запросов
  • COMMUNICATETIME: Интерактивный мониторинг событий
  • FIRSETSCREENJSFEXECUTETIME: мониторинг загрузки первого экрана js
  • SCREENRENDERTIME: мониторинг времени рендеринга первого экрана
  • TOTALTIME: общее время рендеринга
  • JSTEMPLATESIZE: размер шаблона js

Если вы хотите получить доступ к собственной системе мониторинга, прочтитеWXMonitorСвязанный код, вы можете использовать некоторыеAOPРежим записи ошибок в собственный мониторинг, эта часть кода не является предметом эксплуатации, и заинтересованные студенты могут изучить ее самостоятельно.

Инициализировать среду SDK

Это самая важная работа по инициализации через [BMConfigManager initWeexSDK];ErosТакже в это время вводится расширение. Размещаем наше расширение вregisterBmComponents,registerBmModules,registerBmHandlersЗатем эти три метода вводятся равномерно, избегаяWeexСам код слишком сильно связан.

+ (void)initWeexSDK
{
    [WXSDKEngine initSDKEnvironment];
    
    [BMConfigManager registerBmHandlers];
    [BMConfigManager registerBmComponents];
    [BMConfigManager registerBmModules];
    
#ifdef DEBUG
    [WXDebugTool setDebug:YES];
    [WXLog setLogLevel:WeexLogLevelLog];
    [[BMDebugManager shareInstance] show];
//    [[ATManager shareInstance] show];
    
#else
    [WXDebugTool setDebug:NO];
    [WXLog setLogLevel:WeexLogLevelError];
#endif
}

Ниже приведены некоторые из наших расширений. Подробные сведения о расширениях можно найти в нашем исходном коде. Чтобы отделиться от официального расширения интеграции с исходным кодом, мы установили время внедрения наWeex initSDKEnvironmentПозже.

// 扩展 Component
+ (void)registerBmComponents
{
    
    NSDictionary *components = @{
        @"bmmask":          NSStringFromClass([BMMaskComponent class]),
        @"bmpop":           NSStringFromClass([BMPopupComponent class])
        ...
    };
    for (NSString *componentName in components) {
        [WXSDKEngine registerComponent:componentName withClass:NSClassFromString([components valueForKey:componentName])];
    }
}

// 扩展 Moudles
+ (void)registerBmModules
{
    NSDictionary *modules = @{
        @"bmRouter" :         NSStringFromClass([BMRouterModule class]),
        @"bmAxios":           NSStringFromClass([BMAxiosNetworkModule class])
        ...
    };
    
    for (NSString *moduleName in modules.allKeys) {
        [WXSDKEngine registerModule:moduleName withClass:NSClassFromString([modules valueForKey:moduleName])];
    }
}

// 扩展 Handlers
+ (void)registerBmHandlers
{
    [WXSDKEngine registerHandler:[WXImgLoaderDefaultImpl new] withProtocol:@protocol(WXImgLoaderProtocol)];
    [WXSDKEngine registerHandler:[WXBMNetworkDefaultlpml new] withProtocol:@protocol(WXResourceRequestHandler)];
    ...
}

инициализацияSDKзаключается в выполненииWXSDKEngineСодержимое этого файла наиболее важно для регистрации текущегоComponents,Modules,handlers.

+ (void)registerDefaults
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self _registerDefaultComponents];
        [self _registerDefaultModules];
        [self _registerDefaultHandlers];
    });
}

Регистрация компонентов

Студенты Xiaobai могут быть более озадачены, почемуWeexподдерживает только некоторые определенные теги, а неHTMLВсе теги в файле поддерживаются. Прежде всего, разбор тегов должен иметь соответствующие отношения с собственными, и теги этих соответствующих отношений могут быть поддерживаться. Откуда эта корреспонденция? Во-первых, Weex будет инициализировать некоторыеComponents, первым сказатьWeex SDKКакие теги я поддерживаю, в том числеWeexнекоторые из меток предоставлены, и мы проходимWeex ComponentМетод расширения расширения вне метки.

Давайте посмотримComponentsКак зарегистрироваться, находится в вышеуказанном методе_registerDefaultComponents, ниже приведена часть кода для этих методов

// WXSDKEngine.m
+ (void)_registerDefaultComponents
{
    [self registerComponent:@"container" withClass:NSClassFromString(@"WXDivComponent") withProperties:nil];
    [self registerComponent:@"cell-slot" withClass:NSClassFromString(@"WXCellSlotComponent") withProperties: @{@"append":@"tree", @"isTemplate":@YES}];
    ...
}

Между двумя описанными выше методами есть некоторые различия.withPropertiesПараметры разные, если он с@{@"append":@"tree"}, сначала визуализировать дочерние узлы;isTemplateЯвляетсяbooleanзначение, еслиtrue, ему будут переданы все подшаблоны под этим тегом. Роль этих двух параметров будет подробно проанализирована позже.

в инициализацииWeexSDKкогда,Weexпозвоню_registerDefaultComponentsметод будетWeexОфициально расширенные компоненты зарегистрированы, продолжайте смотреть наregisterComponent:withClass:withProperties:метод

+ (void)registerComponent:(NSString *)name withClass:(Class)clazz withProperties:(NSDictionary *)properties
{
    if (!name || !clazz) {
        return;
    }

    WXAssert(name && clazz, @"Fail to register the component, please check if the parameters are correct !");
    // 注册组件的方法
    [WXComponentFactory registerComponent:name withClass:clazz withPros:properties];
    
    // 遍历出组件的异步方法
    NSMutableDictionary *dict = [WXComponentFactory componentMethodMapsWithName:name];
    dict[@"type"] = name;
    
    // 将组件放到 bridge 中,准备注册到 js framework 中。
    if (properties) {
        NSMutableDictionary *props = [properties mutableCopy];
        if ([dict[@"methods"] count]) {
            [props addEntriesFromDictionary:dict];
        }
        [[WXSDKManager bridgeMgr] registerComponents:@[props]];
    } else {
        [[WXSDKManager bridgeMgr] registerComponents:@[dict]];
    }
}

Сначала посмотрите на параметры,nameзарегистрироваться вjsfmсерединаComponentимя (т.е. имя тега),clazzдляComponentсоответствующий класс,propertiesдля некоторых расширенных атрибутов;

Вызывается снова в этом методеWXComponentFactoryМетодыregisterComponent:name withClass:clazz withPros:propertiesзарегистрироватьсяComponent,WXComponentFactoryэто синглтон, отвечающий за синтаксический анализComponentметод и сохраняет все зарегистрированныеComponentсоответствующий метод; продолжайтеWXComponentFactoryпосмотриregisterComponent:name withClass:clazz withPros:propertiesРеализация метода:

// 类
- (void)registerComponent:(NSString *)name withClass:(Class)clazz withPros:(NSDictionary *)pros
{
    WXAssert(name && clazz, @"name or clazz must not be nil for registering component.");
    
    WXComponentConfig *config = nil;
    [_configLock lock];
    config = [_componentConfigs objectForKey:name];
    
    if(config){
        WXLogInfo(@"Overrider component name:%@ class:%@, to name:%@ class:%@",
                  config.name, config.class, name, clazz);
    }
    
    // 实例 WXComponentConfig 并保存到 _componentConfigs 中
    config = [[WXComponentConfig alloc] initWithName:name class:NSStringFromClass(clazz) pros:pros];
    [_componentConfigs setValue:config forKey:name];
    [config registerMethods];
    
    [_configLock unlock];
}

Этот метод создаст экземплярWXComponentConfigобъектconfig, каждыйComponentбудет иметь к этому отношениеWXComponentConfigнапример, тогдаconfigпример какvalue,keyдляComponentизnameСохранить_componentConfigsсередина(_componentConfigsэто словарь),configсохранено вComponentВсе методы выставлены на js, продолжаем смотретьWXComponentConfigизregisterMethodsметод:

- (void)registerMethods
{
	 // 获取类 
    Class currentClass = NSClassFromString(_clazz);
    
    if (!currentClass) {
        WXLogWarning(@"The module class [%@] doesn't exit!", _clazz);
        return;
    }
    
    while (currentClass != [NSObject class]) {
        unsigned int methodCount = 0;
        // 获取方法列表
        Method *methodList = class_copyMethodList(object_getClass(currentClass), &methodCount);
        // 遍历方法列表
        for (unsigned int i = 0; i < methodCount; i++) {
        	  // 获取方法名称
            NSString *selStr = [NSString stringWithCString:sel_getName(method_getName(methodList[i])) encoding:NSUTF8StringEncoding];
            BOOL isSyncMethod = NO;
            // 同步方法
            if ([selStr hasPrefix:@"wx_export_method_sync_"]) {
                isSyncMethod = YES;
            // 异步方法
            } else if ([selStr hasPrefix:@"wx_export_method_"]) {
                isSyncMethod = NO;
            // 其他未暴露方法
            } else {
                continue;
            }
            
            NSString *name = nil, *method = nil;
            SEL selector = NSSelectorFromString(selStr);
            // 获取方法实现
            if ([currentClass respondsToSelector:selector]) {
                method = ((NSString* (*)(id, SEL))[currentClass methodForSelector:selector])(currentClass, selector);
            }
            
            if (method.length <= 0) {
                WXLogWarning(@"The module class [%@] doesn't has any method!", _clazz);
                continue;
            }
            
            NSRange range = [method rangeOfString:@":"];
            if (range.location != NSNotFound) {
                name = [method substringToIndex:range.location];
            } else {
                name = method;
            }
            
            // 将方法保持到对应的字典中
            NSMutableDictionary *methods = isSyncMethod ? _syncMethods : _asyncMethods;
            [methods setObject:method forKey:name];
        }
        
        free(methodList);
        currentClass = class_getSuperclass(currentClass);
    }
    
}

WXComponentConfigДва словарь_asyncMethodsа также_syncMethodsсоответственно сохранить асинхронный метод и синхронный метод;registerMethodsметод заключается в обходеComponentкласс подвергается воздействиюjsfmметод; затем вернемся кWXSDKEngineизregisterComponent:withClass:withProperties:метод.

+ (void)registerComponent:(NSString *)name withClass:(Class)clazz withProperties:(NSDictionary *)properties
{
    if (!name || !clazz) {
        return;
    }

    WXAssert(name && clazz, @"Fail to register the component, please check if the parameters are correct !");
    
    [WXComponentFactory registerComponent:name withClass:clazz withPros:properties];
    // ↑ 到这里 Component 的方法已经解析完毕,并保持到了 WXComponentFactory 中
    
    // 获取 Component 的异步方法
    NSMutableDictionary *dict = [WXComponentFactory componentMethodMapsWithName:name];
    dict[@"type"] = name;
    // 最后将 Component 注册到 jsfm 中
    if (properties) {
        NSMutableDictionary *props = [properties mutableCopy];
        if ([dict[@"methods"] count]) {
            [props addEntriesFromDictionary:dict];
        }
        [[WXSDKManager bridgeMgr] registerComponents:@[props]];
    } else {
        [[WXSDKManager bridgeMgr] registerComponents:@[dict]];
    }
}

ComponentПосле разбора он вызоветWXSDKManagerсерединаbridgeMgrизregisterComponents:метод;WXSDKManagerдержи одинWXBridgeManager,этоWXBridgeManagerДругое свойствоWXBridgeContext,WXBridgeContextдержи другойjs Bridgeссылка, это то, что мы часто говоримBridge. Ниже приведен соответствующий основной код иbridgeОтношение между. (СейчасWXDebugLoggerBridgeбольше не существует)

// WXSDKManager
@interface WXSDKManager ()
@property (nonatomic, strong) WXBridgeManager *bridgeMgr;
@property (nonatomic, strong) WXThreadSafeMutableDictionary *instanceDict;
@end

// WXBridgeManager
@interface WXBridgeManager ()
@property (nonatomic, strong) WXBridgeContext   *bridgeCtx;
@property (nonatomic, assign) BOOL  stopRunning;
@property (nonatomic, strong) NSMutableArray *instanceIdStack;
@end

// WXBridgeContext
@interface WXBridgeContext ()

@property (nonatomic, strong) id<WXBridgeProtocol>  jsBridge;
@property (nonatomic, strong) id<WXBridgeProtocol> devToolSocketBridge;
@property (nonatomic, assign) BOOL  debugJS;
//store the methods which will be executed from native to js
@property (nonatomic, strong) NSMutableDictionary   *sendQueue;
//the instance stack
@property (nonatomic, strong) WXThreadSafeMutableArray    *insStack;
//identify if the JSFramework has been loaded
@property (nonatomic) BOOL frameworkLoadFinished;
//store some methods temporarily before JSFramework is loaded
@property (nonatomic, strong) NSMutableArray *methodQueue;
// store service
@property (nonatomic, strong) NSMutableArray *jsServiceQueue;

@end

Атрибуты трех классов кратко представлены выше.Из атрибутов мы можем увидеть общие функции.Вызывающая связь между ними также относительно ясна.При вызовеWXBridgeManagerпередачаregisterComponentsметод, а затем вызватьWXBridgeContextизregisterComponentsспособ регистрации компонента.

// WXBridgeManager
- (void)registerComponents:(NSArray *)components
{
    if (!components) return;
    
    __weak typeof(self) weakSelf = self;
    WXPerformBlockOnBridgeThread(^(){
        [weakSelf.bridgeCtx registerComponents:components];
    });
}

// WXBridgeContext
- (void)registerComponents:(NSArray *)components
{
    WXAssertBridgeThread();
    
    if(!components) return;
    
    [self callJSMethod:@"registerComponents" args:@[components]];
}

WXPerformBlockOnBridgeThreadЭтот поток являетсяjsThread, это глобально уникальный поток, но в настоящее время, если вы напрямую вызываетеcallJSMethod, обязательно потерпит неудачу, потому что на этот разjs frameworkОн может быть еще не закончен.

если в это времяjs frameworkЕсли выполнение не было завершено, регистрируемые методы будут помещены в_methodQueueкешировать это,js frameworkПосле завершения загрузки он снова пройдет по этому пути._methodQueue, который выполняет все кешированные методы.

- (void)callJSMethod:(NSString *)method args:(NSArray *)args
{
   // 如果 js frameworkLoadFinished 就立即注入 Component
   if (self.frameworkLoadFinished) {
       [self.jsBridge callJSMethod:method args:args];
   } else {
   // 如果没有执行完,就将方法放到 _methodQueue 队列中
       [_methodQueue addObject:@{@"method":method, @"args":args}];
   }
}

- (void)callJSMethod:(NSString *)method args:(NSArray *)args onContext:(JSContext*)context completion:(void (^)(JSValue * value))complection
{
   NSMutableArray *newArg = nil;
   if (!context) {
       if ([self.jsBridge isKindOfClass:[WXJSCoreBridge class]]) {
          context = [(NSObject*)_jsBridge valueForKey:@"jsContext"];
       }
   }
   if (self.frameworkLoadFinished) {
       newArg = [args mutableCopy];
       if ([newArg containsObject:complection]) {
           [newArg removeObject:complection];
       }
       WXLogDebug(@"Calling JS... method:%@, args:%@", method, args);
       JSValue *value = [[context globalObject] invokeMethod:method withArguments:args];
       if (complection) {
           complection(value);
       }
   } else {
       newArg = [args mutableCopy];
       if (complection) {
           [newArg addObject:complection];
       }
       [_methodQueue addObject:@{@"method":method, @"args":[newArg copy]}];
   }
}

// 当 js framework 执行完毕之后会回来调用 WXJSCoreBridge 这个方法
- (JSValue *)callJSMethod:(NSString *)method args:(NSArray *)args
{
   WXLogDebug(@"Calling JS... method:%@, args:%@", method, args);
   return [[_jsContext globalObject] invokeMethod:method withArguments:args];
}

Далее стоит позвонитьjs frameworkизregisterComponentsЗарегистрируйте все соответствующиеComponents, эта часть контента будет подробно разобрана ниже, и будет выполняться в порядке выполненияModulesРегистрация.

Регистрация модулей

вход все ещеWXSDKEngine,передача_registerDefaultModules, читать всеModulesзарегистрироваться, зарегистрироваться, позвонитьregisterModuleметод, то же зарегистрирует модуль, получитWXModuleFactory, затем аналогичным образом перебирает все синхронные и асинхронные методы и, наконец, вызываетWXBridgeManager, зарегистрируйте модуль вWXBridgeManagerсередина.

+ (void)_registerDefaultModules
{
    [self registerModule:@"dom" withClass:NSClassFromString(@"WXDomModule")];
    [self registerModule:@"locale" withClass:NSClassFromString(@"WXLocaleModule")];
    ...
}

+ (void)registerModule:(NSString *)name withClass:(Class)clazz
{
    WXAssert(name && clazz, @"Fail to register the module, please check if the parameters are correct !");
    if (!clazz || !name) {
        return;
    }
    NSString *moduleName = [WXModuleFactory registerModule:name withClass:clazz];
    NSDictionary *dict = [WXModuleFactory moduleMethodMapsWithName:moduleName];
    
    [[WXSDKManager bridgeMgr] registerModules:dict];
}

Модуль регистрации также черезWXModuleFactory, поставить всеmoduleпройти через_registerModuleгенерироватьModuleMap. Регистрация модулей не позволяет использовать модули с одинаковыми именами. Будуnameдляkey,valueдляWXModuleConfigдепозит_moduleMapСловарь,WXModuleConfigспасModuleДля связанных атрибутов, если они имеют одинаковое имя, те, которые были зарегистрированы позже, перезапишут те, которые были зарегистрированы первыми.

@interface WXModuleFactory ()

@property (nonatomic, strong)  NSMutableDictionary  *moduleMap;
@property (nonatomic, strong)  NSLock   *moduleLock;

@end

- (NSString *)_registerModule:(NSString *)name withClass:(Class)clazz
{
    WXAssert(name && clazz, @"Fail to register the module, please check if the parameters are correct !");
    
    [_moduleLock lock];
    //allow to register module with the same name;
    WXModuleConfig *config = [[WXModuleConfig alloc] init];
    config.name = name;
    config.clazz = NSStringFromClass(clazz);
    [config registerMethods];
    [_moduleMap setValue:config forKey:name];
    [_moduleLock unlock];
    
    return name;
}

когда всеModuleПосле создания экземпляра пройдите все методы, включая синхронные и асинхронные методы, можно увидеть следующие методы, перед прохождением методов уже есть некоторые методы в_defaultModuleMethodВ объекте здесь как минимум два методаaddEventListenerа такжеremoveAllEventListeners, Там, где он возвращается из метода, предусмотрены два вышеуказанных метода.

- (NSMutableDictionary *)_moduleMethodMapsWithName:(NSString *)name
{
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    NSMutableArray *methods = [self _defaultModuleMethod];
    
    [_moduleLock lock];
    [dict setValue:methods forKey:name];
    
    WXModuleConfig *config = _moduleMap[name];
    void (^mBlock)(id, id, BOOL *) = ^(id mKey, id mObj, BOOL * mStop) {
        [methods addObject:mKey];
    };
    [config.syncMethods enumerateKeysAndObjectsUsingBlock:mBlock];
    [config.asyncMethods enumerateKeysAndObjectsUsingBlock:mBlock];
    [_moduleLock unlock];
    
    return dict;
}

- (NSMutableArray*)_defaultModuleMethod
{
    return [NSMutableArray arrayWithObjects:@"addEventListener",@"removeAllEventListeners", nil];
}

Далее стоит позвонитьjs frameworkинъекционный метод иregisterComponentПочти, это также будет связано с проблемой потоков, и это также пройдет вышеописанное.WXSDKManager -> WXBridgeManager -> WXBridgeContext. Наконец, вызовите следующий метод. последний звонокregisterModulesвсе клиентыModuleвводить вjs frameworkсередина,js frameworkТакже будет некоторая упаковка, которая будет использоваться в бизнесе.weex.registerModuleдля вызова соответствующего метода.

- (void)registerModules:(NSDictionary *)modules
{
    WXAssertBridgeThread();
    
    if(!modules) return;
    
    [self callJSMethod:@"registerModules" args:@[modules]];
}

инъекция обработчика

Componentа такжеModuleВсем понятнее использовать его часто, ноhandlerЧто тогда?WeexНекоторые методы протокола указаны, и методы в протоколе будут вызываться в определенное время.Вы можете реализовать класс для следования этим протоколам, реализовать методы в протоколе, а затем передатьhandlerЗарегистрированный путь кweex, тогда, когда вам нужно будет вызвать эти методы протокола, он будет вызываться в реализуемом вами классе. НапримерWXResourceRequestHandler:

@protocol WXResourceRequestHandler <NSObject>

// Send a resource request with a delegate
- (void)sendRequest:(WXResourceRequest *)request withDelegate:(id<WXResourceRequestDelegate>)delegate;

@optional

// Cancel the ongoing request
- (void)cancelRequest:(WXResourceRequest *)request;

@end

WXResourceRequestHandlerУказаны два метода, один — метод запроса для загрузки ресурсов, а другой — метод, который нужно запросить, а затем посмотретьWXResourceRequestHandlerDefaultImplДобрый:

//
//	WXResourceRequestHandlerDefaultImpl.m
//

#pragma mark - WXResourceRequestHandler

- (void)sendRequest:(WXResourceRequest *)request withDelegate:(id<WXResourceRequestDelegate>)delegate
{
    if (!_session) {
        NSURLSessionConfiguration *urlSessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
        if ([WXAppConfiguration customizeProtocolClasses].count > 0) {
            NSArray *defaultProtocols = urlSessionConfig.protocolClasses;
            urlSessionConfig.protocolClasses = [[WXAppConfiguration customizeProtocolClasses] arrayByAddingObjectsFromArray:defaultProtocols];
        }
        _session = [NSURLSession sessionWithConfiguration:urlSessionConfig
                                                 delegate:self
                                            delegateQueue:[NSOperationQueue mainQueue]];
        _delegates = [WXThreadSafeMutableDictionary new];
    }
    
    NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
    request.taskIdentifier = task;
    [_delegates setObject:delegate forKey:task];
    [task resume];
}

- (void)cancelRequest:(WXResourceRequest *)request
{
    if ([request.taskIdentifier isKindOfClass:[NSURLSessionTask class]]) {
        NSURLSessionTask *task = (NSURLSessionTask *)request.taskIdentifier;
        [task cancel];
        [_delegates removeObjectForKey:task];
    }
}

WXResourceRequestHandlerDefaultImplпоследовалWXResourceRequestHandlerпротокол и реализуйте метод протокола, затем зарегистрируйтесьHandler, если запрос ресурса отправлен, он будет отправлен наWXResourceRequestHandlerDefaultImplв реализации.

Инициализация клиентаSDKМетоды, связанные с регистрацией, завершены.Выше было упомянуто, что последняя регистрация заключается в регистрации наjsсреду, передайте методjs frameworkпозвонить, ноjs frameworkОн еще не вызывался, и далее следует загрузить этот файл.

Загрузить и запускать JS Framework

в официальномGitHubсерединаruntimeЕсть кучиjs, эта кучаjsв конечном итоге будет упакован вnative-bundle-main.jsфайл, мы будем называть егоmain.js, этот абзацjsэто то, что мы всегда говоримjs framework,существуетSDKВо время инициализации весь код передается в виде строки вWXSDKManagerи положиJavaScript Coreвыполнить. Давайте сначала посмотрим на этоruntimeПод какими файлами

    |-- api:冻结原型链,提供给原生调用的方法,比如 registerModules
    |-- bridge:和客户端相关的接口调用,调用客户端的时候有一个任务调度
    |-- entries:客户端执行 js  framework 的入口文件,WXSDKEngine 调用的方法
    |-- frameworks:核心文件,初始化 js bundle 实例,对实例进行管理,dom 调度转换等
    |-- services:js  service 存放,broadcast 调度转换等
    |-- shared:polyfill  和 console 这些差异性的方法
    |-- vdom:将 VDOM  转化成客户端能渲染的指令

Выглядит и мы упоминали в предыдущей статьеjs bridgeаналогичен по функциям, но почемуWeexУ этого слоя так много функций, в первую очередьWeexОн должен быть совместим с тремя терминалами, поэтомуiOS,android,webРазличия должны быть сглажены, и у них могут быть разные способы и способы принятия инструкций.Например, дизайн клиентаcreateBodyа такжеaddElement,а такжеwebдаcreateElement,appendChildЖдать.

В дополнение к различиям в инструкциях существуют различия в бизнес-языках верхнего уровня, таких какWeexслужба поддержкиVueа такжеRax, и может даже поддерживатьReact, лишь бы он соответствовалjs frameworkРеализация может отображаться в разных хост-средах через разные интерфейсы. Мы можем назвать этот слойDSL, тоже смотримjs frameworkОсновной код этого слоя

    |-- index.js:入口文件
    |-- legacy:关于 VM 相关的主要方法
    |   |-- api:相关 vm 定义的接口
    |   |-- app:管理页面间页面实例的方法
    |   |-- core:实现数据监听的方法
    |   |-- static:静态方法
    |   |-- util:工具类函数
    |   |-- vm:解析指令相关
    |-- vanilla:与客户端交互的一些方法

запустить структуру

После первой регистрации трех модулей, упомянутых выше,WXSDKEngineПродолжать вниз, или звонить вWXBridgeManagerсерединаexecuteJsFramework, затем позвоните вWXBridgeContextизexecuteJsFramework, а затем выполнить в дочернем потокеjs framework.

// WXSDKEngine
[[WXSDKManager bridgeMgr] executeJsFramework:script];

// WXBridgeManager
- (void)executeJsFramework:(NSString *)script
{
    if (!script) return;
    
    __weak typeof(self) weakSelf = self;
    WXPerformBlockOnBridgeThread(^(){
        [weakSelf.bridgeCtx executeJsFramework:script];
    });
}

// WXBridgeContext
- (void)executeJsFramework:(NSString *)script
{
    WXAssertBridgeThread();
    WXAssertParam(script);
    
    WX_MONITOR_PERF_START(WXPTFrameworkExecute);
    // 真正的执行 js framework
    [self.jsBridge executeJSFramework:script];
    
    WX_MONITOR_PERF_END(WXPTFrameworkExecute);
    
    if ([self.jsBridge exception]) {
        NSString *exception = [[self.jsBridge exception] toString];
        NSMutableString *errMsg = [NSMutableString stringWithFormat:@"[WX_KEY_EXCEPTION_SDK_INIT_JSFM_INIT_FAILED] %@",exception];
        [WXExceptionUtils commitCriticalExceptionRT:@"WX_KEY_EXCEPTION_SDK_INIT" errCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_SDK_INIT] function:@"" exception:errMsg extParams:nil];
        WX_MONITOR_FAIL(WXMTJSFramework, WX_ERR_JSFRAMEWORK_EXECUTE, errMsg);
    } else {
        WX_MONITOR_SUCCESS(WXMTJSFramework);
        //the JSFramework has been load successfully.
        // 执行完 js
        self.frameworkLoadFinished = YES;
        
        // 执行缓存在 _jsServiceQueue 中的方法
        [self executeAllJsService];
        
        // 获取 js framework 版本号
        JSValue *frameworkVersion = [self.jsBridge callJSMethod:@"getJSFMVersion" args:nil];
        if (frameworkVersion && [frameworkVersion isString]) {
            [WXAppConfiguration setJSFrameworkVersion:[frameworkVersion toString]];
        }
        
        // 计算 js framework 的字节大小
        if (script) {
             [WXAppConfiguration setJSFrameworkLibSize:[script lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
        }
        
        //execute methods which has been stored in methodQueue temporarily.
        // 开始执行之前缓存在队列缓存在 _methodQueue 的方法
        for (NSDictionary *method in _methodQueue) {
            [self callJSMethod:method[@"method"] args:method[@"args"]];
        }
        
        [_methodQueue removeAllObjects];
        
        WX_MONITOR_PERF_END(WXPTInitalize);
    };
}

Суть вышеприведенного процесса выполнения заключается в том, как выполнитьjs frameworkДа, действительно загружаетсяnative-bundle-main.jsФайл не должен иметь возвращаемое значение после выполнения или содержать паруjs frameworkСсылка просто помещается в память, готовая к вызову в любое время. Также будет ведение журнала до и после выполнения

// WXBridgeContext
- (void)executeJSFramework:(NSString *)frameworkScript
{
    WXAssertParam(frameworkScript);
    if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
        [_jsContext evaluateScript:frameworkScript withSourceURL:[NSURL URLWithString:@"native-bundle-main.js"]];
    }else{
        [_jsContext evaluateScript:frameworkScript];
    }
}

Давай уйдемjs frameworkДля выполнения самого себя сначала посмотрите какую работу выполнит клиент после завершения выполнения.Перед началом загрузки она будет закэширована в_jsServiceQueueа также_methodQueueметод в .

// WXBridgeContext
- (void)executeAllJsService
{
    for(NSDictionary *service in _jsServiceQueue) {
        NSString *script = [service valueForKey:@"script"];
        NSString *name = [service valueForKey:@"name"];
        [self executeJsService:script withName:name];
    }
    
    [_jsServiceQueue removeAllObjects];
}

for (NSDictionary *method in _methodQueue) {
    [self callJSMethod:method[@"method"] args:method[@"args"]];
}

[_methodQueue removeAllObjects];

_methodQueueНетрудно понять, какие из предыдущих нативных методов регистрации кэшируются в_methodQueueсередина,_jsServiceQueueОткуда это?js serviceЭто будет подробно объяснено ниже,broadcastChannelто естьWeexодин предоставленныйjs service,Официальный вариант использованиятакже предоставляет расширенияjs serviceкстати видно чтоjs serviceбудет загружен только один раз,js serviceЭто просто набор строк, поэтому просто выполните его напрямую.

// WXSDKEngine
NSDictionary *jsSerices = [WXDebugTool jsServiceCache];
for(NSString *serviceName in jsSerices) {
    NSDictionary *service = [jsSerices objectForKey:serviceName];
    NSString *serviceName = [service objectForKey:@"name"];
    NSString *serviceScript = [service objectForKey:@"script"];
    NSDictionary *serviceOptions = [service objectForKey:@"options"];
    [WXSDKEngine registerService:serviceName withScript:serviceScript withOptions:serviceOptions];
}

// WXBridgeContext
- (void)executeJsService:(NSString *)script withName:(NSString *)name
{
    if(self.frameworkLoadFinished) {
        WXAssert(script, @"param script required!");
        [self.jsBridge executeJavascript:script];
        
        if ([self.jsBridge exception]) {
            NSString *exception = [[self.jsBridge exception] toString];
            NSMutableString *errMsg = [NSMutableString stringWithFormat:@"[WX_KEY_EXCEPTION_INVOKE_JSSERVICE_EXECUTE] %@",exception];
            [WXExceptionUtils commitCriticalExceptionRT:@"WX_KEY_EXCEPTION_INVOKE" errCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_INVOKE] function:@"" exception:errMsg extParams:nil];
            WX_MONITOR_FAIL(WXMTJSService, WX_ERR_JSFRAMEWORK_EXECUTE, errMsg);
        } else {
            // success
        }
    }else {
        [_jsServiceQueue addObject:@{
                                     @"name": name,
                                     @"script": script
                                     }];
    }
}

_methodQueueВыполнение очереди заключается в вызовеcallJSMethod, позвоню внизWXJSCoreBridgeизinvokeMethod, это вызов соответствующегоjs frameworkпредложенный метод, находяWXJSCoreBridgeфайл, вотWeexизbridge,_jsContextэто все клиенты иjs frameworkВсе способы реального взаимодействия, эти способы предоставляютсяjs frameworkДля вызова основные методы будут подробно описаны позже.

процесс выполнения фреймворка js

js frameworkИсполняемый входной файл/runtime/entries/index.js, который вызовет/runtime/entries/setup.js,здесьjsДетализация модульности очень хорошая, мы не будем показывать код по одному, вы можете перейти кWeexПосмотрите исходный код проекта.

/**
 * Setup frameworks with runtime.
 * You can package more frameworks by
 *  passing them as arguments.
 */
export default function (frameworks) {
  const { init, config } = runtime
  config.frameworks = frameworks
  const { native, transformer } = subversion

  for (const serviceName in services) {
    runtime.service.register(serviceName, services[serviceName])
  }

  runtime.freezePrototype()

  // register framework meta info
  global.frameworkVersion = native
  global.transformerVersion = transformer

  // init frameworks
  const globalMethods = init(config)

  // set global methods
  for (const methodName in globalMethods) {
    global[methodName] = (...args) => {
      const ret = globalMethods[methodName](...args)
      if (ret instanceof Error) {
        console.error(ret.toString())
      }
      return ret
    }
  }
}

Мы в основном смотрим,js frameworkКакие функции завершаются выполнением, главным образом, следующих трех функций:

  • Метод монтирования глобальных свойств и метод цепочки прототипов виртуальных машин
  • Создано на клиентском коммуникационном мосту
  • компенсировать разницу в окружающей среде

Метод монтирования глобальных свойств и метод цепочки прототипов виртуальных машин

просто сказалDSLчто это такое,js frameworkОчень важной функцией в хост-среде и языковой совместимости является хорошая работа. В основном через некоторые интерфейсы для взаимодействия с клиентом, адаптация интерфейсного фреймворка фактически предназначена для адаптацииiOS,androidи браузер. Здесь мы в основном говорим об интерфейсе, который подстраивается под клиента.

  • getRoot: получить узел страницы
  • receiveTasks: слушать клиентские задачи
  • registerComponents: зарегистрировать компонент
  • registerModles: модуль регистрации
  • init: инициализация внутреннего жизненного цикла страницы
  • createInstance: создан во внутреннем жизненном цикле страницы.
  • refreshInstance: обновление внутреннего жизненного цикла страницы
  • destroyInstance: уничтожить внутренний жизненный цикл страницы ...

Эти интерфейсы могут бытьWXBridgeContextувидеть всеjs frameworkПредоставляется клиенту для звонка. вWeex SDKПри инициализации указанногоregisterComponentsа такжеregisterMoudlesЭтот метод также называется .

registerComponents

js frameworkсерединаregisterComponentsВидно, что передняя часть просто делаетmapЗакэшировано, ждет парсингаvDOMПри отображении, а затем передать нативному компоненту для рендеринга.

// /runtime/frameworks/legacy/static/register.js
export function registerComponents (components) {
  if (Array.isArray(components)) {
    components.forEach(function register (name) {
      /* istanbul ignore if */
      if (!name) {
        return
      }
      if (typeof name === 'string') {
        nativeComponentMap[name] = true
      }
      /* istanbul ignore else */
      else if (typeof name === 'object' && typeof name.type === 'string') {
        nativeComponentMap[name.type] = name
      }
    })
  }
}
registerMoudles

registerMoudlesУже почти время, положи этоnativeModulesЭтот объект кэшируется, но его сложнее использовать, о чем речь пойдет позже.

// /runtime/frameworks/legacy/static/register.js
export function registerModules (modules) {
  /* istanbul ignore else */
  if (typeof modules === 'object') {
    initModules(modules)
  }
}

// /runtime/frameworks/legacy/app/register.js
export function initModules (modules, ifReplace) {
  for (const moduleName in modules) {
    // init `modules[moduleName][]`
    let methods = nativeModules[moduleName]
    if (!methods) {
      methods = {}
      nativeModules[moduleName] = methods
    }

    // push each non-existed new method
    modules[moduleName].forEach(function (method) {
      if (typeof method === 'string') {
        method = {
          name: method
        }
      }

      if (!methods[method.name] || ifReplace) {
        methods[method.name] = method
      }
    })
  }
}

Создано на клиентском коммуникационном мосту

js frameworkЭто мост между клиентом и интерфейсным бизнес-кодом, поэтому он более важен.bridge, базовая конструкция моста также обсуждалась в предыдущей статье,WeexВыбор состоит в том, чтобы предоставить метод непосредственно дляjsЗвоните, также звоните напрямуюjsМетоды.

Звонок клиентаjsИспользовать напрямуюcallJs,callJsдаjsПредоставленный метод помещается в текущий поток для вызова клиентом, включаяDOMрассылка событий,moduleОбратный вызов времени при вызове уведомляется через этот интерфейсjs framework, а затем вызовите кеш вjs frameworkметод в .

jsпозвонить клиенту с помощьюcallNative, клиент также предоставит множество методов дляjs frameworkдля,frameworkвызов, эти методы доступны вWXBridgeContextвидеть в,callNativeТолько один из методов, в реальном коде есть много методов, таких какaddElement,updateAttrsтак далее

компенсировать разницу в окружающей среде

В дополнение к основным методам, используемым для завершения функции, клиент также предоставляет некоторые методы для компенсацииjsМетод, который недоступен при вызове, — это разница в среде, чтобы компенсировать разницу в совместимости,setTimeout,nativeLogПодождите, клиент предоставляет соответствующий метод,js frameworkНевозможно вызвать эти методы, например, в браузере, как позвонить этим методам, это требует от обеих сторон для поддержки использования режима совместимости.

осталось немногоployfillметод, такой какPromise,Object.assign,ЭтиployfillЭто может гарантировать, что некоторые среды будут такими же, как браузеры, что снижает стоимость написания кода.

Законченный

воплощать в жизньjs frameworkДругие процессы не будут расширяться один за другим, в основном взаимные вызовы между некоторыми внешними кодами, эта часть также берет на себя много работы.WeexКакие-то проблемы с совместимостью остались от истории, а иногда встречаются какие-то волшебные способы записи, которые могут быть для решения каких-то волшебныхbugбар и различныеistanbul ignoreзаметки.

законченныйjs frameworkПосле клиентаframeworkLoadFinishedбудет установленоYES, задачи, оставленные ранее, также будутjs frameworkВыполните после выполнения, чтобы завершить весь процесс инициализации.

Клиент выполнит первымjs-service,потому чтоjs-serviceпросто нужноJavaScript CoreВыполнить строку в строке, поэтому выполнить напрямуюexecuteAllJsServiceВот и не надо звонитьjs frameworkметод, просто позвольте текущей среде памяти иметьjs serviceПеременная объект.

потом_methodQueueЗадачи выведены для прохождения и выполнения. Вот очередь кэша выполненияregisterComponents,registerModules,registerMethods. Выше также упоминалось, как вызвать два, подробный код находится вздесь.

После казни понятно, что этоjs Threadдолжны быть закрыты, а затем переработаны, но мы также должны сделать этоjs frameworkработает наjs Core, так что это нужно датьjs Threadоткрылrunloop, пусть этоjs Threadвсегда в исполнении

Инициализация экземпляра Weex

Во фронте очень много процессов инициализации, просто чтобы было понятнее в процессе как отображается страница.Фронт эквивалентен подготовке к работе.Давайте посмотрим на этот раз.WeexИнициализация экземпляра.ErosURL-адрес домашней страницы настраивается в файле конфигурации через файл конфигурации, и клиент может напрямую получить домашнюю страницу и инициализировать ее напрямую.

клиент через_renderWithURLзагрузить домашнюю страницуURL,этоURLНезависимо от того, размещены ли они локально или на сервере, на самом деле этоjs bundleфайл это спец.loaderупакованныйjsфайл, после загрузки этого файла вызовите его дляjs frameworkсерединаcreateInstance.

/*
id:Weex 实例的 id
code:js bundle 的代码
config:配置参数
data:参数
*/
function createInstance (id, code, config, data) {
  // 判断当前实例是否已经创建过了
  if (instanceTypeMap[id]) {
    return new Error(`The instance id "${id}" has already been used!`)
  }

  // 获取当前 bundle 是那种框架
  const bundleType = getBundleType(code)
  instanceTypeMap[id] = bundleType

  // 初始化 instance 的 config
  config = JSON.parse(JSON.stringify(config || {}))
  config.env = JSON.parse(JSON.stringify(global.WXEnvironment || {}))
  config.bundleType = bundleType

  // 获取当前的 DSL
  const framework = runtimeConfig.frameworks[bundleType]
  if (!framework) {
    return new Error(`[JS Framework] Invalid bundle type "${bundleType}".`)
  }
  if (bundleType === 'Weex') {
    console.error(`[JS Framework] COMPATIBILITY WARNING: `
      + `Weex DSL 1.0 (.we) framework is no longer supported! `
      + `It will be removed in the next version of WeexSDK, `
      + `your page would be crash if you still using the ".we" framework. `
      + `Please upgrade it to Vue.js or Rax.`)
  }
  // 获得对应的 WeexInstance 实例,提供 Weex.xx 相关的方法
  const instanceContext = createInstanceContext(id, config, data)
  if (typeof framework.createInstance === 'function') {
    // Temporary compatible with some legacy APIs in Rax,
    // some Rax page is using the legacy ".we" framework.
    if (bundleType === 'Rax' || bundleType === 'Weex') {
      const raxInstanceContext = Object.assign({
        config,
        created: Date.now(),
        framework: bundleType
      }, instanceContext)
      // Rax 或者 Weex DSL 调用初始化的地方
      return framework.createInstance(id, code, config, data, raxInstanceContext)
    }
    // Rax 或者 Weex DSL 调用初始化的地方
    return framework.createInstance(id, code, config, data, instanceContext)
  }
  // 当前 DSL 没有提供 createInstance 支持
  runInContext(code, instanceContext)
}

Вышеупомянутое является первым шагом вызова, различныхDSLРазличие уже началось здесь, порождая различныеWeexпример. Следующим шагом является вызов соответствующихDSLизcreateInstanceИ передаются параметры, соответствующие потребностям прошлого

// /runtime/frameworks/legacy/static/create.js
export function createInstance (id, code, options, data, info) {
  const { services } = info || {}
  resetTarget()
  let instance = instanceMap[id]
  /* istanbul ignore else */
  options = options || {}
  let result
  /* istanbul ignore else */
  if (!instance) {
    // 创建 APP 实例,并将实例放到 instanceMap 上
    instance = new App(id, options)
    instanceMap[id] = instance
    result = initApp(instance, code, data, services)
  }
  else {
    result = new Error(`invalid instance id "${id}"`)
  }
  return (result instanceof Error) ? result : instance
}
// /runtime/frameworks/legacy/app/instance.js
export default function App (id, options) {
  this.id = id
  this.options = options || {}
  this.vm = null
  this.customComponentMap = {}
  this.commonModules = {}

  // document
  this.doc = new renderer.Document(
    id,
    this.options.bundleUrl,
    null,
    renderer.Listener
  )
  this.differ = new Differ(id)
}

Главное этоinitAPPВ этом методе есть много методов для завершения цепочки прототипов, таких какbundleDefine,bundleBootstrapПодождите, это все очень важно, вы можете взглянутьinitметод для выполнения вышеуказанных операций.

Самое главное следующий метод, который будет окончательным выполнениемjs bundleМесто. После того, как выполнение будет завершено,WeexЭкземпляр одной страницы помещается вinstanceMap,new Functionэто основной метод, вот и весьJS bundleГенерируется от кода до выполненияVDOM, а затем преобразуется вVNodeОтправлено в собственный модуль для рендеринга.

if (!callFunctionNative(globalObjects, functionBody)) {
  // If failed to compile functionBody on native side,
  // fallback to callFunction.
  callFunction(globalObjects, functionBody)
}
// 真正执行 js bundle 的方法
function callFunction (globalObjects, body) {
  const globalKeys = []
  const globalValues = []
  for (const key in globalObjects) {
    globalKeys.push(key)
    globalValues.push(globalObjects[key])
  }
  globalKeys.push(body)

  // 所有的方法都是通过 new Function() 的方式被执行的
  const result = new Function(...globalKeys)
  return result(...globalValues)
}

выполнение пакета js

js bundleЭто написанный бизнес-код, вы можете написать простой код и сохранить его для просмотра.WeexСвязанныйloader, конкретный код однозначно и условноjsКод другой, после конвертации основной<template>а также<style>часть, две части будут преобразованы в двеJSON, в двух замыканиях. Как упоминалось выше, он наконец выполняется.new Function, конкретные шаги выполнения находятся вinit, так как код слишком длинный, мы в основном смотрим на основную часть.

 const globalObjects = Object.assign({
    define: bundleDefine,
    require: bundleRequire,
    bootstrap: bundleBootstrap,
    register: bundleRegister,
    render: bundleRender,
    __weex_define__: bundleDefine, // alias for define
    __weex_bootstrap__: bundleBootstrap, // alias for bootstrap
    __weex_document__: bundleDocument,
    __weex_require__: bundleRequireModule,
    __weex_viewmodel__: bundleVm,
    weex: weexGlobalObject
  }, timerAPIs, services)

Приведенный выше код является основной частью, которая выполняется,bundleDefineРаздел, вот раздел для разборки компонентов, разборки, которые являются иWeexсоответствующийComponent, которые определяются пользователемComponent, вот рекурсивный процесс обхода.

bundleRequireа такжеbundleBootstrap, который здесь называетсяbootstrapа такжеVm, вот шаг, который я не совсем понимаю.bootstrapОсновная функция — проверка параметров и информации об окружении.Вы можете посмотреть исходный код этой части.

Vmосновывается наComponentсоздать соответствующийViewModel, эта часть делает много вещей, в основном анализируя весьVMОсновной. В основном завершен жизненный цикл инициализации, двойная привязка данных, шаблон построения,UIрисовать.

// bind events and lifecycles
  initEvents(this, externalEvents)

  console.debug(`[JS Framework] "init" lifecycle in Vm(${this._type})`)
  this.$emit('hook:init')
  this._inited = true

  // proxy data and methods
  // observe data and add this to vms
  this._data = typeof data === 'function' ? data() : data
  if (mergedData) {
    extend(this._data, mergedData)
  }
  initState(this)

  console.debug(`[JS Framework] "created" lifecycle in Vm(${this._type})`)
  this.$emit('hook:created')
  this._created = true

  // backward old ready entry
  if (options.methods && options.methods.ready) {
    console.warn('"exports.methods.ready" is deprecated, ' +
      'please use "exports.created" instead')
    options.methods.ready.call(this)
  }

  if (!this._app.doc) {
    return
  }

  // if no parentElement then specify the documentElement
  this._parentEl = parentEl || this._app.doc.documentElement
  build(this)

жизненный цикл инициализации

Код; В этом процессе инициализируются 4 хука жизненного цикла,init,created,ready,destroyed. Помимо жизненного цикла, здесь также связанvmмеханизм событий, способ взаимодействия компонентов друг с другом.

двойная привязка данных

Код;Vue DSLДвойная привязка данных может относиться кVueПринцип реализации двойной привязки данных,RaxЭто также похоже на проксирование данных, затем добавление мониторинга данных, инициализацию вычисляемых свойств и монтирование_methodметод, создатьgetter/setter, методы переопределения массивов, рекурсивное связывание... Эта часть в основномVueСодержание предыдущего блога также было подробно объяснено.VueМеханизм двойной привязки данных.

Разбор шаблона

Код; здесь тожеVueОдин из механизмов разбора шаблонов дляVueРазбор синтаксиса шаблона, такого какv-for,:classПроцесс разбора грамматики представляет собой процесс глубокого обхода, после завершения которогоjs bundleэто становитсяVDOM,этоVDOMЭто больше похоже на соответствие некоторому формату соглашенияJSONданные, потому что клиент иjs frameworkСуществует не так много типов данных, которые можно использовать совместно.JSONэто лучший способ, поэтому, наконец, преобразуйте шаблон вJSONОписание передается клиенту.

Нарисуйте собственный пользовательский интерфейс

Код;пройти черезdiffer.flushзвонок, сработаетVDOMПроцесс сравнения — это процесс сравнения того же уровня, и узелVNodeпо одномуdiffпередано клиенту. Сначала сравните внешние компоненты, если есть дочерние узлы, а затем повторите дочерние узлы, различные части сравнения передаются клиенту, первый рендеринг полностью новый, а затем обновляетсяUIбудет полезно, когдаremove,updateЖдатьAPI.

финальный розыгрышappendChild, который инкапсулирует все иnativeЕсть интерактивные методы.DOMОперация примерноaddElement,removeElementи т.д. метод, вызовtaskCenter.send, вот планирование задач, и, наконец, все методы вызывают через this соответствующий интерфейс, предоставляемый клиентом.

send (type, params, args, options) {
    const { action, component, ref, module, method } = params

    // normalize args and options
    args = args.map(arg => this.normalize(arg))
    if (typof(options) === 'Object') {
      options = this.normalize(options, true)
    }

    switch (type) {
      case 'dom':
        return this[action](this.instanceId, args)
      case 'component':
        return this.componentHandler(this.instanceId, ref, method, args, Object.assign({ component }, options))
      default:
        return this.moduleHandler(this.instanceId, module, method, args, options)
    }
  }

После звонка клиенту просмотрите передWeex SDKПри инициализацииaddElementэто метод, внедренный на стороне клиента, а затем соответствующийComponentСопоставляется с соответствующим собственным методом синтаксического анализа. Родной, а затем найти соответствующийComponentрендерить.

из-заWeexПосле рендеринга родителя будет рендериться дочерний, поэтому порядок прохождения таков: сначала передать родителя, а затем дочернего. инструкции дочерних узлов, чтобы можно было медленнее, упомянутая выше регистрацияComponentКогда есть два параметраappend=treeа такжеistemplate=true, обе из которых являются схемами оптимизации производительности, как упоминалось выше вComponentsПри регистрации есть два параметра.

append=tree
BOOL appendTree = !appendingInTree && [component.attributes[@"append"] isEqualToString:@"tree"];
// if ancestor is appending tree, child should not be laid out again even it is appending tree.
for(NSDictionary *subcomponentData in subcomponentsData){
    [self _recursivelyAddComponent:subcomponentData toSupercomponent:component atIndex:-1 appendingInTree:appendTree || appendingInTree];
}

[component _didInserted];

if (appendTree) {
    // If appending tree,force layout in case of too much tasks piling up in syncQueue
    [self _layoutAndSyncUI];
}

WeexЕсть два способа рендеринга: одинnode, одинtree,nodeзаключается в том, чтобы сначала отобразить родительский узел, а затем отобразить дочерний узел, иtreeСначала нужно отобразить дочерние узлы, а затем — последние.layoutВизуализируйте родительский узел. С точки зрения производительности рендеринга, начальное время рисования,append="node"относительно быстро, но с точки зрения общего времени,append="tree"Тратьте меньше времени.

если текущийComponentимеют{@"append":@"tree"}свойство и его родительComponentОтсутствие этого свойства приведет к изменению макета страницы. Видно, что это сделано для предотвращенияUIСлишком много задач рисования накапливаются вместе и влияют на выполнение задач синхронной очереди.

istemplate=true
WXComponentConfig *config = [WXComponentFactory configWithComponentName:type];
BOOL isTemplate = [config.properties[@"isTemplate"] boolValue] || (supercomponent && supercomponent->_isTemplate);
if (isTemplate) {
    bindingProps = [self _extractBindingProps:&attributes];
    bindingStyles = [self _extractBindings:&styles];
    bindingAttibutes = [self _extractBindings:&attributes];
    bindingEvents = [self _extractBindingEvents:&events];
}

Затем, когда клиент выполняет рендеринг, онComponentДочерний узел получен, а затем переданDataBindingПреобразовано в выражение, существуетbindingMap, соответствующий анализ находится вWXJSASTParser.mВ файле это включает в себя более сложный синтаксический анализ шаблона, синтаксический анализ и преобразование выражений, данные привязки и собственныеUIОтношение.

Во время рендеринга клиент иjs frameworkЕсть также общение событий, которое пропускается через мостcreateFinishedа такжеrenderFinishedмероприятие,js frameworkбудет выполнятьWeexМетод жизненного цикла, соответствующий экземпляру.

На данный момент страница была обработана. После того, как обработка страницы завершена, как происходит событие клика?

доставка события

глобальное событие

Прежде чем понять, как доставляются события, давайте рассмотрим несколько типов событий.ErosИнкапсулирует маршрутизируемые события, инкапсулирует эти события в компонентах, вVueпредоставить шаблонErosобъект, вWeexПри создании экземпляра свяжите эти методы для внедрения обратных вызовов и дождитесь обратного вызова клиента.Клиент уведомляется через глобальное событие, когда происходит соответствующее событие.js frameworkвоплощать в жизньweexМетод обратного вызова экземпляра.

// app 前后台相关 start 
appActive() {
    console.log('appActive');
},
appDeactive() {
    console.log('appDeactive');
},
// app 前后台相关 end 

// 页面周期相关 start 
beforeAppear (params, options) {
    console.log('beforeAppear');
},
beforeBackAppear (params, options) {
    console.log('beforeBackAppear');
},
appeared (params, options) {
    console.log('appeared');
},

backAppeared (params, options) {
    console.log('backAppeared');
},
beforeDisappear (options) {
    console.log('beforeDisappear');
},
disappeared (options) {
    console.log('disappeared');
},
// 页面周期相关 end 

глобальное событиеErosчерез что-то вродеnode jsобработка, вjs coreПоместите в него глобальный объект, это также похоже на использованиеModuleспособ использования, инкапсулируя что-то вродеjsмеханизм события для срабатывания.

событие взаимодействия

В основном мы анализируем события взаимодействия со страницей, такие как клики, как клиент может выполнитьVueКак насчет методов, определенных для экземпляров? В этом процессе событие щелчка должно быть зарегистрировано первым, то есть, когда оно инициализируется,js frameworkОн уже сообщил клиенту, какие компоненты имеют обратные вызовы привязки событий.Если клиент получает какое-либо событие, оно будет передано вjs, производительность определенно будет плохой.

создание события

js frameworkОбнаружен тег события при разборе шаблона@xxx="callback", он будет передан при создании компонентаcallAddEventБудуeventПерейти кnative, но метод обратного вызова события не будет передан, потому что клиент вообще не распознает метод обратного вызова события.После того, как клиент найдет атрибут события, он привяжет нативное событие к событию.При рендеринге компонентов каждый компонент будет генерировать компонентID,то естьref,typeЭто тип события, такой как:click,longpressЖдать.

// https://github.com/apache/incubator-weex/blob/master/runtime/frameworks/legacy/vm/compiler.js
if (!vm._rootEl) {
    vm._rootEl = element
    // bind event earlier because of lifecycle issues
    const binding = vm._externalBinding || {}
    const target = binding.template
    const parentVm = binding.parent
    if (target && target.events && parentVm && element) {
      for (const type in target.events) {
        const handler = parentVm[target.events[type]]
        if (handler) {
          element.addEvent(type, bind(handler, parentVm))
        }
      }
    }
  }
  
  // https://github.com/apache/incubator-weex/blob/master/runtime/vdom/Element.js
  addEvent (type, handler, params) {
    if (!this.event) {
      this.event = {}
    }
    if (!this.event[type]) {
      this.event[type] = { handler, params }
      const taskCenter = getTaskCenter(this.docId)
      if (taskCenter) {
        taskCenter.send(
          'dom',
          { action: 'addEvent' },
          [this.ref, type]
        )
      }
    }
  }

Из вышеизложенного видно, что только одинrefВ прошлом, после того как привязка была завершена до тех пор, пока все компоненты не были отрисованы, когда в представлении происходило соответствующее событие, клиент перехватывал событие и передавалfireEventПередайте соответствующее событие с четырьмя параметрами,ref,type,event,domChanges,пройти черезbridgeпередать эти параметрыjs frameworkизbridge, но также будет нестиWeexпримерID, так как их может быть несколькоweexЭкземпляр по Weex ID找到对应的экземпляр weex.

Если имеется несколько привязок событийrefТакже нужно посмотреть на рекурсивный обход, но и процесс глубокого обхода, а затем найти соответствующее событие, запуская соответствующее событие, событие, которое могут быть изменения в двойной связывать данные, а затем изменитьDOM, поэтому после повторного запуска событияdiffer.flush. Сравните и создайте новыеVDOM, а затем визуализировать новый стиль страницы.

триггер события

// https://github.com/apache/incubator-weex/blob/master/runtime/frameworks/legacy/app/ctrl/misc.js
export function fireEvent (app, ref, type, e, domChanges) {
  console.debug(`[JS Framework] Fire a "${type}" event on an element(${ref}) in instance(${app.id})`)
  if (Array.isArray(ref)) {
    ref.some((ref) => {
      return fireEvent(app, ref, type, e) !== false
    })
    return
  }
  const el = app.doc.getRef(ref)
  if (el) {
    const result = app.doc.fireEvent(el, type, e, domChanges)
    app.differ.flush()
    app.doc.taskCenter.send('dom', { action: 'updateFinish' }, [])
    return result
  }
  return new Error(`invalid element reference "${ref}"`)
}

app.doc.fireEvent(el, type, e, domChanges)Давайте взглянем на этот метод. Сначала получите обратный вызов события в это время, а затем выполните обратный вызов события. В нативном компоненте не будет всплытия событий, ноjsСуществует механизм всплытия событий, поэтому следующее имитирует механизм всплытия событий и продолжает запускать родительскийfireEventОдин за другим пузырем для родителя, который частичноjs frameworkзавершено в.

// https://github.com/apache/incubator-weex/blob/master/runtime/vdom/Element.js
fireEvent (type, event, isBubble, options) {
    let result = null
    let isStopPropagation = false
    const eventDesc = this.event[type]
    if (eventDesc && event) {
      const handler = eventDesc.handler
      event.stopPropagation = () => {
        isStopPropagation = true
      }
      if (options && options.params) {
        result = handler.call(this, ...options.params, event)
      }
      else {
        result = handler.call(this, event)
      }
    }

    if (!isStopPropagation
      && isBubble
      && (BUBBLE_EVENTS.indexOf(type) !== -1)
      && this.parentNode
      && this.parentNode.fireEvent) {
      event.currentTarget = this.parentNode
      this.parentNode.fireEvent(type, event, isBubble) // no options
    }

    return result
  }

Вышеупомянутое завершает полный триггер события, если это простое событие, что-то вродеclickТакая передача завершает обратный вызов события, и особой проблемы нет, но если передача события прокручивается, неизбежно будут проблемы с производительностью, поэтому при обработке клиентом события прокрутки точно будет минимальный временной интервал, однозначно Не триггер на все времена.

Лучшая управляемостьWeexтакже представилexpression binding,БудуjsОбратный вызов события обрабатывается в выражение, которое передается клиенту, когда он связан. Поскольку это выражение, клиент также может распознать выражение. Когда клиент слушает на триггере собственного события, он напрямую выполняет выражение. Это сохраняет процесс передачи.WeexизbingdingXЕго также можно использовать для обработки подобных частых триггеров.jsВзаимодействие с клиентом, например анимация.

использование модуля

было сказано вышеmoduleРегистрация финального звонкаjs frameworkизregisterModulesвводить всеmoduleметод и сохранить метод вnativeModulesНа объекте процесс регистрации завершен.

// https://github.com/apache/incubator-weex/blob/master/runtime/frameworks/legacy/static/register.js
export function registerModules (modules) {
  /* istanbul ignore else */
  if (typeof modules === 'object') {
    initModules(modules)
  }
}

// https://github.com/apache/incubator-weex/blob/master/runtime/frameworks/legacy/app/register.js
export function initModules (modules, ifReplace) {
  for (const moduleName in modules) {
    // init `modules[moduleName][]`
    let methods = nativeModules[moduleName]
    if (!methods) {
      methods = {}
      nativeModules[moduleName] = methods
    }

    // push each non-existed new method
    modules[moduleName].forEach(function (method) {
      if (typeof method === 'string') {
        method = {
          name: method
        }
      }

      if (!methods[method.name] || ifReplace) {
        methods[method.name] = method
      }
    })
  }
}

requireModule

мы проходимweex.requireModule('xxx')получитьmodule, сначала нам нужно понятьweexЭта глобальная переменная появится, и она будет генерировать ее во время рендеринга.weexЭкземпляр, эта информация будет храниться в глобальной переменнойweexGlobalObject,существуетcallFunction, объект будет привязан вjs bundleво время выполненияweexПо объекту подробности следующие.

 const globalObjects = Object.assign({
    ...
    weex: weexGlobalObject
  }, timerAPIs, services)

weexУ этого объекта также есть множество методов и свойств, некоторые из которых можно вызывать дляmoduleпутьrequireModule, этот метод и приведенная выше клиентская инъекцияModuleКогда метод находится в том же модуле, то есть в том же замыкании, его можно использовать совместно.nativeModulesэтот объект.

//https://github.com/apache/incubator-weex/blob/master/runtime/frameworks/legacy/app/index.js
App.prototype.requireModule = function (name) {
  return requireModule(this, name)
}

// https://github.com/apache/incubator-weex/blob/master/runtime/frameworks/legacy/app/register.js
export function requireModule (app, name) {
  const methods = nativeModules[name]
  const target = {}
  for (const methodName in methods) {
    Object.defineProperty(target, methodName, {
      configurable: true,
      enumerable: true,
      get: function moduleGetter () {
        return (...args) => app.callTasks({
          module: name,
          method: methodName,
          args: args
        })
      },
      set: function moduleSetter (value) {
        if (typeof value === 'function') {
          return app.callTasks({
            module: name,
            method: methodName,
            args: [value]
          })
        }
      }
    })
  }
  return target
}

Почему вышеприведенное не использовало простойcallилиapplyметод? Вместо этого он выполняет аналогичную операцию двойной привязки для всех методов этого объекта при возврате. Прежде всего, это должно быть предотвращение загрязнения объекта, этоnativeModulesэто всеweexЕсли объекты, совместно используемые экземплярами, можно получить напрямую, то все интерфейсные объекты являются ссылками и могут быть переписаны, что определенно нехорошо.

Здесь также использовалиcallTasks, это было объяснено в предыдущей инициализации, на самом деле это вызов соответствующегоnativeМетоды,taskCenter.sendОн будет искать метод, соответствующий клиенту, у которого естьtaskCenterСоответствующий код, наконец, принятcallNativeModuleКод для вызова клиенту.

// https://github.com/apache/incubator-weex/blob/master/runtime/frameworks/legacy/app/ctrl/misc.js
export function callTasks (app, tasks) {
  let result

  /* istanbul ignore next */
  if (typof(tasks) !== 'array') {
    tasks = [tasks]
  }

  tasks.forEach(task => {
    result = app.doc.taskCenter.send(
      'module',
      {
        module: task.module,
        method: task.method
      },
      task.args
    )
  })

  return result
}

После завершения вызова подождите, пока клиент обработает вызов, и вернитесь после завершения клиентского процесса. Хотя естьforEachобход, но возвращаетсяresultоба синхронизированы последнимиresult. Здесь не очень строго, но мы видим, что с надстройкой проблем нет,tasksЭто вообще пошаговая задача, и она не будет пройдена.arrayПриходите, и большинство методов клиентских вызовов асинхронны, и есть несколько синхронных обратных вызовов, поэтому можно только сказать, что это неточно.

Суммировать

Благодаря приведенному выше расчесыванию мы можем видеть, чтоWeexДетали принципа работы и общий процесс также разобрались. Через год практики, чисто ли этоWeexприложение все еще существуетAPPДоступ был отработан, поддерживая наш бизнес на сотнях страниц, и в то же время эффективность разработки значительно повысилась, а также улучшилась наша база.Vueстек передовых технологий.

СейчасWeexОн также постоянно обновляется, по крайней мере, после запуска нашего бизнеса, будем верить.WeexОсуществимо, хотя постоянно критикуются различные недостатки, но какая превосходная техника не испытала такого развития. Снимите очки презрения с наших передовых технологий и позвольте технологиям лучше служить бизнесу.

Наконец, после деловой практики и накопления, мы также обобщили на основеWeexтехнические решенияErosИ откройте исходный код, решите экологические проблемы, которые все критиковали, и предоставьте более обильныеComponentа такжеModuleРешайте реальные бизнес-задачи. В настоящее время тысячи разработчиков имеют опыт разработки. Они постоянно жалуются на улучшение нашего решения, стабилизацию базового решения и создание нового метода подключаемых модулей. В настоящее время некоторые разработчики предоставили несколько подключаемых модулей, а мы также собрали данные от разработчиков, которые имеют онлайн40+ APPкейсы и многое другоеAPPво время разработки. Надеюсь, наше решение поможетAPPВы в разработке.

Вот некоторые черезErosонлайнAPPкейс