-
-
Notifications
You must be signed in to change notification settings - Fork 59
Fully Qualified Names vs Imports
В JS и во многих других языках есть импорты двух видов: конкретные и массовые.
Импортируют одно конкретное имя в локальную область видимости.
import Page from 'mol/book/Page'
или
import { Page } from 'mol/book'
или
import { BookPage as Page } from 'mol'
Такие импорты ни чем принципиально не отличаются от локальных алиасов:
const Page = $mol_book_page
Импортируют все имена из удалённого пространства имён в локальную область видимости.
import `mol/book'
Чем короче и абстрактнее имена, тем выше риск конфликтов. Приходится переименовывать импортируемые сущности. Зачастую в имена закладывают полную семантику, даже, когда она и так понятна из пути к модулю:
import { BookPage } from 'mol/book/Page'
При работе со множеством файлов (что типично, когда модули очень маленькие, а значит их много), приходится постоянно сверяться со списком переименовываний, чтобы понимать по какому имени обращаться к нужной сущности.
Начиная использовать сущность необходимо импортировать её в начале файла. Прекращая её использовать, нужно проверить, что нигде в файле она не используется и удалить импорт.
Актуально, когда импортируются не все сущности из внешнего модуля, то есть почти всегда. RollUp, tree shaking и тому подобные костыли.
Вместо того, чтобы писать:
import Page from 'mol/book/Page'
return new Page
Можно было бы просто написать:
return new $mol_book_page
Если класс объявлен как:
export class Page { ... }
То в отладчике/профайлере/консоли отображаться будет именно это короткое имя. Если же использовать полные имена:
class $mol_book_page { ... }
То работа с инструментами разработчика становится куда приятней, так как не нужно гадать "который это Page из 5 и где находится".
Если класс/функция объявлен как:
class $mol_book_page { ... }
То, через свойство name
можно получить полный путь, что можно использовать, например, для генерации глобально-уникальных человекопонятных css-классов в DOM:
[mol_page] { ... }
[mol_book_page] { ... }
[my_app_page] { ... }
Если же класс/функция объявлены лишь с коротким путём:
export class Page { ... }
То всё, что мы можем получить - это локальное имя, что достаточно бесполезно.
Чтобы через консоль посмотреть состояние спрятанное в замыкании нужно поплясать с бубном.
Портянки импортов и экспортов могут занимать не один десяток строк. Типичный пример. Кроме того, появляется необходимость делать специальные "индексные модули", которые импортируют все модули из директирии и экспортят их как один объект.
Захламление исходников захламляет и диффы на код-ревью, а также повышает риск конфликтов при слиянии веток, которые приходится разрешать вручную.
JavaScript импортируются по одним правилам, CSS по другим, шаблоны - третьим. Подключив скрипты нужно не забыть подключить стили, подключив стили - не забыть добавить деплой нужных им картинок и тп связанные вещи. А если стили завязаны на что-то типа modernizr - не забыть подключить соответствующий скрипт.
Вместо всей этой кутерьмы в $mol модулем является директория и все файлы внутри (на каких бы языках они ни были) включаются в соответствующие бандлы.
Сущность может называться Foo, а лежать в файле Bar. В общем случае по имени сущности не понять в каком файле она определена и где этот файл искать.
import Page from 'mol/book/Page'
return new Page
Однако всегда можно сделать короткий локальный алиас, если в этом действительно есть необходимость:
const Page = $mol_book_page
return new Page
Так как import/export попали в стандарт ES, то многие уже вовсю пилят библиотеки используя эти конструкции. Соответственно воспользоваться такими библиотеками проще используя те же механизмы.
use Symfony\Component\Routing\RouteCollectionBuilder;
abstract protected function configureRoutes(RouteCollectionBuilder $routes);
Ребята перестарались с таксономией. Незачем делать такие глубокие иерархии и пытаться впихнуть в название половину её описания. Куда лаконичней смотрелось бы короткое, но полное имя:
abstract protected function configureRoutes(\Symfony\Routes $routes);
http://vibed.org/api/vibe.http.router/URLRouter
import vibe.http.fileserver;
void addGroup(HTTPServerRequest req, HTTPServerResponse res)
router.get("/static/*", serveStaticFiles("public/"));
auto settings = new HTTPServerSettings;
Так как в языке распространены массовые импорты, то часто классы именуют многосложно. И всё-равно не понятно какой класс из какого модуля приехал. Хотя, можно же было бы сделать проще:
void addGroup(.vibe.http.request req, .vibe.http.response res)
router.get("/static/*", .vibe.http.static.serve("public/"));
auto settings = new .vibe.http.server.settings;
Как можно было бы зайдя на Гитхаб догадаться, что NavigationExtras из Angular находится в https://github.com/angular/angular/blob/master/packages/router/src/router.ts ?
В начале файла мы видим типичную портянку из нескольких десятков инклудов.
Многие импорты являются тавтологиями:
import {createRouterState} from './create_router_state';
Зачем переименовывать snake_case в camelCase?
Не обошлось и без полного переименовывания:
import {ChildrenOutletContexts} from './router_outlet_context';
Стоит обратить внимание, что зачастую импортируются сложносоставные имена типа: DetachedRouteHandleInternal
. Потому что в одно-два слова просто не удаётся впихнуть всю необходимую семантику.
Какие имена могли бы быть без импортов:
// import {Location} from '@angular/common';
$ng_location
// import {Compiler, Injector, NgModuleFactoryLoader, NgModuleRef, Type, isDevMode} from '@angular/core';
$ng_compiler , $ng_injector , $ng_loader , $ng_module , $ng_debug
// import {BehaviorSubject} from 'rxjs/BehaviorSubject';
$rx_behaviour
// import {Observable} from 'rxjs/Observable';
$rx_observable
// import {Subject} from 'rxjs/Subject';
$rx_subject
// import {Subscription} from 'rxjs/Subscription';
$rx_subscription
// import {of } from 'rxjs/observable/of';
$rx_of
// import {concatMap} from 'rxjs/operator/concatMap';
$rx_map_concat
// import {map} from 'rxjs/operator/map';
$rx_map
// import {mergeMap} from 'rxjs/operator/mergeMap';
$rx_map_merge
// import {applyRedirects} from './apply_redirects';
$ng_route_redirect
// import {LoadedRouterConfig, QueryParamsHandling, Route, Routes, validateConfig} from './config';
$ng_route_config , $ng_query_handling , $ng_route , $ng_route_list , $ng_validation
// import {createRouterState} from './create_router_state';
$ng_route_state
// import {createUrlTree} from './create_url_tree';
$ng_url_tree
// import {ActivationEnd, ChildActivationEnd, Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events';
$ng_route_activated , $ng_route_child_ended , $ng_route_event , $ng_route_guarded , $ng_route_guarding , $ng_route_cancel , $ng_route_navigated , $ng_route_error , $ng_route_navigating , $ng_route_resolved , $ng_route_resolving , $ng_route_loaded , $ng_route_loading , $ng_route_recognized
// import {PreActivation} from './pre_activation';
$ng_route_activating
// import {recognize} from './recognize';
$ng_route_recognize
// import {DefaultRouteReuseStrategy, DetachedRouteHandleInternal, RouteReuseStrategy} from './route_reuse_strategy';
$ng_route_default , $ng_route_detached , $ng_route_strategy
// import {RouterConfigLoader} from './router_config_loader';
$ng_route_loader
// import {ChildrenOutletContexts} from './router_outlet_context';
$ng_route_outlet
// import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot, advanceActivatedRoute, createEmptyState} from './router_state';
$ng_route_active , $ng_route_snapshot_active , $ng_route_state , $ng_route_snapshot , $ng_route_active_advance , $ng_route_empty
// import {Params, isNavigationCancelingError} from './shared';
$ng_route_params , $ng_route_error_cancel
// import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy';
$ng_url_handling_default , $ng_url_handling
// import {UrlSerializer, UrlTree, containsTree, createEmptyUrlTree} from './url_tree';
$ng_url_serializer , $ng_url_tree , $ng_url_tree_empty
// import {forEach} from './utils/collection';
$ng_each
// import {TreeNode, nodeChildrenAsMap} from './utils/tree';
$ng_tree_node , $ng_tree_dict