vue+uikit3+laravel快速建站

vue+uikit3+laravel快速建站

vue+uikit3+laravel快速建站

目前站已初步建好,待完善中,https://recallsufuture.space,源码已放到 github

1 前言

很早因为折腾科学上网之术,入了vps的坑,然而那个配置只是作为梯子的话实在是浪费资源, 在加上做了一阵子的web开发,却还没自己的一个站,自己的小程序做不了,网站总可以的嘛, 那么下面就开始吧。

2 需求

实际上还真不知到要做个什么样的网站,经过一番艰难的思考,还是决定先做已经被我做烂了的fa爬虫, 如果有其他的想法,就做成新的板块,这样就可以慢慢填坑啦。

2.1 页面

  • 主页面,展示本站作用以及现有板块
  • fa板块,批量爬取原图链接

3 环境

3.1 开发环境

  • php7.2
  • npm
  • composer
  • vscode
  • redis
  • chrome & firefox

3.2 生产环境

  • oneinstack一键环境,包括php,nginx,redis。
  • npm
  • composer

3.3 框架

  • 前端用vue+uikit3制作足够模块化且现代化的页面,并用vue-router来提供路由支持
  • 后端用laravel,优雅且强大

4 Let's code

4.1 主页面

4.1.1 界面构思

这个页面需要有顶部导航栏,主体需要有足够大而鲜明的说明标题文字来介绍本站, 标题下面是几个card来展示现有的板块。

4.1.2 实现

首先是路由,默认的laravel用闭包写了一个welcome路由,这个我们不需要了,删掉routes/web.php内的这行路由, 并删掉对应的blade文件。

因为我用了vue-router来管理路由,所以只需要在web.php中写一个拦截所有请求的路由即可,所有页面请求都导向vue页面, 即:

Route::get('/{view?}', 'HomeController@index')->where('view', '.*')->name('home');

接着修改上面的HomeController的index方法,使它返回app.blade.php模板

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>夙的小站</title>

    <!-- Styles -->
    <link href="{{ mix('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app"></div>

    <!-- Scripts -->
    <script src="{{ mix('js/app.js') }}"></script>
</body>
</html>

因为要用到uikit,所以去找了下脚手架,在网站根目录执行下面四条命令可以获得vue+uikit3脚手架:

composer require laravel-frontend-presets/uikit3
php artisan preset uikit3
php artisan preset vue
npm install

添加vue-router路由配置文件router.js:

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router);

export default new Router({
    mode: 'history',
    routes: [
        {
            path: '/',
            redirect: '/home',
        },
        {
            path: '/home',
            component: require('./pages/Home.vue'),
        },
    ],
})

修改下app.js保证编译顺利通过,然后就可以开始在resources/assets/js/pages/Home.vue里写第一个vue页面啦。

此处省略一千字。。。直接看结果吧

website1.png

4.2 fa页面

最初的想法是先将图片下到服务器,完成后打包发给用户,但实验过后发现等待时间实在太长, 一旦关了浏览器就啥都没有了,而且这样做很费服务器空间,没办法,只能退而求其次,获取来所有的链接让用户自己去下载。

那么这个页面一共有如下几个内容:

  • 界面内需要一个表单来获取一些下载信息
  • 后台暴露出一个接口,由前端ajax访问此接口,这样可以有比较好的体验
  • 界面上要有一个公告窗口,记录当前的下载日志
  • 后台需要配合实现websocket广播功能

前台界面的编写就不再多说,直接看下成品图 website2.png

后台部分,首先要实现那个接口,也就是接受表单数据,处理,然后返回状态码。

当然,还是得先把路由写好:

<?php

Route::prefix('api')->group(function () {
    // 批量获取fa链接
    Route::get('/fa', 'FaController@index')->name('fa.index');
});

// 全部拦截到此页
Route::get('/{view?}', 'HomeController@index')->where('view', '.*')->name('home');

因为可能还会有其他的api,所以我在这里分组并设置了统一的路径前缀(web.php参考了Laravel Horizon)

然后是编写FaController,用php artisan命令创建controller,并修改其中的index方法:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Events\FaDownloadEvent;

