npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

noscript-react

v1.1.4

Published

Расширение noscript, позволяющее использовать React компоненты в качестве отображения

Downloads

15

Readme

React + Noscript

Build Status npm version Dependency Status

Оглавление

CommonJS подключеие

Подключение npm пакета noscript-react в CommonJS стиле производится следующим образом:

var NSReact = require('noscript-react');
var NS = require('noscript');
var ns = NS();

// Наложение расширения на noscript
NSReact(ns);

В этом случае React и ReactDOM будут подключены через require в пакете noscript-react.

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

Есть специальные классы ns.ViewReact, ns.ViewReactCollection и внутренний класс ns.BoxReact. Кроме того, что они имеют все те же поля, что и обычные ns.View, ns.ViewCollection и ns.Box, есть еще поле component — декларация реакт-компонента.

Например,

ns.ViewReact.define('aside', {
    component: {
        render: function() {
            return React.createElement(
                'div',
                { className: 'aside' },
                // YATE: apply /.views.menu ns-view
                this.createChildren('menu')
            );
        }
    }
});

По умолчанию, если это поле не указано или не указан метод render в нём, то отрисовывается ReactElement, реализующий тег div и внутренние вью размещаются в нём. Таким образом, сохраняется аналогичное с YATE поведение по формированию отображения ns.View. Стоит отметить, что указанному div добавляются className и data-key из props, которые может получить вьюшка в результате вызова createChildren с пропсами.

Реакт-компоненты в props получают свою вьюшку view и объект с её моделями - models.

С помощью ссылки на view в пропсах есть знания о кусочке дерева, который лежит ниже этой вьюшки, соответственно есть возможность расставить детей в шаблоне.

Обновляются компоненты по привычной ns-схеме: если реактивная вьюшка стала не валидной (поменялись данные, например), то при следующем ns.Update она будет перерисована. Перерисовка происходит средствами React.

Чтобы это реализовать пока пришлось переопределить приватный _updateHTML и _addView у ns.ViewReact и ns.ViewReactCollection. Рассчитываем на то, что в ns эти методы станут публичным, чтобы можно было законно переопределять.

Есть набор ограничений, которым стоит следовать, когда используются реактивные вьюшки и боксы:

  • корневая вьюшка app должна быть обязательно ноускриптовой;
  • реактивный бокс создаётся только когда он был описан как дочерний элемент реактивной вьюшки. В этом случае обычный бокс создан не будет. Поэтому стоит озаботится о подключении ns.BoxReact к приложению.

Сама реализация ns.ViewReact, ns.ViewReactCollection, ns.BoxReact может находиться в отдельном репо и подключаться к ns в виде плагина, по аналогии с босфорусом.

Серверный рендеринг

Для использования "реактивных" вью на сервере необходимо подключить плагин noscript-bosphorus к приложению и установить глобальных флаг ns.SERVER = true. Это позволит, используя ns.Update и метод ns.Update.prototype.generateHTML, сгенерировать на сервере HTML страницы, включая в него "реактивные" вью.

Например,

ns.SERVER = true;

ns.layout.define('index', {
    app: {
        reactView: true
    }
});

ns.View.define('app');
ns.ViewReact.define('reactView');

var appView = ns.View.create('app');
var appLayout = ns.layout.page('index');
var update = new ns.Update(appView, appLayout, {});
update.generateHTML()
    .then(function(appHTML) {
        // Тут доступен HTML приложения в appHTML
    });

События

Встроенные

Для реактивной вьюшки работают встроенные события.

ns.ViewReact.define('foo', {
    events: {
        'ns-view-init': function() {
            // доопределяем инициализацию
        },
        'ns-view-htmlinit': function() {
            // компонент инициализирован (componentWillMount)
        },
        'ns-view-show': function() {
            // компонент в DOM и виден (componentDidMount)
        },
        'ns-view-hide': function() {
            // компонент cпрячется (меняется лейаут)
        },
        'ns-view-htmldestroy': function() {
            // компонент обновится
        }
    }
})

Порядок всплытия событий сохраняется. ns.Update.prototype.perf учитывает отрисовки и обычных и реактивных видов.

«Космические»

