荷城
高明汽车客运站是我暂时去过最大的公交站场,对于公交迷的我是一个胜地。
高明有轨电车,算是遗址吧~反映了一个城市变迁的缩影。
西江
骑共享电驴游西江是一种享受。
苏村
这个坐落西江,以锦鲤为主题的村落,绝对是来高明必去的打卡地。
高明汽车客运站是我暂时去过最大的公交站场,对于公交迷的我是一个胜地。
高明有轨电车,算是遗址吧~反映了一个城市变迁的缩影。
骑共享电驴游西江是一种享受。
这个坐落西江,以锦鲤为主题的村落,绝对是来高明必去的打卡地。
比亚迪 K9FE 是比亚迪 K9 系列的经典改进型纯电动城市客车,型号常对应 BYD6121LGEV3/4,2017 年后推出,核心定位为高效、安全、适配城市通勤的纯电公交主力。
广州公交284路初期走向为“广园新村—东山”,途经大东门、北京路、纪念堂,由广州市电车公司营运。
1993年,广州公交284路走向由“广园新村—东山”调整为“广园新村—员村生活区”。
约2000年,广州公交284路走向由“广园新村—员村生活区”调整为“广园新村—员村(绢麻厂)”。
音符时值表示音符的持续时间,常见音符及其时值如下:
拍号决定每小节的拍数和每拍的时值,通常表示为分数,如4/4、3/4等。
以4/4拍为例:
音符一拍的时值由拍号决定,分母指定每拍的音符类型,分子决定每小节的拍数。
目前大多数设备的屏幕刷新率为60fps(Frame per Second),即每秒60帧。因此,如果在页面中有一个动画或渐变效果,或者用户正在滚动页面,那么浏览器渲染动画或页面的每一帧的速率也需要跟设备屏幕的刷新率保持一致,即每一帧要在16毫秒(1S/60 = 16.66ms)之内完成。如果无法完成,由于帧率的下降会导致内容在屏幕上抖动。此现象通常称为卡顿,会对用户体验产生负面影响。
在讲性能之前,我们需要先对浏览器渲染页面有一个基础的理解。
浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。需要注意的是,如果图层中某个元素需要重绘,那么整个图层都需要重绘(关于重绘下面会讲到)。
简单来说,浏览器的渲染过程其实就是将页面转换成像素显示到屏幕上,大致有如下几个步骤:
css 的属性大致分为三类:布局类(layout),绘制类(paint),合成类(composite)。
由元素的布局类属性改变所触发的行为过程,我们称为 reflow,也叫做 relayout(重新布局)。当某个节点 reflow 时会重新计算节点的尺寸和位置,还可能会引起其它节点的 reflow。
该系列属性的改变,会执行路径 A 进行重新渲染,所以性能是最差的。(这充分说明,重排会引起重绘)
触发重排的属性
盒子模型相关属性会触发重布局:
定位属性及浮动也会触发重布局:
改变节点内部文字结构也会触发重布局:
由绘制类属性改变触发节点重新绘制其可视效果的过程,我们称为 repaint。
该系列属性的改变,会执行路径 B,所以性能一般。
修改时只触发重绘的属性有:
上面的属性由于不会修改节点的大小和位置,因此不会触发重排,其只是改变了节点内部的渲染效果,所以只会进行重绘以下的步骤。
目前只有两个属性属于 composite 类:
减少动画元素是动画性能优化中首先需要完成的。通过审查页面动画 DOM 元素结构,去除不必要的动画元素,减少元素的数量,相应地会减少布页面局和绘制的时间。
对于动画元素,尽量使用用 fixed、absolute 定位方式,避免影响到其他节点重排。
能用 transform、opacity 优先使用,其属性的改变不会发生重排和重绘。如位移操作的,可以使用translate 来实现,渐隐渐现效果可以使用 opacity 属性来实现。
对动画元素应用
transform: translate3d(0, 0, 0);
/* transform: translateZ(0); */
will-change: transform;
等来开启硬件加速。通常开启硬件加速可以让动画变得更加流畅。但这里需注意,在不需要的时候需去掉避免过多的内存消耗。
Laravel 内置API开发支持,无需额外引入前端脚手架,推荐使用纯净API项目初始化方式:
# 创建Laravel项目
composer create-project laravel/laravel laravel-api
# 进入项目目录
cd laravel-api
# 禁用前端相关依赖(可选,纯API无需前端)
rm -rf resources/js resources/css package.json vite.config.js
修改根目录 .env 文件,配置核心环境变量,确保数据库、时区、接口响应格式适配后端API:
# 应用基础配置
APP_NAME=LaravelAPI
APP_ENV=local
APP_KEY=生成的密钥
APP_DEBUG=true
APP_URL=http://localhost:8000
APP_TIMEZONE=Asia/Shanghai
APP_LOCALE=zh-CN
# 数据库配置(MySQL为例)
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_api
DB_USERNAME=root
DB_PASSWORD=
# 接口跨域配置(后续详解)
ALLOWED_ORIGINS=*
生成应用密钥(必填,保障加密安全):
php artisan key:generate
Laravel 提供 routes/api.php 路由文件,专门用于定义后端接口,默认前缀 /api,无需手动添加,且不包含Session中间件,适配无状态API。
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\UserController;
use App\Http\Controllers\Api\PostController;
// 单接口路由
Route::get('/test', function () {
return response()->json([
'code' => 200,
'msg' => 'API接口正常',
'data' => []
]);
});
// 控制器路由(推荐)
Route::get('/users', [UserController::class, 'index']); // 用户列表
Route::get('/users/{id}', [UserController::class, 'show']); // 单个用户
Route::post('/users', [UserController::class, 'store']); // 创建用户
Route::put('/users/{id}', [UserController::class, 'update']); // 更新用户
Route::delete('/users/{id}', [UserController::class, 'destroy']); // 删除用户
针对标准CRUD接口,使用资源路由简化定义,自动映射RESTful风格接口:
// 完整资源路由
Route::apiResource('posts', PostController::class);
// 仅指定部分接口
Route::apiResource('posts', PostController::class)->only(['index', 'show']);
// 排除部分接口
Route::apiResource('posts', PostController::class)->except(['destroy']);
对接口进行分组管理,统一添加前缀、中间件(如鉴权、跨域):
Route::prefix('v1')->group(function () {
// 公开接口
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);
// 需鉴权接口
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user/info', [AuthController::class, 'userInfo']);
Route::apiResource('posts', PostController::class);
});
});
推荐在 app/Http/Controllers/Api 目录下创建控制器,区分前后端,命令行创建:
# 创建基础控制器
php artisan make:controller Api/UserController
# 创建资源控制器(自带CRUD方法)
php artisan make:controller Api/PostController --api
后端API统一返回JSON格式数据,遵循 code+msg+data 响应规范,避免直接返回原生数据:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class UserController extends Controller
{
/**
* 用户列表
*/
public function index(Request $request)
{
// 分页查询
$users = User::query()->paginate(10);
return $this->success('用户列表获取成功', $users);
}
/**
* 单个用户详情
*/
public function show($id)
{
$user = User::find($id);
if (!$user) {
return $this->fail('用户不存在', 404);
}
return $this->success('用户详情获取成功', $user);
}
/**
* 统一成功响应
*/
protected function success($msg = '操作成功', $data = [], $code = 200)
{
return response()->json([
'code' => $code,
'msg' => $msg,
'data' => $data
]);
}
/**
* 统一失败响应
*/
protected function fail($msg = '操作失败', $code = 400)
{
return response()->json([
'code' => $code,
'msg' => $msg,
'data' => []
]);
}
}
创建独立验证类,分离校验逻辑与业务逻辑,命令行创建:
php artisan make:request Api/UserStoreRequest
编写验证规则(app/Http/Requests/Api/UserStoreRequest.php):
<?php
namespace App\Http\Requests\Api;
use Illuminate\Foundation\Http\FormRequest;
class UserStoreRequest extends FormRequest
{
/**
* 权限判断
*/
public function authorize()
{
return true; // 开放接口直接返回true
}
/**
* 验证规则
*/
public function rules()
{
return [
'username' => 'required|string|unique:users|max:20',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:6|confirmed',
];
}
/**
* 自定义错误提示
*/
public function messages()
{
return [
'username.required' => '用户名不能为空',
'email.unique' => '邮箱已被注册',
'password.confirmed' => '两次密码不一致',
];
}
}
public function store(UserStoreRequest $request)
{
// 验证通过后获取安全参数
$data = $request->validated();
$data['password'] = Hash::make($data['password']);
$user = User::create($data);
return $this->success('用户创建成功', $user);
}
修改 app/Exceptions/Handler.php,将异常转为JSON响应,避免页面报错:
use Illuminate\Validation\ValidationException;
public function register()
{
$this->renderable(function (ValidationException $e, $request) {
if ($request->is('api/*')) {
return response()->json([
'code' => 422,
'msg' => '参数校验失败',
'data' => $e->errors()
], 422);
}
});
}
通过迁移文件管理数据库表,无需手动建表:
# 创建迁移文件
php artisan make:migration create_posts_table
# 执行迁移(建表)
php artisan migrate
# 回滚迁移
php artisan migrate:rollback
迁移文件示例(创建文章表):
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->integer('user_id');
$table->timestamps();
});
}
# 创建模型
php artisan make:model Post
模型配置(app/Models/Post.php):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
// 允许批量赋值字段
protected $fillable = ['title', 'content', 'user_id'];
/**
* 关联用户(一对多)
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
分页查询:Post::paginate(10),自动返回分页元数据
条件筛选:Post::where('user_id', $userId)->get()
关联查询:Post::with('user')->paginate(10)(预加载避免N+1问题)
新增数据:Post::create($validatedData)
更新数据:Post::findOrFail($id)->update($validatedData)
删除数据:Post::findOrFail($id)->delete()
Laravel官方推荐的轻量级API鉴权方案,无状态、适配纯后端接口:
# 安装Sanctum
composer require laravel/sanctum
# 发布配置文件
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
# 执行迁移(生成令牌表)
php artisan migrate
在 app/Models/User.php 引入令牌 trait:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required|string',
]);
if (!Auth::attempt($credentials)) {
return $this->fail('账号或密码错误', 401);
}
$user = Auth::user();
// 签发令牌
$token = $user->createToken('api_token')->plainTextToken;
return $this->success('登录成功', [
'token' => $token,
'user' => $user
]);
}
在路由中添加 auth:sanctum 中间件,保护接口:
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user/profile', [AuthController::class, 'profile']);
Route::apiResource('posts', PostController::class);
});
前端请求时,在请求头携带令牌:Authorization: Bearer 令牌值
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return $this->success('退出登录成功');
}
纯后端API需解决跨域问题,Laravel 自带跨域中间件,修改配置文件 config/cors.php:
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'], // 生产环境替换为具体域名
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
修改后重启服务,跨域中间件自动生效。
使用API资源类统一数据返回格式,避免字段冗余:
# 创建资源类
php artisan make:resource PostResource
资源类编写(app/Http/Resources/PostResource.php):
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => $this->content,
'author' => $this->user->username ?? '',
'create_time' => $this->created_at->toDateTimeString(),
];
}
控制器调用:return $this->success('获取成功', PostResource::collection($posts));
使用Laravel内置限流中间件,限制接口请求频率:
// 限制1分钟内最多60次请求
Route::middleware('throttle:60,1')->group(function () {
Route::post('/login', [AuthController::class, 'login']);
});
迁移文件添加软删除字段,模型引入软删除trait,避免物理删除数据:
// 迁移文件
$table->softDeletes();
// 模型
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
}
php artisan serve,默认地址 http://127.0.0.1:8000php artisan route:list,查看所有API路由详情storage/logs/laravel.log,排查后端异常
2010年,广州电车公司联合宇通客车,推出首款 “典雅之星 3” 电车,车型编号 ZK6120EGQAA。凭借极具辨识度的前脸造型与锐利大灯,这款车一经亮相便备受关注,更被巴士爱好者亲切称作 “电鲨”。广州先后引进五代电鲨车型,而图中这三台便是广州最后一代电鲨,型号为 ZK5120A1。相较上一代,该车改用非全封闭车窗设计,成为一代经典的换代标志。
2019年1月29日,岩手県交通株式会社宣布将在2月1日上午10:00举行比亚迪K9纯电动巴士的发车仪式,这是该地区首台纯电动巴士。将投放于全长3.2公里的永旺购物中心南线,从盛岡站东口至永旺购物中心南每天运营9个来回。新车长12米,宽2.5米,高3.4米,可载客56人,几乎与现有的大型燃油客车相同;续航里程达250km,单次充电需5小时,夜间可前往矢巾町的充电站进行充电。
无意间在 B 站刷到,广州 13 路公交即将停运。作为一名公交迷,心里瞬间涌上许多感慨。这条线路从我孩童时代就陪伴左右,藏着太多散碎的童年记忆。
我印象里,它是广州第一条无人售票线路。小时候坐 13 路,紧紧攥着几毛钱硬币,小心翼翼投进投币箱,听见 “咔嚓” 一声清脆落币,只觉得无比新奇。那时的 13 路,还是经典的广州造 GZK6100,后置引擎、蓝白涂装,在满大街还是通道车的年代,显得格外 “高大上”。
得知它即将停运的消息,下班后我特意和太太来到文德路,再完整坐一遍 13 路的全程。从文德路总站出发,驶过海珠桥,途经工业大道、江南大道…… 每一个站名、每一段路,都熟悉得刻在心里。
熟悉的街景缓缓掠过耳边,是亲切的粤语报站;身旁是匆匆归家的上班族、带着孩子的家长,还有不少和我一样专程来告别的公交迷。大家举着手机、相机,安静地记录着这条线路最后的时光,没有太多言语,却都藏着同一份不舍。
特此纪念一趟车,一座城,一段回不去的时光。