The previous link – the basis of this article – referred to KOA and wrote a rough Web framework in 5 steps

For more information on how to implement the Router, please check out Koa’s easy Router knocking guide

Reference warehouse: point me

Get (path,(CTX,next)=>{ctx.body=… }) are written directly in HTML, which makes the code too difficult to maintain. Templates were invented, and they were used to manage pages. Each HTML is placed in a separate file, making it easy to call and reuse. Here I use EJS syntax to write the template engine middleware.

So, let’s start with the simplest static page

STEP 1 Static page call

Calling the file isn’t too hard, just read it and assign it to ctx.body:

const fs=require("fs")
const path=require("path")
let indexTPL=fs.readFileSync(path.join(__dirname,"/pages/template.ejs"),"utf-8")
ctx.body=indexTPL;
Copy the code

I’m going to focus on logic here, so I use readFileSync as a synchronous method rather than an asynchronous one.

STEP 2 encapsulates a middleware View

Here, we create a new middleware called View for template nesting.

const fs=require("fs")
const path=require("path")
function View(path){
    let tpl="";
    return async (ctx,next)=>{
        tpl = fs.readFileSync(path.join(__dirname,path),"utf-8") ctx.body= tpl await next(); }}Copy the code

We can then apply the middleware directly to the project.

let view=require("./Views")
let router=new Router()
router.get("/",view("/pages/template.ejs"))
Copy the code

or

app.use(view("/pages/template.ejs"))
Copy the code

It works because I’m creating standard middleware

STEP 3 Extract template labels

Why do we use templates?! For dynamic pages, of course! So we need to replace the template tag <%= parameter name %> with the value we want. Templates also need to support functions such as an array loop to fill a list.

So the first step we need is to extract this tag and replace it with our own tag
This allows you to define a special label for placeholders.

That’s right. Extract, replace! So there’s no escaping the regular expression, it’s already on its way to abusing me…

Because there is a big difference between simply assigning and executing a function, I separate them. If you have a better way, please recommend it to me. (Regular dregs shudder)

let allTags=[];
function getTags(){// First fetch the function that needs to be executed, i.e"="Put a pair of tags into the array, and replace the execution function with a placeholder.letoperators = tpl.match(/<%(? ! =)([\s\S]*?) %>([\s\S]*?) The < % (? ! =)([\s\S]*?) %>/ig)||[] operators.forEach((element,index )=> { tpl=tpl.replace(element,`<! --operator${index}-- > `)}); // Select * from '='; // Select '='lettags=tpl.match(/<%=([\s\S]*?) %>/ig)||[] tags.forEach((element,index) => { tpl=tpl.replace(element,`<! --operator${index+operators.length}-- > `)}); AllTags =[...operators,...tags]; }Copy the code

STEP 4 Replace template labels

Now we’re going to do the template substitution, the value that we passed in. The important thing to note here is that we replace allTags one by one with executable JS text, then execute JS, and the resulting string is temporarily stored in an array. And so on the completion of the execution of the previous
The placeholder is replaced.

${} : <%=%> = ${} :

let str="Let TMPL = '<p> string template:${test}< / p > < ul > < li > for loop < / li > < % for (let the user of the users) {% > < li >${user}</li>
    <% } %>
</ul>`
return tmpl"
Copy the code

Then we remove the <%%> of the executable function and add the closing string to it, like this:

let str="Let TMPL = '<p> string template:${test}< / p > < ul > < li > for loop < / li > ` for (let the user of the users) + = {TMPL ` < li >${user}</li>`
    } 
`</ul>`
return tmpl"
Copy the code

But this is a string, so we need to use a method called the Function constructor

We can new a Function and turn the string into an executable JS.

New Function ([arg1[, arg2[,…argN]],] functionBody); new Function ([arg1[, arg2[,…argN]]); Three help us to turn Object into a single parameter to put in.

Here’s an example:

let data={
    test:"admin", the users: [1, 2, 3]}Copy the code

Keys (data), extract the field name, and then use the three-point extension operator… , into the test, the users

new Function(... Object.keys(data), method string)Copy the code

Which is the same thing as

new Function(test,users, method string)Copy the code

Let’s merge the strings at the top. This is what the executable template js looks like.

function xxx(test,users){
   letTMPL = '<p> String template:${test}</p>
            <ul>
            <li>forCycle < / li > `for(let user of users){
            tmpl+=`<li>${user}</li>`
            } 
        `</ul>`
    return tmpl;
}
Copy the code

Feel to become executable JS, the principle is not difficult, is very complex to put together.

Below is the complete execution code:

function render(){// Get the tag getTags(); AllTags =allTags. Map ((e, I)=>{// Start combining the contents of each tag, then turn the text into an executable jslet str = `let tmpl=' '\r\n`;
        str +=  'tmpl+=`\r\n'; STR + = e / / label to replace the first assignment STR = STR. Replace (/ < % = ([\ s \ s] *?) %>/ig,function () {
            return '${'+arguments[1]+'} '}) // Replace the function method, remember the first"`"The closing tag STR = str.replace(/<%([\s\ s]*?) %>/ig,function () {
            return '`\r\n'+arguments[1] +"\r\ntmpl+=`"
        })
        str += '`\r\n return tmpl'; // Extract the key value of an objectfunctionThe parameters of thelet keys=Object.keys(data);
        letfnStr = new Function(... keys,str);returnfnStr(... keys.map((k)=>data[k])); }) allTags.forEach((element,index )=> { tpl=tpl.replace(`<! --operator${index}-->`,element)
    });
}
Copy the code

STEP + If you want to read files asynchronously, I recommend:

Make readFile a Promise and await it in middleware

If you do not know async/await, popular portal.

const util=require("util")
const fs=require("fs")
const path=require("path")
let readFile=util.promisify(fs.readFile)
function view(p,data){
    let tpl="";
    let allTags=[];
    function getTags() {/ / slightly}function render() {/ / slightly}return async (ctx,next)=>{
        tpl = await readFile(path.join(__dirname,p),"utf-8"// Don't forget to run render() instead of the template tag render(); ctx.body=tpl; await next(); }}Copy the code