Sprute.js. Ещё один изоморфный JavaScript фреймворк

image
Картинка для привлечения внимания

Sprute.js — новый изоморфный JS фреймворк. При его проектировании и реализации упор делался в первую очередь на удобство разработки и сохранение самого фреймворка максимально простым и компактным. В первую очередь это касается изоморфности.

Зачем еще один фреймворк?


В существующих фреймворках меня не устраивает подход к реализации изоморфности — моей целью было реализовать изоморфность таким образом, чтобы это не определяло архитектуру и не приходилось строить архитектуру вокруг изоморфности, а сделать её максимально прозрачной — чтобы я мог писать серверный код так, как я это привык, и он так же работал на клиенте. Мой подход можно назвать server side first.

Прошу сильно не пинать за скудность текста — развернутое изложение своих мыслей всегда было моим слабым местом.

Подход к изоморфности


Для реализации изоморфности я решил «эмулировать» node.js в браузере — изоморфный код пишется на сервере и работает так же на клиенте. Для этого пришлось портировать node.js'ный require в браузер и эмулировать файловую систему. Так же я частично портировал node.js'ные модули process, fs, events.

Архитектура


Весь код разделяется на 3 категории — серверный, клиентский, изоморфный и разделен по директориям back, front, common. В директориях back и front находятся базовые классы, специфичные для соответствующего окружения, 90% кода находится в директории common. Функционал фреймворка, такой как сервер, шаблонизатор, сокетное соединение реализован в виде компонентов — по сути модулей. Это позволяет инкапсулировать код c определенной зоной ответственности; так же позволяет писать изоморфные обертки для таких вещей, как сокетное соединение, создавая единый api на клиенте и сервере и позволяя менять реализацию компонента в дальнейшем.

Пример компонента:

'use strict';

module.exports = {
    init() {
        let module;
        app.clientSide(() => {
            module = require('./lib/client')
        });
        app.serverSide(() => {
            module = require('./lib/server')
        });
        return module.init()
    }
};


Статика


Стили, скрипты, реализующие интерактивность интерфейса и т.п., собираются в темы — директория с файлами и объект конфигурации. Это позволяет отделить логику интерфейса от остальной логики; так же это позволяет для каждой страницы задавать отдельную тему — удобно при редизайне сайта или создании различных админок и т.п.

Работа с данными


Работа с данными реализована с использованием паттерна data mapper. Логика сохранения/выбора данных находится в маппере, бизнес логика — например, обладает ли пользователь определенной привилегией — в модели, логика работы с набором — в коллекции. Изоморфность работы маппера реализуется следующим образом — запрос сериализуется в объект и передается на сервер, там объект запроса передается тому же мапперу; результат возвращается на клиент. На данный момент реализован маппер, использующий библиотеку knex для построения запросов и выборки данных.

Пример маппера


const BaseMapper = require(app.get('commonPath')+'/mappers/knex-mapper');

class CoolModel {}

class CoolMapper extends BaseMapper {
    constructor() {
        let connections;
        app.serverSide(() => {
            connections = require(process.cwd()+'/configuration/connections')
        });
        app.clientSide(() => {
            connections = {};
        });
        super({
            client: 'mysql',
            connection: connections.mysql
        })
    }
    get tableName() {
        return 'cool'
    }

    get model() {
        return CoolModel
    }

    beforeCreateTable(table) {
        table.comment('very cool table')
    }

    addColumns(table) {
        table.increments('id').primary();
        table.string('field1');
        table.integer('field2');
        table.string('field3')
    }

    get validator() {
        if(!this._validator) {
            let vE = app.get('validationEngine');
            this._validator = new vE({
                id: 'integer',
                field1: 'not_empty',
                field2: 'integer',
                field3: 'not_empty'
            })
        }
        return this._validator
    }

    validateModel(model) {
        return this.validator.validate(model)
    }
}

const mapper = new CoolMapper();
mapper.find().limit(10).offset(5).then(collection => { /* code here */ });
mapper.findOne().where({id:2}).then(model => { /* code here */ })


Как это работает


Точкой входа является класс App. При инициализации класса производится инициализация компонентов — работа фреймворка здесь завершена. Дальше работу на себя берет компонент сервер, затем регистрируются роутеры. Дальнейшая схема работы состоит в обработке запросов роутерами.

Пример роутера:

'use strict';

