个人网站构建记录

2,428 阅读6分钟

想过要做个人网站,由于感觉过程特别繁琐及技术原因一直拖着,6.18买了个服务器,所以just do it不然有点浪费。

准备

做了个简易版的网站,写这篇文章主要是为了梳理一下自己的思路,主要涉及:mysql,node,axios请求,vue打包文件上传到服务器,nginx,pm2管理node服务 这几个部分,我也会分别来描述这几方面的构建过程。

mysql数据库

在此之前没有接触过数据库,印象中就是增删改查。再决定要写node后台我决定系统的学习下mysql的语法,这里推荐自己这几天看的b站的mysql视频相信你的sql语句也会写的杠杠的。

其实对于前端来说用的最多的就是查,再准备写项目前建立一个数据库,根据页面的增加,增加数据库中的表。具体的语法可以自行查阅资料,或者看我推荐的视频。

node

有了数据,我们需要使用js来操作数据库里的数据。这里我们会用到npm mysql包,通过mysql库来创建与mysql连接池,然后可以使用上面学的sql语句来操作数据库如:

connection.query('SELECT * FROM `books` WHERE `author` = "David"', function (error, results, fields) {});

虽然会写一些sql语句了,但是我用的不是这款js库,我还是比较喜欢sequelize写起来比较简洁优雅。
有了sequelize库,我们需要创建Model(相当于数据库中表)通过它来操作一个表中数据的增删改查。我们会写一些代码如下:

// 连接数据库
const sequelize = new Sequelize('minprogram', 'root', 'password', {
  host: 'localhost',
  dialect: 'mysql'
})
// 指定一个表
const User = sequelize.define(
  'user',
  {
    id: {
      type: Sequelize.STRING(10),
      primaryKey: true,
      autoIncrement: true
    },
    avatar: Sequelize.STRING(100),
    createdAt: Sequelize.BIGINT,
    updatedAt: Sequelize.BIGINT
  },
  { timestamps: false }
)

有了model模型后我们可以开始操作数据库了,可以把操作数据的方法单独存放在对应的业务逻辑下如:

// user.js
const User = require('./model')
async function findAll() {
  return User.findAll()
}
async function createUserId(options) {
  return User.create(options)
}
async function getId(id) {
  return User.findAll({
    where: { id: id }
  })
}
module.exports = {
  findAll,
  createOpenid,
  getOpenid,
  updateTime
}

------------------分割线-------------------

有了操作数据库的能力,可以开始写一些后台逻辑和接口了。比如要做一个登陆的验证,判断(是否有该用户,用户密码是否正确)的逻辑。
来梳理下逻辑的实现思路:

  1. 当点击登陆按钮时调用登陆接口,服务端查询根据用户名查询数据库中是否存在该用户名(不存在多个相同的用户名,注册时就要将该情况规避,提醒用户已经注册)
  2. 查询数据库发现找不到数据,说明该用户名还未注册
  3. 查询数据库发现密码和用户输入的密码不符,提示密码输入成功
  4. 还可以设置登陆过期之类的,目前还没做,到此就算登陆成功了吧
// 项目使用koa框架
// 统一定义类型,便于后期修改维护
const jsonMine = 'application/json'

router.get('/vue/login', async ctx => {
    const { username, password } = ctx.request.query
    const allList = await findUserByName(username)
    if (allList.length === 0) {
      handle(ctx, '', 1, '该用户未注册')
    } else {
      if (allList[0].password !== password) {
        handle(ctx, '', 1, '密码错误')
      } else {
        handle(ctx, '', 0, '登陆成功')
      }
    }
  })
// 统一处理返回数据
function handle(ctx, data, code = 0, message = 'success') {
  ctx.type = jsonMine
  ctx.body = {
    code,
    data,
    message
  }
}

axios请求 跨域问题

对axios进行了一些封装,然后去请求后台服务我设置对端口号是8082。

