Thinkphp5博客系统实战:深度解析与应用

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Thinkphp5博客系统是一个个人免费开源项目,提供学习和实践Thinkphp5框架的平台,同时为开发者构建博客应用提供基础模板。本项目深入解析了Thinkphp5框架,包括其MVC架构、路由系统、数据库操作、模板引擎与视图层设计、安全防护机制、扩展性与社区资源,以及如何通过项目实践提升开发技能。 thinkphp5:个人免费开源项目Thinkphp5博客系统

1. Thinkphp5框架概述

ThinkPHP5作为一款优秀的PHP开发框架,它的出现标志着PHP开发向更简洁、更高效的方向迈进了一大步。它的起源可以追溯到2014年,由徐乐乐主导开发,作为ThinkPHP框架系列的新一代产品,ThinkPHP5迅速在开源社区中确立了自己的地位。这一章节,我们将探讨ThinkPHP5的主要特点和优势,以及它如何在现代Web应用开发中脱颖而出。

1.1 ThinkPHP5的起源与发展

ThinkPHP5是ThinkPHP框架的第五个主版本,它继承了ThinkPHP系列框架的优秀传统,同时吸取了国内外多种流行PHP框架的设计理念,比如Silex、Laravel等。这一代框架的开发目标是提供一个简单、快速、安全、具有丰富扩展性的企业级开发框架,适用于构建各种复杂的Web应用。

1.2 ThinkPHP5的特点

特点之一是遵循 PSR-2 编码规范,保证了代码的标准化和一致性。ThinkPHP5采用了全新的架构设计,大大提升了框架的性能,特别是在路由、中间件、异常处理等方面的优化。此外,ThinkPHP5提供了强大的命令行工具,可以快速生成项目骨架和模块,极大地提高了开发效率。

1.3 ThinkPHP5的优势和影响

ThinkPHP5在开源社区中的地位得益于其核心优势——轻量级、高性能和高度的可扩展性。它不仅能够满足快速开发的需求,而且其安全机制和异常处理机制也很完善。通过ThinkPHP5,开发者能够更加聚焦于业务逻辑的实现,而不必过于担心底层技术细节。随着社区的不断发展和壮大,ThinkPHP5已经成为国内PHP开发者的首选框架之一,并在全球范围内产生了重要的影响。

2. MVC架构解析与实战

2.1 MVC架构的基本概念

2.1.1 MVC的定义和作用

MVC(Model-View-Controller)架构是一种软件设计模式,旨在分离应用程序的内部表示(数据模型)、用户界面(视图)以及这两者之间的交互(控制器)。这种分离实现了关注点分离原则,每个部分被设计为独立的模块,能够更高效地进行开发和维护。

  • Model(模型):代表了应用程序的数据结构,通常包含数据和业务逻辑。
  • View(视图):是用户界面的组成部分,负责展示数据给用户。
  • Controller(控制器):处理用户输入,调用模型,并选择视图来显示。

2.1.2 MVC与Web开发的关系

在Web开发中,MVC架构使得开发人员能够专注于业务逻辑(模型),设计师能够专注于界面设计(视图),而控制器则负责在两者之间协调。这样的分离不仅提高了代码的可读性,还增强了可维护性。MVC模式对Web开发有以下几个关键影响:

  • 降低耦合度:各个组件之间相互独立,便于管理和修改。
  • 更好的分工合作:不同技能的开发人员可以同时工作,互不干扰。
  • 提高可扩展性和重用性:因为各个部分是独立的,所以可以在不影响整个系统的情况下添加新的功能或改进现有功能。

2.2 MVC架构在Thinkphp5中的实现

2.2.1 Thinkphp5的MVC框架结构

在Thinkphp5中,框架的结构遵循MVC模式,提供了一套完整的机制来处理用户的请求,以及反馈响应给用户。框架中的核心组件包括:

  • 模型(Model):对应数据库中的表,可以通过模型类来操作数据。
  • 视图(View):包含HTML代码和一些PHP脚本,用于展示数据。
  • 控制器(Controller):负责接收用户请求,处理请求,并返回响应。

2.2.2 控制器、模型、视图的创建与配置

在Thinkphp5中创建MVC组件的基本步骤如下:

创建控制器