class FaController extends Controller
{
    /**
     * 返回是否成功新建任务
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        // 验证表单
        $validatedData = $request->validate([
            'id'      => "filled",
            'name'    => "required|max:255",
            'type'    => [
                "required",
                "regex:/gallery|scraps/"
            ],
            'pagenum' => "min:1",
            'picnum'  => "min:1|max:72",
            'maxnum'  => "min:0"
        ]);

        // 保存表单数据
        $id      = $validatedData['id'];
        $name    = trim($validatedData['name']);
        $type    = $validatedData['type'];
        $pagenum = $validatedData['pagenum'];
        $picnum  = $validatedData['picnum'];
        $maxnum  = $validatedData['maxnum'];

        event(new FaDownloadEvent($id, $name, $type, $pagenum, $picnum, $maxnum));

        return [
            'status' => 'ok'
        ];
    }
}

可以看到我先是验证了表单数据,然后将数据保存下来并触发了一个事件,验证表单数据是因为作为api, 有可能会被其他地方的代码访问,所以必须要验证数据有效性,而不直接下载改为触发事件是因为需要异步执行下载, 将下载任务放到队列里,通过广播来通知客户进度。

这里涉及到了事件,队列和广播,通过触发事件,预先设定好的的监听器会接受事件并处理,这样可以让代码更加解耦。 队列是用来处理需要长时间运行的任务,比如发送邮件。广播是为了和让后台和浏览器即使的沟通, 利用 websocket 建立长连接,免除了ajax轮询的麻烦,具体的内容请查询laravel中文文档

下面一个个说这些功能的实现:

4.2.1 事件

当前有一个FaDownloadEvent事件,这个事件接收所有表单参数,至于代码部分,其实十分的简单:

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class FaDownloadEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Client id
     *
     * @var string
     */
    public $id;

    /**
     * Author name
     *
     * @var string
     */
    public $name;

    /**
     * Gallery or scraps
     *
     * @var string
     */
    public $type;

    /**
     * Start page number
     *
     * @var string
     */
    public $pagenum;

    /**
     * Start pic number
     *
     * @var string
     */
    public $picnum;

    /**
     * Max download num
     *
     * @var string
     */
    public $maxnum;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($id, $name, $type, $pagenum, $picnum, $maxnum)
    {
        $this->id = $id;
        $this->name = $name;
        $this->type = $type;
        $this->pagenum = $pagenum;
        $this->picnum = $picnum;
        $this->maxnum = $maxnum;
    }
}

只是通过构造函数将参数保存到成员变量里而已,接下来看看接收这个事件的监听器:

4.2.2 监听器

<?php

namespace App\Listeners;

use App\Events\FaDownloadEvent;
use App\Events\DownloadStateEvent;
use App\Models\File;
use App\Utility\Vendor\Fa\FurAffinityAPI;

use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class FaDownloadListener implements ShouldQueue
{
    /**
     * The number of seconds the job can run before timing out.
     *
     * @var int
     */
    public $timeout = 1000;

    /**
     * Handle the event.
     *
     * @param  FaDownloadEvent  $event
     * @return void
     */
    public function handle(FaDownloadEvent $event)
    {
        // 触发一个状态事件,此事件会向浏览器广播下面的内容
        event(new DownloadStateEvent($id, 'info', '正在下载'.$name.'/'.$type.'的第'.$nowpage.'页第'. $nowpic .'个链接'));

        // 下面是具体的下载逻辑,在此省略
    }
}

监听器类接受到事件后,就可以进行下载任务了,但是如果直接开始的话,下载过程还会是同步的, 也就是会阻塞浏览器,让它变成异步只需要简单的添加一个接口就可以啦,

class FaDownloadListener implements ShouldQueu

只要加上这个接口,任务的执行就会自动放到队列里,至于是哪个队列呢?当然是默认队列啦

4.2.3 队列

现在需要配置队列,队列可以看作是一个单独的进程,这个进程就是个死循环,有任务放进来就执行,没有就睡眠等待

配置起来也不难,首先修改.env,将队列设置为redis驱动: QUEUEDRIVER=redis

然后在网站根目录下执行: php artisan queue:worker 即可,这样就开启了一个默认的队列。 想要开启更多的不一样的队列?用 –queue 参数试试。

不过这个进程会一直堵塞着终端,毕竟是个死循环嘛, 不想看到一直堵塞着的话可以让它后台执行: php artisan queue:worker –daemon >> /dev/null &

这个队列配置好后,监听器就可以正常工作了,每接收到一个FaDownloadEvent事件,就会向默认队列中添加一个下载任务。

4.2.4 广播

现在只剩广播部分没有完成

Date: 2018-07-26 18:36

Author: su

Created: 2018-07-29 日 16:00

Validate

posted @ 2018-07-26 19:43  回夙未来  阅读(1006)  评论(0编辑  收藏  举报