The front-end template engine needs to be transparent during development
Transparency means that after I set up the development environment, I can write code and refresh the browser. You can see the latest effects without executing any additional commands or waiting for any process.
So all template engines that rely on the compilation process are not suitable for front-end use. Compilation can only be a feature of the template engine, not a prerequisite for use.
More strictly speaking, using FileWatch, etc. Methods to detect file changes and automatically compile are not within the scope of my consideration, because this will cause additional waiting.
It can be deduced from this that the front-end template engine should have capabilities that can be used in the pure front-end The ability to use in the environment is parsed.
The front-end template engine must have good runtime debugging capabilities
Due to the uncertainty of user behavior, the uncertainty of the execution environment, and the uncertainty of various third-party scripts, Impact, etc., it is difficult for the front-end to achieve complete error handling and tracking, which also leads to the fact that the front-end must directly troubleshoot problems online
And when the problem occurs at the template engine level, templates are needed The engine provides good debugging capabilities
Generally speaking, the debugging capabilities of compiled functions are weaker than those of the original manually written template fragments, because automatically generated functions basically do not have readability and breakpoints. Trackability
So at this point, a template engine for front-end use should be able to switch from "executing the compiled function to obtain HTML" to "parsing the original template and then executing the function to obtain HTML" under certain circumstances. , that is, it should support switching between the two modes
Or better yet, a function compiled and generated by a powerful front-end template engine can be directly mapped back to the original template fragment using Source Map or other customized means, but There is currently no template engine that implements this function
The front-end template engine must be friendly to file merging
Before HTTP/2 became popular, file merging was still a front-end performance An important means in optimization, templates, as part of the file, still need to be merged
In the template engine that provides compilation function, we can use compilation to turn the template into JavaScript source code, and then in JavaScript Basically do file merging
But if we want to keep the original template fragments for reasons such as debugging capabilities mentioned above, then the template engine itself needs to support merging template fragments into one file
Most engines that only support parsing an input string as a template do not have this capability. They are inherently unable to split an entire string into multiple template fragments, and therefore cannot support file merging at the template fragment level.
It is necessary to implement support for file merging. The best way is to make the template syntax based on "fragments"
The front-end template engine must be responsible for preventing XSS
From a security perspective, the front-end has strict requirements for XSS control.
The most appropriate way for the front-end to prevent XSS is to use the "default escape" whitelist policy
Based on this, a reasonable template engine is must support default escape, that is, all data output is processed by escape logic by default, and key symbols are converted into corresponding HTML entity symbols. In order to eliminate the intrusion path of XSS from the root
Of course, not all content must be escaped. In the system, there is inevitably a need for users to input rich text, so specific syntax needs to be supported to generate rich text. Escaped output, but always pay attention to the special case of unescaped output. By default, it must be escaped output
The front-end template engine must support the reuse of fragments
This is not a requirement of the front-end template engine. In fact, any template engine should support the reuse of fragments. Backends such as Velocity, Smarty, etc. all have this function.
The so-called fragment reuse should have the following levels Application:
A fragment can be introduced into another place, which is equivalent to the effect of using a variable everywhere
A fragment is When introduced, different data can be passed to it, which is equivalent to the effect of a function used everywhere.
A fragment can be replaced externally, but if the fragment is not provided externally, a default content will be maintained. , similar to the strategy pattern in design patterns
There are many template engines that meet points 1 and 2, but there are few front-end template engines that meet point 3, and the back-end Razor, Smarty, etc. all have this function
The front-end template engine must support processing during data output
The so-called processing during data output refers to a data that needs to be processed during output Do additional conversions, the most common such as string trim operations, more technical ones such as markdown conversion, etc.
It is true that the data conversion can be completely processed through JavaScript logic before the data is handed over to the template engine. , but this will lead to a lot of ugly and redundant code, and will also have a negative impact on the reusability of the logic itself
Usually the template engine performs additional processing on the data in the form of a filter, similar to the logic of pipelines in bash. The implementation and registration of filters will also have different designs. For example, mustache actually registers the fitler factory, while other template engines will directly register the filter itself. Different designs have different considerations. It is difficult for us to say who is good and who is bad
However, after the template engine supports data output processing, it will cause us a new entanglement in the coding process, that is, which data processing should be implemented by the template engine's filter, and which data should be implemented by the template engine before being handed over to the template engine. Implement your own logic. This topic is a long discussion. If it is not relevant to the current topic, just skip it.
The front-end template engine must support dynamic data
In the development process In fact, a lot of data is not static. For example, EmberJS provides the concept of Computed Property, Angular also has similar things, and Backbone can achieve it in disguise by rewriting the get method of the Model
Although ES5 Getter support is directly provided at the language level, but we still do not use this language feature in most scenarios of front-end development. Instead, we choose to encapsulate dynamic data into get and other methods of some kind of object
The template engine should also pay attention to this when converting data into HTML fragments, and have good support for these dynamically calculated data
To put it more clearly, the template engine should not just accept Pure object (Plain Object) as input, but should be more open to accept dynamic data with get method
A more reasonable logic is that if an object has a get method (the template engine determines this interface ), the data is obtained through this method. In other cases, the input object is regarded as a plain object and standard attribute acquisition logic is used.
The front-end template engine must be closely integrated with the asynchronous process
A very common example is that we have an AMD module that stores globally used constants, and the template engine needs to use these constants. Of course, we can let JavaScript obtain this module asynchronously before using the template engine, and then pass the constants to the template engine as data, but this is a way of coupling the business and the view. Out of obsessive-compulsive disorder, I don’t think this is A beautiful design, so we hope that the output of the
template itself becomes an asynchronous method, instead of returning a string directly like now
Analyzing the dependence of the template on asynchronous operations, the splicing logic of the entire string is interrupted into multiple asynchronous operations
Asynchronous requires waiting, and waiting is unknown. From a performance perspective, do you need to consider Stream-style output in order to complete a paragraph and provide a paragraph?
Should we provide several built-in fixed asynchronous logic, or support any custom asynchronous logic based on Promise? , to make a balance between complexity and practicality
So far I have not fully understood the method and interface of combining templates with asynchronous, and there is no way to continue to discuss this topic in depth
The front-end template engine must support different development models
With the development of the front-end, there are many different development models, such as:
The most common HTML page, using events such as DOMContentLoaded to add logic, and partially refreshing the page under specific interactions
Using the traditional MVC model for single-page development
Using MVVM method with data as the core, data and view direction binding for development
Development based on Immutable Data for data comparison, Diff conversion and DOM update (which may include Virtual Introduction of DOM)
It is a very big challenge for a template engine to support so many different modes, especially the support for two-way binding. So far, almost all development frameworks that support two-way binding come with a dedicated template engine. This is because two-way binding has two major requirements for templates:
Be able to Extract the meta-information of "which data this template depends on" from the template
You can know which part of the template a data change engine is working on, without refreshing the entire thing
General template engines rarely provide these two features, so there is no way to fully support different front-end development models
From the perspective of the implementation of the template engine itself, one method It directly exposes the AST-like structure after template parsing for other frameworks to process reasonably, and at the same time provides a partial refresh function for the template (it can also be considered together with the template fragment mentioned above), but most template engines for performance After considering it, it will not parse a grammatical structure similar to AST
The front-end template engine must have isolation between instances
In large-scale front-end projects, especially In a single-page project, there will be a completely unknown number of template fragments existing at the same time. If these fragments have names (for reuse considerations), it is easy to cause name conflicts
For logic at the same level (for example, everyone is working on the business layer code, or everyone is working on the control layer code), name conflicts can be resolved through some development conventions. But between different layers, due to encapsulation requirements, the outside should not know the names of some fragments that are only used internally. At this time, if unfortunately the names conflict with other layers, the situation will become more troublesome. Such problems may even It is not easy to track and often leads to a lot of waste of energy and time. Therefore, a good template engine should have multiple instances, and different instances should be isolated from each other to avoid such unpredictability. Conflict
If you study this topic further, you will find that simple isolation is not enough. In addition to the need for non-conflict between different layers, there is also the need for fragment reuse. We will also need different Some fixed fragments can be shared between template instances, so the relationship between each instance of the template engine is a combination of dependencies but with basic encapsulation and isolation. That's all.
The above is the detailed content of Detailed explanation of issues with front-end template engines. For more information, please follow other related articles on the PHP Chinese website!