Pull to refresh

Comments 15

Есть решение лучше — не писать мегабайты кода. :-)


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

Если использовать redux, то состояние стора спокойно ложится в какой-нибудь __INITIAL_STATE__ или что-то подобное. Поэтому проверка чексум пройдет.

Насчет подготовки данных, то с этим не так все плохо, никто не запрещает на сервере сделать middleware наподобии:
app.use(function(req, res) {
   req.store = configrureStore()
   res.renderMarkup = function() {
     res.send(renderFullPage(<App />, req.store.getState())
   }
})


И далее в каком-то роуте:
app.get('/some', async function(req, res) {
  req.store.dispatch(await loadSome())
  req.store.dispatch(await doAnotherAsync())
  res.renderMarkup()
})
app.get('*', (req, res) => {
  res.renderMarkup()
})


Пример странный, но можно использовать асинхронные вещи.

У вас получается код одинакового наполнения стора разный для клиента и сервера. И вам необходимо поддерживать их точную эквивалентность.

Я делаю сейчас по другому:
Много кода
class BlogPage extends Component {
    componentDidMount() {
        this.props.fetchPosts(this.props.params.page);
    }
}

BlogPage.dispatchOnServer = [
    params => fetchPosts(params.page)
];

и на сервере используется функция которая резолвит все dispatchOnServer компонентов:
export default (dispatch, components, params) => {
    const needs = components.reduce((prev, current) =>
        (current ? (current.dispatchOnServer || []).concat(prev) : prev),
        []
    );
    const promises = needs.map(fn => {
        let action = fn(params);

        if (isFunction(action)) {
            action = action(dispatch);
        } else {
            dispatch(action);
        }

        if (!('promise' in action)) {
            throw new Error('dispatchOnServer action MUST have \'promise\' key');
        }

        return action.promise;
    });

    return Promise.all(promises);
};

При этом action'ы выглядят так:
export function fetchPosts(offset) {
    return dispatch => dispatch({
        type: actions.FETCH_POSTS,
        offset,
        promise: fetch(`${constants.API}/posts?offset=${offset}&limit=${constants.POSTS_PER_PAGE}`)
            .then(responseData => dispatch({
                type: actions.FETCH_POSTS_SUCCESS,
                posts: responseData.data,
                total: responseData.total
            }))
            .catch(error => dispatch({
                type: actions.FETCH_POSTS_FAILURE,
                error: apiHelpers.formatResponseError(error)
            }))
    });
}

Ее использование:
app.get('*', (req, res) => {
    const memoryHistory = createMemoryHistory(req.url);
    const store = initReduxStore(memoryHistory);
    const location = req.url;
    const history = syncHistoryWithStore(memoryHistory, store);

    match({ routes, location, history }, (error, redirectLocation, renderProps) => {
        if (error) {
            return res
                .status(500)
                .send(error.message);
        }

        if (redirectLocation) {
            return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
        }

        if (renderProps) {
            return fetchComponentsData(store.dispatch, renderProps.components, renderProps.params)
                .then(renderTemplate.bind(null, store, renderProps))
                .then(dataToServing => res
                    .status(dataToServing.status)
                    .end(dataToServing.html)
                )
                .catch(renderError => res.status(500).end(renderError.message));
        }

        return res.status(404).send('Not found');
    });
});



Это позволяет держать всю логику наполнения стора в компоненте, хоть и по разному для клиентской части и сервера. Ничего удобнее я пока не видел, хотя хотелось бы конечно =)
Упоминание реакта в данном контексте несколько странно. Да, он может в изоморфность, однако это лишь одно из специфических решений для seamless-отрисовки как на клиенте, так и на сервере. Но на отрисовке свет клином не сошелся, изоморфность это также и про получение данных, и про их обработку (читай — бизнес-логику). И там тоже есть свои грабли, тысячи их.
Но! С другой стороны пример с React очень показателен, ибо он как известно именно V(iew), собственно то что мы видим, о чём собственно и шла речь выше — отображение.
Я вот не совсем согласен, что главное достоинство изоморфности — это скорость начальной загрузки страницы. Потому что js-файл приложения будет грузиться только 1 раз, при самом первом открытии сайта пользователем. Во всех остальных случаях он будет подгружаться из кэша браузера, а с сервера будет запрашиваться только легкий json. И вот когда js уже в кэше нашего браузера, и мы жмем f5 для полной перезагрузки страницы, то далеко не факт, что рендеринг всей страницы на сервере будет быстрее, чем запросить json (вместо того чтобы тянуть с сервера весь html целиком) и вставить его на клиенте в нужные блоки.
На мой взгляд, гораздо более полезная плюшка изоморфности — это решение проблем с поисковиками, потому что мы пишем код только один раз, и он дает один и тот же результат и поисковику и человеку в браузере. Это избавляет нас от костыльных решений типа генерации снапшотов страниц по крону, или хуже того, написания второго приложения специально для поисковиков.
Не забывайте про то, что быстро загруженный из кэша скрипт — это полбеды, надо еще отрендерить всю страницу в браузере пользователя. Если у браузера ресурсов мало, то и процесс будет долгий. В случае изоморфного приложения вам ничего не мешает кэшировать html, который отдает сервер. Мне кажется решение проблем с поисковиками вытекает как следствие из того, что мы можем быстро отдать первый раз отрендеренную страницу.
Если у браузера ресурсов мало, то и процесс будет долгий

