Создайте живую ленту комментариев с помощью 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>&nbsp;&nbsp;{{comment.name}} &nbsp;<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. Исходный код приложения доступен на Гитхаб.

Этот пост впервые появился на Блог пушера.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *