最近在學習使用 Node.js
框架,邊學習邊使用,花了大概 3周 時間作完這個 Web應用 且在 <time>12月16</time> 凌晨左右上線成功(其實就是把開發環境搬到服務器), 地址: https://a.lishaoy.net html
這個 Web應用 的代碼是開源的,如對這個應用感興趣,想知道代碼是如何運行的,能夠去我 GitHub 下載或 clone
:應用源碼node
首先,來看看用 3周 時間作出來的應用都有些什麼功能,以後再看看選用的 Node.js
框架,最後看看 Node.js
項目如何部署到服務器。nginx
登陸功能git
註冊功能github
登陸進來,會顯示文章列表頁面,顯示內容以下:web
點擊文章標題可進入文章詳情頁面,內容以下:sql
新建文章和修改文章都支持 Markdown 語法,且會每隔6秒鐘自動保存數據庫
我的信息頁面顯示內容以下npm
文章刪除編輯快捷入口,如圖bash
下面是我用另外一個用戶登陸,進入到我的信息頁面就會顯示關注按鈕,如圖
點擊文件上傳小圖標可進入文件上傳頁面,點擊 Files 連接可進入文件上傳列表,顯示內容如圖:
從文件列表頁面點擊標題可進入文件預覽頁面,顯示內容以下:
點擊鈴鐺小圖標可進入消息通知頁面,內容以下:
點擊我的頭像可展開工具欄列表,內容以下:
點擊工具欄上的設置按鈕能夠設置頁面,內容以下:
我的信息設置
Gravatar
提供的功能,根據郵箱生成頭像修改密碼設置
需填寫原密碼,新密碼,再次輸入密碼
點擊 Chatroom 連接可進入聊天室,固然這個是用的 websocket
作的,內容以下:
加入房間和離開房間都有消息通知,如圖
這個應用的開發我選擇的是 Adonisjs
框架,他和 PHP
的 Laravel
有些像,Adonisjs
是在操做系統上運行的 Node.js
MVC 框架。
接下來,來看看 Adonisjs
框架有哪些特性:
不論是開發環境仍是生產環境,安裝 Adonisjs
運行環境都是很是簡單,先來看看開發環境的安裝,生產環境後面會提到。
首先,咱們的電腦上須要安裝好 Node.js
大於 8.00 版本,管理 Node.js
可使用 nvm
其次,就可使用 npm
安裝 Adonis CLI
命令行工具(管理 npm
使用源可使用 nrm
)
npm i -g @adonisjs/cli
這樣就能夠在全局使用 adonis
命令
再次,能夠是 adonis new
命令建立項目
adonis new adonis_pro
在 cd
進入項目,執行 adonis serve --dev
運行項目
cd adonis_pro adonis serve --dev
這樣您的開發環境就搭建完成。
RMVC
就是路由、模型、視圖、控制器。
建立一條路由很是簡單,如
Route.get('liked/:userId/:postId', 'LikedController.liked')
這條路由就是用來處理上面提到的點贊功能的
固然,Adonisjs
提供了 資源路由 以便您更方便的建立路由,例如
Route.resource('posts', 'PostController').middleware( new Map([ [ [ 'create', 'store', 'edit', 'update', 'destroy' ], [ 'auth' ] ], [ [ 'update', 'destroy', 'edit' ], [ 'own:post' ] ] ]) ).validator(new Map([ [['posts.update', 'posts.store'], ['StorePost']] ]))
這個路由是來處理上面應用提到的文章的 增、刪、改、查 ,這個可能有些複雜,使用了 中間件 來處理用戶登陸狀態和操做權限,使用了 驗證器 來處理表單驗證,這裏不介紹的太複雜,如想了解這些具體功能,能夠須要花點時間瞭解學習。
咱們能夠去掉 中間件 和 驗證器 ,以下:
Route.resource('posts', 'PostController')
這條資源路由,其實就包含了如下路由:
Route.get(url, closure) Route.post(url, closure) Route.put(url, closure) Route.patch(url, closure) Route.delete(url, closure)
Adonisjs
還提供了路由組和其餘一些功能,路由組以下:
Route.group(() => { Route.get('profile', 'ProfileController.edit').as('profile.edit') Route.post('profile', 'ProfileController.update').as('profile.update').validator('UpdateProfile') Route.get('password', 'PasswordController.edit').as('password.edit') Route.post('password', 'PasswordController.update').as('password.update').validator('UpdatePassword') }) .prefix('settings') .middleware([ 'auth' ])
使用 .prefix
和 Route.group
來建立路由組,這條路由組是處理 我的信息設置 功能的,這樣訪問頁面是就統一要帶上 settings/**
。
Adonisjs
提供了命令行來建立控制器,如
adonis make:controller User --type http
這樣就建立了一個 User
控制器,自動生成代碼以下:
'use strict' class UserController { } module.exports = UserController
固然,咱們還可使用 --resource
建立資源類型的控制器
adonis make:controller Post --resource
自動生成代碼,代碼以下:
'use strict' class PostController { /** * Show a list of all posts. * GET posts */ async index ({ request, response, view }) {} /** * Render a form to be used for creating a new posts. * GET posts/create */ async create ({ request, response, view }) {} /** * Create/save a new posts. * POST posts */ async store ({ request, response, view }) {} /** * Display a single posts. * GET posts/:id */ async show ({ request, response, view }) {} /** * Render a form to update an existing posts. * GET posts/:id/edit */ async edit ({ request, response, view }) {} /** * Update posts details. * PUT or PATCH posts/:id */ async update ({ request, response, view}) {} /** * Delete a posts with id. * DELETE posts/:id */ async destroy ({ params, request, response }) {} } module.exports = PostController
和上面的資源路由是對應的,如用 GET
請求訪問 posts 就會調用 index
方法(通常用來顯示) ,再如:用 DELETE
請求訪問 posts/1 就會執行 destroy
方法(通常用來刪除)。
Adonisjs
提供了兩種模式來處理數據,Query builder
和 LUCID
首先,咱們能夠經過 adonis make:migration
來建立數據表
adonis make:migration users
會自動生成代碼,以下:
'use strict' const Schema = use('Schema') class UsersSchema extends Schema { up () { this.create('users', (table) => { table.increments() table.timestamps() }) } down () { this.drop('users') } } module.exports = UsersSchema
這是咱們只需在其中添加想要的字段就行,如:
'use strict' const Schema = use('Schema') class UsersSchema extends Schema { up () { this.create('users', (table) => { table.increments() table.string('username', 80).notNullable().unique() table.string('email', 254).notNullable().unique() table.string('password', 60).notNullable() table.timestamps() }) } down () { this.drop('users') } } module.exports = UsersSchema
在執行 adonis migration:run
命令就能夠在數據庫生成數據表
再來看看,如何獲取數據,可使用 Query builder
和 LUCID
兩種方式
先來看看 Query builder
:
const Database = use('Database') class UserController { async index (request, response) { return await Database .table('users') .where('username', 'admin') .first() } }
查詢 user
表 name
是 admin
的用戶
Adonisjs
提供了很是多的方法去操做數據,不是特複雜的關係都夠用,若是,關係比較複雜,還能夠用原生的 sql
操做,如
'use strict' const Database = use('Database') class NotificationController { async followNotice ({ auth, view }) { const notices = await Database.raw('select users.id as user_id,users.username,users.email,b.title,b.created_at,b.is_read,b.id as post_id from adonis.users , (select posts.id,posts.title, a.user_id,a.created_at,a.is_read from adonis.posts,(SELECT post_user.post_id, post_user.user_id, post_user.created_at, post_user.is_read FROM adonis.post_user where post_user.post_id in (SELECT posts.id FROM adonis.posts where user_id = ?)) as a where posts.id = a.post_id) as b where b.user_id = users.id and b.user_id <> ? order by b.created_at desc limit 50',[ auth.user.id, auth.user.id ]) } } module.exports = NotificationController
使用 Database.raw
來運行原生的 sql
,以上這條 sql
是用來查詢全部用戶給本身全部文章點讚的用戶信息和文章信息用於消息通知。
再來看看,LUCID
的模式是如何操做數據的:
使用 LUCID
模式,咱們先須要用命令行工具建立 Models
,如:
adonis make:model User
自動生成代碼以下:
'use strict' const Model = use('Model') class User extends Model { } module.exports = User
模型和模型之間須要定義一些關係,如:
const Model = use('Model') class User extends Model { profile () { return this.hasOne('App/Models/Profile') } } module.exports = User
意思是 一個用戶對應一個用戶信息檔案,一對一 的關係
定義好關係以後,就能夠方便的獲取數據,如:
const User = use('App/Models/User') const user = await User.find(1) const userProfile = await user.profile().fetch()
意思是,從用戶表和用戶我的信息表裏獲取用戶 id
是 1
的用戶信息及我的信息,
其中,關係能夠定義爲 3 種 一對1、一對多、多對多 ,多對多須要定義中間表
再來看看,上面的應用中的實際應用,如:
async update ({ params, request, response, session, auth }) { const { title, content, user_id, tags } = request.all() const post = await Post.findOrFail(params.id) post.merge({ title, content}) await post.save() await post.tags().sync(tags) session.flash({ type: 'primary', message: 'Post updated successfully.' }) return response.redirect( Route.url('PostController.show', { id: post.id }) ) }
以上,是更新文章的方法,文章 和 標籤 是 多對多 的關係,一個標籤能夠屬於多篇文章,一篇文章能夠有多個標籤,await post.tags().sync(tags)
這句代碼就能夠經過 Models
裏定義的關係自動把標籤和文章關聯起來保存到 posts
和 tags
表裏且把關聯關係保存到中間表 post_tag
。
固然,Adonisjs
提供了不少方便的方法,想了解更多的話須要您花點時間去了解學習。
Adonisjs
框架裏視圖使用了 edge
模板,咱們可使用命令行工具建立視圖文件,如:
adonis make:view post
我看能夠看下簡單的例子:
@loggedIn <h2> You are logged in </h2> @else <p> <a href="/login">Click here</a> to login </p> @endloggedIn
視圖模板裏可使用標籤來作邏輯判斷,視圖模板就沒什麼好說的,基本都是通用的,關於 edge
視圖模板更多語法 Edge官方文檔
最後,Adonisjs
框架還提供了不少其它的實用工具,如:Middleware
中間件、Validator
驗證器、Error Handling
自定義異常、Events
事件、Mails
郵件、Websocket
等來處理各類問題。
首先,咱們須要用 ssh
鏈接到阿里雲(或者其餘服務器供應商)的主機上,安裝一些必要的工具。
咱們須要安裝 epel-release
軟件包倉庫,epel-release
裏面有不少最新的軟件包,如,以後安裝的 git
就會用到
sudo yum install epel-release - y
sudo yum install git -y
接下來,咱們須要安裝 Node.js
以便咱們的 Node.js
項目可以跑起來,咱們可使用 nvm
安裝和管理 Node.js
,使用 nrm
來管理切換安裝源。
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
安裝好以後,咱們須要配置下環境變量,以便可以在命令行使用 nvm
命令,用 vi ~/.bash_profile
編輯下配置文件
vi ~/.bash_profile
加入如下代碼:
export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
而後,在 source ~/.bash_profile
刷新下配置文件,讓它生效
source ~/.bash_profile
此時,咱們就可使用 nvm
來安裝 Node.js
nvm install node
安裝好後,可使用 nvm list
來查看有哪些版本可使用
nvm list
結果:
-> v10.13.0 v11.2.0 system default -> v10.13.0 node -> stable (-> v11.2.0) (default) stable -> 11.2 (-> v11.2.0) (default) iojs -> N/A (default) lts/* -> lts/dubnium (-> v10.13.0) lts/argon -> v4.9.1 (-> N/A) lts/boron -> v6.14.4 (-> N/A) lts/carbon -> v8.13.0 (-> N/A) lts/dubnium -> v10.13.0
我使用的是 v10.13.0 的版本,默認安裝的都是比較新的版本,多是 v11.2.0 或 v11.1.0,因此咱們也能夠用 nvm install v10.13.0
來安裝指定版本。
nvm install v10.13.0
而後,就可使用 nvm use v10.13.0
來使用指定版本
nvm use nvm v10.13.0
結果:
Now using node v10.13.0 (npm v6.4.1)
使用 npm 安裝的程序包,默認的來源是 http://registry.npmjs.org,國內的下載速度會有些慢,咱們能夠是 nrm
來切換到 taobao
的源
安裝 nrm
npm install nrm --global
切換到 taobao 源
nrm use taobao
以上工做完成以後,咱們的服務器就能夠正常運行 Node.js
項目,如今咱們須要把本地的項目上傳到服務器,上傳方法有不少,如:
git
,先把項目傳到 GitHub,而後用 git
下載到服務器scp -r 本地目錄 root@服務器IP:/var/www/
發項目文件上傳到服務器的指定目錄下,如:www
接下來,咱們能夠是 PM2 來管理 Node 進程,先須要安裝 PM2
npm install pm2@latest --global
這些工做做爲以後,就能夠來測試一下,啓動項目,在本地訪問服務器 IP:PORT
來測試是否能夠訪問
在測試以前,咱們須要改下應用的配置文件,adonisjs
框架裏是 .env
文件,修改下 HOST
的值:
HOST=0.0.0.0 PORT=3333 ...
HOST
默認是 127.0.0.1,須要改爲 0.0.0.0 這樣就能夠在本身電腦上用服務器 IP:PORT
來訪問應用
改完後,進入到項目的根目錄,運行應用,adonisjs
的啓動文件是 server.js
,如:
pm2 start server.js
如啓動成功會提示:
[PM2] Applying action restartProcessId on app [server](ids: 0) [PM2] [server](0) ✓ [PM2] Process successfully started ┌──────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬─────┬──────────┬──────┬──────────┐ │ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼─────┼──────────┼──────┼──────────┤ │ server │ 0 │ 4.1.0 │ fork │ 7171 │ online │ 30 │ 0s │ 0% │ 3.4 MB │ root │ disabled │ └──────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴─────┴──────────┴──────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app
而後,在本身電腦上用服務器 IP:PORT
來訪問應用。
爲了讓服務器更好地處理網絡請求,咱們須要添加使用 Nginx 反向代理 把請求轉發給 Node.js
應用
sudo yum install nginx -y
若是你的服務以前安裝過可不用安裝,個人阿里雲服務器運行了 4 個站點以前安裝過,以後我只需添加配置就行。
sudo systemctl start nginx
通常狀況 Nginx 安裝好後會有 /etc/nginx/conf.d 目錄,進入這個目錄,建立一個配置文件爲 Node.js
而準備,名字可隨意命名,如:adonis.conf
server { listen 80; location / { proxy_pass http://127.0.0.1:3333; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_cache_bypass $http_upgrade; } }
而後,在 Nginx 的主配置文件裏把剛纔新建立的配置文件(/etc/nginx/nginx.conf) include
進去就能夠,如:
include /etc/nginx/conf.d/*.conf;
由於,個人主機裏運行了4個站點,*
的意思就是加載這個目錄下的全部配置文件
而後,記得把剛纔項目裏的 .env 配置文件改爲 127.0.0.1 ,由於咱們如今使用了代理,網絡請求交給了 Nginx
再進入到項目的根目錄下運行:
pm2 stop server.js #中止項目 pm2 start server.js #啓動項目
這時候再用服務器 IP 訪問就是用的 Nginx 去處理請求
若是你有域名能夠去對應的供應商解析好,如想使用 https
協議,也能夠去對應的供應商下載好證書(下載好的證書要放到服務器某個目錄裏)。
再修改下剛纔建立的配置文件,讓它可以支持 https
和 域名 訪問:
server { listen 80; listen 443 ssl http2; #SSL server_name a.lishaoy.net; #域名 ssl on; ssl_certificate /etc/letsencrypt/live/a.lishaoy.net/server.pem; #證書目錄 ssl_certificate_key /etc/letsencrypt/live/a.lishaoy.net/server.key; #證書目錄 ssl_protocols TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; if ($ssl_protocol = "") { rewrite ^(.*) https://$host$1 permanent; } error_page 497 https://$host$request_uri; error_page 404 /404.html; error_page 502 /502.html; location / { proxy_pass http://localhost:3333; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_cache_bypass $http_upgrade; } }
這樣再重啓 Ningx
服務和項目的服務,就大功告成了。