В том и дело, что слишком много «если». Особенно в 2016-м году, когда даже телефоны имеют 4-ядерные процессоры. А если у меня мощный компьютер, а интернет так себе? Тогда мне быстрее будет получить данные через json и отрендерить страницу у себя, чем тянуть полностью весь html с сервера (и опять же, не в 100% случаях это будет так).

ничего не мешает кэшировать html, который отдает сервер

Кэшировать отрендеренный сервером html на стороне клиента? В таком случае, никто не мешает и кэшировать html, отрендеренный клиентом при первой загрузке страницы.

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

Нет, решение проблем с поисковиками вытекает из того, что мы пишем один и тот же код для сервера и клиента, и используем его и там, и там. И это позволяет нам не тратить время на создание дополнительных решений специально для поисковиков (и для всех других нужд, которые могут потребовать отрендеренную страницу без участия браузера). То, что страница рендерится сервером в некоторых случаях быстрее, это просто приятный бонус.
Пользователи GMail с вами не согласны. Если нужно открыть несколько писем в новых вкладках, ждать придётся прилично долго. Поэтому, на мой взгляд, генерация html на сервере — часть юзабилити в такого рода веб-приложениях. Т.е. даже в случае, когда индексация поисковиками не нужна.

Изоморфность может понадобиться для решения довольно специфического круга задач, когда, к примеру, требуется отрендерить на сервере и закэшировать частые запросы к выводу элементов каталога, которые должны быть проиндексированы в таком виде поисковиком. И тут Реакт очень далеко не оптимален, как решение. В случае, когда вы создается SPA, работающее с данными на клиенте, изоморфность — не нужна совсем.

При работе с готовыми gui компонентами. Например material ui, есть проблема, была раньше, чексум из-за вендорных префиксов. На сервере их не было, на клиенте были, в связи с этим суммы не сходились.

Все эти схемочки очень интересны конечно, но в них не учитывается один момент — запросы к АПИ/базе при рендеринге на сервере. И эти запросы (их обычно много) обычно получаются заметно медленнее чем скачивание бандла (рендеринг происходит после самого долгого из них). То есть при обычном подходе пользователь скачивает бандл, дальше у него рендерятся какие-то блоки, которые отправляют запросы за своими данными, выводят лоадер и по мере получения данных раздупляются, тут пользователь уже получает какую-то обратную связь, он видит, что что-то грузится, в случае изоморфного приложения здесь пользователь всё ещё видит белый экран. Первые полностью готовые блоки обычно получается быстрее чем рендеринг на сервере, хотя полностью готовый прил в случае с изоморфностью будет конечно раньше. В общем, без прогрессивного рендеринга вся эта изоморфность может быть интересна только для SEO (и то слишком дорогое SEO выходит, проще по старинке), в ощущаемой скорости загрузки изоморфные приложения всё же проигрывают. А понимая как работает реакт я сомневаюсь, что в него можно нормально этот прогрессивный рендеринг вкорячить, пока видел только одну попытку, и явно неудачную. Вот в этой ветке https://habrahabr.ru/post/280636/#comment_8882284 я как раз встал на сторону реакта (для выяснения истины так сказать) и получил ровно то, чего сам и придерживаюсь.

UFO just landed and posted this here
Sign up to leave a comment.

Articles