Создайте живую ленту комментариев с помощью Go и Vue.js
Вам понадобится Go и SQLite, установленные на вашем компьютере. Базовые знания Go и JavaScript будут полезны.
Интернет является питательной средой для всех видов социальной деятельности, потому что он расширяет возможности общения. Чтобы веб-приложения оставались социальными и приятными, важно, чтобы они имели один или несколько интерфейсов для взаимодействия пользователей. Одним из таких интерфейсов является раздел комментариев.
В разделе комментариев пользователи могут обсуждать тему (публикацию, видео, изображение), к которой у них есть доступ. В прошлом, чтобы пользователь мог увидеть комментарий от другого пользователя, ему приходилось обновлять окно браузера. Тем не менее, с комментариями в реальном времени теперь мы можем автоматически получать комментарии в реальном времени. В этой статье мы расскажем, как мы можем создавать комментарии в реальном времени с помощью Pusher.
К концу этой статьи мы создадим приложение, которое выглядит так:
Требования
Чтобы следовать этой статье, вам потребуется следующее:
- Go (версия >= 0.10.x), установленная на вашем компьютере. Вот как вы можете установить Go.
- SQLite (v3.x) установлен на вашем компьютере. Инструкция по установке.
- Базовые знания языка программирования Go.
- Базовые знания JavaScript (ES6).
- Базовые знания Vue.js.
Получение приложения Pusher Channels
Первым шагом будет получение приложения Pusher Channels. Нам потребуются учетные данные приложения, чтобы наши функции реального времени работали.
Перейдите на сайт Pusher и создайте учетную запись. После создания учетной записи следует создать новое приложение. Следуйте указаниям мастера создания приложения, после чего вам должны быть предоставлены учетные данные приложения, которые мы будем использовать позже в этой статье.
Теперь, когда у нас есть наше приложение, давайте перейдем к следующему шагу.
Настройка кодовой базы
Начнем с навигации в src
каталог, расположенный в $GOPATH
. Затем мы создадим там новый каталог для нашего приложения.
$ cd $GOPATH/src
$ mkdir go-realtime-comments
$ cd go-realtime-comments
Создать comments.go
файл в этом каталоге.
Прежде чем мы напишем код, нам нужно импортировать несколько пакетов Go, которые помогут запускать наши проекты. Мы установим Эхо-фреймворк и Пакеты SQLite. Выполните следующие команды, чтобы загрузить пакеты:
$ go get github.com/labstack/echo
$ go get github.com/labstack/echo/middleware
$ go get github.com/mattn/go-sqlite3
⚠️ Если вы используете Windows и сталкиваетесь с ошибкой «cc.exe: извините, не реализовано: 64-битный режим не скомпилирован в», то вам нужен порт Windows gcc, например https://sourceforge.net/projects/mingw-w64/. Также см. это Проблема с GitHub.
В вашем любимом редакторе откройте comments.go
файл и вставьте в него следующие строки кода:
<span class="hljs-keyword">package</span> main
<span class="hljs-keyword">import</span> (
<span class="hljs-comment">// "database/sql"</span>
<span class="hljs-string">"github.com/labstack/echo"</span>
<span class="hljs-string">"github.com/labstack/echo/middleware"</span>
<span class="hljs-comment">// _ "github.com/mattn/go-sqlite3"</span>
)
Настройка базы данных и маршрутов
Каждое приложение Go должно иметь main
функция. Отсюда начнется выполнение приложения, поэтому давайте создадим наш main
функция:
в comments.go
файл, добавьте следующее ниже импорта:
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
<span class="hljs-comment">// Echo instance</span>
e := echo.New()
<span class="hljs-comment">// Middleware</span>
e.Use(middleware.Logger())
e.Use(middleware.Recover())
<span class="hljs-comment">// Define the HTTP routes</span>
e.GET(<span class="hljs-string">"/comments"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span> <span class="hljs-title">error</span></span> {
<span class="hljs-keyword">return</span> c.JSON(<span class="hljs-number">200</span>, <span class="hljs-string">"GET Comments"</span>)
})
e.POST(<span class="hljs-string">"/comment"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span> <span class="hljs-title">error</span></span> {
<span class="hljs-keyword">return</span> c.JSON(<span class="hljs-number">200</span>, <span class="hljs-string">"POST a new Comment"</span>)
})
<span class="hljs-comment">// Start server</span>
e.Logger.Fatal(e.Start(<span class="hljs-string">":9000"</span>))
}
В основной функции мы определили некоторые основные функции обработчика маршрутов, эти функции в основном возвращают жестко закодированный текст в браузер по запросу. Последняя строка запускает стандартный HTTP-сервер Go, используя метод запуска Echo, и прослушивает порт запросов 9000.
Мы можем проверить, работает ли приложение на этом этапе, запустив его и выполнив несколько запросов, используя Почтальон.
Вот как вы можете запустить приложение:
$ go run ./comments.go
Мы можем отправлять HTTP-запросы с помощью Postman. Вот пример запроса GET с использованием Postman:
POST-запрос с почтальоном:
Мы создадим функцию, которая будет инициализировать базу данных, и для этого нам понадобятся драйверы SQL и SQLite3. Мы уже добавили их в import
оператор, поэтому раскомментируйте их. Мы также создадим функцию, которая будет переносить базу данных, используя схему базы данных, определенную внутри функции.
Открой comments.go
файл и вставьте следующий код перед main
функция:
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initDB</span><span class="hljs-params">(filepath <span class="hljs-keyword">string</span>)</span> *<span class="hljs-title">sql</span>.<span class="hljs-title">DB</span></span> {
db, err := sql.Open(<span class="hljs-string">"sqlite3"</span>, filepath)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(err)
}
<span class="hljs-keyword">if</span> db == <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(<span class="hljs-string">"db nil"</span>)
}
<span class="hljs-keyword">return</span> db
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">migrate</span><span class="hljs-params">(db *sql.DB)</span></span> {
sql := <span class="hljs-string">`
CREATE TABLE IF NOT EXISTS comments(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name VARCHAR NOT NULL,
email VARCHAR NOT NULL,
comment VARCHAR NOT NULL
);
`</span>
_, err := db.Exec(sql)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(err)
}
}
Затем добавьте следующий код в верхнюю часть main
функция:
<span class="hljs-comment">// [...]</span>
<span class="hljs-comment">// Initialize the database</span>
db := initDB(<span class="hljs-string">"storage.db"</span>)
migrate(db)
<span class="hljs-comment">// [...]</span>
Теперь мы можем проверить, вызываются ли эти функции и создается ли база данных во время выполнения, запустив приложение:
go run comments.go
⚠️ Если вы уже запускали приложение Go, вам нужно будет завершить процесс, нажав Ctrl+C на клавиатуре, а затем перезапустить его, чтобы увидеть изменения.
Когда приложение запускается в первый раз, storage.db
файл будет создан в рабочем каталоге, если он ранее не существовал.
Настройка обработчиков
Мы проверили, что наше приложение прослушивает указанный порт 9000 и обрабатывает HTTP-запросы так, как мы его настроили. Однако текущие функции-обработчики просто возвращают браузеру жестко закодированный текст, поэтому давайте создадим новые функции-обработчики для обработки ответов на маршруты.
Создайте новую папку в корневом каталоге с именем handlers
:
$ mkdir handlers
$ cd handlers
Затем создайте handlers.go
файл и вставьте следующее:
<span class="hljs-keyword">package</span> handlers
<span class="hljs-keyword">import</span> (
<span class="hljs-string">"database/sql"</span>
<span class="hljs-string">"go-realtime-comments/models"</span>
<span class="hljs-string">"net/http"</span>
<span class="hljs-string">"github.com/labstack/echo"</span>
)
Теперь нам нужно вернуться к comments.go
файл и импортируйте пакет обработчиков:
import (
"go-realtime-comments/handlers"
// [...]
)
В том же файле замените определения маршрутов из более ранних на приведенные ниже:
<span class="hljs-comment">// [...]</span>
<span class="hljs-comment">// Define the HTTP routes</span>
e.File(<span class="hljs-string">"/"</span>, <span class="hljs-string">"public/index.html"</span>)
e.GET(<span class="hljs-string">"/comments"</span>, handlers.GetComments(db))
e.POST(<span class="hljs-string">"/comment"</span>, handlers.PushComment(db))
<span class="hljs-comment">// [...]</span>
Затем вставьте следующий код в handlers.go
файл под оператором импорта:
<span class="hljs-keyword">type</span> H <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">interface</span>{}
<span class="hljs-comment">//GetComments handles the HTTP request that hits the /comments endpoint</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">GetComments</span><span class="hljs-params">(db *sql.DB)</span> <span class="hljs-title">echo</span>.<span class="hljs-title">HandlerFunc</span></span> {
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span> <span class="hljs-title">error</span></span> {
<span class="hljs-keyword">return</span> c.JSON(http.StatusOK, models.GetComments(db))
}
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">PushComment</span><span class="hljs-params">(db *sql.DB)</span> <span class="hljs-title">echo</span>.<span class="hljs-title">HandlerFunc</span></span> {
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c echo.Context)</span> <span class="hljs-title">error</span></span> {
<span class="hljs-keyword">var</span> comment models.Comment
c.Bind(&comment)
id, err := models.PushComment(db, comment.Name, comment.Email, comment.Comment)
<span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
<span class="hljs-keyword">return</span> c.JSON(http.StatusCreated, H{
<span class="hljs-string">"created"</span>: id,
})
}
<span class="hljs-keyword">return</span> err
}
}
GetComments
функция извлекает и возвращает комментарии из базы данных, в то время как PushComment
сохраняет комментарии в базу данных и возвращает ответ.
Настройка моделей
Чтобы создать пакет модели, нам нужно создать новую папку в корневом каталоге нашего приложения:
$ mkdir models
$ cd models
Затем создайте models.go
файл и вставьте следующий код:
<span class="hljs-keyword">package</span> models
<span class="hljs-keyword">import</span> (
<span class="hljs-string">"database/sql"</span>
_ <span class="hljs-string">"github.com/mattn/go-sqlite3"</span>
)
Давайте создадим комментарий type
представляющий собой структуру с четырьмя полями:
ID
— идентификатор комментария.Name
— имя пользователя, оставившего комментарий.Email
— электронная почта пользователя, оставившего комментарий.Comment
— комментарий.
В Go мы можем добавлять метаданные к переменным, помещая их в обратные кавычки. Мы можем использовать это, чтобы определить, как должно выглядеть каждое поле при преобразовании в JSON
. Это также поможет c.Bind
функция знает, как отображать JSON
данные при регистрации нового комментария.
Давайте определим структуры для Comment
а также CommentCollection
. в models.go
вставьте файл в следующий ниже импорт:
<span class="hljs-keyword">type</span> Comment <span class="hljs-keyword">struct</span> {
ID <span class="hljs-keyword">int</span> <span class="hljs-string">`json:"id"`</span>
Name <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
Email <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"email"`</span>
Comment <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"comment"`</span>
}
<span class="hljs-keyword">type</span> CommentCollection <span class="hljs-keyword">struct</span> {
Comments []Comment <span class="hljs-string">`json:"items"`</span>
}
Затем вставьте следующий код после структур:
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">GetComments</span><span class="hljs-params">(db *sql.DB)</span> <span class="hljs-title">CommentCollection</span></span> {
sql := <span class="hljs-string">"SELECT * FROM comments"</span>
rows, err := db.Query(sql)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(err)
}
<span class="hljs-keyword">defer</span> rows.Close()
result := CommentCollection{}
<span class="hljs-keyword">for</span> rows.Next() {
comment := Comment{}
err2 := rows.Scan(&comment.ID, &comment.Name, &comment.Email, &comment.Comment)
<span class="hljs-keyword">if</span> err2 != <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(err2)
}
result.Comments = <span class="hljs-built_in">append</span>(result.Comments, comment)
}
<span class="hljs-keyword">return</span> result
}
GetComments
функция отвечает за извлечение всех доступных комментариев из базы данных и их возврат в виде экземпляра CommentCollection
что мы определили.
Затем вставьте следующий код ниже кода выше:
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">PushComment</span><span class="hljs-params">(db *sql.DB, name <span class="hljs-keyword">string</span>, email <span class="hljs-keyword">string</span>, comment <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int64</span>, error)</span></span> {
sql := <span class="hljs-string">"INSERT INTO comments(name, email, comment) VALUES(?, ?, ?)"</span>
stmt, err := db.Prepare(sql)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(err)
}
<span class="hljs-keyword">defer</span> stmt.Close()
result, err2 := stmt.Exec(name, email, comment)
<span class="hljs-keyword">if</span> err2 != <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(err2)
}
<span class="hljs-keyword">return</span> result.LastInsertId()
}
PushComments
Функция добавляет новый комментарий в базу данных.
Создание интерфейса
Далее создайте public
папку в корневом каталоге нашего приложения и создайте index.html
файл внутри него.
Открой index.html
файл и вставьте в этот код:
<span class="hljs-meta"></span>
<span class="hljs-tag"><<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">head</span>></span>
<span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"ie=edge"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">" <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">title</span>></span>Realtime comments<span class="hljs-tag"></<span class="hljs-name">title</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">" class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">" class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
<span class="hljs-tag"><<span class="hljs-name">style</span>></span><span class="css">
@<span class="hljs-keyword">media</span> (min-width: <span class="hljs-number">48em</span>) {
<span class="hljs-selector-tag">html</span> {
<span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
}
}
<span class="hljs-selector-tag">body</span> {
<span class="hljs-attribute">font-family</span>: Georgia, <span class="hljs-string">"Times New Roman"</span>, Times, serif;
<span class="hljs-attribute">color</span>: <span class="hljs-number">#555</span>;
}
<span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-class">.h1</span>, <span class="hljs-selector-tag">h2</span>, <span class="hljs-selector-class">.h2</span>, <span class="hljs-selector-tag">h3</span>, <span class="hljs-selector-class">.h3</span>, <span class="hljs-selector-tag">h4</span>, <span class="hljs-selector-class">.h4</span>, <span class="hljs-selector-tag">h5</span>, <span class="hljs-selector-class">.h5</span>, <span class="hljs-selector-tag">h6</span>, <span class="hljs-selector-class">.h6</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Helvetica Neue"</span>, Helvetica, Arial, sans-serif;
<span class="hljs-attribute">font-weight</span>: <span class="hljs-number">400</span>;
<span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
}
<span class="hljs-selector-class">.blog-masthead</span> {
<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3rem</span>;
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#428bca</span>;
<span class="hljs-attribute">box-shadow</span>: inset <span class="hljs-number">0</span> -.<span class="hljs-number">1rem</span> .<span class="hljs-number">25rem</span> <span class="hljs-built_in">rgba</span>(0,0,0,.1);
}
<span class="hljs-selector-class">.nav-link</span> {
<span class="hljs-attribute">position</span>: relative;
<span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
<span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;
<span class="hljs-attribute">color</span>: <span class="hljs-number">#cdddeb</span>;
}
<span class="hljs-selector-class">.nav-link</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.nav-link</span><span class="hljs-selector-pseudo">:focus</span> {
<span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
<span class="hljs-attribute">background-color</span>: transparent;
}
<span class="hljs-selector-class">.nav-link</span><span class="hljs-selector-class">.active</span> {
<span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
}
<span class="hljs-selector-class">.nav-link</span><span class="hljs-selector-class">.active</span><span class="hljs-selector-pseudo">::after</span> {
<span class="hljs-attribute">position</span>: absolute;
<span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
<span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
<span class="hljs-attribute">width</span>: <span class="hljs-number">0</span>;
<span class="hljs-attribute">height</span>: <span class="hljs-number">0</span>;
<span class="hljs-attribute">margin-left</span>: -.<span class="hljs-number">3rem</span>;
<span class="hljs-attribute">vertical-align</span>: middle;
<span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
<span class="hljs-attribute">border-right</span>: .<span class="hljs-number">3rem</span> solid transparent;
<span class="hljs-attribute">border-bottom</span>: .<span class="hljs-number">3rem</span> solid;
<span class="hljs-attribute">border-left</span>: .<span class="hljs-number">3rem</span> solid transparent;
}
@<span class="hljs-keyword">media</span> (min-width: <span class="hljs-number">40em</span>) {
<span class="hljs-selector-class">.blog-title</span> {
<span class="hljs-attribute">font-size</span>: <span class="hljs-number">3.5rem</span>;
}
}
<span class="hljs-selector-class">.sidebar-module</span> {
<span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
}
<span class="hljs-selector-class">.sidebar-module-inset</span> {
<span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f5f5f5</span>;
<span class="hljs-attribute">border-radius</span>: .<span class="hljs-number">25rem</span>;
}
<span class="hljs-selector-class">.sidebar-module-inset</span> <span class="hljs-selector-tag">p</span><span class="hljs-selector-pseudo">:last-child</span>,
<span class="hljs-selector-class">.sidebar-module-inset</span> <span class="hljs-selector-tag">ul</span><span class="hljs-selector-pseudo">:last-child</span>,
<span class="hljs-selector-class">.sidebar-module-inset</span> <span class="hljs-selector-tag">ol</span><span class="hljs-selector-pseudo">:last-child</span> {
<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>;
}
<span class="hljs-selector-class">.blog-post</span> {
<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">4rem</span>;
}
<span class="hljs-selector-class">.blog-post-title</span> {
<span class="hljs-attribute">margin-bottom</span>: .<span class="hljs-number">25rem</span>;
<span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.5rem</span>;
<span class="hljs-attribute">text-align</span>: center;
}
<span class="hljs-selector-class">.blog-post-meta</span> {
<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1.25rem</span>;
<span class="hljs-attribute">color</span>: <span class="hljs-number">#999</span>;
<span class="hljs-attribute">text-align</span>: center;
}
<span class="hljs-selector-class">.blog-footer</span> {
<span class="hljs-attribute">padding</span>: <span class="hljs-number">2.5rem</span> <span class="hljs-number">0</span>;
<span class="hljs-attribute">color</span>: <span class="hljs-number">#999</span>;
<span class="hljs-attribute">text-align</span>: center;
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f9f9f9</span>;
<span class="hljs-attribute">border-top</span>: .<span class="hljs-number">05rem</span> solid <span class="hljs-number">#e5e5e5</span>;
}
<span class="hljs-selector-class">.blog-footer</span> <span class="hljs-selector-tag">p</span><span class="hljs-selector-pseudo">:last-child</span> {
<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>;
}
<span class="hljs-selector-tag">input</span>{
<span class="hljs-attribute">width</span>: <span class="hljs-number">45%</span> <span class="hljs-meta">!important</span>;
<span class="hljs-attribute">display</span>: inline-block <span class="hljs-meta">!important</span>;
}
<span class="hljs-selector-tag">textarea</span> {
<span class="hljs-attribute">width</span>: <span class="hljs-number">90%</span>;
<span class="hljs-attribute">height</span>: <span class="hljs-number">150px</span>;
<span class="hljs-attribute">padding</span>: <span class="hljs-number">12px</span> <span class="hljs-number">20px</span>;
<span class="hljs-attribute">box-sizing</span>: border-box;
<span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#ccc</span>;
<span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f8f8f8</span>;
<span class="hljs-attribute">resize</span>: none;
}
<span class="hljs-selector-tag">textarea</span><span class="hljs-selector-pseudo">:focus</span>, <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span>{
<span class="hljs-attribute">outline</span>: none <span class="hljs-meta">!important</span>;
}
<span class="hljs-selector-id">#comment-section</span>{
<span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgb</span>(178, 191, 214);
<span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5em</span> <span class="hljs-number">2em</span>; <span class="hljs-attribute">width</span>: <span class="hljs-number">90%</span>;
<span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span> <span class="hljs-number">0</span>;
<span class="hljs-attribute">border-radius</span>: <span class="hljs-number">15px</span>;
}
<span class="hljs-selector-id">#comment-section</span> > <span class="hljs-selector-tag">div</span> > <span class="hljs-selector-tag">p</span> {
<span class="hljs-attribute">color</span>: black;
<span class="hljs-attribute">display</span>:inline;
}
<span class="hljs-selector-tag">img</span>{
<span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
<span class="hljs-attribute">float</span>: left;
}
</span><span class="hljs-tag"></<span class="hljs-name">style</span>></span>
<span class="hljs-tag"></<span class="hljs-name">head</span>></span>
<span class="hljs-tag"><<span class="hljs-name">body</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">header</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-masthead"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link active"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Home<span class="hljs-tag"></<span class="hljs-name">a</span>></span>
<span class="hljs-tag"></<span class="hljs-name">nav</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">header</span>></span>
<span class="hljs-tag"><<span class="hljs-name">main</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"main"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-sm-12 blog-main"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-post"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-post-title"</span>></span>Realtime Comments With Pusher<span class="hljs-tag"></<span class="hljs-name">h2</span>></span>
<span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-post-meta"</span>></span>January 1, 2018 by <span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Jordan<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"><<span class="hljs-name">p</span>></span>This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported.This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported
<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"comment-section"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">form</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-signin"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">h5</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"comment"</span>></span>Comment<span class="hljs-tag"></<span class="hljs-name">h5</span>></span>
<span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"username"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"username"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"John Doe"</span> <span class="hljs-attr">required</span> <span class="hljs-attr">autofocus</span>></span>
<span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Johndoe@gmail.com"</span> <span class="hljs-attr">required</span>></span>
<span class="hljs-tag"><<span class="hljs-name">textarea</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"comment"</span>></span><span class="hljs-tag"></<span class="hljs-name">textarea</span>></span>
<span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-lg btn-primary"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"sendComment"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>></span>Comment<span class="hljs-tag"></<span class="hljs-name">button</span>></span>
<span class="hljs-tag"></<span class="hljs-name">form</span>></span>
<span class="hljs-tag"><<span class="hljs-name">br</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"comment-section"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"comment in comments"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">" <span class="hljs-attr">width</span>=<span class="hljs-string">"65px"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"65px"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">p</span>></span> {{comment.name}} <span class="hljs-tag">< {{<span class="hljs-attr">comment.email</span>}} ></span><span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"><<span class="hljs-name">hr</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color:black"</span>></span>{{comment.comment}}<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">main</span>></span>
<span class="hljs-tag"><<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"blog-footer"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">p</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>></span>Back to top<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"></<span class="hljs-name">footer</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">body</span>></span>
<span class="hljs-tag"></<span class="hljs-name">html</span>></span>
Теперь в том же файле вставьте следующий код перед закрытием body
тег HTML:
<script>
<span class="hljs-keyword">var</span> app = <span class="hljs-keyword">new</span> Vue({
<span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
<span class="hljs-attr">data</span>: {
<span class="hljs-attr">comments</span> : []
},
<span class="hljs-attr">created</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
axios.get(<span class="hljs-string">'/comments'</span>).then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> {
<span class="hljs-keyword">this</span>.comments = response.data.items ? response.data.items : []
})
},
<span class="hljs-attr">methods</span>: {
<span class="hljs-attr">sendComment</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">index</span>) </span>{
<span class="hljs-keyword">let</span> comment = {
<span class="hljs-attr">name</span>: <span class="hljs-keyword">this</span>.$refs.username.value,
<span class="hljs-attr">email</span>: <span class="hljs-keyword">this</span>.$refs.email.value,
<span class="hljs-attr">comment</span>: <span class="hljs-keyword">this</span>.$refs.comment.value
}
axios.post(<span class="hljs-string">'/comment'</span>, comment).then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> {
<span class="hljs-keyword">this</span>.$refs.username.value = <span class="hljs-string">''</span>,
<span class="hljs-keyword">this</span>.$refs.email.value = <span class="hljs-string">''</span>,
<span class="hljs-keyword">this</span>.$refs.comment.value = <span class="hljs-string">''</span>
})
}
}
})
<<span class="hljs-regexp">/script></span>
Выше у нас есть код Vue.js для нашего приложения, и это краткое изложение того, что оно делает:
- Мы создаем массив комментариев, который будет содержать все доступные комментарии.
- в
created()
метод, мы используем Аксиос извлекать все комментарии, доступные из API, и сохранять их вcomments
множество. - в
sendComment
метод, мы отправляем запрос в API для создания новогоcomment
.
На этом этапе мы можем создать наше приложение и посетить мы должны увидеть это:
$ go run comments.go
Наше приложение должно выглядеть так:
Следующее, что нам нужно сделать, это убедиться, что комментарии отображаются в реальном времени. Для этого нам нужно запускать событие каждый раз, когда добавляется новый комментарий. Мы сделаем это в бэкенде, используя Библиотека Pusher Go.
Чтобы загрузить библиотеку Pusher Go, выполните следующую команду:
$ go get github.com/pusher/pusher-http-go
Далее импортируем библиотеку. В нашем models.go
файл выполните следующие действия в операторе импорта:
<span class="hljs-keyword">package</span> models
<span class="hljs-keyword">import</span> (
<span class="hljs-comment">// [...]</span>
pusher <span class="hljs-string">"github.com/pusher/pusher-http-go"</span>
)
В этом же файле перед type
определение, вставьте следующий код:
<span class="hljs-comment">// [...]</span>
<span class="hljs-keyword">var</span> client = pusher.Client{
AppId: <span class="hljs-string">"PUSHER_APP_ID"</span>,
Key: <span class="hljs-string">"PUSHER_APP_KEY"</span>,
Secret: <span class="hljs-string">"PUSHER_APP_SECRET"</span>,
Cluster: <span class="hljs-string">"PUSHER_APP_CLUSTER"</span>,
Secure: <span class="hljs-literal">true</span>,
}
<span class="hljs-comment">// [...]</span>
Здесь мы инициализировали клиент Pusher, используя учетные данные из нашего ранее созданного приложения.
⚠️ Заменить
PUSHER_APP_*
ключи с вашими учетными данными приложения Pusher.
Далее давайте запускать событие каждый раз, когда комментарий сохраняется в базе данных. Заменить PushComment
функцию со следующим кодом:
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">PushComment</span><span class="hljs-params">(db *sql.DB, name <span class="hljs-keyword">string</span>, email <span class="hljs-keyword">string</span>, comment <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int64</span>, error)</span></span> {
sql := <span class="hljs-string">"INSERT INTO comments(name, email, comment) VALUES(?, ?, ?)"</span>
stmt, err := db.Prepare(sql)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(err)
}
<span class="hljs-keyword">defer</span> stmt.Close()
result, err2 := stmt.Exec(name, email, comment)
<span class="hljs-keyword">if</span> err2 != <span class="hljs-literal">nil</span> {
<span class="hljs-built_in">panic</span>(err2)
}
newComment := Comment{
Name: name,
Email: email,
Comment: comment,
}
client.Trigger(<span class="hljs-string">"comment-channel"</span>, <span class="hljs-string">"new-comment"</span>, newComment)
<span class="hljs-keyword">return</span> result.LastInsertId()
}
В этой новой версии функции мы создаем newComment
объект, содержащий информацию о последнем комментарии, сохраненном в базе данных. Всякий раз, когда создается новый комментарий, мы отправляем его на канал Pusher. comment-channel
срабатывать по событию new-comment
.
Отображение данных в реальном времени на клиентеДля получения комментариев необходимо зарегистрироваться толкатель JavaScript-клиент в нашем внешнем коде. Добавьте эту строку кода в тег заголовка нашего HTML в файле index.html:
<script src="
Далее мы зарегистрируем экземпляр Pusher в created()
крючок жизненного цикла:
created: function() {
<span class="hljs-keyword">const</span> pusher = <span class="hljs-built_in">new</span> Pusher(<span class="hljs-string">'PUSHER_APP_KEY'</span>, {
cluster: <span class="hljs-string">'PUSHER_APP_CLUSTER'</span>,
encrypted: <span class="hljs-literal">true</span>
});
<span class="hljs-keyword">const</span> channel = pusher.subscribe(<span class="hljs-string">'comment-channel'</span>);
channel.bind(<span class="hljs-string">'new-comment'</span>, data => {
this.comments.push(data)
});
<span class="hljs-comment">// [...] </span>
}
⚠️ Замените
PUSHER_APP_*
keys с учетными данными для вашего приложения Pusher.
В приведенном выше коде мы создаем экземпляр Pusher, а затем подписываемся на канал. На этом канале мы слушаем new-comment
мероприятие.
Теперь мы можем запустить наше приложение:
$ go run comments.go
Мы можем указать веб-браузер на этот адрес и мы должны увидеть приложение в действии:
Вывод
В этой статье мы рассмотрели, как создать систему комментариев в реальном времени с использованием Go, Vue.js и Pusher Channels. Исходный код приложения доступен на Гитхаб.
Этот пост впервые появился на Блог пушера.