Backend in VueJS single-file components

One day, while reading the Vue Loader documentation, I came across an interesting innovation in version 15. These are custom blocks that can be embedded in Vue single-file components. The example shows how you can access the content of this block directly in the component. At first, I didn’t seem to give special value to this possibility, but then I thought, hm ... and if I shove the back of the associated front piece there ... And it started ...


Back at my time (a year ago) was on php . To begin with, I decided to see how my favorite PhpStorm editor would cope with inserting php code into this block. No matter how hard I tried, there was no talk of any code highlighting and other auto-complete functions. I think I'll write an issue in those support JetBrains. After a while, I received a negative response, about any possibility to set it up, but they sent instructions on how to set it up for javascript. Well, I think, okay, you still have to try to implement the idea. I've never had to develop anything for a Webpack before. Day studied the documentation, and over the next two evenings I developed a Loader and a plugin. All this worked, but without elementary syntax highlighting in custom blocks .vue, the php code brought only pain ...


Time went by. Slowly getting acquainted with nodejs and following the change log of changes in new versions, finding useful and ready-made solutions for myself, I began to understand that when choosing — what to write backing on — I would still use the node. Running multiple copies of applications on a node and resolving the load on these copies using ngnix gave the best results. Recently I returned to this topic and finalized the loader and plugin.


I'll start with the template


Backend template


The template is a workpiece in which pieces of the backend from custom blocks of vue files should fall. All this after processing is stored in the resulting file. Template example:


Backend template
const WEB_PORT = 314;
const Koa = require('koa');
var Router = require('koa-router');
const app = new Koa();
var router = new Router();
app
    .use(router.routes())
    .use(router.allowedMethods());
const body = require('koa-json-body')({ limit: '10kb' });
app.listen(WEB_PORT);
app.context.db = require('../lib/db.js');
/*{{endpoints}}*/

/*{{endpoints}}*/ - this is the place where the code from custom blocks will be inserted.


Webpack loader


Loader code
var loaderUtils = require("loader-utils");
var path = require('path');
const id = 'gavrilow_backend_plugin';
exports.default = function (source) {
    this.cacheable();
    // Отправляем данные далее следующему загрузчику// ВАЖНО!!! Отправляем пустую строку, иначе все что отправим попадет в конечную сбркуthis.async()(null, '');
    // Удаляем все переносы строк. Их очень много.const _source = source.replace(/^\n/img, '');
    // Путь к файлу в котором содержится Custom Block [blockType=backend]const file_path = this.resourcePath;
    // this._compiler - глобальный объект, который доступен из плагинаif (this._compiler[id] === undefined)
        this._compiler[id] = {
            change: true,
            arr: []
        };
    var fp_exists = false;
    // Перебираем массив и ищем ранее добавленный код из Custom Blocks vue// Идентификатор блока - полный путь файлу.for (let i = this._compiler[id].arr.length - 1; i >= 0; i--) {
        if (this._compiler[id].arr[i].file_path === file_path) {
            fp_exists = true;
            // если нашли, то сравним с прошлой версией.if (this._compiler[id].arr[i].data !== _source) {
                // если есть изменения то сохраяем исменения в объект и для палагина выставляем флаг, что были измененияthis._compiler[id].arr[i].data = _source;
                this._compiler[id].change = true;
            }
            break;
        }
    }
    if (fp_exists) return; // Если выше был заход в первое условие в цикле, то выходим// Добавлеме новый объект в массив, содержащий тест Custom Blocks и полный поуть к файлу// и сигнализируем флагом [ change = true ] для плагина что есть изменения.this._compiler[id].change = true;
    this._compiler[id].arr.push({
        file_path: file_path,
        data: _source
    });
};

In the bootloader, files of * .vue containing custom blocks get into processing. You can set your own custom block name.


Webpack plugin


Plug code
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
var footer_header_template;
classgavrilow_backend_plugin{
    constructor(options) {
        this.options = options;
        this.logMess = '';
    }
    endLog(){
               this.logMess = '------ gavrilow-backend-plugin ------------------------------------------------------------------\n'
            +this.logMess;
        this.addLogMess('-------------------------------------------------------------------------------------------------');
        console.log(this.logMess);
        this.logMess = '';
    }
    addLogMess(mess){
        this.logMess += mess+'\n';
    }
    async prepareTemplate(){
        try {
            if (footer_header_template === undefined) {
                let contents = await readFile(this.options.backend_template, "utf-8");
                footer_header_template = contents.split(/^\/\*+?{{.*endpoints.*}}+?\*\/$/img);
                if (footer_header_template.length !== 2) {
                    footer_header_template = undefined;
                    this.addLogMess('Не удалось найти точку вставки блоков.');
                    this.endLog();
                    returnfalse;
                } elsereturntrue;
            } elsereturntrue;
        } catch (err) {
            footer_header_template = undefined;
            throw err;
        }
    }
    apply(compiler) {
        compiler.hooks.emit.tapAsync(
            'gavrilow_backend_plugin',
            (compilation, callback) => {
                callback();
                if (this.options.backend_template === undefined || this.options.backend_template === '') {
                    this.addLogMess('Необходимо создать и/или указать файл-шаблон для бэкэнда...');
                    this.endLog();
                    return;
                }
                if (this.options.backend_output === undefined || this.options.backend_output === '') {
                    this.addLogMess('Необходимо указать путь и имя js файла для бэкэнда...');
                    this.endLog();
                    return;
                }
                if (!compiler.gavrilow_backend_plugin) {
                    this.addLogMess('В Вашем проекте нет ни одной секции для бекенда [ <backend>...</backend> ].');
                    this.endLog();
                    return;
                }
                (async ()=>{
                    try {
                        // Подготваливаем шаблонif (!awaitthis.prepareTemplate())
                            return;
                        // Если загрузчик не выставил флаг сигнализирующий о каких-либо измененийif (!compiler.gavrilow_backend_plugin.change) return; // Если ничего для бэка не поменялось// сбрасываем флаг
                        compiler.gavrilow_backend_plugin.change = false;
                        if (compiler.gavrilow_backend_plugin.arr.length === 0) {
                            this.addLogMess('По какой-то причине нет данных из секции [ <backend>...</backend> ]');
                            this.endLog();
                            return;
                        }
                        this.addLogMess('Собираем beckend: "'+this.options.backend_output+'"\n...');
                        // записываем все что выше /*{{endpoints}}*/ в шаблонеvar backend_js = footer_header_template[0]+"\n";
                        // конкатенация кусков кода из Custom Blocksfor (let i = 0; i < compiler.gavrilow_backend_plugin.arr.length; i++) {
                            backend_js +=compiler.gavrilow_backend_plugin.arr[i].data+"\n";
                            this.addLogMess('['+compiler.gavrilow_backend_plugin.arr[i].file_path+']');
                        }
                        // присоединяем все что ниже /*{{endpoints}}*/ в шаблоне
                        backend_js += footer_header_template[1];
                        // асинхронно записываем результатawait writeFile(this.options.backend_output, backend_js);
                    } catch (err) {
                        throw err;
                    } finally {
                        this.endLog();
                    }
                })();
            }
        );
    }
}
gavrilow_backend_plugin.loader = require.resolve('./loader');
module.exports = gavrilow_backend_plugin;

The plugin is triggered at the end of the project build. Prepares a template by breaking it into 2 parts: before /*{{endpoints}}*/and after. /*{{endpoints}}*/If the array change flag was set by the loader, then the final script is assembled from all available parts.




How to try it all


The project flooded the githab .


There is also a description of the settings.


Also popular now: