如何從零構建個人部落格系統

Rina發表於2017-12-25

簡介

這篇文章主要分享部落格裡涉及的 Ruby, Rails,前端 CSS,JS,ubuntu 系統命令等知識。如果有什麼不解的地方可以通過<http://liuzhen.me&gt;頁面下方的二維碼掃描加我微信。

  • Ruby 是一種純粹的物件導向程式語言。它由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)建立於 1993 年。

  • Ruby on Rails(官方簡稱為 Rails,亦被簡稱為 RoR),是一個使用 Ruby 語言寫的開源 Web 應用框架,它是嚴格按照 MVC 結構開發的。它努力使自身保持簡單,來使實際的應用開發時的程式碼更少,使用最少的配置。指南: <https://ruby-china.github.io/rails-guides/>

  • CSS 指層疊樣式表, 你在頁面看到的展示效果都是通過 CSS 做出來的,頁面的佈局,字型大小,顏色,邊框,選單等等. 詳情可以檢視: <http://www.runoob.com/css/css-intro.html>

  • JS 是屬於網路的指令碼語言, 能做的事太多了,像我部落格裡的相簿功能,時間線都是 JS 做出來的效果。

安裝 Rails 環境

你可以通過搜尋 Mac/windows/ubuntu install rails 來找到相關文件,這裡提供 ubuntu 16.04 版本的安裝文件: <https://gorails.com/setup/ubuntu/16.04> , Mac 的安裝文件: <https://ruby-china.org/wiki/mac-nginx-passenger-rails>

建立一個 Rails 專案

安裝好 Rails 環境之後,你可以建立一個 Rails 專案了,如果你從來沒用過 Rails,可以先用 15 分鐘學習一下 Rails 入門, 瞭解 Rails MVC 結構。

如果你對 Rails 有一定的瞭解,可以按照這個模版 <https://github.com/80percent/rails-template> 提供的操作步驟, 建立一個 Rails 專案,使用這個模版建立 Rails 專案的好處是,這個模版相當於一個全家桶,預先新增一個專案經常需要使用的 Gem 包,釋出需要的 puma, mina, monit, nginx 配置檔案,關於這幾個東西是什麼,有什麼用後面會講到。

啟動 Rails

$ rails s

訪問 localhost:3000 就能看到 hello world 頁面了。

建立資料模型

我的部落格在設計之初只想要文章,相簿,簡歷這幾個功能,這三個功能比較相似,都有標題,內容和可有可無的描述。所以我就用了單表繼承,建了個 base 表。

$ rails g model Base title:string content:text subtitle:string type:string

type 欄位就是用於單表繼承。

執行完這條命令之後,你會看到 db/migrate/xxxx_create_bases.rb 多了一個這樣的檔案,裡面的內容是:

class CreateBases &lt; ActiveRecord::Migration[5.1]
  def change
    create_table :bases do |t|
      t.string :title
      t.string :subtitle
      t.text :content
      t.string :type
      t.timestamps
    end
  end
end

t.timestamps 是時間戳,系統會自動在這個表裡面加上 created_at, updated_at 兩個欄位。

新增完之後,需要把做一下資料遷移,我一開始學 rails 的時候對 資料遷移 這個詞很不理解。其實資料遷移的意思就是,我們現在通過命令建立了個資料表的檔案,但是這個檔案沒有被執行,不執行資料庫裡就還沒有這張表,只有在執行了 rails db:migrate 之後,rails 才在資料庫裡把這張表給加上,這個操作就叫做 資料遷移。

建立完了 Base 表,現在就要建立文章表了 Article, 我們需要新增一個 app/models/article.rb 檔案,寫上:

class Article &lt; Base
end

因為 Article 繼承了 Base, 所以就擁有了 Base 的所有欄位了。

你可以通過 rails c 從控制檯輸入 Article.new 可以看到:

irb(main):006:0* Article.new
=&gt; #&lt;Article id: nil, title: nil, subtitle: nil, content: nil, type: &quot;Article&quot;, created_at: nil, updated_at: nil&gt;

type 欄位自動就是 Article, 這是 Rails 的一個特性,單表繼承。

輸入Article.all, 看到的 sql 語句實際是從 bases 裡查詢 type 為 Article 的所以記錄。

irb(main):007:0&gt; Article.all
Article Load (127.0ms)  SELECT  &quot;bases&quot;.* FROM &quot;bases&quot; WHERE &quot;bases&quot;.&quot;type&quot; IN ('Article') LIMIT $1  [[&quot;LIMIT&quot;, 11]]

相簿表Photo,簡歷表ResumeArticle的建立方式相同.

表建立好了,我們就可以建立Controller了,Controller需要區分前端和後端,前端就是提供給使用者查詢的,後臺是提供自己新增,更新,刪除操作的。另外後臺因為是管理的地方所以不能讓所有人都訪問,所以需要設定成通過使用者名稱和密碼登入。這樣別人就無法訪問你的後臺。

後端設計

為了與前臺有所區分,所以需要加一下名稱空間: 這裡設定成 admin. 先在 config/routes.rb 裡新增路由,

Rails.application.routes.draw do
  namespace :admin do
    root 'dashboard#index', as: 'root'
    resources :articles
    resources :photos
    resource :resume, only: [:edit, :update]
  end
end

root 'dashboard#index', as: 'root' 設定後臺的root路由。使用 rails routes 命令可以檢視具體的路由資訊。

控制器

controllers 目錄下新增admin目錄,這個目錄下用於存放所有的後臺檔案,後臺新增一個 app/controllers/admin/base_controller.rb 檔案,繼承了 ApplicationController, 這麼做是為了admin下的所有controller繼承 Admin::BaseController 後,使用者訪問後端連結就會先校驗當前使用者是否登入,如果沒有登入就跳轉到登入頁面。 程式碼如下:

class Admin::BaseController &lt; ApplicationController
  layout 'admin'
  before_action :authenticate_user
  def authenticate_user
    unless session[:login]
      redirect_to new_session_path
    end
  end
end

因為article, photo, resume這幾個功能比較相似,所以我只講一下article控制器:app/controllers/admin/articles_controller.rb

程式碼:

class Admin::ArticlesController &lt; Admin::BaseController
  def index
    @articles = Article.all.order(created_at: 'DESC').page(params[:page])
  end
  def new
    @article = Article.new
  end
  def create
    @article = Article.new(article_params)
    if @article.save
      redirect_to admin_articles_path
    else
      render 'new'
    end
  end
  def edit
    @article = Article.find(params[:id])
  end
  def update
    @article = Article.find(params[:id])
    if @article.update(article_params)
      flash[:notice] = '更新成功'
      redirect_to admin_articles_path
    else
      render 'edit'
    end
  end
  def destroy
    @article = Article.find(params[:id])
    if @article.destroy
      flash[:notice] = '刪除成功'
    else
      flash[:notice] = &quot;刪除失敗, 原因: #{@article.errors.messages.to_s}&quot;
    end
  end
  private
  def article_params
    params.require(:article).permit(:title, :subtitle, :content)
  end
end

控制檯裡面很簡單,就是增,刪,改,查。需要注意的就是redirect_to, render的區別,什麼時候要用render, 什麼時候用redirect_to.

render 是指直接薰染某個頁面.

redirect_to 是指告訴瀏覽器,讓瀏覽器再重新傳送一個指定路由的請求操作。

如:create action 裡寫到如果儲存成功就 redirect_to admin_articles_path, 如果失敗就 render 'new'.

  1. 假如儲存成功,就會告訴瀏覽器, 讓瀏覽器再向伺服器傳送一個admin/articles路由請求,然後進入index action裡,查詢所有Action記錄,再薰染index.html頁面,返回給瀏覽器。

  2. 假如儲存失敗,就用使用者填寫的@article資訊薰染new.html頁面,並用 flash 裡的資訊,告訴使用者提交失敗的原因,如果失敗後用 redirect_to new_admin_articles_path,也能跳轉到new頁面,但是使用者提交的資訊就沒有了。

view

.row
  .offset-md-2.col-md-8
    = simple_form_for [:admin, @article] do |f|
      = f.error_notification
      = f.input :title
      = f.input :subtitle
      = f.text_area :content, id: 'editor_content', class: 'simditor', autofocus: true
      = f.submit '提交', class: 'btn btn-primary'
      = link_to '取消', admin_articles_path
javascript:
  new Simditor({
    textarea: $('#editor_content'),
    toolbar: ['title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', 'color', '|', 'ol', 'ul', '|', 'blockquote', 'code', 'table', 'link', 'image', 'hr', 'indent', 'outdent', 'alignment']
  });

