最近在学习使用 Node.js
框架,边学习边使用,花了大概 3周 时间作完这个 Web应用 且在 凌晨左右上线成功(其实就是把开发环境搬到服务器), 地址: a.lishaoy.nethtml
这个 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 安装的程序包,默认的来源是 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
服务和项目的服务,就大功告成了。