const BaseRouter = require(app.get('classPath')+'/routers/base'),
    process = require('process'),
    theme = require(process.cwd()+'/configuration/theme-light');

module.exports = class extends BaseRouter {
    constructor(params, DomDocument) {
        super(params);
        this.DomDocument = DomDocument || require(app.get('classPath')+'/classes/dom-document')
    }

    index(req, res) {
        const view = new (require('../views/main-page'))(theme),
            DomDocument = new this.DomDocument(theme);
        view.render().then(html => {
            DomDocument.setBlock('main', html);
            this.loadPage(DomDocument, res)
        })
    }
};


Состояние на данный момент


В данный момент на нем работает один сайт — bel31stroy.ru и еще один находится в разработке. Сам фреймворк периодически подвергается доработкам. Pull реквесты и баг репорты приветствуются.

Github: github.com/one-more/sprute
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 49

    0
    В вашем фреймворке реализована реактивность? Если да, то как?
      0
      Что вы имеете в виду под реактивностью?
        +1

        Привет, claygod недавно интересовался изоморфным фреймворком RiftJS, который использует cellx для реактивности. Если захотите встроить cellx в свой фреймворк — спрашивайте, попробую помочь, чем смогу)) Ну и сам RiftJS стоит поковырять, там много интересных идей, я его к сожалению забросил в пользу чисто клиентского фреймворка.

          0
          Riim, именно это я и имел в виду. Однако возможно, что one_more эту парадигму программирования не применял.
            0
            Архитектура фреймворка позволяет легко добавить такой функционал, если есть необходимость, используя любую реализацию (cellx например). Нет необходимости встраивать это по умолчанию на мой взгляд (это может быть не нужно в некоторых проектах). Я сосредоточился в основном на гибкости и возможности расширения и стараюсь сохранять фреймворк максимально компактным.
          0

          Реактивность еще очень часто упоминают в контексте meteor.js

        –5
        оперделенной привилегией
        За что Вы ее так? )
          0
          исправил)
          0
          как я понял это js фреймворк исключительно для генерации страниц на сервере? А что на клиенте делать?
            0
            На клиенте страницы рендерятся так же, как и на сервере. Шаблонизатор является изоморфным.
            +1
            На демо-сайте навигация через хэшбенг и не работает
              0
              Кажется, сайт накрыл хабраэффект. У меня ничего не работает.
                0
                Посмотрел сайт (строительный). Походил по страницам. Решил проверить, как он хранит историю. Щёлкаю подряд пункты меню сверху вниз, потом нажимаю «Назад, назад, назад… » и сайт меня кидает по страницам в каком-то странном порядке…
                  0
                  Пункты меню в левой части не меняют адрес страницы — это просто якори на определенный блок страницы. А так ссылки работают через history api.
                    +1
                    @ one_more, вы же не будете каждому посетителю объяснять про якоря. Он будет щёлкать по меню и ожидать при щелчке на кнопку «Назад» или нажатии «Backspace», что попадёт в предыдущее состояние. Это простое юзабилити, и ничего больше.
                  +4
                  А нужна ли изоморфность в 2016-ом? Ведь с точки зрения современных подходов построения веб-приложений (SPA/PWA) это костыль для поисковиков, ради которого приходится корректировать всю архитектуру приложения (к слову, паук от Google уже не требует такого костыля). Проще этот костыль отдать на откуп Prerender.io, чем вносить лишние ограничения в архитектуру. ИМХО.
                    0
                    основная цель феймворка как раз состоит в том, чтобы реализация изоморфности не накладывала никаких ограничений на архитектуру.
                      +3
                      Абстракции изоморфности фреймворка рано или поздно «протекут» в виде ограничений, даже если их сразу и не было заметно.
                        0
                        Будет такое. Но архитектура фреймворка позволяет разбивать код на изоморфный и специфичный для каждой платформы — подобные ограничения можно обходить без использования костылей.
                      +3
                      Помимо гугла есть и другие пауки, причём, не только поисковые.
                      Хотя насчёт изоморфности не могу не согласиться в том, что это протекающая абстракция.
                      Речь максимум может идти о частичной изоморфности — у клиента и сервера разные задачи и разные возможности.
                        0
                        > Помимо гугла есть и другие пауки, причём, не только поисковые.
                        И зачем о них тревожиться?
                          0
                          Довольно большой процент задач все-таки является одинаковым — выборка/обработка/отображение данных. Например, поисковый движок можно сделать изоморфным.
                          0
                          В некоторых случаях от вас могут потребовать, чтобы first paint был в таком районе, когда еще js толком то не распарсился. Этот случай довольно редкий, но под него нужны инструменты и решения для server-side рендера.
                            0
                            Я уверен, что в любые времена и при любых фреймворках останется место некорректно поставленным, но добросовестно выполненным архитектурным задачам.
                            0

                            У "изоморфности" двя плюса с которыми, как по мне, сложно спорить: переиспользование кода и скорость отображения контента.

                              +3
                              Первый плюс, как показывает практика, касается только разделяемых валидаторов моделей (хотя в большинстве приложений таки часто получается, что серверные и клиентские модели данных отличаются), а второй плюс нивелируется кешированием скриптов и ассетов SPA/PWA на клиенте. На более-менее посещаемом ресурсе скорее клиент отрисует картинку быстрее, чем это сделает сервер. Ну а статичные страницы, независимые от логина (которые нужно отдавать анонимным пользователям, посетившим ресурс впервые) просто оформляют вне SPA, в виде так называемых лэндинг-страниц.
                                0

                                Ну не скажите, а утилиты, хэлперы, сервисы? А если строить систему на интерфейсах с внедрением зависимостей? А юнит-тесты? В любом случае, валидаторами дело не заканчиваеться, поэтому зря вы ими ограничиваетесь. По второму пункту: с чего вы взяли что клиент отресует картинку быстрее? Даже если скрипты закешированы их нужно вытащить-распарсить; шаблоны закешированы? и сново достать распарсить, без этого шаблонизатор не начинает создавать dom/html; также для рендеринга нужны данные, и за ними обычно идут по http. Как бы вы не противились, если трезво оценить ситуацию, это действительно два плюса. Я не утверждаю, что это прям всем нужно и без этого никак, но от этого они не перестают быть плюсами)

                                  0
                                  > Ну не скажите, а утилиты, хэлперы, сервисы?
                                  Если считать, что на сервере нет рендеринга, то они оказываются ненужными.
                                  > Я не утверждаю, что это прям всем нужно и без этого никак, но от этого они не перестают быть плюсами)
                                  Если что-то кажется вам (или вашей команде разработчиков) более привычным, чем какие-то абстрактные идеалы и сферические архитектуры в вакууме, то нет особого смысла пытаться оправдать свой выбор технологий чем-то ещё.
                                    0
                                    Если считать, что на сервере нет рендеринга, то они оказываются ненужными.

                                    Всё ясно, спасибо)

                                      0
                                      Судя по процитированному тезису — ясно не всё. Аргументы о ненужности серверного рендеринга — в корневом комментарии данной ветки.
                              +3
                              Во-первых, почему вы думаете, что prerender.io — это меньший костыль? По факту вы запускаете браузер на лету, ждете его ответа и потом отдаете. Рендеринг JS, что на клиенте, что на сервере — все-таки документированная возможность у браузера и у Ноды. Вот еще более развернуто.
                              Во-вторых, индексация сайтов с клиентским рендерингом (то есть в случае, когда от сервера приходит JSON) работает только у гугла и недавно. Даже если забыть, что в РФ у него даже не половина поискового рынка, то подгрузка содержимого нужна еще фейсбуку, вконтакту, скайпу, твиттеру и еще хреновой туче сайтов.

                              Ну и в третьих
                                +2
                                1) Потому что в минимальной степени влияет на архитектуру приложения. 2) Да, потому о prerender и вспомнили, пока остальные поисковики не подтянутся. 3) https://github.com/prerender/prerender.
                                  +1
                                  Какое-то у вас костыльно-ориентированное программирование получается — костыль для браузера, костыль для сервера; вместо того, чтобы использовать нормальный инструмент, который хорошо справляется с работой в обоих случаях
                                0
                                В корне с вами не согласен. Изоморфность — это фактически единственная уникальная возможность, которую дает нам NodeJs в данный момент. Для большинства серверных задач есть и более подходящие инструменты, чем NodeJs, но возможность изоморфного подхода к веб-приложениям по факту гарантирует нам Progressive Enchantment в любой момент развития приложения. В связке с микросервисной архитектурой такой подход очень эффективный. Да подстроить изоморфное веб-приложение сложнее, но это того стоит. Единственное что я бы не советовал рефакторить существующие SPA в таком ключе, реально можно огрести и проще подключить тот же prerender. Все же изоморфным приложение должно строиться изначально.
                                  0
                                  Progressive enhancement совсем не про изоморфность ( http://css-discuss.incutio.com/wiki/Progressive_Enhancement ). Для вас изоморфность — самоцель («уникальная возможность»), а не средство решения реальных проблем.
                                    0
                                    Progressive enhancement это совсем не про CSS. Это про способ достижения доступности приложения для различных групп пользователей. Изоморфность, которая по умолчанию позволяет пользователям без клиентского js или с его ограниченными возможностями получить полноценный доступ к вашему приложению, вполне себе ложиться на эту парадигму. А главное решает совершенно конкретный пул задач, и не только связанных с SEO. Это реальные проблемы, многие из которых блокируют создание интерактивных сайтов и веб-приложений пр типу SPA для очень многих проектов. Если вы с этими проблемами не сталкивались — это говорит лишь о вашем опыте, но никак не об отсутствии проблем.
                                      0
                                      Очередной решатель «конкретного пула проблем» пользователей lynx. Progressive enhancement — это совсем не про изоморфность. Совсем.
                                        0
                                        С чего вы взяли? Progressive enhancement — это прежде всего принцип, который я кратко описал. Изоморфность — это один из способов достижения этого принципа в веб-приложениях. Не самый простой в реализации, но один из самых эффективных в итоге.

                                        Также советую посмотреть в сторону Progressive Web Application от Google один из основных принципов данной парадигмы также является Progressive enhancement. И опять же без какого-либо упоминания про использование этого принципа только в рамках CSS.
                                          0
                                          Полимер не рендерится на сервере (Полимер-тулкит вообще не включает в себя серверной части, только дев-сервер и инструменты релизной сборки), а концепция PWE перпендикулярна концепции Progressive Enhancement, они о разном (и друг другу в каком-то смысле противоречат, т.к. применяются к сайтам двух сильно разных классов — к веб-приложениям и к веб-страницам). PWE — это о том, как SPA становится оффлайновым приложением с локальной БД и фоновыми сервисами (т.е., это уже по определению требует современного браузера и джаваскрипта), общающимся с серверной стороной посредством REST-API, а Progressive Enhancement — это о том, как поддержать устаревшие браузеры, верстая веб-страницы сначала в виде семантичной разметки, на которую лишь потом навешивается CSS и несложная JS-логика (отсутствие которой некритично для навигации по этим страницам). Попытка покрыть оба этих кейса в одном фреймворке (через изоморфность) кажется мне глупостью — дешевле для «примитивных» устройств реализовать отдельную логику отдачи лёгких страниц (по юзерагенту), чем пытаться обобщить Хром и Линкс сквозь всю архитектуру единого приложения. Имхо.
                                            0
                                            > Цитата с сайта гугла:

                                            What is a Progressive Web App?
                                            A Progressive Web App is:

                                            Progressive — Works for every user, regardless of browser choice because it's built with progressive enhancement as a core tenet.
                                            Responsive — Fits any form factor: desktop, mobile, tablet, or whatever is next.
                                            Connectivity independent — Enhanced with service workers to work offline or on low-quality networks.
                                            App-like — Feels like an app to the user with app-style interactions and navigation because it's built on the app shell model.
                                            Fresh — Always up-to-date thanks to the service worker update process.
                                            Safe — Served via HTTPS to prevent snooping and to ensure content hasn't been tampered with.
                                            Discoverable — Is identifiable as an «application» thanks to W3C manifest and service worker registration scope, allowing search engines to find it.
                                            Re-engageable — Makes re-engagement easy through features like push notifications.
                                            Installable — Allows users to «keep» apps they find most useful on their home screen without the hassle of an app store.
                                            Linkable — Easily share via URL, does not require complex installation.

                                            Меня не оставляет ощущение, что вы даете свою интерпретацию всем понятиям, при этом даже не изучаете материалы. Уж извините, но по-моему «Works for every user, regardless of browser choice because it's built with progressive enhancement as a core tenet.» достаточно конкретно, чтобы сказать что концепция PWA не просто включает в себя принцип «progressive enhancement», но и базируется на нем. В связи с этим, все что вы написали — это не более чем ваше личное видение. Да и Полимер тут совсем не при чем. Мы же говорим о принципах и концепциях.
                                              0
                                              Да, «лозунг» звучит так же, однако в случае с PWA речь уже не идёт о работоспособности без джаваскрипта. В Полимере под «прогрессивным улучшением» подразумевается возможность использования шади-дома на браузерах без шадоу-дома, а так же возможность (технически обеспечивается набором компонентов Paper и идеологией материального дизайна) запилить UI сразу «для всех устройств», потом допиливая под их нюансы по мере необходимости/возможности. Полимер в принципе не работает без клиентского JS, по своей архитектуре. Вообще никак. И серверного рендеринга там не предусмотрено. Идеологически фронт отделён от бэка (Полимер — это только клиентские компоненты).

                                              Последовательно улучшать можно очень разные аспекты и очень по-разному, но на словах эти улучшения, как это ни удивительно, можно во всех случаях назвать прогрессивными/постепенными. Факт (а не моё видение) заключается в том, что разработка PWE не начинается со статичных страниц без CSS и JS для lynx, а сразу стартует с композиции «умных» базовых компонентов. Уж извините, но не очень похоже, что вы практически понимаете, о чём эти баззворды и теория.
                                                0
                                                *PWE -> PWA (во всех комментариях)
                                                  0
                                                  Вы так и не объяснили, какая связь между PWA и Polymer? Мне кажется вы смешали в кучу мух с котлетами. PWA — прежде всего концепция. Быть может Polymer и связывают с PWA, но привязывать PWA к Polymer мне кажется не имеет смысла.

                                                  Никто не мешает вашей собственной реализации сделать более продвинутые вариант «progressive enhancement» и дать возможность приложению работать без js/css/internet/etc. Именно поэтому я и считаю, что концепция PWA и изоморфный способ создания приложений очень даже хорошо дружатся между собой.

                                                  > Уж извините, но не очень похоже, что вы практически понимаете, о чём эти баззворды и теория.

                                                  Уж извините, но я в последние 1,5 года только так и пишу веб-приложения, а вы похоже что нет, потому что сами заявили, что считаете это «костылем для поисковиков» и вообще ненужным подходом. Мой опыт показывает обратное. И чтобы дальше не спорить о пустом, еще раз выскажу свое мнение:

                                                  «Progressive enhancement — это про способ достижения максимальной доступности приложения для различных групп пользователей. Изоморфность, которая по умолчанию позволяет пользователям без клиентского js или с его ограниченными возможностями получить полноценный доступ к вашему веб-приложению, вполне себе ложиться на эту парадигму. „
                                                    0
                                                    Думаю, своё личное видение вы уже достаточно подробно разжевали для меня, спасибо. Моё, как понимаю, тоже вами понято. Спасибо за беседу.
                                  0
                                  Прошу прощения за оффтоп, ну и, конечно, это ни в коем разе не претензия, но просто когда я зашёл на гитхаб-страницу проекта и увидел там всё на русском языке, почему-то возник серьёзный диссонанс! Видимо, настолько привык к тому, что всё-всё-всё англофицируется, даже если проект делается локально, при этом разработчики с английским никак не связаны. Текста на гитхабе у вас пока не так много, думаю, вы будете расширяться, поэтому, как мне кажется, лучше бы вам уже подумать про более распространённый язык в сфере IT — тогда могут к вашему проекту появиться «глаза» и из других стран :)

                                  PS. Спрут у вас, конечно, милашка! Только, вот, в английском такого слова нет (там это обычно octopus), но, думаю, это вы так специально :)
                                    0
                                    Насчет sprute — специально, вы правы. А насчет английского — мне пока уровень владения не позволяет — много ошибок в письменной речи. Собираюсь переписать когда буду владеть языком на должном уровне.
                                      0
                                      Не в моём праве вас поучать или к чему-то принуждать, но, как преподаватель, я всё-таки посоветовал бы вам перевести все имеющиеся части описания проекта на английский. Боитесь большого кол-ва ошибок? А вы не бойтесь — иначе они никогда не исчезнут. Practice makes perfect — пишите, пишите, пишите, т.е. тот самый «должный уровень» — это дело практики и времени, а не удачного стечения обстоятельств! А люди вряд ли будут критично относиться к ошибкам (английским в мире владеет такое несчётное кол-во человек, что какого только уровня он ни бывает!) — напротив, могут исправить какие-то погрешности, PR им в помощь :) Так что моё мнение — дерзайте!
                                    +2
                                    https://pbs.twimg.com/media/CFp86XJVIAAYrhd.png:large
                                    Вставьте кто-нибудь картинку

                                  Only users with full accounts can post comments. Log in, please.