Laravel-admin 在 Laravels 运行的的一些问题解析——记一次因为莽而付出的代价

初生牛犊不怕虎,直接莽上laravels

前因

1240

我们公司一直都是采用 think3.2 ,虽然使用起来确实不错,但是哪能有 laravel 舒服

于是我才用 laravel ,但是经理觉得后台太慢了

1240

我做了一部分优化还是慢,我太难了,之前他让我随便选框架

1240

我就想到了大佬的 laravels 心里想laravels+laravel-admin岂不是美哉,毕竟有swoole 以前学过一些,觉得应该可以把持住

结果一使用出大问题

发现问题,问题介绍

  1. 第一个问题就是比较常见的,顶部的刷新无限增多,如图

laravel-admin在Laravels运行的的一些问题解析——记一次因为莽而付出的代价

  1. 第二个问题就是,删除这个却进入上一个进入的删除路由,导致各种删除失败

laravel-admin在Laravels运行的的一些问题解析——记一次因为莽而付出的代价

  1. 导出excel提示exit

4.Pjax中间件提示exit

目前比较真实的问题就是这么一些,具体其他问题请等待我后续的发现

解决问题,提供思路

授人以鱼不如授人以渔

我们先排查第一个问题,这个比较常见,其实就是我们的Admin实例并没有被清除,导致无限添加navBar

1240

然后在 admin/bootstrap.php中每次都调用一下

于是我想到第一个办法,这个方法比较 憨批 ,因为当时认为这个实例重新创建应该挺麻烦,所以采用局部清理的方式

解决问题之后测试删除功能,又发现新的问题,也就是问题2

laravel-admin在Laravels运行的的一些问题解析——记一次因为莽而付出的代价

laravel-admin在Laravels运行的的一些问题解析——记一次因为莽而付出的代价

这个问题很明显是实例没清理干净,导致上一步生成的js下一步仍然存在,排除在控制器生成的可能,
所以初步判断只有可能存在于\Encore\Admin\Admin实例中,因为这个实例是存在于容器中
所以直接从中寻找,发现原来是\Encore\Admin\Admin静态变量

//因为是表单所以我们先进入 Encore\Admin\Form\Form 寻找Tool 发现renderDelete 
 protected function renderDelete()
 {
 $trans = [
  'delete_confirm' => trans('admin.delete_confirm'),
  'confirm' => trans('admin.confirm'),
  'cancel' => trans('admin.cancel'),
  'delete' => trans('admin.delete'),
 ];
    .
    .
    .

  Admin::script($script);

  return <<<HTML
<div class="btn-group pull-right" style="margin-right: 5px">
 <a href="javascript:void(0);" class="btn btn-sm btn-danger {$class}-delete" title="{$trans['delete']}">
 <i class="fa fa-trash"></i><span class="hidden-xs"> {$trans['delete']}</span>
 </a>\</div>
HTML;
 }

看到核心 Admin::script 这个原来是在Admin 下的 HasAssets,


/**
 * @param string $script
 * @param bool   $deferred
 * @return array|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
 public static function script($script = '', $deferred = false)
{
  if (!empty($script)) {
         if ($deferred) {
              return self::$deferredScript = array_merge(self::$deferredScript, (array) $script);
         }
          return self::$script = array_merge(self::$script, (array) $script);
     }
      $script = array_unique(array_merge(static::$script, static::$deferredScript));

  return view('admin::partials.script', compact('script'));
}

那么解决起来就狠方便了 直接在bootstrap中初始化一下这些静态变量即可

我觉得麻烦所以就直接写了一个clean,之前憨批的方法我也就换成更加粗暴的方式,最终还是清理掉Admin在容器中的实例

        \Encore\Admin\Admin::$script=[];
        \Encore\Admin\Admin::$deferredScript=[];
        \Encore\Admin\Admin::$headerJs = [];
        \Encore\Admin\Admin::$manifestData = [];
        \Encore\Admin\Admin::$extensions = [];
        .
        .
        .
        $app->forgetInstance(\Encore\Admin\Admin::class);
        Facade::clearResolvedInstance(\Encore\Admin\Admin::class);

1240

由此,问题一和问题二已经基本解决,菜鸟也想学习大佬的操作,想要更加优雅一点

下面就是解决exit问题了,中间复杂心里斗争,最后只能采用抛出异常的方式

Pjax中的exit 在56行

Export中 这里就很多,我就不一一指出

 //导出操作
     $res = Response::stream(function () {
            $handle = fopen('php://output', 'w');
            $titles = [];
            $this->chunk(function ($records) use ($handle, &$titles) {
                if (empty($titles)) {
                    $titles = $this->getHeaderRowFromRecords($records);
                    // Add CSV headers
                    fputcsv($handle, $titles);
                }

                foreach ($records as $record) {
                    fputcsv($handle, $this->getFormattedRecord($record));
                }
            });
            // Close the output stream
            fclose($handle);
        }, 200, $headers);
        swoole_exit($res);
//Pjax 修改后
    $next = function () use ($response) {
              return $response;
     };

  swoole_exit((new static())->handle(Request::capture(), $next));

//swoole_exit神秘面纱

if (!function_exists('swoole_exit')){
  function swoole_exit($response)
     {
         throw new App\Exceptions\SwooleExitException($response);
     }
 }
 //修改Handler  
 <?php

namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Response;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

class Handler extends ExceptionHandler\
{
  /**
 * A list of the exception types that are not reported.\ *\ * @var array
 */ 
 protected $dontReport = [
  SwooleExitException::class
  ];

  /**
 * A list of the inputs that are never flashed for validation exceptions.\ *\ * @var array
 */  
 protected $dontFlash = [
  'password',
  'password_confirmation',
 ];
  /**
 * Report or log an exception@param \Exception  $exception\
 * @return void\
 */
 public function report(Exception $exception)
 {  
     parent::report($exception);
 }
  /**
 * Render an exception into an HTTP response.\ *\ * @param \Illuminate\Http\Request  $request
 * @param \Exception  $exception
 * @return \Illuminate\Http\Response
 */
 public function render($request, Exception $exception)
 {
     //判断是否为我们的自定义异常
     if ($exception instanceof SwooleExitException) {
             //直接调用perpare
          return $exception->getResponse()->prepare($request);
     }
     return parent::render($request, $exception);
 }
}
<?php

