# 第一章 关于教程

# 教程简介

本教程通过实战开源前后端分离CMS——Lin CMS全家桶(lin-cms-vue & lin-cms-tp5)为一个前端应用实现内容管理系统。一套教程入门上手vue、ThinkPHP两大框架,自用、工作、私单一次打通。

# 线上Demo

由于时间关系,线上demo效果与教程内容保持同步更新,非最终实现效果,仅供预览

点击访问

账号:super 密码:123456

# 教程特点

  • 从0到1,涵盖开发环境搭建、项目分析、代码编写、线上部署完整教学
  • 前后端分离的应用,紧跟潮流,前端VUE、后端ThinkPHP的主流技术栈。
  • 充分利用框架特点实现各种实用、常用功能
  • 结合自己爬坑的经历,力求每个知识点、功能点都解释清楚
  • 相关技术和知识点、源码均可应用于企业项目中

通过本课程可以掌握包括但不限于以下几点:

  • 掌握前后端分离的概念及运用、部署
  • 掌握前端Vue应用开发
  • 掌握后端ThinkPHP框架
  • 掌握RESTFul接口开发
  • PHP高级特性的运用
  • debug思路和技巧

# 前后端分离介绍

前后端分离是这几年在业界中经常看到的字眼,到如今已经俨然成为一种发展趋势甚至业界标准,很多人可能就是跟着用,但是不知道到底什么是前后端分离、为什么要分离以及分离后的优缺点是什么。下面就请读者跟随者作者脚步,由浅及深地来一一剖析下这些问题。

名词解释

  • 前端:指一个具体的应用,直接被用户操作的,如一个PC网页、移动端H5、移动端App以及现在很火的小程序

  • 后端:也叫服务端,由一堆你编写的代码组成,负责处理前端的业务请求并响应。

  • MVC:M(model模型层)V(view视图层)C(controller控制层),一种软件架构模式。

首先我们把两种不同的应用架构做一个简单的对比:

  • 前后端不分离,指前端的页面和数据都由后端通过某种技术手段渲染而成;后端是一个典型的MVC架构

  • 前后端分离,指前端页面由前端自己生成,后端只负责提供接口,前端通过请求后端接口获取数据;后端有M和C层,但是没有V层。

由上面的对比可知,两种架构的关键区别在于后端有没有V层。在前后端分离的架构中,后端没有V层的实现,而是由前端自己去实现。说到这里就不得不先解释一下MVC这三层的具体含义,知道了含义我们才能理解为什么V层实现方式改变会带来的一系列连锁反应。

  • C(Controller控制层)

控制层是具体业务实现的入口,每一个对接口发起的请求都会先被转发到这里,控制层下代码的内容一般是对某个业务实现做流程控制而不是具体的实现。怎么理解这句话呢,比如说你在网页点击了登陆,这个请求会先到控制层,控制层接收到请求后,会做一些判断,进而决定接下来做些什么,是继续调用真正的登陆实现逻辑还是直接拒绝,控制器层的任务就是接收、分配任务、返回结果,就像一个调度员角色一样,但它不具体负责处理业务实现。

  • M(Model模型层)

模型层的主要任务是数据封装和数据持久化,这两者即可以合在一起讲,又可以拆出来说,理解起来会相对比较抽象,而且不同的语言和框架对模型层的运用和理解也略有差异,但总的来说都离不开这两点,这里我先举一个小的事例来解释说明,后面结合实战章节内容可以有个更直观的体会。

我们无论做什么开发都好,都免不了要操作数据库,操作数据库就面临了两个问题,一个是SQL语句以及数据格式问题,以ThinkPHP(以下简称:TP)为例,TP框架内置了一个Model模型类,一个类继承了Model类后就被定义为一个模型类,一个模型类对应一张数据库里的表。继承之后我们就可以通过实例化这个类来达到对表的增删改查,而整个过程你完全看不到任何SQL语句。

    // 假设有一个User类,继承了Model,这时候他对应了数据中的user表。
    $user = new User(); #实例化模型类
    // 查询user表的所有数据,并输出一个数组,格式是框架已经封装好的
    $userList = $user->select(); #调用模型的方法select()
1
2
3
4

