This is a problem that came about two years ago when I was doing the first real site in my life.
The site uses a back-and-forth separation method that returns JSON data from the REST of the backend and renders the front-end to the page.
Like many beginner Javascript novices, I used to embed the JSON data in HTML in the form of a concatenation string. There is less code at the start and is acceptable for the time being. But when the page structure is complex, its weaknesses begin to become unbearable:
- The writing is incoherent. Each write a variable will be broken, insert a + and. It's very easy to make mistakes.
- cannot be reused. HTML fragments are discrete data and it is difficult to extract the parts that are duplicated.
- The <template> tag is not well utilized. This is a new tab in HTML5, and it is highly recommended that you put HTML templates into <template> tags to make your code simpler.
- That's how I felt at the time:
Is this the hell you're kidding me?
To solve this problem, I temporarily put down the project on hand and spent half an hour implementing a very simple string template.
Requirements Description
Implement a render (template, context) method that fills the placeholders in template Requirements:
There is no need to have control flow components (such as loops, conditions, etc.), as long as there is a variable substitution function
Cascading variables can also be expanded
The escaped delimiter {and} should not be rendered, and the delimiter is allowed to have white space characters between the variables
Example:
Render (' My name was {name} ', {
name: ' Hsfzxjy '
});//My name is Hsfzxjy
render (' I am in {profile.location} ', {
name: ' Hsfzxjy ', profile
: {
location: ' Guangzhou '
}
}]//I AM in Guangzhou
render (' { Greeting}. \\{this block won't be rendered} ', {
greeting: ' Hi '
});//Hi. {This block won't be rendered}
Realize
First write down the frame of the function:
function render (template, context) {
}
Obviously, the first thing to do is to match the placeholder in the template.
Matching placeholders
The matching thing must be given to the regular expression to complete. So what should this regular expression look like?
According to the description of Requirement 1 and 2, we can write:
var reg =/\{([^\{\}]+) \}/g;
As for Demand 3, I first thought of the concept of forward matching, unfortunately Javascript does not support, but to use a compromise approach:
var reg =/(\) \{([^\{\}\\]+) (\) \}/g;
If the first or third grouping value is not empty, it is not rendered
now, the code should be:
function render (template, context) {
var tokenreg =/(\)? \{([^\{\}\\ ]+) (\) \}/g;
Return Template.replace (Tokenreg, function (Word, SLASH1, token, slash2) {
if (Slash1 | | slash2) {//Match to escape character
Retu RN word.replace (' \ \ ', '); If the delimiter is escaped, do not render
}
//...
}
}
Placeholder Replacement
Well, the regular expression is OK, and the next thing to do is to replace the job.
According to demand 2, the template engine should not only be able to render first-level variables, but also to render multilevel variables. How do you do that?
Actually very simple: will token press. Separated, the step-by-look can be:
var variables = token.replace (/\s/g, '). Split ('. '); Cutting token
var currentobject = context;
var i, length, variable;
Step
-by-step lookup context for (i = 0, length = variables.length, variable = variables[i]; i < length; ++i)
Currentobjec t = currentobject[variable];
return currentobject;
However, it is possible that the specified variable does not exist, and the code above token the error. For a better experience, the code is better able to have fault tolerance:
var variables = token.replace (/\s/g, '). Split ('. '); Cutting token
var currentobject = context;
var i, length, variable;
for (i = 0, length = variables.length, variable = variables[i]; i < length; ++i) {
Currentobject = Currentobject[va Riable];
if (Currentobject = = Undefined | | | currentobject = = NULL) return '; If the object of the current index does not exist, the empty string is returned directly.
}
return currentobject;
By combining all the code together, you get the final version:
function render (template, context) {
var tokenreg =/(\)? \{([^\{\}\\]+) (\)? \}/g;
Return Template.replace (Tokenreg, function (Word, SLASH1, token, slash2) {
if (Slash1 | | slash2) {return
Word.rep Lace (' \ \ ', ');
}
var variables = token.replace (/\s/g, '). Split ('. ');
var currentobject = context;
var i, length, variable;
for (i = 0, length = variables.length, variable = variables[i]; i < length; ++i) {
Currentobject = Currentobject[v Ariable];
if (Currentobject = = Undefined | | | currentobject = = NULL) return ';
}
return Currentobject
})
}
Remove the blank line, altogether 17 lines.
To hook a function to a prototype chain of String
Even, we can make some cool effects by modifying the prototype chain:
String.prototype.render = function (context) {return
render;
After that, we can call it this way:
"{greeting}! My name is {author.name}. ". Render ({
greeting: "Hi",
Author: {
name: "Hsfzxjy"
}
});
Hi! The My name is Hsfzxjy.