namespace App\Exceptions;

use Exception;
use Throwable;

class SwooleExitException extends Exception\
{
  protected $response;
  public function __construct($response,$message = "", $code = 0, Throwable $previous = null)
 {
 $this->response = $response;
  parent::__construct($message, $code, $previous);
 }
//获取响应内容
public function getResponse(){
  return $this->response;
}

1240

如果你直接把exit去掉,那么 导出csv 会提示你 oops ... ob_end_clean() 这个报错

我们可以在Hhxsv5\LaravelS\Illuminate\Laravel中的 handleDynamic 加一个判断ob_get_length()也可以不加,因为后期解决exit退出问题,也就不会有这种报错

解决思路因为exit其实就是不执行后续的响应 ,所以我们想到异常就是执行到异常抛出之前,于是我们可以定义一种特定的异常,来提前结束,用来代替exit ,至于 swoole/laravels 为什么不能使用这些函数我就不在这赘述

使用异常,但是我们也得返回请求,但是我们的请求不能直接send掉,因为laravels需要使用swoole的方式返回,我们直接执行 send 是不起作用的,那我们就可以通过特定的异常返回我们所需要的响应,

1240

public static function toResponse($request, $response)\
{
      if ($response instanceof Responsable) {
          $response = $response->toResponse($request);
     }
      if ($response instanceof PsrResponseInterface) {
          $response = (new HttpFoundationFactory)->createResponse($response);
     } elseif ($response instanceof Model && $response->wasRecentlyCreated) {
          $response = new JsonResponse($response, 201);
     } elseif (! $response instanceof SymfonyResponse &&
                      ($response instanceof Arrayable ||
                      $response instanceof Jsonable ||
                      $response instanceof ArrayObject ||
                      $response instanceof JsonSerializable ||
                      is_array($response))) {
                          $response = new JsonResponse($response);
     } elseif (! $response instanceof SymfonyResponse) {
          $response = new Response($response);
     }
     if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
              $response->setNotModified();
    }
    return $response->prepare($request);
}

替换Router::toResponse();$exception->prepare() 无需其他中间过程

由于使用过程中发现toResponse()导出多次会发现异常问题,所以替换,具体原因还在查找中

感谢z-song提供的laravel-admin优质扩展

感谢hhxsv5提供的laravel-s优质扩展

具体分析请等下次更新,我们把laravels大致走一遍,学习一下

laravels可以获取到最终响应然后返回,目的就达到了

学习,冲冲冲~

附言 1  ·  1周前

如果使用了dingo,记得修改dingo的handle,否则会出现json的报错

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 12
dreamfish

:+1:

1周前 评论
blankqwq

@dreamfish 谢谢大佬围观 :grin:多向你学习

1周前 评论
Epona

很赞,不过我觉得 laravel-admin 有点慢是需要加载 太多东西了。。。

另外 上了 laravels 之后 速度有提升么?

1周前 评论
blankqwq

@Epona 响应基本200ms左右

1周前 评论
沈益飞

哈哈哈,我们这边是后台慢无所谓,接口必须要快。打开慢一般来说是路由太多性能损耗都在路由上面。

1周前 评论
hkzj0571 1周前
沈益飞 (作者) 1周前
blankqwq

@沈益飞 蛤蛤蛤,用了laravels,接口也一样很快,就是可能用其他扩展会容易出问题,不过暂时我还没遇到什么,主要是后台问题比较多,遇到了再看看代码改改

1周前 评论
Epona

@blankqwq 后台慢我觉得和 Laravels 无关,主要是Laravel-admin前端要加载的东西太多了。。。(包括乱七八糟的JS等等

1周前 评论
felo

:+1:楼主太及时了,正在用这个

1周前 评论
blankqwq

@felo 遇到其他问题也可以分析一波,一起进步一起学习

1周前 评论
blankqwq

@Epona emmm大佬说的没错,每次都是动态加载静态资源,导致加载比较耗时,于laravel无关,用上laravels快很多,基本秒开

1周前 评论
Epona

@blankqwq 可以试试把laravel-admin 的 js啥的放到 cdn上,情况应该能好一些

1周前 评论
blankqwq

@Epona 嗯嗯,谢谢大佬思路,我觉得应该需要一个生成blade文件的扩展,这样应该还能再快一些,我有空试一下 :smiley:

1周前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!