Heim >Web-Frontend >js-Tutorial >Webpack implementiert das verzögerte Laden von AngularJS_AngularJS
Wenn Ihre Single-Page-App wächst, erhöht sich auch die Downloadzeit. Das wird der Benutzererfahrung nicht viel nützen (Hinweis: Aber die Benutzererfahrung ist der Grund, warum wir Single-Page-Apps entwickeln). Mehr Code bedeutet größere Dateien, und bis die Codekomprimierung Ihren Anforderungen nicht mehr genügt, können Sie Ihren Benutzern nur noch weiterhelfen, sie nicht mehr aufzufordern, die gesamte Anwendung auf einmal herunterzuladen. Hier bietet sich Lazy Loading an. Anstatt alle Dateien auf einmal herunterzuladen, darf der Benutzer nur die Dateien herunterladen, die er gerade benötigt.
Also. Wie können Sie das Lazy Loading Ihrer Anwendung ermöglichen? Es ist im Grunde in zwei Dinge aufgeteilt. Teilen Sie Ihr Modul in kleine Blöcke auf und implementieren Sie einen Mechanismus, der es ermöglicht, diese Blöcke bei Bedarf zu laden. Klingt nach einer Menge Arbeit, nicht wahr? Nicht, wenn Sie Webpack verwenden. Es unterstützt sofort Code-Splitting-Funktionen. In diesem Artikel gehe ich davon aus, dass Sie mit Webpack vertraut sind. Falls nicht, finden Sie hier eine Einführung. Um es kurz zu machen: Wir werden auch AngularUI Router und ocLazyLoad verwenden.
Der Code ist auf GitHub verfügbar. Sie können es jederzeit forken.
Webpack-Konfiguration
Eigentlich nichts Besonderes. Eigentlich könnten Sie einfach etwas aus der Dokumentation kopieren und einfügen. Der einzige Unterschied besteht in der Verwendung von ng-annotate, um unseren Code sauber zu halten, und von babel, um etwas von der ECMAScript 2015-Magie zu nutzen. Wenn Sie an ES6 interessiert sind, können Sie sich diesen vorherigen Beitrag ansehen. Obwohl diese Dinge alle großartig sind, ist keines davon notwendig, um Lazy Loading zu implementieren.
// webpack.config.js var config = { entry: { app: ['./src/core/bootstrap.js'], }, output: { path: __dirname + '/build/', filename: 'bundle.js', }, resolve: { root: __dirname + '/src/', }, module: { noParse: [], loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'ng-annotate!babel' }, { test: /\.html$/, loader: 'raw' }, ] } }; module.exports = config;
Bewerben
Das Anwendungsmodul ist die Hauptdatei, die in bundle.js enthalten sein muss, das auf jeder Seite heruntergeladen werden muss. Wie Sie sehen, laden wir außer globalen Abhängigkeiten nichts Kompliziertes. Im Gegensatz zum Laden des Controllers laden wir nur die Routing-Konfiguration.
// app.js 'use strict'; export default require('angular') .module('lazyApp', [ require('angular-ui-router'), require('oclazyload'), require('./pages/home/home.routing').name, require('./pages/messages/messages.routing').name, ]);
Routing-Konfiguration
Alle verzögerten Ladevorgänge sind in der Routenkonfiguration implementiert. Wie gesagt, wir verwenden AngularUI Router, weil wir verschachtelte Ansichten implementieren müssen. Wir haben mehrere Anwendungsfälle. Wir können das gesamte Modul laden (einschließlich untergeordneter Statuscontroller) oder einen Controller pro Status laden (unabhängig von Abhängigkeiten von übergeordneten Status).
Das gesamte Modul laden
Wenn der Benutzer den /home-Pfad eingibt, lädt der Browser das Home-Modul herunter. Es enthält zwei Controller, für die beiden Staaten Home und Home.about. Wir können verzögertes Laden über das Auflösungsattribut im Statuskonfigurationsobjekt implementieren. Dank der require.ensure-Methode von Webpack können wir das Home-Modul als ersten Codeblock erstellen. Es heißt 1.bundle.js . Ohne $ocLazyLoad.load erhalten wir die Fehlermeldung Argument 'HomeController' is not a function, got undefined , da es im Angular-Design nicht möglich ist, Dateien nach dem Start der Anwendung zu laden. Aber $ocLazyLoad.load ermöglicht es uns, ein Modul während der Startphase zu registrieren und es dann zu verwenden, nachdem es geladen wurde.
// home.routing.js 'use strict'; function homeRouting($urlRouterProvider, $stateProvider) { $urlRouterProvider.otherwise('/home'); $stateProvider .state('home', { url: '/home', template: require('./views/home.html'), controller: 'HomeController as vm', resolve: { loadHomeController: ($q, $ocLazyLoad) => { return $q((resolve) => { require.ensure([], () => { // load whole module let module = require('./home'); $ocLazyLoad.load({name: 'home'}); resolve(module.controller); }); }); } } }).state('home.about', { url: '/about', template: require('./views/home.about.html'), controller: 'HomeAboutController as vm', }); } export default angular .module('home.routing', []) .config(homeRouting);
Controller werden als Modulabhängigkeiten behandelt.
// home.js 'use strict'; export default angular .module('home', [ require('./controllers/home.controller').name, require('./controllers/home.about.controller').name ]);
Nur Laderegler
Was wir tun, ist der erste Schritt nach vorne, dann lasst uns zum nächsten Schritt übergehen. Dieses Mal wird es keine großen Module geben, sondern nur schlanke Controller.
// messages.routing.js 'use strict'; function messagesRouting($stateProvider) { $stateProvider .state('messages', { url: '/messages', template: require('./views/messages.html'), controller: 'MessagesController as vm', resolve: { loadMessagesController: ($q, $ocLazyLoad) => { return $q((resolve) => { require.ensure([], () => { // load only controller module let module = require('./controllers/messages.controller'); $ocLazyLoad.load({name: module.name}); resolve(module.controller); }) }); } } }).state('messages.all', { url: '/all', template: require('./views/messages.all.html'), controller: 'MessagesAllController as vm', resolve: { loadMessagesAllController: ($q, $ocLazyLoad) => { return $q((resolve) => { require.ensure([], () => { // load only controller module let module = require('./controllers/messages.all.controller'); $ocLazyLoad.load({name: module.name}); resolve(module.controller); }) }); } } })
Ich glaube, dass es hier nichts Besonderes gibt und die Regeln gleich bleiben können.
Ansichten werden geladen
Lassen Sie uns nun für einen Moment den Controller loslassen und uns auf die Ansicht konzentrieren. Wie Sie vielleicht bemerkt haben, betten wir die Ansicht in die Routing-Konfiguration ein. Das wäre kein Problem, wenn wir nicht die gesamte Routing-Konfiguration in bundle.js unterbringen würden, aber jetzt müssen wir es tun. In diesem Fall geht es nicht um die Routenkonfiguration mit verzögertem Laden, sondern um die Ansicht. Wenn wir sie also mit Webpack implementieren, ist dies sehr einfach.
// messages.routing.js ... .state('messages.new', { url: '/new', templateProvider: ($q) => { return $q((resolve) => { // lazy load the view require.ensure([], () => resolve(require('./views/messages.new.html'))); }); }, controller: 'MessagesNewController as vm', resolve: { loadMessagesNewController: ($q, $ocLazyLoad) => { return $q((resolve) => { require.ensure([], () => { // load only controller module let module = require('./controllers/messages.new.controller'); $ocLazyLoad.load({name: module.name}); resolve(module.controller); }) }); } } }); } export default angular .module('messages.routing', []) .config(messagesRouting);
Vorsicht vor doppelten Abhängigkeiten
Werfen wir einen Blick auf den Inhalt von „messages.all.controller“ und „messages.new.controller“.
// messages.all.controller.js 'use strict'; class MessagesAllController { constructor(msgStore) { this.msgs = msgStore.all(); } } export default angular .module('messages.all.controller', [ require('commons/msg-store').name, ]) .controller('MessagesAllController', MessagesAllController); // messages.all.controller.js 'use strict'; class MessagesNewController { constructor(msgStore) { this.text = ''; this._msgStore = msgStore; } create() { this._msgStore.add(this.text); this.text = ''; } } export default angular .module('messages.new.controller', [ require('commons/msg-store').name, ]) .controller('MessagesNewController', MessagesNewController);
Die Ursache unseres Problems ist require('commons/msg-store').name . Es erfordert den msgStore-Dienst, um den Nachrichtenaustausch zwischen Controllern zu realisieren. Dieser Service ist in beiden Paketen enthalten. Es gibt einen in „messages.all.controller“ und einen weiteren in „messages.new.controller“. Jetzt gibt es keinen Raum mehr für Optimierungen. Wie kann man es lösen? Fügen Sie einfach msgStore als Abhängigkeit des Anwendungsmoduls hinzu. Das ist zwar nicht perfekt, reicht aber in den meisten Fällen aus.
// app.js 'use strict'; export default require('angular') .module('lazyApp', [ require('angular-ui-router'), require('oclazyload'), // msgStore as global dependency require('commons/msg-store').name, require('./pages/home/home.routing').name, require('./pages/messages/messages.routing').name, ]);
单元测试的技巧
把 msgStore 改成是全局依赖并不意味着你应该从控制器中删除它。如果你这样做了,在你编写测试的时候,如果没有模拟这一个依赖,那么它就无法正常工作了。因为在单元测试中,你只会加载这一个控制器而非整个应用模块。
// messages.all.controller.spec.js 'use strict'; describe('MessagesAllController', () => { var controller, msgStoreMock; beforeEach(angular.mock.module(require('./messages.all.controller').name)); beforeEach(inject(($controller) => { msgStoreMock = require('commons/msg-store/msg-store.service.mock'); spyOn(msgStoreMock, 'all').and.returnValue(['foo', ]); controller = $controller('MessagesAllController', { msgStore: msgStoreMock }); })); it('saves msgStore.all() in msgs', () => { expect(msgStoreMock.all).toHaveBeenCalled(); expect(controller.msgs).toEqual(['foo', ]); }); });
以上内容是小编给大家分享的Webpack 实现 AngularJS 的延迟加载,希望对大家有所帮助!