In daily projects, sometimes it is inevitable to maintain some JQ official website projects. In the face of such needs, many are still the old routines, front end to write pages to the back end to set data. It is very annoying that there is no ~ ~ and after the change, it has to be handed over to the back end to modify again. Time and communication are a trouble. At the same time, writing static page is also very troublesome, can not be reused, can not use the current tools, heart tired, of course, a lot of solutions

  • Write your own Webpack script to maintain it, and bring in the engineering set of things.
  • Use off-the-shelf server rendering frameworks such as Nust \ Next
  • With node+ template engine etc
  • .

This article describes how Node’s egg.js framework works with a template engine to quickly develop projects. Easy to get started and fast, one person to handle the front and back end. PS: Can learn new knowledge again, I good (grass) open (dirt) heart (horse), ollie give ~ ~ ~

Initialize the project

# initialization
cnpm init egg --type=simple

# Install dependencies
cnpm i

# Start service
npm run dev
Copy the code

A simple look at the generated file directory (ps: individual files are not, later added their own)

Basic introduction

First, let’s introduce the basic functions of these files in the app in egg, and give a general idea:

  • Write routes in app/router.js
  • Write the corresponding controller under app/controller/
  • App /service/
  • Write the corresponding template under app/view/

Routing to write

  • Routing, similar to the routing in front end projects. Is used to define the URL of the request.
  • When a user accesses the route address, it is mapped to the corresponding controller for further processing.
  • Therefore, a router is a mapping relationship between a router and a controller.

To define a route, first open the app/router.js file and define it in it. For example:

'use strict';

module.exports = app= > {
  const { router, controller } = app;
  // Define the route of the home page
  // That is, when accessing the domain directly, the request is mapped to controller.home.home for further processing
  router.get('/', controller.home.home);
  // Define the route about us
  router.get('/about', controller.home.about);
  // Define routing for news details
  router.get('/details/:id', controller.home.details);
};
Copy the code
  • The route here can be understood as the specific path address after the domain name we are accessing. For example, /about in xxx.com/about

  • Controller.xx. xx means that when we access the route, the service maps the current route to the controller. The specific functions of the controller are described in detail below.

Controller is the Controller

  • The controller, which handles the parameters of the user request, can then call the service to get the data we need, and then return the data or render a template directly.
  • In this case, we render a page based on the Router (router parameter is not required). We render an HTML page and return it, so that the browser opens directly to the page.
'use strict';

const Controller = require('egg').Controller;

class HomeController extends Controller {
  async home() {
    const { ctx } = this;
    await ctx.render('index.njk')}}module.exports = HomeController

// Or you can simplify this
module.exports = class extends require('egg').Controller {
    // ...
}
Copy the code
  • By calling the ctx.render(‘index. NJK ‘) method, we render a template and return it to the browser.
  • Render (‘index. NJK ‘, {}); render(‘index. NJK ‘, {}); render();
  • Usually this data is read from the database by a service or is obtained by calling another interface. This will be covered in the following service.

Here is an example of calling a service:

// app/controller/home.js

'use strict';
module.exports = class extends require('egg').Controller {
    async details() { const { ctx } = this; Try {// Call the Details service in the home module of the service // // Render an await ctx.render template with const data = await ctx.service.home.details(ctx.params.id)'details.njk', data)
    } catch (error) {
      ctx.body = 'News acquisition error'}}}Copy the code
  • More information about templates is covered below
  • The definition and use of a service will be explained shortly

Service Service

What is the service used for? As mentioned above in the controller, it is mainly used to retrieve data, which can also be formatted and returned to the controller.

Here is an example of a service calling some interface service to retrieve data and return it to the controller:

// Create a service folder under app/ / and then create a home.js folder under service, and finally create app/service/home.js'use strict';

module.exports = class extends require('egg').service {// Obtain the article data according to the article ID // The ID here is controll The ID passed by the Service async Details (id) {try {const URL = `https:xxxx.com/api/${id}`
            const { data } = await this.ctx.curl(url, { dataType: 'json' })
            if(data.data && data.code === 200) {// It is also possible to process data and return // return datareturn data.data
            }
            throw 'Data acquisition failed'
        } catch (error) {
          throw error
        }
    }

}
Copy the code
  • The namehome.jsBecause of uscontrollerIs used inctx.service.home
  • service.homeIn thehomeisserviceFile Name of the file
  • This is where the simplest data acquisition takes place, throughthis.ctx.curlMethod to request data from another interface. Similar to usingaxiosThe operation of obtaining data.
  • Of course, the operation of adding, deleting, changing and checking database data can also be carried out. I will not do a demonstration this time, and I will consider opening a new article about egg project development later to introduce it in detail.
  • The domain name configuration of the interface can be stored in the configuration file
  • Curl methods can also be further encapsulated in a helper
  • The interception of data return can also be encapsulated in the middleware for interception
  • If you are interested in these contents, you can read the corresponding instructions in the egg file, which is quite detailed.

Template rendering

With the basic routes, controllers, and services defined, the template is all that remains. First of all, the template may be a static page written by the front end friend to us (PS: the front end inflatable friend may be ourselves! Oh pooh, not inflatable)

All right, back to business! The documentation for template rendering is available in the Egg. Egg itself has built-in Egg-View as the template solution, in which view supports the plug-in egg-view-Nunjucks, which is also used for template development in this article.

# Install first
cnpm i egg-view-nunjucks --save

# Then use the plugin in the root directory of config/plugin.js
'use strict';

module.exports = {
  nunjucks: {
    enable: true,
    package: 'egg-view-nunjucks',}};# After the plug-in is installed and introduced, we need to configure the parameters of the plug-in
