Hacker News Reader For No Reason - в 5K из Vanilla JS, HTML и CSS
Перевод статьи - A Hacker News Reader For No Reason - In 5K of Vanilla JS, HTML, & CSS
Автор - Mike Elliott
Источник оригинальной статьи:
Только наполовину названия верно.
Прошлой ночью я думал о том, как мой опыт полностью закрепился в React и современной экосистеме JavaScript в последнее время, возможно, повлиял на то, как я буду писать приложение вручную в vanilla HTML, CSS и JS без библиотек или чего-либо еще.
В тот же час я смотрел Hacker News и некоторые из его читателей, отмечая, как все они трудились до UX (в том числе HN - только в последнее время добавление комментария рушится и поддержка мобильных устройств), и обнаружил Hacker News API.
Ясно, что это дало возможность построить мой собственный HN reader самым легким способом я мог - полностью клиентская сторона, без библиотек, полностью vanilla.
Так что я сделал это в 2 файлы, 99 строк кода JavaScript и 99 строк HTML и CSS.
Файлы будут автоматически проходить через мой скрипт, так как они размещены на этом сайте (как описано здесь). В первую очередь JS составляет всего 4,4 КБ, но его сокращение и архивирование (gzipping) снизило его до 1,5 КБ, что довольно важно. HTML-файла составляет 3.5 КБ, минимизировано и архивированная.
Я сэкономлю вам арифметику, это в общей сложности 5 КБ на начальной нагрузке.
Перейти к A Hacker News Reader For No Reason!
Особенности
- Получает 30 лучших рассказов в порядке с их точками, так же, как на первой странице HN.
- Если рассказ имеет URL-адрес, название перенесет вас в новую вкладку с URL-адресом, в противном случае он будет переключать текстовое содержание рассказа (как Ask, Show, Jobs и т. д.).
- Если в рассказе есть комментарии, есть ссылка для переключения комментариев.
- Каждый комментарий и их дети являются разборными.
- Там есть ссылка на оригинальный рассказ на HN и на пользователя, который представил рассказ или комментарии.
- Выглядит прилично на всех устройствах.
- Супер крошечные и довольно быстро.
Ограниченные возможности
Поскольку я использую современные функции ECMAScript, только более современные браузеры будут поддерживать его, но большая часть моей аудитории, вероятно, в порядке с этим.
Это только показывает лучшие рассказов - нет поддержки New, Ask, Jobs и т. д. если они на первой странице. Это не ограничение для меня лично, так как я трачу 99% своего времени HN только на первой странице.
Нет входа, проголосовать за/против, или возможности публикации. К сожалению, HN API еще не поддерживает это, но если/когда это произойдет, возможно, я попытаюсь получить все это с помощью еще 99 строк: D
Я не дизайнер. Это было введено в моду с моим типичным астетическим минимальной темной темы с зеленым цветом повсюду. Наверное, не все любимые, но, конечно, мои, и это мои, так что неважно.
Код
Я написал стили в теге <style>
в index.html
. Поскольку одна из целей, которые я имел с этим, состояла в том, чтобы держать его супер маленьким и легким, я характерно установил произвольный предел для себя в числе, которое, как я считал, было разумным закончить его.
Я подделал некоторые из моих обычных правил стиля кода, и несколько раз я неуважительно относился к правилу линии 80-символов, которое я предпочитаю соблюдать, чтобы поразить метку строки 99 в обоих файлах, но ничего слишком сумасшедшего, и я не пересекаю никаких жестких строк плохой практики.
Вы можете проверить РЕПО здесь, но поскольку это всего 99 строк, я разместил содержимое файла JS ниже.
Обратите внимание на использование нескольких функций ES6+ и сходство с React и JSX с шаблонизацией. Конечно, здесь нет причудливого Virtual DOM или чего-то еще, и он просто манипулирует DOM по мере необходимости. Я использую функциональные и неизменяемые шаблоны, где это применимо, на языке нашего времени.
const hnBaseUrl = 'https://hacker-news.firebaseio.com/v0' const state = {} function fetchTopStories() { const topStoriesUrl = `${hnBaseUrl}/topstories.json` return fetch(topStoriesUrl).then(response => response.json()) .then((data) => fetchStories(data)) } function fetchStories(data) { const topStories = data.slice(0, 29) const storyIds = topStories.map((storyId) => { const storyUrl = `${hnBaseUrl}/item/${storyId}.json` return fetch(storyUrl).then((response) => response.json()) .then((story) => story) }) return Promise.all(storyIds).then((stories) => { state.stories = stories renderStories(stories) }) } function renderStories(stories) { return stories.map((story) => { const userUrl = `https://news.ycombinator.com/user?id=${story.by}` const storyItemUrl = `https://news.ycombinator.com/item?id=${story.id}` const html = ` ` document.getElementById('hn').insertAdjacentHTML('beforeend', html) }) } function toggleStoryText(storyId) { const storyText = document.getElementById(`storyText-${storyId}`) storyText.style.display = (storyText.style.display === 'block') ? 'none' : 'block' } function fetchComments(kids, storyId) { const commentIds = kids.split(',') const allComments = commentIds.map((commentId) => { const commentUrl = `${hnBaseUrl}/item/${commentId}.json` return fetch(commentUrl).then((response) => response.json()).then((comment) => comment) }) return Promise.all(allComments).then((comments) => { state[storyId] = comments renderComments(comments, storyId) }) } function fetchOrToggleComments(kids, storyId) { function toggleAllComments(storyId) { const allComments = document.getElementById(`comments-${storyId}`) allComments.style.display = (allComments.style.display === 'block') ? 'none' : 'block' } state[storyId] ? toggleAllComments(storyId) : fetchComments(kids, storyId) } function toggleComment(commentId) { const comment = document.getElementById(commentId) const toggle = document.getElementById(`toggle-${commentId}`) comment.style.display = (comment.style.display === 'block') ? 'none' : 'block' toggle.innerHTML = (toggle.innerHTML === '[ - ]') ? '[ + ]' : '[ - ]' } function renderComments(comments, storyId) { return comments.map((comment) => { const userUrl = `https://news.ycombinator.com/user?id=${comment.by}` const html = comment.deleted || comment.dead ? '' : `${story.url ? `${story.title}` : `${story.title}`}
${story.score} points by ${story.by}${story.kids ? ` [toggle ${story.descendants} comments] ` : '' } [view on HN]${story.text ? ` ` : '' }[ - ] ${comment.by}` comment.parent == storyId ? document.getElementById(`comments-${storyId}`).insertAdjacentHTML('beforeend', html) : document.getElementById(comment.parent).insertAdjacentHTML('beforeend', html) if (comment.kids) return fetchComments(comment.kids.toString(), storyId) }) } fetchTopStories()${comment.text}
Вот оно у вас есть. Может быть, кто-то еще также найдет причину использовать A Hacker News Reader For No Reason.