When it comes to project architecture, there are many things to consider:
- Convenient. For example, using jquery is a lot easier than not using jquery, so most websites have access to similar libraries;
- Performance optimization. Including loading speed, rendering efficiency;
- Code management. Large-scale projects need to consider the modularity of the code, low-coupling between the high-cohesion, for the purpose of team cooperation efficiency;
- Scalability. That's needless to say.
- Learning costs. A good framework, the new team members are difficult to master, learning difficult, the result is easy to cause code confusion.
According to the actual experience, convenience is the inevitable priority, in addition, it should be code management. Teamwork, collaboration, code conflicts, and so on, all pose strange challenges to an excellent framework. So, having a good framework is not enough, we also need to tailor or modify the framework to find the best implementation based on our own business and team situation.
First talk about the 1th kind: requirejs+angular+angular-route
Mobile Single page Web relative multi-page, modular management is very important, because if not modular, the page initialization when all the JS and all templates are loaded in, will cause the first screen speed extremely slow. This, we all understand.
So, Requirejs or a similar modular framework is essential. Requirejs is more popular, with Grunt can do a complete set of automation tools, we take this as an example.
First, take a look at the overall architecture of the demo project.
In addition to the class library, the business code is divided into modules directory, so as to facilitate the actual development, according to the modular combination of JS and HTML, but also conducive to the parallel development of many people, each modified different modules, do not affect each other.
Also, say three key root files:
- Index.html, this is the single page of the only one HTML, the other is just the fragment template (tpl.html). You can usually put this HTML on the dynamic server, keep 0 cache, while here can carry a variety of JS version control information and necessary user data.
- Main.js, this is the first business JS introduced by Requirejs, mainly configuration Requirejs;
- Router.js, this is the entire site/app routing configuration, in the actual deployment, you can combine Main.js and router.js.
First step, let's see what changes index.html need to make.
<! DOCTYPE html>
Relative angular, here because the use of Requirejs to manage all modules, so index.html do not need to introduce angular, etc., just set up a div with Ng-view attribute, used to act as the entire app view area.
Data-baseurl is an added property, and the main benefit is that you can easily modify the URL of JS in HTML (0 cache).
Data-main is REQUIREJS standard notation, skip not to say.
The second step, Main.js, is the configuration of Requirejs
' Use strict ';(function (Win) {//config baseUrl var baseUrl = document.getElementById (' main '). getattribute (' Data-baseurl ') ); /* * File dependent */var config = {Baseurl:baseurl,//Dependent relative path paths: {//IF The dependency of a prefix is not as simple as baseurl stitching, it needs to be pointed out here underscore: ' Libs/underscore ', angular: ' Libs/angular ', ' Angular-route ': ' Libs/angular-route ', text: ' Libs/text '//For REQUIREJS import of HTML type dependency}, Shim: {//Introduce a class library that does not use the Requirejs module. For example underscore this class library, there would have been a global variable ' _ '. Here shim is equivalent to quickly define a module, the original global variable ' _ ' encapsulated in the local, and exported to a exports, and become the same as the normal Requirejs module underscore: {exports: ' _ ' }, Angular: {exports: ' angular '}, ' Angular-route ': { Deps: [' angular '],//dependent on what module exports: ' Ngroutemodule '}}; Require.config (config); require ([' angular ', ' router '], function (AnguLar) {angular.bootstrap (document, [' WebApp ']); });}) (window);
Requirejs's syntax, a long story, is simply commented out in the code. Interested to know the details of the website: Http://requirejs.org/;angular can refer to: Https://docs.angularjs.org/guide/filter
Once the REQUIREJS is configured here, the first step is to introduce the routing configuration of angular and angular and then use
Angular.bootstrap (document, [' WebApp ']);
Manually start angular, where webapp is the angular module defined in Router.js.
The third step is to configure this router
define ([' angular ', ' require ', ' angular-route '], function (angular, require) {var app = Angular.module (' WebApp ', [ ' Ngroute ']); App. Config ([' $routeProvider ', ' $controllerProvider ', function ($routeProvider, $controllerProvider) {$ro Uteprovider. When ('/module1 ', {templateurl: ' module1/tpl.html ', controller: ' Module1controller ', Resolve: {/* This key value is injected into the controller, corresponding to the function returned by the Value, or promise the value of the final resolve. The parameters of the function are the required services, and angular automatically injects the corresponding controller notation according to the parameter name (note keyname): controllers.contr Oller (' Module2controller ', [' $scope ', ' $http ', ' keyName ', function ($scope, $http, KeyName) { }]); */Keyname:function ($q) {var deferred = $q. Defer (); Require (['Module1/module1.js '], function (Controller) {$controllerProvider. Register (' Module1controll Er ', controller); Because the controller is dynamically loaded, register first, then use Deferred.resolve (); }); return deferred.promise; } } }). Otherwise ({redirectto: '/module1 '//angular like the slash start}); }]); return app;});
The above code looks long, actually very short, because there are a bunch of green notes, hey ...
If you have used Angular-route, the syntax here is very simple, if not used, it is recommended to read directly in the Angular-route source code comments, very clear.
In simple terms, the When function configures a routing rule that corresponds to a template and a controller. Otherwise is the default route, that is, how to jump when encountering an undefined path.
If Requirejs is not used, then we need to load the full controller before the routing configuration. All Angular-route need to do is switch the HTML template, recompile, and bind the new controller.
But.
But..
With the requirejs here, things change. We want to load on demand, it is impossible for the page to just load all controller load back, so how much traffic ...
So, this takes advantage of the resolve function provided by Angular-route, which is to do the resolve inside before routing changes to HTML.
The resolve is very special, accepting a Key:value object, KeyName will be imported into the controller (if the controller has a dependency). and value should be a function that resembles controller,angular automatically imports dependent services based on the name of the parameter, such as $q, $route.
In the example above, Module1.js defines the controller for Module 1, and then we look at the code later.
Because the controller does not exist before the routing configuration, it is now necessary to register the controller dynamically. That is
$controllerProvider. Register (' Module1controller ', Controller);
Fourth step, see how the Controller for Module 1 is written.
define ([' angular '), function (angular) { //angular will automatically import the corresponding service return function ($scope, $, according to the parameter name of the controller function). HTTP, $interval) { $scope. info = ' Kenko '; Inject data //simulation request to view/template CGI gets the data, after the data is returned, automatically modifies the interface without needing verbose $ (' #xxx '). html (XXX) $http. Get (' module2/tpl.html '). Success (data) { $scope. info = ' Vivi '; }); var i = 0; Angularjs modified the original settimeout and setinterval, to use these two things, you must introduce $timeout and $interval, otherwise you can not modify the scope of the angular $interval ( function () { i++; $scope. info = i; }, +);}; });
Angular has too many awesome features, but in fact my business is too simple to use. So here are just 3 of the simplest things to show.
It has to be said that, because of two-way binding, it is very easy to pull CGI and modify the DOM.
Seems.
Seems...
Everything's settled? This kind of modularity seems to be good enough to jump to a module before loading the corresponding HTML and controller JS.
But.
But..
For the pursuit of the ultimate team, the module's HTML and JS should be packaged together, once the request is pulled back, this can greatly reduce the time of the HTTP request. Now, according to Angular-route, you can only use Templateurl to pull an HTML file alone.
So then, let's move on and revise.
Fifth step, modify the Angular-route, implement HTML and JS package loading.
function Ngviewfillcontentfactory ($compile, $controller, $route) { return { restrict: ' ECA ', priority :- link:function (Scope, $element) {var current = $route. Current, locals = current.locals; $element. html (current.template); It turned out to be locals. $template
First of all, the first modification of Angular-route source code, the source code is very concise, not too tangled, mercilessly to modify it.
In addition, want to ask me why do I know or think in this change? Cough cough, I will strut to say I know the author of Angular-route? Joking, what the author called, I did not go to find, also said to know the author. In fact, is the gradual adjustment, a little variable search, found something wrong, did this knife.
In addition, there are experts to make a decision, such a disorderly change, certainly bring trouble. Yes, I have to say, I did not thoroughly check whether there is a problem, but according to the actual situation, there is no problem at the moment.
Then, make a new when configuration:
When ('/module2 ', { template: ', controller: ' Module2controller ', resolve:{ keyname:function ($ Route, $q) { var deferred = $q. Defer (); Require ([' module2/module2.js '], function (module2) { $controllerProvider. Register (' Module2controller ', Module2.controller); $route. current.template = MODULE2.TPL; Deferred.resolve (); }); return deferred.promise;}} )
Here is an example with module2, unlike Module1, where the initial set of the template is an empty string, and then require back in resolve, dynamically modify $route.current.template.
Because I know that this change will be able to catch up with the angular-route before modifying HTML, that is, the trick can be effective.
Accordingly, see how Module2 writes:
define ([' angular ', ' text!module2/tpl.html '], function (angular, TPL) { //angular automatically imports the appropriate service according to the parameter name of the controller function return { controller:function ($scope, $http, $interval) { $scope. Date = ' 2015-07-13 '; }, Tpl:tpl };});
Finished, so that the HTML template will not be taken over by the Angular-route, but by the requirejs load, we can control the scope and flexibility to become larger.
However, the function of the controller in this case may be confused by the compression of the original parameter name, so we can also take the explicit injection method:
You can also use this explicit injection method, which reads the $inject controller before angular executes the controller function . $inject = [' $scope ']; function controller (s) { s.date = ' 2015-07-13 '; } return {controller:controller, tpl:tpl};
Here, the whole structure is basically formed, each module in WebApp can be very independent, so it is very good for Web site opening speed and co-development.
However, the configuration of the routing table is still slightly more complex, each time we have to write a lot of code, this is not what we want, then you can extract the common code, and then optimize.
The sixth step is to optimize the routing table to become a true configuration.
define ([' angular ', ' require ', ' angular-route '], function (angular, require) {var app = Angular.module (' WebApp ', [ ' Ngroute ']); App. Config ([' $routeProvider ', ' $controllerProvider ', function ($routeProvider, $controllerProvider) {var Routemap = {'/module2 ': {//route path: ' Module2/module2.js ', The code path of the module controller: ' Module2controller '//Controller name}}; var defaultroute = '/module2 '; The default jumps to a route $routeProvider. Otherwise ({redirectto:defaultroute}); for (var key in Routemap) {$routeProvider. When (key, {Template: ', Controller:routemap[key].controller, resolve:{keyname:requiremodule (routemap[ Key].path, Routemap[key].controller)}}); } function reqUiremodule (path, controller) {return function ($route, $q) {var deferred = $q. Defer (); require ([path], function (ret) {$controllerProvider. Register (Controller, RET.C Ontroller); $route. current.template = RET.TPL; Deferred.resolve (); }); return deferred.promise; } } }]); return app;});
Routemap can be directly out of the server, achieve 0 cache, complete decoupling, more convenient for team work.
Finally, since both Requirejs and angular have module management, but the two concepts are inconsistent, here's what I think about it:
- Requirejs module Management, not only the code modularity, but also provides the function of module loading;
- Angular module management, more concerned about the code logic on the modularization, to avoid global variable pollution, does not provide JS file level loading function;
- As a logical module management, in fact, with REQUIREJS module management is enough, so I think in addition to angular the original controller, service, our business-related public library, with Requirejs bar.
H5 Single page Schema: Requirejs + angular + angular-route