通过以上的代码片段可以看到,虽然我们在操作数据库里的一张表,但看起来更像是在操作一个对象,是的,Model类把跟数据库有关东西都封装成一个类供你去实例化调用里面的方法并以一个统一的格式返回,这就是模型层的具体运用体现。模型的存在,让你的数据能够变得可复用、可扩展,因为这时候他已经是一个类了,这种形式的数据库操作有个专门的名词——ORM(对象关系映射),我们来看看百度百科是怎么说的:

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。

前面一句看不懂没关系,最重要是第二句,如果一开始我们直接通过查看百度百科的解释,你会两句都看不懂,但有了前面的小示例,这最关键的第二句相信读者已经明白得差不多了,还不明白?不要紧,在后面的实战章节中,你会不停地与ORM打交道。

这时候读者可能会有另外一个疑问,模型层一定是和数据库相关吗?不是的。不继承Model类也可以是个模型类,比如说作者是做票务系统的,我有一个查询航班的接口,这个接口会返回不同公司的票务信息,每家公司的票务信息字段都不同,于是我就定义一个模型类,这个类里面会分别获取不同公司的票务信息,并把每家公司的票务信息封装成一个格式统一且排好序的数组,这样我在某个业务逻辑需要航班数据的时候,只需要调用这个模型类的某个查询方法,比如定义一个getFlight(),就会返回一个包含每个不同公司的航班列表数组,而且由于格式统一,在调用的时候就可以避免因为不同公司票务字段差异导致的额外代码量。同时由于封装了模型类,我可以还通过控制传入的参数,实现指定要获取的公司航班等等,这也是模型层的具体运用体现。

模型层在现实业务中运用非常广泛,体现的形式也多种多样,甚至有些也不叫模型层,或者是再细分为其它层。但无论是以什么形式体现,最终实现的目的都是从数据到模型。

  • V(View视图层)

视图层通常由一个个模板文件组件组成,用于被一个叫模板引擎的东西渲染成具体的页面。以TP框架为例,我们在项目根目录新建了一个view目录,用于存放视图层的文件即模板文件。假设我们在该目录下面新建了一个名为edit的模板,这时候就可以在控制器层调用