Работают «космические» события по аналогии с обычными вью.

ns.ViewReact.define('foo', {
    events: {
        'my-global-event@show': function() {},
        'my-global-event@init': function() {}
    }
})

Наследование

Как и для обычного вида, для реактивного, можно указывать базовый вид.

ns.ViewReact.define('bar', {
    methods: {
        helloFromViewBar: function() {}
    },
    component: function() {
        helloFromComponentBar: function() {}
    }
});
ns.ViewReact.define('foo', {
    events: {
        'ns-view-htmlinit': function() {
            // унаследовали метод родительской вьюшки
            this.helloFromViewBar();
        }
    },
    component: {
        hello: function() {
            // унаследовали метод родительского компонента
            this.helloFromComponentBar();
        }
    }
}, 'bar');

Наследуются методы родительского вида, а для компонента — методы компонента родительского вида и миксины, которые были определены у родительского компонента.

API ns.ViewReact

ns.ViewReact - это наследник ns.View, который вместо YATE использует ReactComponent. Выделяется 3 типа связанных компонентов с ns.ViewReact:

  • none - компонент ещё не создавался (отсутствует).
  • root - корневой компонент. С него начинается создание вложенных в ns.ViewReact компонентов (других ns.ViewReact).
  • child - дочерний компонент. Это компонент, который размещён в какому-то root на любом уровне вложенности.
  • destroyed - компонент уничтожен в момент уничтожения ns.ViewReact.

Такое деление было введено для того, чтобы понимать, когда необходимо вызвать ReactDOM.render, а когда forceUpdate для ReactComponent.

Каждый раз, когда _updateHTML вызывается у ns.ViewReact, происходит актуализация состояния вложенных в неё вью. Это позволяет выяснить, какая часть дерева стала невалидной и перерисовать её. При первом вызове - невалидно всё дерево.

Перерисовка чаще всего вызывается на root компоненте. Но возможен вызов и на child компоненте. Например, если ns.ViewReact, содержащая child компонент, является асинхронной или обновление было вызвано через метод ns.ViewReact~update.

#mixComponent

Статичный метод ns.ViewReact, позволяющий расширить описанный при декларации view компонент базовым миксином, обеспечивающим отрисовку компонента по описанным выше правилам.

#createClass

Статичный метод ns.ViewReact. Создаёт React компонент по его декларации, который потом будет использоваться для рендринга.

#getChildView

Позволяет получить дочернее ns.ViewReact по указанному id (в случае ns.ViewCollection по указанной модели). Используется в методе createChildren связанного с view компонента, что позволяет при наследовании при необходимости переопределить поведение.

#forEachItem

Проходит по всем доступным для работы дочерним view для ns.ViewReact. В случае бокса - это активные вью, в случае коллекции - это активные элементы коллекции. Данный метод служит точкой переопределения перебора дочерних элементов в createChildren методе компонента.

#createElement

Создаёт React элемент c указанием view и models в props. В качестве ключа использует ns.ViewReact~__uniequeId. Также позволяет передать дополнительный props для создаваемого компонента.

#reactComponentType

Тип React компонента.

  • none (по умолчанию) - компонент ещё не создан
  • root - корневой (родительский) компонент
  • child - дочерний компонент
  • destroyed - компонент уничтожен

#softDestroy

"Тихо" удлаяет React компонент, связанный с ns.ViewReact. Для этого, ns.ViewReact помечается типом, что компонент уничтожен, и уничтожается. Сам же компонент будет удалён при первом же ns.Update. Используется в ns.ViewReactCollection.

API ns.ViewReactCollection

Коллекция наследуется от ns.ViewReact, поэтому имеет схожее с ним API. Определение коллекции производится аналогично ns.ViewCollection. Отличием является то, что элементы ns.ViewReactCollection - это реактивные вью ns.ViewReact. Поэтому они должны быть определены через ns.ViewReact.define.

Пример создания коллекции:

ns.Model.define('list', {
    split: {
        items: '/',
        params: {
            'id': '.id'
        },
        model_id: 'item'
    },

    methods: {
        request: function() {
            return Vow.fulfill([
                {id: 1, value: 1},
                {id: 2, value: 2},
                {id: 3, value: 3}
            ]).then(function(data) {
                this.setData(data);
            }, this);
        }
    }
});

