Recall our definition of directory structure in engineering development, generally divided into two kinds, single-page multi-module, multi-page multi-module. In a single-page multi-module engineering structure, we will consider the reuse of the module, such as: How to extract public things (styles, functions, etc.) to facilitate the reuse of other modules. In multi-page multi-module scenarios, it is the same, but in addition to the global common styles and methods extracted to the public directory, we will also use a number of local modules as a common module processing.
a discussion on the problems of common development mode
is a single-page multi-module Project catalog structure diagram:
. ├──gruntfile.js├──package.json├──build└──src ├──base │ ├──base.sass │ └──global.js ├──mods │ ├──preference │ │ ├──index.js │ │ ├──index.sass │ │ └──index.xtpl.html │ ├──promo │ ├──qr │ └──response └──index.js
We put the source in the SRC folder, the public files (Iconfont, sprite Pictures, CSS and JS, etc.) into the base directory, each module in the page will create a new folder under the mods, used index.js
to manage the rendering of the module.
//Index.jsDefinefunction(require) {varLazyload = require (' Lazyload ')); varPreference = require ('./mods/preference/index ')); varQr = require ('./mods/qr/index ')); varPromo = require ('./mods/promo/index ')); varResponse = require ('./mods/response/index ')); NewResponse (); if(XXX) {Newpromo (); } lazyload (function(){ NewQr (); NewPreference (); });});
Such engineering structure is very general, the structure is relatively clear, but in the management of the module, there will be two problems:
- AB module has more common code, we have two ways to deal with, one is to extract the public part of the base directory, and the second is the B module directly according to the relative path reference A module. Once the business needs, said A module to the offline, after the offline, the first solution placed in the base directory of the code is unreasonable, the second scenario in the B module can not be used, you need to move the part of a module to the B module.
- Problem 1 Inverse process: There is currently a module on the line, business needs to add a module similar to a B module, if you want to directly reuse the code of a module, one way is to split the A module more granular, then B uses a relative path to refer to a, the other way is to extract the common code of a to base Under Both processes have a certain amount of work, and problems that are mentioned in question 1 are also present.
In fact, after all, the coupling of modules is high, as long as there is an intersection between the modules, the change of one module may affect other modules. Multi-person development, there are other aspects of the problem:
- Not every developer to take over the project has a global control, offline a module, will not dare to delete the base directory and related to the module, and even do not dare to delete the module, just commented on the initialization of the
index.js
module. Over the rest of the program, the redundant code will seep into every part of the project ...
- Modifying a module needs to compile and package all the code (in some cases need to compile, such as the existence of an offline template, the HTML module compiled into JS), this is very inefficient debugging, and this module error, it can cause the entire program crash.
- Code History version Management granularity is not enough, such as I modified A, B, C three modules, followed by three times, now to roll back the operation of modify A, how to handle? If the ABC three modules are able to manage code with code management tools, it's much easier to roll back.
Second, modular processing
The way to decouple is to reduce the share between the modules, when there is no common content between the modules, the coupling degree is basically zero.
. ├──INIT.JS├──BUILD└──SRC <git> │ ├──index.js │ ├──index.sass │ └──index.xtpl.html <git> <git><git>
As shown, there are a lot less things than the previous structure:
index.js
Initializing the module something is missing, one moreinit.js
base
The catalogue is missing.
- Each module becomes a git repository
1. Initialization of the script
Let's see what we init.js
're doing:
//Init.jsvar$mods = $ ("[Tb-mods]"); $mods. each (Functon ($mod) {if($mod. attr ("Finish")!==Finish_tag) {$mod. attr ("Finish", Finish_tag); //Lazy loading required lazy loading if($mod. attr ("Lazyload") {lazyload ($mod); return; } //Otherwise, direct initializationS.use ($mod. attr ("path"),function(S, Mod) {NewMod ($MOD); }); }});functionlazyload () {//code here.}
init.js
The module is no longer precisely initialized, the document is traversed from top to bottom, the module is directly initialized, if you need lazy loading to join the lazy load queue, developers do not care about how many modules on the page, not to mention the name of each module.
index.js
In require many many modules, each time to add a module or delete the module to change the file, but with init.js
the problem does not exist.
2. Version control of the module
<!--index.xtpl.html -<Divtb-mods lazyload Path= "tb/promo/1.0.0"></Div><Divtb-mods lazyload Path= "tb/qr/2.0.0"></Div><Divtb-mods lazyload Path= "tb/preference/2.2.1"></Div><Divtb-mods Path= "tb/response/3.0.2"></Div>
The DOM on the page is the identifier, and the DOM attribute identifies the script that executes the identity, and the order of execution is the order in which the DOM is placed.
Each module code is managed using a single git repository, which can better track the changes and versions of a single module, as well as solve the problem raised above (modify the ABC module in turn, and go online three times, if you need to roll back the A module, then the BC module changes will also be rolled back).
3. Abtest Requirements
After modifying a module, you only need to modify his version number in the DOM to go live. If you meet the needs of abtest, it is also very good to run:
<!--index.xtpl.html -{{#if condition}}<Divtb-mods lazyload Path= "tb/promo/1.0.0"></Div>{{Else}}<Divtb-mods Path= "tb/promo/2.0.0"></Div>{{/if}}<Divtb-mods lazyload Path= "tb/qr/2.0.0"></Div><Divtb-mods Path= "tb/response/3.0.2"></Div>
tb/promo
There are currently two versions, 1.0.0 and 2.0.0, the requirement is two versions with a probability of 50% to appear, directly in index.xtpl.html
doing as modified, the program is very clear.
4. Handling of public documents
So, where's the public code going? In fact, we do not want to have public code generation, the previous section has proposed the coupling to us the maintenance problem, but a project will inevitably have a lot of reusable things, especially when the page has a lot of similar modules.
1) Reuse of modules
A module of rendering, need two things, the 渲染壳子(模板) + 数据
rendered shell may be the same, but the data source is not the same, in many cases we can reuse a set of CSS and JS code, in the following way:
<!-- index.xtpl.html --> < div tb-mods lazyload path = "tb/promo/1.0.0" source = "Data/st/json/v2" ></ div > < div tb-mods lazyload path = "tb/promo/1.0.0" source = "Data/wt/json/v1" ></ div >
In two similar modules, we used the same set of JS- tb/promo/1.0.0
but used two different data sources data/st/json/v2
data/wt/json/v1
.
// Init.js $mods. Each (Functon ($mod) { if($mod. attr ("Finish")!== finish_tag) { // ... function (S, Mod) { // pass the data source to the new Mod ($mod, $mod. attr ("source")); } ); // ... }});
In the initialization script, we pass the data source that the module needs to use into the module initializer, so that the page succeeds in reusing tb/promo/1.0.0
the resource.
2) CSS reuse problem using less mixin processing
@a:red;@b:white;. S1 (){ color: @a; Background: @b;} { color: @a; Background: @b;}
Less is the pre-processing language of the CSS, the above code is packaged, .s1
it does not exist, only .s2
will be packaged, but both can be mixin to other classes:
{ . S1; . S2;}
Using this feature, we can wrap the shared CSS into a similar .s1
less code, which is mixin when needed in the module, and does not matter if it is not needed, and does not cause code redundancy.
3) JavaScript Code reuse problem
The page level of JS code is not much, for example, we usually use more frequently have Slide, Lazyload, Tab, Storage, etc., but these things are introduced in the form of components into the page. Think carefully, JS in which code is required to share the page? How much is the shared part relative to the file size of the whole project?
The basic library method we use is not exhaustive, for example: There is no method for URL parsing, unparam
and this method is used more and hopefully put in the public section. Back to think, such a small function to achieve what is the difficulty, three or four lines of code can be written out of things, it is recommended to put inside the component. This results in a certain amount of code redundancy, but the decoupling gains are entirely acceptable compared to the cost of writing a few lines of code.
Page common statistical Code, error collection code, data caching scheme, component communication code, and so on, the volume is relatively large, the use of more frequent content, can be encapsulated into components, introduced in the form of components.
A lot of thinking is needed here ...
5. Communication between modules
Communication between modules the most tangled thing is that a module wants to talk to the B module, but the B module is not initialized yet. So we need to introduce a man-in-the-middle s, each module after the initialization is successful to ask the S, there is no one to leave me a message.
// B give a message, if a exists, then send msg directly to a // Message Queue fed to S if not present S.tell ("A", { "B", msg: {}}); // when a module is initialized, get messages from other modules function (msg) { // dosomething ...});
Third, summary
There are many things that are not covered in the subject matter, and are not listed.
The more people involved in the project development, the more difficult the code to maintain, constraints are only temporary, programming, encoding format and other constraints do not fundamentally solve the problem, once the constraints of the point is not covered, the structure will begin to scatter, and eventually inevitably usher in a whole reconstruction.
Methods and results can not change habits, so we should start from the pattern.
Discussion of front-end engineering architecture