SPA basé sur un navigateur indépendant du framework

1. Mais ... pourquoi?

  1. Il existe un grand nombre de frameworks pour dĂ©velopper  SPA  (Single Page Application).





  2. Il existe une énorme quantité de documentation qui illustre comment créer une application basée sur un cadre spécifique.





  3. Cependant, une telle documentation place le cadre au premier plan. Ainsi, transformer le cadre d'un dĂ©tail de mise en Ɠuvre en un facteur dĂ©terminant. Ainsi, une partie importante du code est Ă©crite non pas pour rĂ©pondre aux besoins de l'entreprise, mais pour rĂ©pondre aux besoins du framework.





Compte tenu de la façon dont le dĂ©veloppement de logiciels axĂ© sur le battage publicitaire est de nos jours, vous pouvez ĂȘtre sĂ»r que dans quelques annĂ©es, il y aura de nouveaux cadres Ă  la mode pour le dĂ©veloppement front-end. Au moment oĂč le framework sur la base duquel l'application est construite se dĂ©mode, vous ĂȘtes obligĂ© de maintenir la base de code hĂ©ritĂ©e ou de dĂ©marrer le processus de transfert de l'application vers un nouveau framework.





Les deux options sont préjudiciables à l'entreprise. Le maintien d'une base de code obsolÚte entraßne des problÚmes d'embauche de nouveaux développeurs actuels et de motivation. Le transfert d'une application vers un nouveau framework coûte du temps (et donc de l'argent) mais n'apporte aucun avantage commercial.





Cet article est un exemple de création d'un SPA à l'aide de principes de conception d'architecture de haut niveau. Ce faisant, des bibliothÚques et des frameworks spécifiques sont choisis pour répondre aux responsabilités définies par l'architecture souhaitée.





2. Objectifs et limites de l'architecture

Objectifs:





  1. Un nouveau dĂ©veloppeur peut comprendre le but d'une application avec un coup d'Ɠil rapide sur la structure du code.





  2. La séparation des préoccupations est favorisée et donc la modularité du code afin que:





    • Les modules sont faciles Ă  tester





    •   (boundaries)          .       Â« »





  3.     .  ,       .





  4. . ( )     , .





  5.      .





  6.     . ,     .





:





 .   (  ) HTML+CSS  JavaScript .





3.

  .   : (layered), (onion)   (hexagonal).    .





  / SPA . (domain)   (application) . ,  â€”   .





     ,   .





  (  Ports and Adapters)     .    localStorage  TodoMVC      ( boundaries/local-storage).





4. . SPA ?

.





.      :





 1: ,  





?   2  .





 2: ,   1





  ‘shared’ UI , , , .





  ( ) . ‘’       ‘parts’. ( 3).





 3: ‘parts’





, ’goods catalogue’. ‘goods-catalogue/parts/goods-list/parts/good-details.js’     .    â€”  .





  «parts»   .   4.





 4:   ‘parts’





‘goods-catalogue/goods-list’  . goods-list.js () â€” ,   .   , - (js, html, css)   ,  ,     .





:





  1.  â€” .





    • goods-list      , .





    • filters    ,   .





  2. (    )    â€”   «_».     .





    • _goods-list folder   goods-catalogue    .





    • goods-list.js   _goods-list   .





    • _good-details.js   _goods-list  .





 5: «_»   





!         , .   .  pages   components   5.      HTML        component.    components  , «» .





5. . JavaScript?

  JavaScript. .      (  1-20),   ...





 ,  .   .  4-     . ,   4 .        .  ,    2015 ,      .   ,   ,   .





JavaScript (babel)   JavaScript,    Â«  Â» JavaScript.    â€” ,   .





,    â€” TypeScript  :





  •  





  • - JavaScript, JavaScript





  • (typings) JavaScript . , npm .   ,   TypeScript .   -.





:   asm.jsblazor  elm     





6.  

, : HTML, CSS, JavaScript. ,   4: , .





  [6.1]  HTML  CSS    .





HTML     . ,  underscore.jshandlebars.js.   ,       .





  [6.2]  TypeScript ,   ().          .





UI        . HTML   HTML . . .        .    ,        .





  [6.3]       . .





[6.4]    :





  • ,   .





  •   .     .





  •   Domain  Application.     ,    Dependency Injection.  .





  â€”        .   .  ,   , ----html-.     . , .





, , .   , .  :





  • , ..   .





  • ..   .





,  [6.5] â€” TypeScript . , .





 , :





  • (Components) â€” HTML + CSS





  • (ViewModels) â€” , , (  ).





  • (ViewModel facades) â€” ,   .





 6:  





  • - . .





  •   ().





  •    â€” . / .   «shared».





  •  â€” .    /.





?   6  . ()   .    ,    .





  [6.6] â€” .





 7:        





7.

    .      â€” .





7.1.

- tsx ( jsx). tsx ,  ReactPreact and Inferno. Tsx   HTML,     / HTML.  tsx ..     HTML, .





  ,   , ,   JSX .        React.





:         React.  react hooks  -     . API React     ,   .





  .   UI=F(S)





  • UI â€”





  • F â€”





  • S â€” (   â€” )





:





interface ITodoItemAttributes {
  name: string;
  status: TodoStatus;
  toggleStatus: () => void;
  removeTodo: () => void;
}

const TodoItemDisconnected = (props: ITodoItemAttributes) => {
  const className = props.status === TodoStatus.Completed ? 'completed' : '';
  return (
    <li className={className}>
      <div className="view">
        <input className="toggle" type="checkbox" onChange={props.toggleStatus} checked={props.status === TodoStatus.Completed} />
        <label>{props.name}</label>
        <button className="destroy" onClick={props.removeTodo} />
      </div>
    </li>
  )
}

      
      



  todo  TodoMVC .





   â€”   JSX. .     ,   «».





   [6.1]  [6.2].





:   react  TodoMVC     .





7.2. ()

,     TypeScript   -:





  • .





  •   domain/application dependency injection.





,   , .





(reactive UI).   .  WPF (C#)   Model-View-ViewModel.  JavaScript ,   (observable) (stores)  flux.    ,   :





  • .





  • ,          .





  • .





,    .





  :





  • , ,     .





  •          ,   .





  mobx  , . :





class TodosVM {
    @mobx.observable
    private todoList: ITodoItem[];

    // use "poor man DI", but in the real applications todoDao will be initialized by the call to IoC container 
    constructor(props: { status: TodoStatus }, private readonly todoDao: ITodoDAO = new TodoDAO()) {
        this.todoList = [];
    }
    public initialize() {
        this.todoList = this.todoDao.getList();
    }
    @mobx.action
    public removeTodo = (id: number) => {
        const targetItemIndex = this.todoList.findIndex(x => x.id === id);
        this.todoList.splice(targetItemIndex, 1);
        this.todoDao.delete(id);
    }
    public getTodoItems = (filter?: TodoStatus) => {
        return this.todoList.filter(x => !filter || x.status === filter) as ReadonlyArray<Readonly<ITodoItem>>;
    }
/// ... other methods such as creation and status toggling of todo items ...
}

      
      



   mobx ,     .





     mobx   .   mobx.       .





 {status: TodoStatus}



.   [6.6].      . :





interface IVMConstructor<TProps, TVM extends IViewModel<TProps>> {
    new (props: TProps, ...dependencies: any[]) : TVM;
}
interface IViewModel<IProps = Record<string, unknown>> {
    initialize?: () => Promise<void> | void;
    cleanup?: () => void;
    onPropsChanged?: (props: IProps) => void;
}

      
      



. :













  • (-).





   , ( statefull).      .





  7, .   DOM(mounted)      (unmounted).   (higher order components).





:





 type TWithViewModel = <TAttributes, TViewModelProps, TViewModel>
  (
    moduleRootComponent: Component<TAttributes & TViewModelProps>,
    vmConstructor: IVMConstructor<TAttributes, TViewModel>,
  ) => Component<TAttributes>

      
      



moduleRootComponent, :





  •   (mount) .





  • () (unmount).





      TodoMVC . .. IoC ,   .





:





const TodoMVCDisconnected = (props: { status: TodoStatus }) => {
    return <section className="todoapp">
        <Header />
        <TodoList status={props.status} />
        <Footer selectedStatus={props.status} />
    </section>
};
const TodoMVC = withVM(TodoMVCDisconnected, TodosVM);

      
      



  ( ,   ),  <TodoMVC status={statusReceivedFromRouteParameters} />



. ,  TodosVM



  -  TodoMVC



.





,   , withVM.





  • TodoMVCDisconnected    





  • TodoMVC  ,    





  • TodosVM  . , ,    mobx .





:    ,  withVM   react context API.     . ,        â€”  connectFn   .





7.3.

  , / .   6        .   .





«» , ( )   /, .   (slicing function). , ,   ?





 8: ( /slicing function)





  (  ):





type TViewModelFacade = <TViewModel, TOwnPropsTVMProps>(vm: TViewModel, ownProps?: TOwnProps) => TVMProps
      
      



  connect   Redux.    mapStateToProps



mapDispatchToActions



  mergeProps



   â€” ,   .  TodoItemDisconnected



   TodosVM



.





const sliceTodosVMProps = (vm: TodosVM, ownProps: {id: string, name: string, status: TodoStatus; }) => {
    return {
        toggleStatus() => vm.toggleStatus(ownProps.id),
        removeTodo() => vm.removeTodo(ownProps.id),
    }
}

      
      



:   , ‘OwnProps’ -    react/redux.





 â€”   .      withVM



. ,  ,   â€” ,  :





type connectFn = <TViewModel, TVMProps, TOwnProps = {}>
(
    ComponentToConnect: Component<TVMProps & TOwnProps>,
    mapVMToProps: TViewModelFacade<TViewModel, TOwnProps, TVMProps>,
) => Component<TOwnProps>
const TodoItem = connectFn(TodoItemDisconnected, sliceTodosVMProps);

      
      



  todo : <TodoItem id={itemId} name={itemName} status={itemStatus} />







 connectFn



  :





  •  TodoItemDisconnected



        sliceTodosVMProps



     â€”        JSX.





  • , , , .





   connectFn  TodoMVC ,   .





8.

,   ,  . TypeScript , , TSX â€”    .





SPA .     SPA  Â«   »  Â«   ».





 ,       ?





-  mobx, react  mobx-react   , :





  •  mobx





  • - , .   TodoMVC    react-router  react-router-dom.





  •   , , JSX.





,     .

  , .





      . React   ,        .





P.S.      SPA:

  •     React/Redux:  reducersaction creators  middlewares. ( stateful). time-travel. . connect     . Redux-dirven        connected  .     ,   .





  •    vue: TSX.    ,   , . Vue.js     ‘data’,’methods’,  .. vue-    .





  •    angular: TSX. angular-    .   (two-way data binding). : , ,  .





  •     react   (hooks,  useState/useContext): .   , -      . :





    •   .





    • useEffect ‘deps’ .





    •   .





    •     .





    , (   â€” useEffect) .   ,   «», « (mental model)» « (best practices)».      react.      :





    •   ?





    • ,   /     , ?  â€”     . :    





  •    react-mobx .   react-mobx     .   .   .





  • ComparĂ© Ă   mobx-state-tree : Les Viewmodels sont des classes rĂ©guliĂšres et ne nĂ©cessitent pas l'utilisation de fonctions de bibliothĂšques tierces, ni ne doivent satisfaire l'interface dĂ©finie par des frameworks tiers. La dĂ©finition de type  Ă  l'intĂ©rieur de l'arborescence d'Ă©tat de mobx repose sur les fonctions spĂ©cifiques de ce paquet. L'utilisation de mobx-state-tree en conjonction avec TypeScript provoque la duplication des informations - les champs de type sont dĂ©clarĂ©s comme une interface TypeScript distincte mais doivent ĂȘtre rĂ©pertoriĂ©s dans l'objet utilisĂ© pour dĂ©finir le type.





L'article original en anglais dans le blog de l'auteur (moi)








All Articles