Background:

Our cms is build up as a multiple pages application.

For the frontend our project using webpack multi entries&multi outputs.eg.

const entryConfig = {
  header: "./app_fe/header/header.tsx",
  left_menu: "./app_fe/left_menu/left_menu.tsx",
  login: "./app_fe/login/login.tsx"};Copy the code

For the backend we using egg.js view rendering.eg.

  nunjucks: {
    enable: true,
    package: 'egg-view-nunjucks',},Copy the code
config.view = {
    defaultViewEngine: 'nunjucks',
    mapping: {
      '.tpl': 'nunjucks',}};Copy the code
await ctx.render('notfound.tpl', pageOpt);
Copy the code

It’s almost a perfect and a stable structure but you know,we do have some problems.

  1. Everytime we make a new page,we need to make a new tpl files.
  2. Everytime we make a new page,we need to push a new entry into the entryConfig.
  3. we need to manipulate the tpl file every time we make some changes to clear the cache of the js libray.

Accomplishment:

So here we gonna resolve the problems metioned above.

Extract the base tpl files

The first thing is to seperate the business tpl files from base tpl files.why? As we wanna the business tpl files to be generated automatically,we don’t wanna push these files to the git storage.All we need to care are the base tpl files. Our base tpl files including: 1.base.tpl (for importing the base js library) 2.header.tpl and left_menu.tpl (for the basic layout) 3.login.tpl 4.user_data.tpl (it’s gonna be replaced by cookies sooner or later) 5.template.tpl (template for generating other business tpl files),here’s the structure of it

{% extends "base.tpl" %}

{% block body %}
  {% include "user_data.tpl" %}
  {% include "header.tpl" %}
  {% include "left_menu.tpl" %}
  <span id="nemo-wrapper"></span>
  <%= script %>
{% endblock %}
Copy the code

We put all the ** base tpl files** in the templates fold in the root directory.

Why dont’ we put’em under the like app/baseView files and import it in a relative path? like below

{% extends "baseView/base.tpl" %}
Copy the code

check here you’ll get the answer,cause Nunjuck can even not support relative path~~~~

Generating the business tpl files

here we use HtmlWebpackPlugin to generate the file,basing on the template.tpl we create files according to the key of entryConfig So here we got all our business files.

for(const key of object.keys (entryConfig)) {// Business TPLif(! templates.includes(key)) { array.push( new HtmlWebpackPlugin({ filename: `.. /.. /.. /view/${key}.tpl`,
          templateParameters: {
            script: getScripts(key)
          },
          template: "./templates/template.tpl", chunks: [] }) ); }}Copy the code

What if in some business files we need to import some extra js library?

we define a extraScriptMap here,like the code show below,app_user need import firebasejs,so we can concat it by the getScripts function.

const extraScriptMap = new Map().set("app_user", [
  `<script src="https://www.gstatic.com/firebasejs/5.5.2/firebase-app.js"></script>`,
  `<script src="https://www.gstatic.com/firebasejs/5.5.2/firebase-auth.js"></script>` ]); // get the page complete script const getScripts = key => {let script = `<script src="/public/webpack/js/${key}.js"></script>`;
    if (extraScriptMap.has(key)) {
      script += extraScriptMap.get(key).join("");
    }
    return script;
  };
Copy the code

besides the business file we also need to insert the base tpl files into the app/view folder.

const templates = [];
  glob.sync("templates/*.tpl").forEach(filePath => {
    const templateName = filePath.split("/")[1].split(".") [0]; templates.push(templateName); }); // non-business TPL (header,left_menu,user_data,login)for(const key of templates) {// Remove template.tplif (Object.is(key, "template")) continue; array.push( new HtmlWebpackPlugin({ filename: `.. /.. /.. /view/${key}.tpl`,
        template: `./templates/${key}.tpl`,
        chunks: []
      })
    );
  }
Copy the code

using glob we can find out all the files under templates folder.

As same as the buiness part , we push it into a HtmlWebpackPlugin and finally they are all insert into the app/view folder.

const htmlPluginArray = getHtmlPluginArray(entryConfig);
plugins: [
      new ForkTsCheckerWebpackPlugin({
        async: false,
        tsconfig: "./tsconfig.json"
        // tslint:'./tslint.json', }), // Ignore all locale files of moment.js new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), ... htmlPluginArray ],Copy the code

Till here , we solve the problem 1. For the problem 3 we just need to add a version or timestamp params after the script path.

Stuff still need to figure out:

  1. entryConfig should be generate automatically,as same as the solution of tpl files,we just use glob to fetch’em out.

  2. header tpl and left menu should not be refresh every single time,for user we wanna see something instantly,so I’m thinking using puppeteer for ssr caching.