Ristrutturazione di una grande estensione di Chrome / WebApp

7

Ho un Chrome Extension molto complesso che è diventato troppo grande per essere mantenuto nel suo formato attuale. Mi piacerebbe ristrutturarlo, ma ho 15 anni e questa è la prima webapp o estensione del suo tipo che ho creato, quindi non ho idea di come farlo.

TL; DR: ho una webapp grande / complessa che vorrei ristrutturare e non so come farlo. Devo seguire il mio attuale piano di ristrutturazione (sotto)? Suona come un buon punto di partenza, o c'è un approccio diverso che mi manca? Dovrei non fare nessuna delle cose che ho elencato?

Sebbene non sia pertinente alla domanda, il codice effettivo è Github e l'estensione è sul webstore .

La struttura di base è la seguente:

index.html

<html>
    <head>
        <link href="css/style.css" rel="stylesheet" /> <!-- This holds the main app styles -->
        <link href="css/widgets.css" rel="stylesheet" /> <!-- And this one holds widget styles -->
    </head>

    <body class="unloaded">
        <!-- Low-level base elements are "hardcoded" here, the unloaded class is used for transitions and is removed on load. i.e: -->

        <div class="tab-container" tabindex="-1">           
            <!-- Tab nav -->
        </div>

        <!--
            Templates for all parts of the application and widgets are stored as elements here.
            I plan on changing these to <script> elements during the restructure since <template>'s need valid HTML.
        -->
        <template id="template.toolbar">
            <!-- Template content -->
        </template>

        <!-- Templates end -->

        <!-- Plugins -->
        <script type="text/javascript" src="js/plugins.js"></script>

        <!-- This contains the code for all widgets, I plan on moving this online and downloading as necessary soon. -->
        <script type="text/javascript" src="js/widgets.js"></script>

        <!-- This contains the main application JS. -->
        <script type="text/javascript" src="js/script.js"></script>
    </body>
</html>

widgets.js

(initLog || (window.initLog = [])).push([new Date().getTime(), "A log is kept during page load so performance can be analyzed and errors pinpointed"]);

// Widgets are stored in an object and extended (with jQuery, but I'll probably switch to underscore if using Backbone) as necessary
var Widgets = {
    1: { // Widget ID, this is set here so widgets can be retreived by ID
        id: 1, // Widget ID again, this is used after the widget object is duplicated and detached
        size: 3, // Default size, medium in this case
        order: 1, // Order shown in "store"
        name: "Weather", // Widget name
        interval: 300000, // Refresh interval
        nicename: "weather", // HTML and JS safe widget name
        sizes: ["tiny", "small", "medium"], // Available widget sizes
        desc: "Short widget description",
        settings: [
            { // Widget setting specifications stored as an array of objects.  These are used to dynamically generate widget setting popups.
                type: "list",
                nicename: "location",
                label: "Location(s)",
                placeholder: "Enter a location and press Enter"
            }
        ],
        config: { // Widget settings as stored in the tabs object (see script.js for storage information)
            size: "medium",
            location: ["San Francisco, CA"]
        },
        data: {}, // Cached widget data stored locally, this lets it work offline
        customFunc: function(cb) {}, // Widgets can optionally define custom functions in any part of their object
        refresh: function() {}, // This fetches data from the web and caches it locally in data, then calls render. It gets called after the page is loaded for faster loads
        render: function() {} // This renders the widget only using information from data, it's called on page load.
    }
};

script.js

(initLog || (window.initLog = [])).push([new Date().getTime(), "These are also at the end of every file"]);

// Plugins, extends and globals go here. i.e. Number.prototype.pad = ....

var iChrome = function(refresh) { // The main iChrome init, called with refresh when refreshing to not re-run libs
    iChrome.Status.log("Starting page generation"); // From now on iChrome.Status.log is defined, it's used in place of the initLog

    iChrome.CSS(); // Dynamically generate CSS based on settings

    iChrome.Tabs(); // This takes the tabs stored in the storage (see fetching below) and renders all columns and widgets as necessary

    iChrome.Status.log("Tabs rendered"); // These will be omitted further along in this excerpt, but they're used everywhere

    // Checks for justInstalled => show getting started are run here

    /* The main init runs the bare minimum required to display the page, this sets all non-visible or instantly need things (such as widget dragging) on a timeout */
    iChrome.deferredTimeout = setTimeout(function() {
        iChrome.deferred(refresh); // Pass refresh along, see above
    }, 200);
};

iChrome.deferred = function(refresh) {}; // This calls modules one after the next in the appropriate order to finish rendering the page

iChrome.Search = function() {}; // Modules have a base init function and are camel-cased and capitalized

iChrome.Search.submit = function(val) {}; // Methods within modules are camel-cased and not capitalized


/*
  Extension storage is async and fetched at the beginning of plugins.js, it's then stored in a variable that iChrome.Storage processes.

  The fetcher checks to see if processStorage is defined, if it is it gets called, otherwise settings are left in iChromeConfig
*/
var processStorage = function() {
    iChrome.Storage(function() {
        iChrome.Templates(); // Templates are read from their elements and held in a cache

        iChrome(); // Init is called
    });
};

if (typeof iChromeConfig == "object") {
    processStorage();
}

