Translated from: http://kb.cnblogs.com/page/504518/
This article is the third of a series of articles on building a highly scalable web interactive system, taking the Nej framework of NetEase as an example to analyze and introduce the scalability of the module.
Example analysis
The NEJ framework is implemented according to the description of the first two articles, and the following is a concrete example of how to use the module scheduling system in Nej to split a complex system, develop test modules, integrate systems, and so on.
System decomposition
Draw a hierarchy diagram
When we get a complex system, we can draw a hierarchical diagram of the modules that make up the system according to the interactive draft, and determine the modules that the system can access externally.
Abstract dependency Tree
From the module's hierarchical diagram, we can easily abstract the dependency tree of the module:
We then format the abstract dependency tree according to the Umi rule. The main operations for formatting include:
- Add a root node called "/" (can also change "m" Node to "/")
- Each node adds "/" to the child nodes as the default node
The dependency tree for this output has the following characteristics:
- Any node (outside the root node) to the root node path node name is combined with "/" to be the Umi value of the node, such as the Umi value of the list node is/m/blog/list
- The modules on any node depend on the existence of modules on his ancestor nodes (registered modules), such as the blog node and the list node are registered with the module, then the module display on the list node must be a prerequisite for the display of the module on the blog node.
Identify the external module registration node
Five externally accessible modules: Log, tag, basic information, personal experience, permission settings, find the right node in the dependency tree (leaf node, hierarchical tree in the dependency tree corresponding node or "/" node) to register externally accessible modules:
Determining the Layout Module registration node
The nodes that are registered from the accessible module go to the root node, where the nodes that meet the two-module intersection are registered nodes of the layout module, and the modules related to the system need to be registered to the root node, so that any module can be used to ensure that the components are already loaded.
Mapping module functions
Principle: The common parent node of the node implements the public function of the module registered on the node.
For example: blog node and setting node of the common parent node is m node, then we can switch the blog module and setting module to identify the same function as the M module implementation of the function, the same with other modules.
Decomposition of complex modules
Further decomposition of the complex modules, generally need to decompose the modules include:
- A common module, such as a log list, can be rendered on the Log Management page or displayed in a projectile layer.
- There is no logical connection between the module, such as the log module in the log list and the right side of the label to view the list of labels there is no inevitable connection between the removal or addition of any one module will not affect the business logic of another module
At this point we can get two system decomposition of the dependency tree-External module dependency tree:
and the Private Module dependency tree:
Drawing Module Function Specification table
In this example, all the decomposed modules are decomposed in order to illustrate the decomposition process. The actual project to see the specific situation, such as the/?/tab/module of the M-module here can be directly implemented in the/M module, without the need to create a new/?/tab/module to achieve this function.
The canonical table example looks like this:
Building a Directory
Project directory
The project directory is built as shown in:
Description of each directory
Webroot Project Front-end development related Directory | -res static resource file directory, can be configured to use the static resources in this directory with version information |-src front-end source directory, the directory will not be deployed to the line when the final release |-HTML | module Catalog, the implementation of all modules of the system is in this directory |-app.html single page entry file
Module Unit Catalog
According to the module encapsulation rule A module unit consists of the following parts:
- Module testing: module-implemented functions can be tested independently via the module test page
- Module structure: The structure of the module involved in the decomposition of a number of template collection
- Module logic: module business logic implemented according to module specification, inheriting from module base class
- Module Style: module-specific style, generally this part of the style can be directly implemented in the CSS directory
The structure example is as follows:
At this point we can get the directory structure of all the modules as follows:
Module implementation
Structure
Here we assume that the static page of the system has been done, the implementation of the module here is based on the original structure of the structure decomposition and the implementation of business logic, part of the structure of the module is mainly related to the static structure into several nej template. Attention:
- An external resource in the template, such as a Css,js file address, is relative to the module's HTML file path if you are using a relative path
- The external resources in the template collection must be identified with the @template tag, which is described in detail in the following package release chapters
Nej Template Description
Example of modular structure
<meta charset= "Utf-8"/><textarea name= "txt" id= "m-ifrm-module" > <div class= "N-login" > <div class= "Iner j-flag" > <span class= "cls J-flag" >x</span> <span class= "min j-flag" >-</span> </div> <div class= "cnt j-cnt" ></div> </div></textarea ><!--@TEMPLATE--><textarea name= "JS" data-src= "./index.css" ></textarea><textarea name= "JS "Data-src="./index.js "></textarea><!--/@TEMPLATE
Logic
Depending on the Util/dispatcher/module module, we extend the module base class of a project from the _$ $ModuleAbstract, completing the abstraction of the module-specific properties and behaviors in the project.
/* *------------------------------------------* Project module base class implementation file * @version 1.0 * @author genify ([email protected]) *------------------------------------------*/nej.define ([ ' Base/klass ', ' util/dispatcher/module '), function (_k,_t,_p) { //variable declaration var _pro; /** * Project Module base class object * @class {_$ $Module} * @extends {_$ $ModuleAbstract} * @param {Object} Optional configuration parameters, the list of processed parameters is as follows * /_p._$ $Module = _k._$klass (); _pro = _p._$ $Module. _$extend (_t._$ $ModuleAbstract); /** * Operation * @param {Object} * @return {Void} * /_pro.__dosomething = function (_args) { //TODO }; TODO return _p;});
Depending on the state of the module, we need to implement the following interfaces when implementing a module:
Interfaces for each phase:
- Build-__dobuild: Build the module structure, the nodes that the cache module needs to use, and initialize the configuration parameters of the combined control
- Display-__onshow: Place the module in the specified container, assign the combined control, add related events, and execute __onrefresh's business logic
- Refresh-__onrefresh: Get the data and show it according to the parameter information entered by the outside world (mainly doing data processing)
- Hide-__onhide: Modules are put into memory to reclaim the combined controls and added events that are assigned in the __onshow, recycling the views generated in the __onrefresh (where possible to ensure that after the completion of the execution, the recovery to the post-__dobuild state)
Concrete Module Implementation Examples
/* *------------------------------------------* Project module implementation FILE * @version 1.0 * @author genify ([email protected]) *-- ----------------------------------------*/nej.define ([' Base/klass ', ' util/dispatcher/module ', '/path/to/project /module.js '],function (_k,_e,_t,_p) {//variable declaration var _pro; /** * Project Module Object * @class {_$ $ModuleDemo} * @extends {_$ $Module} * @param {object} optional configuration parameter */_p._$$ Moduledemo = _k._$klass (); _pro = _p._$ $ModuleDemo. _$extend (_t._$ $Module); /** * Building blocks, mainly dealing with the following business logic *-building the module structure *-Cache subsequent required nodes *-Initialize the configuration information of the component to be used * @return {Void} */_pro._ _dobuild = function () {this.__super (); TODO}; /** * Display module, mainly dealing with the following business logic *-Add Event *-Assign Component *-Process input information * @param {Object} input parameter * @return {Void} */ _pro.__onshow = function (_options) {this.__super (_options); TODO}; /** * Refresh the module, mainly dealing with the following business logic *-assigning components, assigning prior verification *-processing input information *-Sync Status *-load Data * @return {Void} */_pro.__onrefresh = function (_options) {this.__super (_options) ; TODO}; /** * Hidden modules, mainly dealing with the following business logic *-Recycling events *-Recycling components *-try to ensure the recovery to the state of the build * @return {Void} */_pro.__onhide = f Unction () {this.__super (); TODO}; Notify Dispatcher _e._$regist (' Umi_or_alias ', _p._$ $ModuleDemo); return _p;});
News
Point-to-point messages
The module can send messages to the specified Umi module via the __dosendmessage interface, or it can receive messages from other modules by implementing the __onmessage interface.
Send Message
_pro.__dosomething = function () { //TODO this.__dosendmessage ( '/m/setting/account/', { A: ' aaaaaa ') , B: ' bbbbbbbbb ' } );
Receiving messages
_pro.__onmessage = function (_event) { ///_event.from Message source //_event.data message data, this could be {a: ' aaaaaa ', B: ' BBBBBBBBB '} //TODO};
Publish a subscription message
Publish a message
_pro.__dosomething = function () { //TODO this.__dopublishmessage ( ' OnOK ', { A: ' aaaaaa ', b : ' BBBBBBBB ' } );
Subscribe to Messages
_pro.__dobuild = function () { //TODO this.__dosubscribemessage ( '/m/message/account/', ' OnOK ', This.__onmessagereceive._$bind (this) );};
Self - rated
Create HTML pages and use templates to introduce module implementation files
<!--template box--><div id= "Template-box" style= "Display:none;" > <textarea name= "html" data-src= ". /index.html "></textarea></div>
The module is placed in the container specified by the Document.mbody
Nej.define ([ ' Util/dispatcher/test '],function (_e) { document.mbody = ' module-id-0 '; Test module _e._$testbytemplate (' Template-box ');});
System integration
Mapping dependency Trees
When the system is integrated, we only need to map the node that needs to register the module in the dependency tree with the module implementation file.
External module Integration
Private Module Integration
Extracting System configuration Information
Rule Configuration Example
rules:{rewrite:{' 404 ': '/m/blog/list/', '/m/blog/list/': '/m/blog/', '/m/setting/account/': '/m/set ting/'}, title:{'/m/blog/tag/': ' Log tag ', '/m/blog/list/': ' Log list ', '/m/setting/permission/': ' Rights Management ', '/m/setting/account/': ' Basic information ', '/m/setting/account/edu/': ' Educational experience '}, alias:{' System-tab ': '/?/t ab/', ' blog-tab ': '/?/blog/tab/', ' blog-list-box ': '/?/blog/box/', ' blog-list-tag ': '/?/blog/tag/', ' Blog-list-class ': '/?/blog/class/', ' blog-list ': '/?/blog/list/', ' setting-tab ': '/?/setting/tab/', ' Setting-account-tab ': '/?/setting/account/tab/', ' layout-system ': '/M ', ' layout-blog ': '/m/blog ', ' Layo ' Ut-blog-list ': '/m/blog/list/', ' layout-setting ': '/m/setting ', ' layout-setting-account ': '/m/setting/account ', ' Blog-tag ': '/m/blog/tag/', ' setting-edu ': '/m/setting/account/edu/', ' setting-profile ': '/m/setting/ac count/', ' setting-permission ': '/m/setting/permission/'}}
Module Configuration Example
modules:{'/?/tab/': ' module/tab/index.html ', '/?/blog/tab/': ' module/blog/tab/index.html ', '/?/blog/box/': ' module/ Blog/list.box/index.html ', '/?/blog/tag/': ' module/blog/list.tag/index.html ', '/?/blog/class/': ' module/blog/ List.class/index.html ', '/?/blog/list/': ' module/blog/list/index.html ', '/?/setting/tab/': ' module/setting/tab/ Index.html ', '/?/setting/account/tab/': ' module/setting/account.tab/index.html ', '/M ': {module: ' module/layout/ System/index.html ', composite:{tab: '/?/tab/'}, '/m/blog ': {module: ' Module/layout /blog/index.html ', composite:{tab: '/?/blog/tab/'}, '/m/blog/list/': {module: ' MoD Ule/layout/blog.list/index.html ', composite:{box: '/?/blog/box/', tag: '/?/blog/tag/', List: '/?/blog/list/', Clazz: '/?/blog/class/'}, '/m/blog/tag/': ' module/blog/tag/index.html ', '/m/setting ': {module: ' Module/layout/setting/index.html ', composite:{tab: '/?/setting/tab/'}}, '/m/setting/account ': { Module: ' module/layout/setting.account/index.html ', composite:{tab: '/?/setting/account/tab/' }}, '/m/setting/account/': ' module/setting/profile/index.html ', '/m/setting/account/edu/': ' module/setting/edu/ Index.html ', '/m/setting/permission/': ' module/setting/permission/index.html '}
Module combination
The module opens the container of the combined module through the __export property, and the parent in __export is the container node of the submodule, and the top-level module (such as "/M") can be overridden by __doparseparent to explicitly specify the container in which the application resides.
_pro.__dobuild = function () { this.__body = _e._$html2node ( _e._$gettexttemplate (' module-id-l2 ') ); 0-box Select //1-class list box //2-tag list box //3-sub module box var _list = _e._$getbycl Assname (this.__body, ' J-flag '); This.__export = { box:_list[0], clazz:_list[1], tag:_list[2], list:_list[3], parent:_ LIST[3] };
Configuring module combinations with Composite
'/m/blog/list/': { module: ' module/layout/blog.list/index.html ', composite:{ box: '/?/blog/box/', tag: '/?/blog/tag/', list: '/?/blog/list/', clazz: '/?/blog/class/' }
You can specify the processing state of the combined module when the module is combined:
- OnShow-The combined module is configured here only when the module is displayed, and subsequent module refresh operations do not result in the refresh of the combined module, which is suitable for modules that will not change with the external input after display
- Onrefresh-The modules configured here are combined when the module is displayed, followed by a refresh operation if the module is refresh, for modules that need to be synchronized with the external input for the combined module
- Modules that do not specify OnShow or Onrefresh are equivalent to Onrefresh-configured modules
composite:{ onshow:{ //module OnShow when combined //Combined modules are not refreshed during module Onrefresh }, onrefresh{ // Module OnShow when the combination //combination of modules when the module Onrefresh also refresh }//The combination module configured here is equivalent to the module configured in Onrefresh}
Launch the App
Launch apps based on configuration
Nej.define ([ ' Util/dispatcher/dispatcher '],function (_e) { _e._$startup ({ //rule configuration rules:{ rewrite:{ //Rewrite rule configuration }, title:{ //Header Configuration }, alias:{ //alias configuration // It is recommended that the module implement the registration in the file using the alias configured here } }, //module configuration modules:{ //module Umi corresponding implementation file mapping table //Simultaneous completion of module combination } });});
Package release
Package release details Nej toolset related documents
System changes
When the system requirements change and the module changes we just need to develop new modules or remove the module configuration
New module
If you add a new module, just follow the logical implementation steps above to develop a module. If the new module functionality is already implemented in the system, you can simply modify the configuration. In the example above we need to add a copy of the tag module under Log Management to the blog settings, Access path is/m/setting/tag/
Modify Rule Configuration
rules:{ //... alias:{ //... ' Blog-tag ': ['/m/blog/tag/', '/m/setting/tag/' }}
Modify the module configuration
modules:{ //... ' /m/setting/tag/': ' module/blog/tag/index.html '}
If you want to add a label to the structure template of the/?/setting/tab module, you can
<textarea name= "txt" id= "module-id-8" > <div class= "ma-t w-tab f-cb" > <a class= "itm fl" href= "#/ setting/account/"data-id="/setting/account/"> Account management </a> <a class=" itm fl "href=" #/setting/ permission/"data-id="/setting/permission/"> Permissions settings </a> <a class=" itm fl "href=" #/setting/tag/" Data-id= "/setting/tag/" > Log labels </a> </div></textarea>
Deleting a module
Removing degraded modules from the system simply remove the module's corresponding Umi configuration from the module configuration without having to modify the specific business logic.
Summarize
With the rapid development of web technology, the application of single-page system (SPA) has become more and more extensive, and as the complexity of such systems increases, the demand for the scalability of platforms and modules becomes more and more important. For these two aspects, the industry has given a lot of solutions, this article we mainly discuss the NetEase Nej framework in these aspects of the solution. NetEase in the single page system has also done a few years of practice and technology accumulation, such as in recent years, NetEase cloud Music PC version, easy letter Webim, NetEase mailbox assistant, such as the early years of NetEase album, NetEase mailbox, mobile NetEase Cloud album ipad, Lofter Android version and other products, Are the application of this kind of single-page system. The students who are interested in this field can make further exchanges in the course of practice.
"Forwarding" builds a highly scalable web-interactive system (bottom)