ns.Model.define('item', {
    params: {
        id: null
    }
});

ns.ViewReactCollection.define('list', {
    models: ['list'],
    split: {
        byModel: 'list',
        intoViews: 'item'
    },
    component: {
        render: function() {
            return React.createElement(
                'div',
                { className: 'list' },
                this.createChildren()
            )
        }
    }
});

ns.ViewReact.define('item', {
    models: ['item'],
    component: {
        render: function() {
            return React.createElement(
                'div',
                { className: 'item' },
                this.state.item.value
            )
        }
    }
});

API ns.BoxReact

Поведение ns.BoxRact, его методы и описание в layout полностью соответствует ns.Box. Поэтому каких-то особых правил описания его в lyaout нет.

API ReactComponent

Каждый компонент, связанный с реактивной вьюшкой, расширяет поведение реакт-компонента с помощью специального миксина.

getModel

Возвращает модель по id

getModelData

Возвращает данный указанной модели по определенному jpath. Если jpath не указан — вернутся все данные.

ns.ViewReact.define('articleCaption', {
    models: ['article'],
    component: {
        render: function() {
            return React.createElement(
                'h1',
                { className: 'article-caption' },
                // YATE: model('article').caption
                this.getModelData('article', '.caption')
            )
        }
    }
});

createChildren

Аналог apply /.views.view ns-view или apply /.views.* ns-view в yate.

Создаст реакт-элементы для указанных реактивных вьюшек, если они есть среди активных потомков текущей вьюшки. Если указанной вьюшки нет, вернет null. Позволяет передать props для создаваемых реакт-элементов.

Возможные варианты вызова:

this.createChildren() // создаст компоненты для всех дочерних view

this.createChildren({length: 25}); // создаст компоненты для всех дочерних view и передаст им указанные props

this.createChildren('child-view') // создаст дочернее view с id `child-view`.

this.createChildren('child-view', {length: 25}) // создаст дочернее view с id `child-view` и передаст в неё указанные props

this.createChildren(['child-view1', 'child-view2']); // создаст дочерние view с id `child-view1`, `child-view2`

this.createChildren(['child-view1', 'child-view2'], {length: 25}); // создаст дочерние view с id `child-view1`, `child-view2`  и передаст в них указанные props

Различия:

  1. Для ns.ViewReact метод принимает id вьюшек, которые нужно создать, и props для их компонентов.
  2. Для ns.ViewReactCollection метод принимает модели коллекций, с которыми связаны создаваемые вьюшки, и props для компонентов элементов коллекции.

Особенности

Дефолтный displayName

Если не указывать displayName у компонента, то он будет сгенерирован автоматически на основании айдишника вьюшки, приведенный из camelCase к минус-разделителям.

ns.ViewReact.define('myView', {
    component: {
        render() {
            // my-view
            console.log(this.constructor.displayName)
        }
    }
})

Это удобно, если использовать реакт-миксин для генерации БЭМ-классов, который в качестве имени блока берет displayName компонента.

Если displayName определен в декларации явно, то будет использован он.

Работа со стейтом

Реактивные вьюшки могут использовать стейт реакт-компонента. Стейт сохраняется между перерисовками вьюшки.

setState не вызывает перерисовку

Простой вызов setState не вызывает перерисовку компонента, связанного с вью. Для того, чтобы это произошло необходимо явно вызвать this.props.view.invalidate(), перед тем как устанавливать новый стейт.

У реакт-компонента, который связан с вью определен shouldComponentUpdate, который разрешит перерисовку в одном из следующих случаев:

  • вьюшка невалидная (поменялись версии моделек или был вызван invalidate)
  • один из деток невалидный
  • у вьюшки еще нет экземпляра компонента

Умные перерисовки в боксах

В реактовых боксах разным экземплярам одной и той же вьюшки будет соответствовать один и тот же экземпляр реакт-компонента. Это значит, что:

  • создается меньше экземпляров реакт-компонентов
  • перерисовки между двумя экземлярами вида происходит в виртуальном доме, именно за счет того, что render возвращает тот же самый экземпляр реакт-компонента