创建一个控制器类,通常位于 application/controller 目录下。例如,创建一个名为 Index 的控制器:

<?php
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    // 默认操作方法
    public function index()
    {
        // 输出内容
        return 'Hello, ThinkPHP5!';
    }
}
创建模型

创建一个模型类,位于 application/common/model 目录下。例如,创建一个名为 User 的模型:

<?php
namespace app\common\model;

use think\Model;

class User extends Model
{
    // 模型定义
}
创建视图

创建一个视图文件,位于 application/index/view/index 目录下。例如,创建 index.html 文件:

<!DOCTYPE html>
<html>
<head>
    <title>Hello World</title>
</head>
<body>
    <h1>{$hello}</h1>
</body>
</html>

在控制器中,可以使用 assign 方法分配变量到视图:

public function index()
{
    $this->assign('hello', 'Hello, ThinkPHP5!');
    return $this->fetch();
}

2.3 MVC架构实战应用

2.3.1 开发流程和操作示例

开发流程可以概括为以下步骤:

  1. 需求分析 :明确应用程序需要完成的功能。
  2. 设计架构 :根据需求设计合适的MVC结构。
  3. 实现模型 :创建代表数据结构的模型类。
  4. 创建控制器 :处理用户请求和响应。
  5. 编写视图 :设计用户界面,展示数据。
  6. 集成测试 :将各部分组合进行测试。

操作示例:

假设我们有一个博客系统,需要创建一个文章列表展示的功能。我们可以按照以下步骤进行:

  1. 创建 Article 模型,映射到数据库中的 article 表。
  2. 创建 ArticleController 控制器类,在其中实现读取文章列表的逻辑。
  3. 创建 article_list.html 视图文件,在其中展示文章列表。
实现步骤

控制器:

<?php
namespace app\index\controller;

use app\common\model\Article;
use think\Controller;

class ArticleController extends Controller
{
    // 文章列表操作
    public function index()
    {
        $articles = Article::order('create_time', 'desc')->paginate(15);
        $this->assign('articles', $articles);
        return $this->fetch();
    }
}

视图:

<!DOCTYPE html>
<html>
<head>
    <title>Article List</title>
</head>
<body>
    <ul>
        {volist name="articles" id="article"}
            <li>{$article.title}</li>
        {/volist}
    </ul>
    <div> {$articles->render()} </div>
</body>
</html>

2.3.2 常见问题的解决和调试技巧

在实际开发过程中,遇到的问题可能包括数据不一致、视图渲染错误、路由配置问题等。这里介绍一些基本的调试技巧:

  1. 查看错误日志 :Thinkphp5提供了详细的错误日志记录,可以帮助开发者快速定位问题。
  2. 使用调试模式 :在开发环境中开启调试模式,能够显示更详细的错误信息,帮助调试。
  3. 检查路由配置 :确保定义的路由能够正确匹配到对应的控制器和操作。
  4. 测试不同的浏览器和设备 :确保应用在不同的环境和设备上能够正常运行。

通过这些方法,开发者可以有效地解决开发过程中遇到的问题,并保证应用的稳定性和可用性。

3. 路由系统的设计与应用

在Web开发中,路由系统扮演着极其重要的角色。它负责将用户的请求映射到相应的控制器和方法上。Thinkphp5框架以其灵活而强大的路由系统,使得开发者能够轻松管理URL结构,并创建易于维护的应用程序。本章将深入探讨Thinkphp5中的路由系统设计,包括其基础概念、配置方法以及高级功能与实践。

3.1 路由系统的概念与功能

3.1.1 路由的作用和重要性

路由(Routing)可以被视为一个应用程序中用于“指挥交通”的指挥官。它根据用户请求的URL,将请求转发到对应的处理函数或方法。Thinkphp5中的路由系统不仅定义了URL的结构,还允许开发者通过路由配置来控制如何处理这些请求。一个良好的路由系统设计对于网站的可维护性、可扩展性、以及搜索引擎优化(SEO)至关重要。

3.1.2 Thinkphp5中路由的配置方法

在Thinkphp5中,路由的配置通常在 route.php 文件或模块的 route.php 文件中进行。Thinkphp5提供了一个非常灵活的路由定义方式,支持闭包、控制器类和操作方法,以及自动路由等。以下是一个基本的路由配置示例:

// route.php
use think\Route;

// 定义基础路由
Route::get('home', 'index/index');

// 定义一个带有参数的路由
Route::get('user/:id', 'user/read');

// 定义默认路由
Route::get('/', 'index/index');
Route::post('/', 'index/index');

这个配置定义了三个路由规则。第一个是一个基础路由,用户访问 home 时会被重定向到 index 控制器的 index 方法。第二个路由带有参数,访问 user/1 时, id 参数值为 1 ,会被传送到 user 控制器的 read 方法。第三个路由是默认路由,处理所有未被前两个规则匹配的GET和POST请求。

3.2 路由的高级功能与实践

3.2.1 URL重写与自定义路由规则

Thinkphp5支持URL重写,这意味着开发者可以隐藏URL中的真实入口文件名(如 index.php ),使URL更简洁。要开启URL重写功能,需要在服务器配置中设置,通常是 .htaccess 文件(Apache服务器)或 nginx.conf 文件(Nginx服务器)中进行配置。

自定义路由规则允许开发者根据需要定义复杂的路由模式。例如,可以为用户创建更灵活的URL结构:

// 自定义用户详情路由规则
Route::get('user/:id', 'user/read');
Route::get('user/:id/view', 'user/view');
Route::get('user/:id/edit', 'user/edit');

这里定义了三个路由规则,分别对应用户的简要视图、详细视图和编辑页面。

3.2.2 路由与控制器、模型的交互实践

Thinkphp5的路由系统不仅负责URL的解析,还可以与控制器和模型紧密集成。开发者可以通过路由直接指定使用的控制器和方法,甚至可以绑定模型实例到控制器方法上。

// 绑定模型实例到控制器方法
Route::get('article/:id', 'article/read');

在这个例子中,当用户访问 article/1 时, article 控制器的 read 方法会自动接收一个文章模型的实例。这种设计减少了在控制器方法中实例化模型的代码,使代码更加简洁和易读。

// 控制器方法示例
class article
{
    public function read($id)
    {
        // 这里可以直接使用 $id 做进一步的操作
        // 同时模型实例可以通过依赖注入的方式直接可用
    }
}

在Thinkphp5中,依赖注入是在容器中自动处理的,这为开发者提供了一个干净的、依赖注入的控制器方法,同时避免了样板代码。

3.3 路由系统的设计与应用

3.3.1 配置文件的组织结构

在Thinkphp5项目中,路由配置文件通常位于 application/route.php 或者 application/{module}/route.php 。这允许开发者为不同的模块设置特定的路由规则。以下是一个组织结构示例:

application/
    route.php          // 全局路由配置文件
    common/
    index/
        route.php       // 模块化路由配置文件,例如首页模块
    blog/
        route.php      // 模块化路由配置文件,例如博客模块

这种组织方式有利于项目的模块化管理,以及在大型项目中保持路由配置的清晰和有序。

3.3.2 路由模式的测试与验证

在实际开发过程中,路由模式可能需要进行测试和验证以确保正确配置。Thinkphp5提供了一个简单的路由测试工具,允许开发者模拟请求并查看路由匹配结果。例如,使用CLI命令行工具:

php think route:dispatch -r

执行以上命令,将启动路由测试模式。在测试模式中,开发者可以输入不同的URL,观察哪些路由规则匹配,以及匹配到的控制器和方法。

3.4 案例研究与技巧

3.4.1 特定需求下的路由配置案例

根据特定需求进行路由配置时,开发者可能会用到通配符和正则表达式。例如,如果希望匹配所有文章的URL,可以使用通配符:

// 匹配所有文章URL
Route::get('articles/:id', 'article/read');

如果需要匹配一系列特定格式的URL,可以使用正则表达式进行路由定义:

// 匹配特定格式的日期
Route::get('news/:year(\d+)/:month(\d+)/:day(\d+)', 'news/read');

这种配置允许开发者处理类似 news/2023/04/15 这样的URL请求,并将其映射到 news 控制器的 read 方法上。

3.4.2 路由优化与调试技巧

路由配置完成后,性能优化是不可忽视的环节。Thinkphp5允许开发者对路由规则进行缓存,这样可以加快路由的解析速度,特别是在拥有大量路由规则的应用中。使用以下命令进行路由缓存:

php think route:cache