# in the root directory of config/config.default.jsModule. exports = appInfo => {// exports // /... // Configure our plugin parameter config.view = {// Define the file name extension for the mapping. // Here we define.njk, so all our templates need to end with.njk, so that files of this type will be processed by our template plugin.'.njk': 'nunjucks'}} // Other code //... }Copy the code
  • Notice, if you don’t bark.njkYes, this is a custom, as long as your template file has the same suffix as you defined, then the plugin will handle it.
  • egg-view-nunjucksThe packaging isnunjucks.nunjucksRecommended that.njk

Egg-view-nunjucks document Nunjucks document

With a template engine, it’s much easier to nest data and so on. Here’s a quick look at the next template folder:

  • As shown in the figure above, you need to create a new app/ View folder and put all the templates in that folder. This is also configurable (if you need it);
  • The above template files, depending on your project, are here for demonstration purposes only. In general, we may take some common page headers, bottom, menu and so on and separate them for reuse, depending on your project.

Let’s take a quick look at what the base.njk template does:


      
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0, the minimum - scale = 1.0, the maximum - scale = 1.0, user - scalable = no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{% endblock %}</title>
    <meta name="keywords" content="{% block keywords %} Keywords Default content {% endblock %}">
    <meta name="description" content="{% block description %}description defaults to {% endblock %}">
    

    <link rel="stylesheet" type="text/css" href=".. /.. /public/css/base.css">
    {% block head %}{% endblock %}
  </head>

  <body>
    <! -- Body Content -->
    {% block content %}{% endblock %}

    <script src=".. /.. /public/js/jquery.js"></script>
    <! -- part of script -->
    {% block script %}{% endblock %}
  </body>
</html>
Copy the code
  • Most of the time, the basic structure of a static page is the same, or similar. So we can define a basic template, and then all the pages inherit from our basic template. We’ll talk about inheritance later.
  • For example, here we define the basic template with a header, but the header title and other content may be different from page to page, so let’s customize oneblockBlock, so that you can replace the contents of the block in the page. Something likeVue slotSlot.
// Define block {% block title %} default {% endblock %}Copy the code
  • Here’s a quick rundown of the basic blocks defined by the individual:
The name of the instructions
The head piece To insert some code at the very bottom of the head. For example, common templates introduce common CSS, but each page may also introduce separate CSS files, except for those with only one CSS file globally
The content of Used to place the body of each page
The script block This is used to place the JS files that need to be imported separately for each page

With the basic templates and blocks defined, let’s take a look at how to use them on the page:

{% extends "./base/base.njk"%} {% block title %} {% endBlock %} {% block description %} {% endBlock %} {% block keywords Join us, contact us {% endblock %} {%set navActive = "about" %}

{% block head %}
  <link rel="stylesheet" href=".. /public/css/swiper.min.css">
{% endblock %}

{% block content %}
  <div class="page-wrapper"> <! Import the public nav template --> {% include'./base/nav.njk'%} <! --> <section class="banner-wrapper">
      <img src=".. /public/img/icon/about-banner.jpg" alt="Background LOGO"> <span>{{ userName }}</span> </section> <! - rendering HTML templates demo - > < div > {{content | safe}} < / div > <! -- import the public bottom template --> {% include'./base/foot.njk' %}
  </div>
{% endblock %}

{% block script %}
  <script src=".. /public/js/swiper.min.js"></script>
  <script src=".. /public/js/about.js"></script>
{% endblock %}
Copy the code
  • From a simple template above, many of the most commonly used and critical features are included. In the first line, we want the current page to inherit the base template we defined:
// Relative path {% extends"./base/base.njk" %}
Copy the code
  • Overrides the blocks we custom in the base template. It can also be understood as passing new content to vue’s slot:
// For example, set the title of the page // If not, the default base template {% block title %} will be used. This is a new title{% endBlock %}.Copy the code
  • Set variables that are available in the current template. Other imported templates can be used as long as they are present in the current template.
// Define a navActive variable // a common scenario {% will be explained laterset navActive = "about" %}
Copy the code
  • Use variables, render content/HTML
// For example, the userName is a property of the data object we get from the controller by calling service. // Then we pass the object to the template. }}}}}}}}}}}}}}}}}}}}}}}}}'jack'}, {{userName}}, {{userName}}, {{userName}}, {{userName}}, {{userName}}, {{userName}}, {{userName}}, {{userName}} / / filter can also be customized, specific view documentation of {{content | safe}}Copy the code
  • Introduce additional templates. As you can see above, we inherited the basic template, but we can also extract some common ones like the NAV template for reuse.
// Import our nav template {% include'./base/nav.njk' %}
Copy the code
  • It’s very common for nav templates to have a scenario where the current page options need to be highlighted. Let’s take a look at the nav template:
<! -- Navigation header --> <nav class="nav-wrapper">
  <ul class="menu-content">
    <li>
      <a href="/news" class="{{ 'active' if navActive == 'news' else '' }}"> < span style = "box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; white-space: inherit! Important;"/about" class="{{ 'active' if navActive == 'about' else '' }}"> </nav> </nav> </nav>Copy the code

As you can see, we’ve made an if judgment on the class name to add an active class name to the variable navActive if it has some value, and an empty class name otherwise. Just pay attention to how you write it. So how do we define variables? Going back to defining variables, we’ve already defined a variable, navActive, on the page, so we just need to define one for each page with the value we want.

  • As a final note, the adjustment path in the template is directly written as we are inrouter.jsInstead of writing the template address.
// <a href="/news"></a> // js is the same, using router-js defined route window.location.href="/news"
Copy the code

I am your old friend Leng Hammer ~ like friends welcome to focus on praise, share and exchange more front-end fun technology!