Obiettivi della ristrutturazione

  1. Utilizzo della memoria: Chrome sembra avere una perdita di memoria nelle estensioni, stanno cercando di risolverlo ma la memoria continua ad aumentare ogni volta che viene caricata la pagina. Anche l'app usa molto da sola.
  2. Leggibilità del codice: A questo punto non riesco a seguire ciò che viene chiamato nel codice. Mentre riscrivo il codice, ho intenzione di commentare correttamente tutto.
  3. Interdipendenza del modulo: In questo momento i moduli si richiamano molto, AFAIK non va affatto bene dato che qualsiasi modifica apportata a un modulo potrebbe interessare innumerevoli altri.
  4. Tolleranza agli errori: al momento la tolleranza agli errori o la gestione degli errori sono molto ridotti. Se un widget sta causando il blocco del resto della pagina, l'utente dovrebbe almeno rimuoverlo.

La velocità non è attualmente un problema e mi piacerebbe mantenerla in questo modo.

Come penso che dovrei farlo

  • La ristrutturazione dovrebbe essere eseguita utilizzando Backbone.js e gli eventi che chiamano i moduli (cioè su storage.loaded = > init).
  • I moduli dovrebbero andare ognuno nel proprio file, penso che ci dovrebbe essere un insieme di file core su cui tutti i moduli possono fare affidamento e chiamare direttamente e tutto il resto dovrebbe essere basato su eventi.
  • La struttura del widget dovrebbe essere mantenuta in gran parte uguale, ma forse dovrebbero anche essere suddivisi nei propri file.
  • AFAIK non puoi caricare tutti i modelli in una cartella, quindi devono rimanere in linea.
  • Grunt dovrebbe essere usato per unire tutti i moduli, plugin e widget in un unico file. Anche i modelli dovrebbero essere precompilati.

Domanda:

Devo seguire il mio attuale piano di ristrutturazione? Suona come un buon punto di partenza, o c'è un approccio diverso che mi manca? Dovrei non fare nessuna delle cose che ho elencato?

Le applicazioni scritte con Backbone tendono ad essere più intensive (memoria e velocità) di quelle scritte in Vanilla JS?

Inoltre, posso aspettarmi di migliorare questo con una ristrutturazione adeguata o il mio codice attuale è buono come ci si può aspettare?

    
posta A.M.K 27.05.2014 - 19:33
fonte

1 risposta

2

Memory usage: Chrome apparently has a memory leak in extensions, they're trying to fix it but memory still keeps on getting increased every time the page is loaded. The app also uses a lot on its own.

È necessario definire cosa sia "alot" e quale sia "troppo". L'utilizzo della memoria è solo un problema quando non ce n'è abbastanza. Lo studio dei problemi di memoria ha poco a che fare con il refactoring. È necessario individuare qualsiasi codice offendente e collegare la perdita. Se sai che il tuo codice sta perdendo memoria, allora proverei a isolare le aree incriminate e andare su stackoverflow.com per chiedere consigli su come risolverlo.

Code readability: At this point I can't follow what's being called in the code. While rewriting the code I plan on properly commenting everything.

Questa è una questione molto soggettiva. La struttura del codice, i commenti del codice sorgente e la leggibilità del codice sono tutti argomenti di opinione.

Module interdependence: Right now modules call each other a lot, AFAIK that's not good at all since any change you make to one module could affect countless others.

Ora è qui che possiamo aiutarti. Ci sono più piattaforme e strumenti disponibili per gli sviluppatori di Javascript per risolvere questi problemi. Esistono anche diversi modelli di progettazione che risolvono questi problemi. Esistono costruttori di pacchetti, compilatori di codice e iniettori di dipendenza.

Uso AngularJS con il compilatore Closure di Google. Angular mi dà l'iniezione di dipendenza, e la chiusura mi dà avvisi di tempo e packaging in fase di compilazione.

Dovrai decidere quali strumenti funzionano per te. Fidati di me. È un processo doloroso scegliere uno strumento.

Fault tolerance: There's very little fault tolerance or error handling right now. If a widget is causing the rest of the page to stop rendering the user should at least be able to remove it.

Questo è un requisito di alto livello. È qualcosa da "tenere a mente" quando lo stai riscrivendo.

Do applications written with Backbone tend to be more intensive (memory and speed) than ones written in Vanilla JS?

No. Backbone non offre nulla che risolva i tuoi problemi specifici. È progettato per essere una libreria leggera che offre solo un paio di classi base per simulare un pattern MVC. Gli sviluppatori volevano qualcosa che avesse un'impronta molto leggera. Non penso che un'estensione di Chrome debba essere leggera. È installato sul PC e funziona in locale. Puoi permetterti di andare con qualcosa di più pesante e offre una struttura più solida.

Also, can I expect to improve this with a proper restructure or is my current code about as good as can be expected?

Hai commesso alcuni errori di base se risolto migliorerai notevolmente il tuo tempo mantenendo il codice.

Potrei creare un elenco di problemi, ma non è questo il punto di questa risposta.

Quello che vuoi imparare è la base di un codice altamente gestibile.

  • Hai messo il codice in un file facile da individuare, oppure è tutto in un unico file?
  • Puoi dire guardando i tuoi file cos'è "nuovo codice non testato" e "vecchio codice affidabile"?
  • Puoi testarlo?
  • Ogni file ha un nome chiaro e contiene un codice che è chiaro al suo scopo.
  • Ogni file è semplice. Breve e al punto.
  • Le tue funzioni sono scritte per prevenire l'abuso.

L'elenco continua così, ma spero che tu abbia l'idea.

    
risposta data 27.05.2014 - 20:30
fonte

Leggi altre domande sui tag