這個有點需要講的是編輯器使用了simditor外掛,具體要加哪些資訊可以看一下這個文件: <http://simditor.tower.im/>, 但是要支援上傳圖片功能需要在 admin 下新增一條路由: post '/upload', to: 'photos#upload', 在photos controller裡新增一個upload action,把上傳的圖片儲存到資料庫。

前端設計

前端的controller繼承ApplicationController,前端的因為只設計到查詢,所以新增路由的時候加上only, 如: resources :articles, only: [:index, :show], 就只新增兩條路由,如果不加only預設會建立 7 條路由。

前端功能主要就涉及到 css.

css 除錯步驟:

  1. 右擊選擇'檢查',就能開啟控制檯,在控制檯處,通過點選(2) 處的圖示,可以選擇頁面上任意節點,選擇後 (3) 處會顯示這個節點所對應的 CSS 樣式。同樣在 style 處可以通過新增和註釋 css 來對頁面樣式進行除錯。

樣式這裡涉及的東西太多,我不一一講解,只講一些我認為值得講一講的知識點。如果想學習更多的 css 樣式知識,可以在文章開頭處提供的文件檢視學習。

  1. 文章的展示對字型,間距,背景,顏色等等都要求很高,如果設計的不好,文章看久了就容易累,而且容易給別人一種不想去看的感覺。如果間距很窄,一大段落全是文字,就給人一種很不舒服的感覺。如果你對這些資訊瞭解不多,不知道把這些值設定成多少比較好,也不要擔心,找一個你覺得文字展示效果看起來很舒服的網站,開啟他的控制檯,看一下這個網站上這些資訊設定的值是多少,跟著一樣設定就行了。具體的細節可以再另做調整。

  2. 部落格的頁面底部用的 fa 字型,在gemfile裡新增 font-awesome-sass 後,就能展示出這些字型圖示。但是目前的字型中沒有支付寶的字型圖示,你先不要看程式碼,想一想,如果是你,你要怎麼實現一個跟 fa 字型相同效果的圖示,這個圖示帶有 hover 效果,當滑鼠放上去的時候背景變成了藍色。

我的實現方法:

一開始我想的是用一個黑白圖片代替,弄完之後我發現 hover 效果無法實現。於是我就用一個背景透明只有一個支字的圖片代替,設定 border-radius,background-color 和字型達成一致效果,當滑鼠放上去的時候就改變 background-color: #0085A1;

程式碼:

footer .fa-alipay {
    border-radius: 50%;
    margin-bottom: 4px;
    background-color: #222529;
    width: 41px;
}
footer .fa-alipay:hover {
    background-color: #0085A1;
}

除錯頁面上關於hover,visited, focus, active效果,可以像圖片中勾選來檢視相應的樣式效果。

時間線

時間線是用的一個 js 庫,<https://github.com/RyanFitzgerald/vertical-timeline>, 具體可以檢視文件。值得說一下的是,一開始看到這個時間線的效果是在一個網站看到的。然後我通過頁面控制檯,看到裡面 class 名稱命名很規範,所以感覺是個 js 庫,直接在 google 搜尋 cd-timeline-block 第一個結果就是這個庫的資訊。除了這種方式,還可以通過控制檯的Sources檢視assets檔案資訊,一般都是經常壓縮的,但是有些外部庫是有註釋的,會寫上這個是來自哪個庫之類的資訊。不過最簡單快速的辦法還是用 google 搜尋來的快一點。如果你搜的 class 名字沒有找到相應的資訊,可以換個 class 名字試試。

另外一個要說的是,這個 JS 庫裡的一個 js 檔案main.js, 與 turbolink 一起載入,沒生效,載入的時候就沒被執行,然後我就把它用$(document).on 'turbolinks:load', 載入就好了。

相簿

部落格裡我最喜歡的就是這個相簿功能了,當初也是看了這個翻書的效果,我才有重寫部落格的衝動。看到這個 js 庫是在github Trending上, 這上面會推薦 github 上比較火的專案。這個庫的地址: <http://www.turnjs.com>, 這裡面提供了幾個 demo. 這個 turnjs 用的 yepnope 載入 js,這麼載入是因為,有些內容需要在其他檔案載入之後去執行。但是有個問題是在生產環境這些 js 檔案都是被轉譯了的。所以直接在yepnope裡面寫上檔名,在生產環境上就會找不到對應的檔案。對於這個我沒有想到特別好的處理辦法,就用