此外,Thinkphp5的路由调试工具也可以帮助开发者在开发过程中诊断问题。开发者可以查看被匹配的路由规则以及相关的匹配参数,这些调试信息对于确保路由按预期工作非常有用。

通过以上章节,我们深入探讨了Thinkphp5的路由系统设计与应用,从基本概念到高级功能实践,以及路由的优化与调试技巧。掌握这些知识,开发者可以更有效地利用Thinkphp5的路由系统,创建出结构良好、易于维护的应用程序。

4. 数据库操作与ORM使用

4.1 ORM的基本概念和优势

4.1.1 ORM的定义和工作原理

对象关系映射(Object-Relational Mapping,简称ORM)是一种编程技术,用于在不同的系统之间转换数据。在Web开发中,它主要用于将面向对象的程序设计语言中的对象与关系数据库中的表相互映射。通过ORM,开发者可以直接操作对象,ORM框架则负责将这些对象映射到数据库中的数据。

ORM的优势在于抽象化了数据库访问,使得开发者无需编写复杂的SQL语句,就能完成数据的增删改查操作。它大大提高了开发效率,同时增强了代码的可维护性和可读性。

4.1.2 ORM与传统数据库操作的对比

传统的数据库操作通常涉及到大量的SQL语句编写,这不仅使得代码冗长,而且难以维护。任何对数据库结构的改动都可能导致大量的代码需要修改。相比之下,ORM的优势非常明显:

  • 更高的抽象层次 :开发者只需要操作对象和属性,无需直接编写SQL代码。
  • 避免SQL注入风险 :ORM框架自动生成的查询语句,通常带有参数绑定,减少了SQL注入的风险。
  • 数据库无关性 :通过修改配置,可以更换后端数据库而不影响业务逻辑代码,提高项目的灵活性和可移植性。

4.2 ORM在Thinkphp5中的具体应用

4.2.1 数据库的连接和配置

在Thinkphp5中,数据库配置通常位于 application/common/config/database.php 文件中。开发者需要根据实际使用的数据库类型(如MySQL、PostgreSQL等),配置相应的连接信息,例如数据库地址、用户名、密码以及数据库名等。

return [
    // 默认使用的数据库
    'default'   => 'mysql',
    // 默认的数据库类型
    'type'      => 'mysql',
    // 数据库服务器端口号
    'hostname'  => '***.*.*.*',
    // 数据库用户名
    'username'  => 'root',
    // 数据库密码
    'password'  => '',
    // 数据库名
    'database'  => 'test',
];

在Thinkphp5中,一旦配置完毕,就可以使用 Db 类来进行数据库操作。例如,查询操作可以使用如下代码:

$data = Db::name('user')->where('id', 1)->find();

4.2.2 模型的定义、查询与操作

在Thinkphp5中,模型是ORM的基础,用于表示数据库表的单一数据类型。模型通过定义类并继承 Think\Model 来创建,模型名通常与对应的表名保持一致,但首字母大写。

use think\Model;

class User extends Model
{
    // 指定与数据库表对应
    protected $table = 'user';
}

使用模型进行数据查询和操作时,Thinkphp5提供了非常丰富的API。以下是一些常见操作的示例:

  • 创建新记录
$user = new User;
$user->name = 'John Doe';
$user->save();
  • 条件查询
$user = User::get(1); // 根据主键查询
$users = User::where('age', '>', 30)->select(); // 条件查询多个结果
  • 数据更新
$user = User::get(1);
$user->name = 'Jane Doe';
$user->save();
  • 数据删除
$user = User::get(1);
$user->delete();

4.3 ORM实战技巧和性能优化

4.3.1 复杂查询的处理方法

随着业务逻辑的复杂化,有时需要构建复杂的查询,这时候ORM提供的链式操作可以大大简化查询构建过程。例如,进行多条件查询、分组、排序等操作:

$user = User::where('status', 1)
           ->where('age', '>', 25)
           ->field('id, name, age')
           ->limit(10)
           ->order('age desc')
           ->select();

对于一些非常复杂的查询,比如涉及到多个表的联合查询,我们可能需要使用原生SQL语句。这时可以通过ORM提供的 Db::query() 方法来执行:

$sql = 'SELECT * FROM `user` LEFT JOIN `profile` ON `user`.`id` = `profile`.`user_id`';
$data = Db::query($sql);

4.3.2 数据库操作的性能优化技巧

数据库操作的性能优化是一个重要的话题,下面是一些常见的优化技巧:

  • 索引优化 :合理使用数据库索引,可以显著提高查询效率。对于经常用于搜索的列,应考虑添加索引。
  • 查询优化 :避免全表扫描,减少不必要的数据加载。例如使用 field() 方法来限制查询的字段,使用 limit() 来减少返回的数据量。
  • 懒加载 :在ORM中使用懒加载(延迟加载)可以减少不必要的数据加载,直到确实需要这些数据。
  • 事务控制 :合理使用数据库事务,可以减少不必要的锁竞争,提高并发性能。
Db::startTrans();
try {
    // 执行相关数据库操作
    Db::commit();
} catch (\Exception $e) {
    Db::rollback();
}

通过这些方法,开发者可以利用Thinkphp5的ORM框架进行高效和优雅的数据操作。这些操作不仅提高了代码的可维护性,也为数据库操作的性能优化提供了便利。在实际开发过程中,合理地运用ORM提供的工具和技巧,可以在保证开发效率的同时,兼顾代码的执行效率和性能表现。

5. 模板引擎与视图层设计

Thinkphp5的视图层设计是基于模板引擎的,模板引擎的作用是分离逻辑代码和展示代码,使得开发者能够更加专注于业务逻辑的开发,而设计师则可以专注于页面的布局和样式设计。模板引擎为视图层提供了强大的功能,包括模板继承、模板变量替换、模板函数等,这些功能极大地提高了开发效率并降低了维护成本。

5.1 模板引擎的作用和原理

5.1.1 模板引擎的概念与价值

模板引擎是用于将数据渲染到模板文件中的工具,它使得前端展示与后端逻辑分离,从而简化了代码结构并增强了可维护性。Thinkphp5内置了ThinkTemplate模板引擎,该模板引擎支持模板继承,标签库等特性,它能够帮助开发人员快速构建出复杂的视图层。

5.1.2 Thinkphp5中模板引擎的特性

Thinkphp5的模板引擎支持变量输出、控制结构(if-else、for等)、函数调用等。它还支持模板标签,如{volist}用于遍历数组或集合,{insert}用于模板片段的插入,这些都为模板设计提供了灵活的扩展性。

5.2 模板引擎在视图层的应用

5.2.1 视图层的构建与模板的使用

在Thinkphp5中,视图文件通常位于项目根目录下的 application/view 目录下。模板文件以 .html 为后缀,使用ThinkTemplate模板引擎的语法。通过控制器中的 assign 方法,我们可以将数据传递给视图文件,然后由视图文件解析并展示数据。

5.2.2 模板继承和布局的应用

模板继承是ThinkTemplate模板引擎的一个重要特性,它允许开发者创建一个基础布局模板(通常命名为 layout.html ),然后在其他模板文件中继承这个布局。在子模板中,可以使用 {block}...{/block} 标签来定义可以被替换的区域。例如:

<!-- 布局模板 layout.html -->
<html>
<head>
    <title>{volist name="blocks" id="block"}{$block.title|default='Default Title'}{/volist}</title>
</head>
<body>
    {volist name="blocks" id="block"}{$block.content}{/volist}
</body>
</html>

在子模板中,我们可以这样使用:

<!-- 子模板 index.html -->
{extends file="layout.html"}
{block name='content'}
    <h1>Welcome to ThinkPHP5</h1>
{/block}

5.3 视图层实战技巧和安全防范

5.3.1 视图层常见问题的解决方法

在模板层面上,开发者可能遇到的问题包括数据输出的安全问题、模板标签使用错误等。对于输出数据的安全问题,可以使用 strip_tags 等函数来过滤输出内容。对于模板标签错误,需要仔细检查模板语法,确保标签闭合正确。

5.3.2 模板安全防范与数据展示

模板安全防范是非常重要的。例如,避免直接输出未经处理的用户输入数据,防止XSS攻击。可以通过模板引擎提供的过滤函数来处理输出数据:

// 控制器中设置
$data['username'] = $this->security->strip_tags($user->username);

使用 {strip}{/strip} 标签来阻止标签的解析,从而防止潜在的XSS攻击:

<!-- 模板文件 -->
{strip} {$username} {/strip}

总结起来,模板引擎是Thinkphp5中构建视图层不可或缺的部分,它提供了一套完整的视图层构建方案,包含了从基础布局到模板继承和模板安全的方方面面。通过本章节的介绍,读者应该能够掌握如何在Thinkphp5项目中高效地使用模板引擎,构建安全可靠的视图层。

6. 安全防护机制的实施

6.1 安全防护的基本原则和策略

网站安全是一个复杂的问题,涉及到多个层面的防御机制。其中,Thinkphp5作为一个成熟的PHP框架,提供了一系列的安全特性来帮助开发者构建安全的应用程序。

6.1.1 网站安全的重要性

在互联网时代,网站面临着各种各样的安全威胁。不论是由于技术缺陷导致的系统漏洞,还是人为的恶意攻击,都可能导致数据泄露、服务中断甚至更严重的后果。因此,实施安全防护机制对于保障网站的稳定运行至关重要。

6.1.2 Thinkphp5中的安全特性概览

Thinkphp5框架内置了许多安全相关的特性,例如:

  • CSRF(跨站请求伪造)保护
  • XSS(跨站脚本攻击)过滤
  • SQL注入防护
  • 内容安全策略(CSP)

这些安全特性可以在一定程度上减少常见的安全威胁。

6.2 安全防护的详细实施方法

为了在实际项目中实现上述安全防护措施,开发者需要进行一系列操作。

6.2.1 输入输出过滤与XSS防护

XSS攻击指的是恶意用户在网页上插入恶意脚本代码,当其他用户浏览这个页面时,嵌入的脚本被执行,从而引发安全问题。Thinkphp5提供了多种方法来过滤输出内容,避免XSS攻击。

// 使用内置的Html类进行输出过滤
use think\facade\Html;

$htmlContent = '<script>alert("XSS");</script>';
$safeHtml = Html::stripTags($htmlContent); // 输出安全的HTML内容

此外,开发者还可以自定义过滤规则来增加防护的力度。

6.2.2 SQL注入防护与数据验证

SQL注入是一种常见的攻击方式,攻击者通过构造恶意的SQL语句来破坏后端数据库。Thinkphp5的ORM系统在设计时就考虑了SQL注入防护,但开发者仍需遵守一些最佳实践来确保安全。

// 使用模型查询数据时确保使用安全的参数绑定
$user = Model\User::find(1); // 安全的查询方式
$user = Model\User::get(['id' => $_GET['id']]); // 非安全的查询方式,易受到SQL注入攻击

确保数据验证的逻辑正确执行,也是防止SQL注入的一个重要手段。

6.3 安全防护的高级应用和维护

随着应用的发展,安全防护机制也需要不断地进行优化和更新。

6.3.1 防止CSRF攻击的策略

CSRF攻击利用用户的身份在不被察觉的情况下执行操作。为了防御CSRF攻击,Thinkphp5框架中可以使用其内置的Token验证机制。

// 生成一个Token
$token = think.token();
// 将Token输出到表单中
<input type="hidden" name="token" value="<?php echo $token; ?>">

// 在提交表单时验证Token
if (!Input::post('token') || think.token() !== Input::post('token')) {
    // Token验证失败处理
}

6.3.2 安全机制的持续更新和管理

安全防护机制需要定期更新和维护,以应对新出现的威胁。开发者需要定期检查Thinkphp5框架的安全更新,并应用最新的补丁。

// 使用Composer更新Thinkphp5框架
composer update topthink/framework --with-dependencies

同时,还需监控应用的运行状态,及时处理安全警告和异常报告。

通过实施上述安全防护措施,可以在一定程度上保护网站不受攻击,并在面对安全威胁时能够及时做出反应。然而,安全防护是一项持续的工作,需要不断地学习、更新知识,以及进行安全审查和代码审计。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Thinkphp5博客系统是一个个人免费开源项目,提供学习和实践Thinkphp5框架的平台,同时为开发者构建博客应用提供基础模板。本项目深入解析了Thinkphp5框架,包括其MVC架构、路由系统、数据库操作、模板引擎与视图层设计、安全防护机制、扩展性与社区资源,以及如何通过项目实践提升开发技能。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值