Project Background I just finished a project. The background is: the backend is in Java, and the backend service has been developed almost. Now we need to provide services to the outside world through the web, which is the B/S architecture. The backend focuses on business logic, and we don’t want to do page rendering on the backend, but only provide data interfaces to the frontend. So after negotiation, we plan to completely separate the frontend and backend. All data on the page is retrieved from the backend through ajax, and the page rendering is done entirely by the frontend. In addition, there is an emergency situation. The project needs to be launched urgently, and the development time of the entire web site is only two weeks, two weeks! So in this context, I decided to start an attempt to completely separate the frontend and backend. Previously, development was a mix of synchronous rendering and asynchronous rendering. Some things could be compiled by the backend PHP, such as common page templates, page parameters returned by the backend, etc. I anticipated that this complete separation might encounter some difficulties, but the project was online and I couldn't go deep into the architecture, so I planned to use jQuery+handlebars, jQuery to complete the page logic and DOM operations, and handlebars to complete the page rendering. This solution is so simple and crude, but the advantage is that it can most reliably ensure that the project is completed on schedule. In fact, separating the front and back ends is not an easy task, and there are many imperfections in doing so, which will be discussed later. A brief discussion on front-end and back-end separation What exactly is the separation of the front-end and back-end? In fact, it is the rendering of the page. Before, the back-end rendered the page and handed it over to the front-end for display. After the separation, the front-end needs to assemble the HTML code by itself and then display it. There are many benefits to the front-end managing page rendering, such as reducing the number of network requests and making single-page applications. Things sound simple, but such a separation will involve many problems, such as:
Each of the above problems is tricky enough. To solve them properly, we need a well-designed solution that meets the actual project requirements. There are many frameworks that can help us do these things, such as Backbone, EmberJS, KnockoutJS, AngularJS, React, Avalon, etc., which can be used to build a rich front-end. However, a framework is a framework after all. To use it in actual projects, you still need to have your own design. The framework cannot solve all problems. I have seen the practice of the Taobao team before, using nodejs as a middle layer to handle page rendering, routing control, SEO and other things, redefining the boundary between the front-end and the back-end. I personally feel that this should be the right direction, a bit subversive, the front-end is moving towards engineering, and will become a real full-stack front-end. I don't know if this architecture is now fully rolled out in Taobao, I am really looking forward to seeing the effect. The above frameworks and Taobao's practices are all great works. As a junior, I have only learned from them and have not used them in actual projects. Looking down at the project I am working on, I have 1 front-end and 2 weeks to complete a complete web project. I still use the most reliable and advanced method to do it. Basic structure The project as a whole is not a single-page application, but some modules need to be made into local single-page operations. For operations like this that need to be completed step by step, you only need to load the sub-page locally.
Therefore, a module has a main html page, which initially has only some basic skeletons, a js file with the same name, in which the module logic is located, and a css file with the same name, in which all styles of the module are defined. For sub-pages that need to be loaded asynchronously, such as the pages of each step in the figure above, I use jQuery's $.load() method to load them. This method can load content in a container of the page and specify a callback function, which is very convenient to use. I use _ to start the sub-pages that are loaded asynchronously, such as _step1.html, to distinguish them. In order to ensure that the forward and back buttons of the browser are available, I used hash as the routing tag, and the page address is like: publish.html#step2. One drawback is that the hash will not be sent to the server, so SEO is useless. In fact, using the history API can also solve the problem more elegantly, but it needs to consider compatibility and additional work to be done. Considering the time factor, I will settle for the second best, and there is no need to do SEO for this project. Or like Taobao's solution, the nodejs layer and the browser layer are unified in routing, and the SEO problem can be easily solved. But it is obviously beyond my ability, haha! Except for the sub-pages asynchronously loaded with $.load, the remaining partial pages are rendered with the templates provided by handlebars. I used the pre-compilation function of handlebars, which is very powerful. On the one hand, it saves the compilation time required for the page loading stage (compiling handlebars templates), and on the other hand, the compiled templates (js files) are easy to reuse. The next step is how to organize the front-end logic. Since I don't use the mv* framework, I can only write a structure that is easy to develop. As mentioned above, each module has a main js file, and the file content structure is as follows:
Each module is given a namespace, and all methods are hung on it. The js file only defines the function and does not execute anything immediately. Then the entry method is called in the html file: publish.init(). Business logic is encapsulated in the function, such as renderData above, and then called from other places. The event listeners of the page are uniformly registered on the body element, using event proxy. In order to avoid writing too much on, click and other codes, a delegates method is extended for jQuery to uniformly bind listeners in a configured manner. The usage is shown above. Let's also put the code defined by delegates:
This is the basic structure. There is no new technology. It is just a combination of existing things. But the work is far from over. There are still some things that need to be dealt with in practical applications. Let's talk about it in detail: References at the bottom of the public header This is a tricky problem. Generally, the common header and footer will contain some common codes, such as the outer structure HTML code of the page, the libraries used by the site such as jQuery, handlebars, and the common JS and CSS files of the site. In traditional development, it is usually necessary to write a separate file such as head.html, and use the backend code such as include statement to introduce it in other pages for reuse. Now that the front-end and back-end are separated, you can no longer rely on the back-end to render for you, so you have to do it on the front-end. Since handlebars is used, it is easy to think of writing the common part into a template, then precompiling it, generating a header.js file, and then referencing it in other pages. However, a problem was discovered in actual operation. Handlebars is a static template. The string generated after compilation is inserted into the page through innerHTML. This is fine in general templates. Now there is a problem that there are some <script> tags in the header, which link to the library to be used. When the <scirpt> tag is inserted through innerHTML, the browser does not send a request to load the corresponding js file, so there is a problem. After searching and trying many methods, the final solution is to use document.write() to write the compiled results to the page, so that the <script> tag can be loaded normally. So the code used in the header of each page becomes like this:
The code in includeHead.js is as follows:
It looks a bit awkward, but in order to achieve the function, this is the only way for now. Although the native innerHTML cannot load the content in the <script> tag, jQuery's $().html() method is optimized to find the <script> tag and execute the code inside, so $().html() can complete the above work. From this perspective, this poor solution can be replaced. Routing Control As mentioned above, jQuery's $.load() method can meet the needs of loading subpages. Now the problem that needs to be solved is that no matter whether the user refreshes the page or moves forward or backward, we have to render the corresponding view according to the hash value, which is actually routing control. At this time, we need to listen to the hashchange event. I defined a loadPage method to load subpages, and then bound the listener as follows:
In the loadPage method, the $.load() method is called according to the value of hash, and the initialization of the subpage is specified in the callback function of $.load(). There is another convenience in doing this. We don't need to call the loadPage method manually to switch views. We only need to modify the hash of the page. The hash change is monitored and the corresponding subpage is automatically loaded. For example, click Next to enter step 2:
In this way, a simple routing control is realized. Since it is not a single page for the whole site and there is no multi-level routing, it can fully meet the needs. As for SEO, I can only laugh. It happens that the project does not need SEO, otherwise this method will have to be abandoned. Another point I want to mention is page caching. Asynchronously loaded content can be stored in localStorage or placed on the page for visibility control. This way, users do not need to request again when switching views frequently, and the previously filled form data will not disappear when returning to the previous step, which provides a very good experience. Passing parameters between pages Sometimes we need to pass parameters to the page we are visiting. For example, to visit the detailed information page of a device, we need to pass the device ID, detail.html?id=1, so that the detail page can request the corresponding data based on the ID. Traditionally, for pages rendered by the backend, the parameters in the URL will be sent to the server, which can then render them to the page for js to use. This is no longer possible now. The request page does not interact with the backend at all, but this parameter is essential, so the frontend needs to have a mechanism for passing parameters. It's actually very simple. You can get the current URL address through location.href, then do string matching and extract the parameters. It looks quite rustic, but it works well. I also considered using cookies to pass it, but it feels a bit troublesome. Since these parameters are usually written on the <a> tag, and the <a> tag is rendered based on dynamic data (because it is a dynamic parameter), we cannot use js to modify the href value of all <a> tags and add a parameter to it after the page is rendered. What should we do? At this time, handlebars comes in handy. We can use handlebars***'s helper to directly query the parameters in the URL when rendering the page, and then output them in the compiled code. I registered a helper in handlebars as follows:
This helper named param can output the parameter value you want to query, and then you can write it directly in the template, such as:
This is much more convenient! But is there any problem with this? Actually, it is a bit unfriendly if you consider the word "performance". The value of a parameter in a URL is fixed, and you will calculate it every time you use this helper, which is redundant. It would be great if handlebars could define constants in templates, but unfortunately I didn't find this function after searching through the documentation. I can only sacrifice performance for convenience, which also proves what I said in the title: "simple and crude", haha. Data verification and processing Since the data is sent from the backend, there are many uncertainties. The data may be illegal, or the structure may be wrong, or it may be empty. Therefore, it is necessary for the frontend to verify the legitimacy of the data. With handlebars, data verification can be easily performed. That's right, use helpers. Handlebars' built-in helpers such as if and each support else statements, and error messages can be output in else. If personalized verification is required, we can define helpers ourselves to complete it. Regarding how to customize helpers, I have studied it before and wrote an article: http://www.cnblogs.com/lvdabao/p/handlebars_helper.html. In short, custom helpers are very powerful and can complete any logic you need. Data formatting, such as dates, numbers, etc., can also be done through helpers. On the other hand, the front end should also perform HTML escape on the data to avoid XSS. Since handlebars has already done HTML escape, we can ignore this item directly. Summarize This article was written after I just participated in a project. It records the problems encountered in the whole process and how to deal with them. Other details, such as asynchronous form submission, are not the focus of this article, so I will not write about them. This is my first project to practice complete separation of the front-end and back-end. The entire front-end was designed and developed by me. In 2 weeks, with this solution, the project was completed on schedule and ahead of schedule, leaving more than a day for testing. Although the development task was completed, looking back at the entire solution, it was not very elegant and had no technical content. The problems mentioned at the beginning of the article were not solved. Therefore, the most simple and crude solution was to meet the deadline. ***, if I were given another chance and enough time, I would definitely try to use the mv* solution, or angular, or avalon. |
<<: How Chinese programmers get Facebook offers
>>: An article to understand the best route to learn Android
Mini Programs are the last blue ocean of traffic ...
On a cold winter day, people gather around a smal...
Why can this "key" crack Wi-Fi? Does it...
Entering 2019, both QQ and WeChat are being updat...
Human beings have never done anything like this b...
Based on my own experience, I shared how the mobi...
Recently, a netizen named "Haiyan" from...
On November 5, at the press conference of the Joi...
"Men shake their legs when they are poor, wo...
Today, we want to discuss a problem that seriousl...
From being prosperous to dying down, Tencent Weibo...
After studying the course [Wang Nan·Short Video S...
At the 2017 Guangzhou Auto Show, Chery officially...
...
CGI stands for Common Gateway Interface, which al...