<?php
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    public function index()
    {
        return $this->fetch('edit');# 调用了框架内置的fetch()方法用于渲染模板文件,这里看不懂没关系,模板渲染并不是本教程的重点
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13

这样当我们访问这个控制器的时候,就会得到一个已经渲染好的html页面,内容都是在edit模板文件中定义好的。

目前主流的项目基本都采用前端后分离架构,所以本教程不会涉及view层相关的教学,这里读者只需要知道有这么一个东西即可。(面试有时候会考)

通过上面对MVC的介绍,我们已经大概了解了MVC的作用以及含义,那么回到前面的问题,将后端原有的View层分离出去,实现前后端分离架构的意义在哪里:

  • 分而治之

这也许是最直观的作用,以前可能并没有明确的前端开发者、后端开发者这种概念,都是基于MVC一把梭全干。但其实每个开发者内心还是有倾向性的,有些人就是喜欢写PHP、Java、Python,但不喜欢写html、js、css(比如我),有些人则相反。前后端分离架构的出现,让开发者有了选择的可能,专注做前端的事,或者专注做后端的事,也有看钱份上前、后端全包的,但是依然不会选择传统MVC架构。

  • 并行开发

前后端分离之后,前端只负责调用接口获取数据、实现页面和交互,后端只负责提供接口和接口的内在实现。正因为基于这种开发模式,前端开发者和后端开发者就可以根据各自的任务内容去实现自己那一部分的代码,期间互不影响,最后通过已经约定并实现好的接口进行联调测试,最后发布上线。

前后端不分离也可以并行开发,但是效率太低。因为模板文件里面会同时掺杂html代码、js代码和PHP代码,比如说负责写模板文件的开发者在处理模板内的PHP代码的时候,就必须知道一些PHP变量的含义以及规则等,核心业务逻辑的改变可能也会导致模板内容要跟着改变。前后端分离架构下,只要接口不变则不存在这个问题。

  • 接口复用

前后端分离之后,后端跟前端的交互必须是以接口调用的形式,接口本身只是提供数据和接收数据,和语言与实现方式无关。这就意味着后端不用关心前端到底是什么应用(PC、小程序、安卓、iOS等),前端也不需要关心后端是什么语言、怎么实现的。只要有接口,各个前端应用获取到数据后根据自己的需要进行数据加工处理或者发送数据到接口,这样子就可以实现接口复用,后端不必为每一个不同类型的应用进行重复开发。就拿我个人手头一个项目来说,前端有12个小程序、PC端网站1个、移动端H5 1个,都是共用同一套接口。

接口,也叫API,一个很高大上的名词(特别是用三个字母说出来的时候)。在我还没接触编程的时候,每当听到技术人员左一句接口又一句接口顿时就感觉自己气势就输了。直到后来我接触了编程,也做了接口开发,也开始忽悠别人。其实就是一个普普通通的术语,和高大上没有什么必然关系。打个比方,你墙上的电源插口,对于电器来说就是一个接口,你插上去了就是实现电器与电网的对接,通过电源插口这个接口,而电器不需要知道电怎么产生的,电网也不需要知道是什么电器在用我的电。同理,对于软件系统来说,接口无处不在,类与类之间会存在接口,应用与应用之间也存在接口,接口本质上就是A给B提供统一、规范、明确数据的一种途径。

  • 定位问题

前后端分离之后,当BUG出现的时候定位问题显得可能容易,不用先纠结到底是业务逻辑出问题了还是模板文件里出现了,可以快速定位问题所处的位置并针对性的debug。

当然,没有什么是完美的,就如同MVC模式的出现虽然解决一些问题,但随着互联网的发展,又产生了另外一些问题,前后端分离架构也是如此。从工作的角度上来说,是否前后端分离需要客观考虑公司的实际情况,比如说在人员配置不足的情况下,你需要同时负责前后端的开发工作,这时候前后端分离等于你还需要掌握另一端的技术栈(主流的前端框架有React、Vue、Angular),你也还是要写完一端写另一端,如果这时候没有接口复用的需求那前后端分离无疑是增加了自己的学习成本和工作成本,但从学习、成长、长远职业规划角度上来说还是必须得掌握的,毕竟稍有规模的公司前后端分离已经是标配而且前端转行做后端,后端转行做前端也是很普遍的事。

以上就是关于前后分离的介绍,网上也有不少关于前后端分离的介绍和思考读者可自行查阅参考。

可能有些读者还是理解不了架构中分层的概念,通俗点来说,分层就是为了把整个项目代码按其作用划分成几个不同层,每个层只放相同作用的类文件,每一层只负责自己范围内的事,这样当你在写代码的时候,你就很容易找到想要找到的类,别人在读你的代码的时候,整体的可读性也会很高。打比方你去一些大型商场,商城每一层的定位都是不一样的,你想要吃饭就去餐饮那一层,想要逛衣服就去商品那一层。当然,要做几层,每一层该怎么定位是什么都是有其理论和实践支撑的,软件架构设计也是如此,所以在一些大型项目中,往往还分到6~7层,所以分了几层和层叫什么其实并不是关键,关键是了解分层这件事本身。

# RESTful

# 什么是RESTful API

一种基于Rest设计理论(一篇一般人看不懂的博士论文)的web接口设计风格,不是标准,只是提供了一组设计原则和约束条件。目前绝大多数的开放接口或者企业web应用接口都是RESTful风格,虽然各自有一些细微的差别,但都遵循了几点基本原则,比如说明确的HTTP请求方法(GET对应查询/POST对应提交/DELETE对应删除),简明的url地址(http://xxx.com/list),轻量、易于解析的数据格式(JSON)。假设现在我们要设计一个查询用户的接口,这个接口要基于RESTful风格,那么接口会是这样的:

# 请求数据
请求类型:GET
url:http://xxx.com/user
# 返回一段json数据
{
    uid:1,
    name:'哈哈哈'
}
1
2
3
4
5
6
7
8

这里定义了一个地址为http://xxx.com/user,请求方法为GET的接口,在熟悉RESTful接口开发的小伙伴看来,一眼就可以看出这是跟查询用户相关的接口;同样,当我们新增一个同样url的地址,但是请求方法改为POST,别人也一样可以一眼看出是关于新增一个用户的接口。

思考题:HTTP请求类型为DELETE,地址为http://xxx.com/user?id=1 代表什么?

看到这里读者可能觉得,RESTful风格的接口除了看起来比较简洁且清晰,对具体开发工作似乎没啥帮助。这里就要先说说一个在RESTful流行之前的传统接口协议SOAP,以往我们在试图调用一个SOAP接口的时候,需要后端语言能实现SOAP代理(比如一个网页应用,JavaScript是不支持直接调用SOAP接口的),后端语言通过SOAP代理才能访问某个SOAP接口,这里就需要写几行实现SOAP代理请求SOAP接口的代码(各个语言实现SOAP代理的方法不同,有的简单,有的复杂),调用成功后,我们还需要后端解析这个返回结果(xml格式),又是几行代码。这还没完,不同公司的SOAP接口,调用和解析的方式可能还会有一些差异,为了解决这个差异,我们又需要几行代码,很麻烦,是吧?那么RESTful风格的接口呢?除了后端,前端也可以直接调用,不需要额外的支持,因为RESTful风格的接口提倡以JSON数据格式交互,和语言无关,你仅需要发送一个HTTP请求,就会返回一个JSON格式的结果,你需要做的仅仅是把JSON解析成你需要的数据类型(各种后端语言都可以很轻松的实现解析成数组或者字典等,前端则可以直接读取JSON格式变成一个对象),这就意味着你可以封装一个类来集中处理所有基于RESTful风格的接口而不用考虑接口解析的差异性,基于这几个特点,就大大简化了你的代码量。

SOAP非本教程的重点,读者可以自行查阅资料。目前web接口开发基本都基于RESTful

# RESTful API最佳实践

在明白了什么是RESTful以及其优点之后,那么怎样才算是一个RESTful风格的接口呢?有如下几点需要注意:

  1. 接口返回的数据格式推荐使用JSON
    JSON属于有结构体的数据格式,本质上是字符串,轻量,有利于网络传输和解析。

  2. url中不要出现动词
    比如getXXXXXXupdateXXXXX的字眼。

  3. 请求动作以HTTP请求方法类型来表示
    这点和第二点有一定关联,比如要获取用户列表,以往我们可能会把url地址定义成http://xxx.com/getUser,更符合规范的做法是http://xxx.com/user,然后请求类型定义为GET,如果是修改用户,则请求类型定义为PATCH。常见HTTP请求方法类型及含义如下:

序号 类型 描述
1 GET 请求指定的页面信息,并返回实体主体。
2 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。
3 PUT 从客户端向服务器传送的数据取代指定的文档的内容。
4 PATCH 是对 PUT 方法的补充,用来对已知资源进行局部更新 。
5 DELETE 请求服务器删除指定的页面。

PUT(全部更新)PATCH(局部更新)都是对应修改操作,区别在于PATCH请求代表本次请求的要修改的只是一个完整数据中的一部分,比如说你在网站中修改个人头像,在数据库中,头像字段一般属于user表,表中除了头像字段还有很多其他的字段,你本次的请求只是修改其中一个字段,这时候应该定义为PATCH操作,即只提交包含了一个avatar字段的表单数据。如果说你规定每个用户修改头像都需要完整的提交所有字段信息(即便后端在处理过程中并不会用到),那么就应该定义为PUT。

  1. 明确的HTTP状态码

HTTP状态码用于让接口调用方可以清楚知道本次请求是否已经正常执行,如果请求失败也可以从状态码大致定位问题。

后端在返回接口数据时可以指定状态码,当不指定时默认情况下,请求成功为200,后端异常则为500或502,请求的地址或资源不存在为404

常见HTTP状态码如下:

状态码 状态码英文名称 描述
200 OK 请求成功。一般用于GET与POST请求
201 Created 已创建。成功请求并创建了新的资源
301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
400 Bad Request 客户端请求的语法错误,服务器无法理解
401 Unauthorized 请求要求用户的身份认证
402 Payment Required 保留,将来使用
403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
500 Internal Server Error 服务器内部错误,无法完成请求
  1. 明确的错误提示

一个接口调用的请求发送成功,但不一定是执行成功。比如说你在一个网站中,你明明不是VIP,但是却访问了一个只有VIP才能访问的接口,通常我们在后端会对一些特殊接口做权限校验,这时候校验不通过就要返回一个错误提示信息,错误提示信息一般需要包含状态码自定义错误码错误信息请求的url,状态码在前面已经介绍过,这里说说后面三个如果体现,当后端接收到前端的接口调用请求后请求内容有异常时,应给出一段类似如下内容的JSON数据:

// 此时HTTP的状态码应该为403
{
    "msg": "权限不足",
    "error_code": "20000", 
    "request_url": "GET vip/music"
}
1
2
3
4
5
6

msg字段用于描述本次错误的具体原因,error_code字段内容是一系列自定义的错误码,错误码应该是接口开发者定义且长期维护的一个清单,每个错误码对应一个msgrequest_url字段用于显示本次请求的HTTP请求方法及地址。

错误码的作用在于补充解释HTTP状态码,打比方,同样是403错误,可能会对应不同情况。

完整、规范的错误提示有利于接口调用方快速定位问题,同时对后端开发者在接口运维时也有很重要的意义。

正因为RESTful只是规范而不是标准,导致虽然同样是RESTful API但一些开发体验却不怎么一致,多数大厂的开放接口在可接受的范围内,倒是一些自己公司内部或者其他合作伙伴的接口却未必合乎规范,特别是在错误提示这一块,这里强烈建议读者从自身做起,惠己及人。

# LinCMS介绍

# 什么是Lin CMS

Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套内容管理系统框架。Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率。

也许你还是没看懂,因为你可能连CMS是什么都不知道,我们先来了解下CMS的专业解释是什么:

内容管理系统(英语:content management system,缩写为 CMS)是指在一个合作模式下,用于管理工作流程的一套制度。该系统可应用于手工操作中,也可以应用到计算机或网络里。作为一种中央储存器(central repository),内容管理系统可将相关内容集中储存并具有群组管理、版本控制等功能。版本控制是内容管理系统的一个主要优势。内容管理系统在物品或文案或数据的存储、掌管、修订(盘存)、语用充实、文档发布等方面有着广泛的应用。现在流行的开源CMS系统有WordPress、Joomla!、Drupal、Xoops、CmsTop等。

依然看不懂,接下来是我的翻译:

假设你现在有一个电商网站,网站有商品售卖功能,那么有几件事是你必须要做的:

  • 商品上架
  • 商品下架
  • 订单报表查询

怎么实现以上3点需求呢?我们知道绝大部分数据都是会存放在数据库的,那么直接对数据库进行增、删、查、改可以实现吗?可以实现。但是,这会带来几个问题:

  1. 效率低。数据库中实际存储的数据有可能是加密的或者是一个枚举值,如果直接插入业务数据会导致报错或其他不可预知的情况出现。你要先了解每个字段的含义和格式然后才能进行插入。如果数据库中表与表之间存在关联关系,你修改一张表,同时还得修改其他表。并且表与表的关联关系根据业务需求的不同,可能会是一对一、一对多、多对多(对这几个关系不了解没关系,后续章节会有详细介绍和实战)。
  2. 技术门槛高。无论是可视化的数据库管理后台还是命令行界面,都是面向专业技术人员而不是普通业务人员,在不具备技术知识的情况下操作数据库可能会引发删库到跑路。如果每一次商品的变动和订单查询都让专业技术人员来操作则显得更加不合理。
  3. 安全性差 。正常情况下,不同部门和职位级别都对应了不同的权限,可操作的内容和范围都不尽相同,数据库管理系统没办法提供细粒度的权限控制。

这里仅列举了几点比较简单易懂的场景,更多体现读者可结合自身开发经历或者公司项目去联想

鉴于遇上3点问题,直接操作数据库不是一个长久、现实的方式,CMS(内容管理系统)作为一种解决方案,应运而生。

# CMS解决了什么问题

CMS在可视化了数据库数据的基础上,让数据的呈现方式和内容更能满足日常业务管理的需求,例如让数据以各种不同类型的图表显示,支持导出excel等。同时还提供了可分配权限,比如说CMS会有一个左侧导航菜单,菜单上面对应了订单查询、会员管理、商品管理等等业务模块,你可以点击进去进行查询、新增、删除、修改(如果你有权限),你能看到的、可以操作的内容都是可以定制的。对于使用者来说,只要会基本的电脑操作,就可以使用CMS去管理自己负责的那块业务数据。

回到开始的问题,CMS是什么东西到这里读者应该有个大致的了解了,那么Lin CMS到底是什么?这里我们先访问一下这个线上的demo,顺便可以消化吸收下前面讲过的内容,Lin CMS demo,账号super,密码123456。
在demo中我们看到了诸如权限管理、图书管理、日志管理等模块,你在里面的操作最终都会作用于数据库,而整个过程你不需要直接接触数据库,看到这里读者再回过头看开头那两段介绍应该就明白得差不多了。LinCMS在实现CMS的基础上又增加了几个特点:

  1. 开源。开源意味着你可以直接拿到项目的源代码进行自由的定制和阅读学习源码的设计和实现思路。
  2. 优雅美观的页面设计。开发者可以复用内置的样式轻松实现统一、美观的界面而不用自己去编写复杂的样式代码
  3. 流畅的交互体验 。框架已经帮你定义或者解决好了诸多性能和交互问题,开发者只需要专注于开发自己的业务逻辑。
  4. 灵活、可扩展的开发框架。无论是前端的Vue还是服务端的各个语言版本,都实现了高质量的代码设计,内置了许多常用和核心的功能模块,开箱即用。开发者在开发的时候只需专注自己的业务逻辑,按需调用,也可以扩展或者重写框架原有的组件或者类库来实现一些定制化的需求。
  5. 前后端分离的架构。目前市面上多数CMS系统都不是前后端分离的应用,Lin CMS提供了完整的解决方案,前端是VUE,后端则有基于Python的Flask框架、基于Node.js的koa,基于PHP的ThinkPHP可供选择,未来还有会其他语言的支持,如Java、golang等。

总的来说就是提高开发效率,让开发者专注业务逻辑。

更多了解请访问TaleLin

# 技术储备要求

  • PHP基础语法及面向对象概念
  • TP5基础知识(至少需要了解控制器与路由的基本概念)
  • JavaScript基础知识
  • vue基础知识(至少知道组件的基本概念)

后面随着教程的深入,技术要求会逐步提高,但作者尽量保证整个过程对于读者来说是平滑的。

# 起步参考资料

ThinkPHP5.1完全开发手册

Vue官方文档

文档这东西,浏览一遍,大概知道都有什么东西即可,有需要的时候再拿出来翻一翻看具体某个章节。

Vue2.5开发去哪儿网App 从零基础入门到实战项目

微信小程序商城构建全栈应用

# 专栏食用建议指南

  1. 不要复制粘贴,请手敲。你可能会遇到各种错误,自己尝试解决一下,这个过程的意义比你复制粘贴后一次跑通实现了功能要大得多。

  2. 局限于专栏是文字载体,有些知识点或者概念作者很难在有限的篇幅中给读者讲明白,所以作者在相关内容处添加了外链作为辅助参考资料,请读者务必点击查看。

  3. 在完成功能的基础上,多思考是不是还能再优化或者哪些细节再处理下,不要局限于专栏的内容,尝试融入一些自己工作中的具体业务场景或者假想场景。

# 维护与提问

# 更新

  • 由于目前LinCMS目前还处在不断迭代更新阶段,所以本教程会根据版本迭代情况对内容作出相应的调整。
  • 连载的频率保持每周至少有1~2个小节内容,读者可留意更新日志

# 催更、提问与交流

读者对本教程或者GitHub项目有任何疑问、建议都可以在《Lin CMS PHP&Vue教程》读者反馈贴 中提出。

有关专栏的动态和更新计划请留意《Lin CMS PHP&Vue教程》公告贴

或者

QQ群1:643205479(林间有风开源项目交流群) QQ群2:565428048(专栏内容交流群)

# 请我喝茶

没有收款二维码的线上专栏是没有灵魂的,大佬说了,放了就会有人打赏,验证一下:)

最后更新: 2020-08-02 03:50:30
0/140
评论
0
暂无评论
  • 上一页
  • 首页
  • 1
  • 尾页
  • 下一页
  • 总共1页