$('head').append('&lt;%= javascript_include_tag 'turn.min.js' %&gt;')

來載入檔案,然後再執行yepnope({complete: loadApp})。如果你有更好的辦法可以交流一下。

其他知識點

  • 效果支援手機端頁面需要加上: meta width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no

  • 前端和後端的 JS, CSS 儘量分開,這樣載入速度會快一些

  • 使用的外部庫最好重新命名成可讀名稱, 不然時間長了你就不知道這個庫是幹什麼的了. 比如我用了 timeline 的 JS 庫, 裡面有個 main.js 的檔案, 我就把它重新命名為 timeline-main.js, 這樣不需要再加註釋來說明這個檔案是做什麼的了.

釋出

釋出需要在伺服器上安裝好 rails 後,配置nginx.conf,一般放在/etc/nginx/conf.d/xxx.conf檔案.

puma.rbdeploy.rb 配置檔案是 mina 部署的相關配置資訊,具體操作了什麼可以通過bundle exec mina deploy -v來檢視:

$ bundle exec mina deploy -v
-----&gt; Creating a temporary build path
-----&gt; Server: liuzhen.me
-----&gt; Path: /home/ruby/RBlog
       $ echo &quot;-----&gt; Branch: master&quot;
-----&gt; Branch: master
-----&gt; Using RVM environment &quot;2.3.1&quot;
-----&gt; Quiet sidekiq (stop accepting new work)
-----&gt; Fetching new git commits
       $ (cd &quot;/home/ruby/RBlog/scm&quot; &amp;&amp; git fetch &quot;https://github.com/liuzhenangel/RBlog.git&quot; &quot;master:master&quot; --force)
-----&gt; Using git branch 'master'
       $ git clone &quot;/home/ruby/RBlog/scm&quot; . --recursive --branch &quot;master&quot;
       Cloning into '.'...
       done.
-----&gt; Using this git commit
       $ git rev-parse HEAD &gt; .mina_git_revision
       $ git --no-pager log --format=&quot;%aN (%h):%n&gt; %s&quot; -n 1
       liuzhenangel (eb06b54):
       &gt; update timeline
       $ rm -rf .git
-----&gt; Symlinking shared paths
-----&gt; Installing gem dependencies using Bundler
       $ bundle install --without development test --path &quot;vendor/bundle&quot; --deployment
-----&gt; DB migrations unchanged; skipping DB migration
-----&gt; Skipping asset precompilation
-----&gt; Cleaning up old releases (keeping 5)
-----&gt; Deploy finished
-----&gt; Building
-----&gt; Moving build to /home/ruby/RBlog/releases/41
-----&gt; Build finished
-----&gt; Launching
-----&gt; Updating the /home/ruby/RBlog/current symlink
-----&gt; Restart Puma -- hard...
-----&gt; Stopping Puma...
-----&gt; Starting Puma...

從這些日誌資訊可以看出, 首先會根據你配置的伺服器的域名和使用者名稱 ssh 到伺服器上,載入 ruby 環境,從 github 拉取最新程式碼,安裝 gem 包,如果 css, js, 圖片有更新就重新編譯壓縮 js, css, 圖片, 執行 db:migrate 資料遷移, 首次釋出還會執行rails db:create來建立資料庫。然後重啟 puma.

為什麼要 nginx,puma

nginx 相當於一個代理,當你在瀏覽器輸入:http://liuzhen.me 的時候,先通過 DNS 找到這個域名對應的 IP,然後通過路由到達 IP 所在的伺服器上,伺服器發現使用者請求的是 liuzhen.me,然後 nginx 就根據配置檔案裡配置的 liuzhen.me 找到對應的專案,這個 Rails 專案是由 puma 啟動的,然後 nginx 就把這個事交給 puma, puma 收到後就根據路由去到對應的 controller,然後返回對應的頁面給到瀏覽器。

monit 是什麼

沒有 monit 也能釋出成功,但是有時候你的 puma 可能異常關掉了,如果有 monit 的話,就能時刻監聽這個程式是不是啟動狀態,一旦關閉了,就重新啟動。

部落格地址: <http://liuzhen.me>

部落格程式碼: <https://github.com/liuzhenangel/RBlog>

原文來源: <http://liuzhen.me/articles/16>

更多原創文章乾貨分享,請關注公眾號
  • 如何從零構建個人部落格系統
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章