Apabila apl satu halaman anda berkembang, begitu juga masa muat turunnya. Ini tidak akan memberi banyak manfaat untuk pengalaman pengguna (petunjuk: tetapi pengalaman pengguna itulah sebabnya kami membangunkan apl satu halaman). Lebih banyak kod bermakna fail yang lebih besar, dan sehingga pemampatan kod tidak lagi memenuhi keperluan anda, satu-satunya perkara yang boleh anda lakukan untuk pengguna anda ialah berhenti meminta mereka memuat turun keseluruhan aplikasi sekali gus. Di sinilah pemuatan malas berguna. Daripada memuat turun semua fail sekali gus, pengguna dibenarkan memuat turun hanya fail yang dia perlukan sekarang.
Jadi. Bagaimana untuk menjadikan aplikasi anda malas memuatkan? Ia pada asasnya terbahagi kepada dua perkara. Pisahkan modul anda kepada bahagian kecil dan laksanakan beberapa mekanisme yang membolehkan bahagian ini dimuatkan atas permintaan. Kedengaran seperti banyak kerja, bukan? Tidak jika anda menggunakan Webpack. Ia menyokong ciri pemisahan kod di luar kotak. Dalam artikel ini saya menganggap anda biasa dengan Webpack, tetapi jika anda tidak, berikut adalah pengenalan. Untuk menyingkat cerita, kami juga akan menggunakan AngularUI Router dan ocLazyLoad.
Kod ini tersedia di GitHub. Anda boleh garpu pada bila-bila masa.
Konfigurasi pek web
Tiada yang istimewa, sungguh. Anda sebenarnya boleh menyalin dan menampal daripada dokumentasi, satu-satunya perbezaan ialah penggunaan ng-anotasi untuk memastikan kod kami bersih, dan babel untuk menggunakan beberapa sihir ECMAScript 2015. Jika anda berminat dengan ES6, anda boleh menyemak siaran sebelum ini. Walaupun perkara ini semuanya hebat, tiada satu pun daripada mereka perlu untuk melaksanakan pemuatan malas.
// 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;
Mohon
Modul aplikasi ialah fail utama, yang mesti disertakan dalam bundle.js, yang diperlukan untuk dimuat turun pada setiap halaman. Seperti yang anda lihat, kami tidak memuatkan apa-apa yang rumit kecuali kebergantungan global. Tidak seperti memuatkan pengawal, kami hanya memuatkan konfigurasi penghalaan.
// 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, ]);
Konfigurasi penghalaan
Semua pemuatan malas dilaksanakan dalam konfigurasi laluan. Seperti yang saya katakan, kami menggunakan Penghala AngularUI kerana kami perlu melaksanakan pandangan bersarang. Kami mempunyai beberapa kes penggunaan. Kami boleh memuatkan keseluruhan modul (termasuk pengawal keadaan kanak-kanak) atau memuatkan satu pengawal bagi setiap keadaan (tanpa mengira kebergantungan pada keadaan induk).
Muat keseluruhan modul
Apabila pengguna memasuki laluan /home, penyemak imbas akan memuat turun modul rumah. Ia termasuk dua pengawal, untuk dua negeri rumah dan rumah.tentang. Kita boleh melaksanakan pemuatan malas melalui atribut menyelesaikan dalam objek konfigurasi keadaan. Terima kasih kepada kaedah require.ensure Webpack, kami boleh mencipta modul rumah sebagai blok kod pertama. Ia dipanggil 1.bundle.js . Tanpa $ocLazyLoad.load , kami akan mendapati bahawa kami mendapat ralat Argument 'HomeController' is not a function, got undefined , kerana dalam reka bentuk Angular, ia tidak boleh dilakukan untuk memuatkan fail selepas memulakan aplikasi. Tetapi $ocLazyLoad.load membenarkan kami mendaftarkan modul semasa permulaan dan kemudian menggunakannya selepas ia dimuatkan.
// 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);
Pengawal dianggap sebagai kebergantungan modul.
// home.js 'use strict'; export default angular .module('home', [ require('./controllers/home.controller').name, require('./controllers/home.about.controller').name ]);
Muatkan pengawal sahaja
Apa yang kita lakukan ialah langkah pertama ke hadapan, kemudian mari kita teruskan ke langkah seterusnya. Kali ini, tidak akan ada modul besar, hanya pengawal yang diperkemas.
// 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); }) }); } } })
Saya percaya tiada apa yang istimewa di sini dan peraturannya boleh kekal sama.
Memuatkan Paparan
Sekarang, mari lepaskan pengawal seketika dan fokus pada paparan. Seperti yang anda mungkin perasan, kami membenamkan pandangan ke dalam konfigurasi penghalaan. Ini tidak akan menjadi masalah jika kita tidak meletakkan semua konfigurasi penghalaan di dalam bundle.js , tetapi sekarang kita perlu melakukannya. Kes ini bukan mengenai konfigurasi laluan memuatkan malas tetapi pandangan, jadi apabila kami menggunakan Webpack untuk melaksanakannya, ini akan menjadi sangat mudah.
// 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);
Berhati-hati dengan kebergantungan pendua
Mari kita lihat kandungan messages.all.controller dan 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);
Punca masalah kami adalah require('commons/msg-store').name . Ia memerlukan perkhidmatan msgStore untuk merealisasikan perkongsian mesej antara pengawal. Perkhidmatan ini terdapat dalam kedua-dua pakej. Terdapat satu dalam messages.all.controller dan satu lagi dalam messages.new.controller. Kini, ia tidak mempunyai ruang untuk pengoptimuman. Bagaimana untuk menyelesaikannya? Hanya tambah msgStore sebagai pergantungan modul aplikasi. Walaupun ini tidak sempurna, dalam kebanyakan kes ia sudah mencukupi.
// 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 的延迟加载,希望对大家有所帮助!