import axios from 'axios'
export default ({ api, params }) => {
  const baseURL = 'http://' + window.location.hostname + ':8082'
  return axios({
    url: api,
    method: 'get',
    baseURL,
    // headers: { 'client-test': 'cors' },
    params,
    'Content-Type': 'application/x-www-form-urlencoded'
  }).then(res => {
    const { status } = res
    if (
      (status >= 200 && status < 300) ||
      status === 304 ||
      status === 302
    ) {
      return new Promise((resolve, reject) => {
        resolve(res.data)
      })
    } else {
      return Promise.reject(
        new Error(
          `${'*'.repeat(10)}  状态吗:${status}  ${'*'.repeat(
            10
          )}\n ${'*'.repeat(10)}  api:  ${'*'.repeat(10)}\n ${'*'.repeat(
            10
          )}  `
        )
      )
    }
  })
}

出现跨域对问题。控制台打出错误提示需要我们后台设置允许访问的域。也就是设置 Access-Control-Allow-Origin字段。
可以写一个中间件cors.js,这样统一处理所有路由:

  • 核心代码就是 let origin = options.origin || ctx.get('Origin') || '*' ctx.set('Access-Control-Allow-Origin', origin)
// cors.js
module.exports = function crossOrigin(options = {}) {
  const defaultOptions = {
    allowMethods: ['GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']
  }

  options = Object.assign({}, defaultOptions, options) 
  return async function cors(ctx, next) {
    // 设置允许访问后台对域,可以自行设置,或者取请求头中的origin,再不行就是所有的域都可以访问
    let origin = options.origin || ctx.get('Origin') || '*'
    ctx.set('Access-Control-Allow-Origin', origin)

    if (ctx.method === 'OPTIONS') {
      if (options.allowMethods) {
        ctx.set('Access-Control-Allow-Methods', options.allowMethods.join(','))
      }
      // Access-Control-Allow-Headers
      if (options.allowHeaders) {
        ctx.set('Access-Control-Allow-Headers', options.allowHeaders.join(','))
      } else {
        ctx.set(
          'Access-Control-Allow-Headers',
          ctx.get('Access-Control-Request-Headers')
        )
      }

      ctx.status = 204 // No Content
    } else {
        ctx.set('Access-Control-Allow-Credentials', 'true')
      }
      await next()
    }
  }
}

vue打包文件上传到服务器

完成了客户端访问服务端并拿到数据更新UI和状态。将完成的代码通过webpack打包。生成如下文件,接下来就是将dist文件夹发到服务器上指定的目录下。

  • 发送步骤如下
  1. tar -cvf dist.tar dist 将 dist 文件夹压缩在根目录下生成 dist.tar 文件
  2. scp dist.tar root@118.31.127.59:/repo/dist 将 dist.tar 文件上传到服务对应到路径下
  3. tar -xvf dist 解压 dist.tar 文件 还原 dist 文件夹

nginx 代理

通过nginx代理指向服务器本地的index.html,当访问域名时相当于打开的是服务器指定目录下的index.html文件。
在服务器安装nginx的情况下 vi /etc/nginx/nginx.conf 打开配置文件查看下。可自行查看,发现一大堆也看不懂的东西,就看到一句include /etc/nginx/conf.d/*.conf;

意思是会引入conf.d文件下的所以.conf结尾的配置文件。所以为了看着舒服,在 /etc/nginx/conf.d目录下新建一个html.conf文件来单独写自己的nginx配置。

server {
   listen 8980 ;
   server_name 118.31.127.59;
   root /repo ;
   location /dist {
      index index.html;
   }
 }

这个配置通过root 指定根目录,通过location 指定uri 。当页面访问118.31.127.59:8980/repo/dist 是会打开该目录下当index.html文件。

  • 疑问为什么本地打开dist文件夹下的html文件运行报错,服务器端通过nginx代理就可以正确打开? 关键还是路径问题,如果想在本地的dist文件夹下正确打开,需要路径从 file://开始,而服务器中通过nginx指定根目录为 /repo 这样可以拿到正确的文件路径。

service

  • 使用pm2管理node服务端的代码
    1. pm2 ecosystem 在(local 本地)项目的根目录下执行,生成配置文件,指定服务地址和github地址
    2. 初始化服务器,这样服务器上就 copy 本地的项目文件 pm2 deploy ecosystem.config.js production setup
    3. 部署 pm2 deploy ecosystem.config.js production
    4. 在服务器中 pm2 start app.js 启动node服务

第一次构建个人网站

现在面临的问题是打开页面加载时间太长。还有很多值得学习优化的地方,欢迎👏大佬在留言区给我提建议和交流。