【项目】视频点播系统
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:https://blog.csdn.net/qinjh_/category_13016668.html

目录
视频点播系统认识
实现目标
服务端程序负责功能
服务端功能模块划分
环境搭建-下载 httplib 库
环境搭建- Mysql 数据库及开发包安装
ubuntu 通过 apt 安装 MariaDB
启动
第三方库认识- json 认识
jsoncpp 认识
jsoncpp 实现序列化
jsoncpp 实现反序列化
第三⽅库认识- Mysql 数据库 API 认识
Mysql-API 实现数据的增删改查操作
第三⽅库认识- httplib 库
httplib 库搭建简单服务器
项⽬实现
服务端⼯具类实现
⽂件实⽤⼯具类设计
Json 实⽤⼯具类设计
服务端数据管理模块-视频数据表的设计
服务端数据管理模块-数据管理类设计
服务端业务处理模块-⽹络通信接⼝设计
服务端业务处理模块-业务处理模块类的设计
前端界⾯实现
HTML 基础认识
CSS 基础认识
vue.js 基础认识
jquery.ajax 基础认识
前端视频展示⻚⾯
前端视频观看⻚⾯
项⽬总结
前言
💬 hello! 各位铁子们大家好哇。
今日更新了视频点播系统的内容
🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝
视频点播系统认识
搭建视频共享点播服务器,可以让所有人通过浏览器访问服务器,实现视频的上传查看,以及管理并播放的功能。
实现目标
主要是完成服务器端的程序业务功能的实现以及前端访问界面 html 的编写,能够支持客户端浏览器针对服务器上的 所有视频进行操作。
服务端程序负责功能
- 针对客户端上传的视频文件以及封面图片进行备份存储。
- 针对客户端上传的视频完成增删改查功能
- 支持客户端浏览器进行视频的观看功能
服务端功能模块划分
- 数据管理模块:负责针对客户端上传的视频信息进行管理。
- 网络通信模块:搭建网络通信服务器,实现与客户端通信。
- 业务处理模块:针对客户端的各个请求进行对应业务处理并响应结果。
- 前端界面模块:完成前端浏览器上视频共享点播的各个 html 页面,在页面中支持增删改查以及观看功能。
环境搭建-下载 httplib 库
git clone https://github.com/yhirose/cpp-httplib.git

环境搭建- Mysql 数据库及开发包安装
ubuntu 通过 apt 安装 MariaDB
安装 mariadb 服务
sudo apt install -y mariadb -server
验证安装
mariadb --version
安装 mariadb C library
sudo apt install libmariadb-dev
安装 mariadb 开发包
sudo apt install mariadb-server mariadb-client libmariadb-dev
启动
启动服务
sudo systemctl start mariadb
设置服务开启自启动
sudo systemctl enable mariadb
查看服务状态
systemctl status mariadb
第三方库认识- json 认识
json 是一种数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据。

json 数据类型:对象,数组,字符串,数字
对象:使⽤花括号 {} 括起来的表⽰⼀个对象。
数组:使⽤中括号 [] 括起来的表⽰⼀个数组。
字符串:使⽤常规双引号 "" 括起来的表⽰⼀个字符串
数字:包括整形和浮点型,直接使⽤。
jsoncpp 认识
jsoncpp 库用于实现 json 格式的序列化和反序列化,完成将多个数据对象组织成为 json 格式字符串,以及将 json 格式字符串解析得到多个数据对象的功能
这其中主要借助三个类以及其对应的少量成员函数完成:


jsoncpp 实现序列化



编译时,要记得链接jsoncpp库。
jsoncpp 实现反序列化


第三⽅库认识- Mysql 数据库 API 认识
Mysql 是 C/S 模式,其实编写代码访问数据库就是实现了⼀个 Mysql 客⼾端,实现的专 有功能。

Mysql-API 实现数据的增删改查操作


编译时要连接库。
第三⽅库认识- httplib 库
httplib 库,⼀个 C++11 单⽂件头的跨平台 HTTP/HTTPS 库。安装起来⾮常容易。只需包含 httplib.h 在你的代码中即可。
httplib 库实际上是⽤于搭建⼀个简单的 http 服务器或者客⼾端的库,这种第三⽅⽹络库,可以 让我们免去搭建服务器或客⼾端的时间,把更多的精⼒投⼊到具体的业务处理中,提⾼开发效率。

httplib 库搭建简单服务器

项⽬实现
服务端⼯具类实现
⽂件实⽤⼯具类设计
在视频点播系统中因为涉及到⽂件上传,需要对上传的⽂件进⾏备份存储,因此⾸先设计封装⽂件操 作类,这个类封装完毕之后,则在任意模块中对⽂件进⾏操作时都将变的简单化
access

作用:检测用户对文件的操作权限
R_OK判断文件是否可读,W_OK是否可写,F_OK测试文件是否已经存在。
返回值 ,成功返回0
stat


util.hpp
#ifndef __MY__UTIL__
#define __MY__UTIL__
#include
#include
#include
#include
#include
namespace aod
{
class FileUtil
{
private:
std::string _name;//文件路径名称
public:
FileUtil(const std::string name)
:_name(name)
{}
bool Exists()//判断文件是否存在
{
//access的F_OK专门用于检测文件是否存在--存在则返回0
int ret=access(_name.c_str(),F_OK);
if(ret!=0)
{
std::cout<<"file is not exists
";
return false;
}
return true;
}
size_t Size()//获取文件大小
{
if(this->Exists()==false)
{
return 0;
}
struct stat st;
//stat接口用于获取文件属性,其中st_size就是文件大小成员
int ret=stat(_name.c_str(),&st);
if(ret!=0)
{
std::cout<<"get file stat failed!
";
return 0;
}
return st.st_size;
}
bool GetContent(std::string *body)//读取文件数据到body中
{
//读取数据是数据的输入,写入数据是数据的输出,输出到设备里面
std::ifstream ifs;
ifs.open(_name,std::ios::binary);
if(ifs.is_open()==false)
{
std::cout<<"open file failed!
";
return false;
}
size_t flen=this->Size();
body->resize(flen);
ifs.read(&(*body)[0],flen);
if(ifs.good()==false)//判断上一个操作是否OK
{
std::cout<<"read file content failed!
";
ifs.close();
return false;
}
ifs.close();
return true;
}
bool SetContent(const std::string &body)//向文件写入数据
{
std::ofstream ofs;
ofs.open(_name,std::ios::binary);
if(ofs.is_open()==false)
{
std::cout<<"open file failed!
";
return false;
}
ofs.write(body.c_str(),body.size());
if(ofs.good()==false)
{
std::cout<<"write file content failed!
";
ofs.close();
return false;
}
ofs.close();
return true;
}
bool CreateDirectory()//针对目录时创建目录
{
if(this->Exists())
{
return true;
}
mkdir(_name.c_str(),0777);
return true;
}
};
}
#endif
Json 实⽤⼯具类设计
class JsonUtil
{
public:
static bool Serialize(const Json::Value &value,std::string *body)
{
Json::StreamWriterBuilder swb;
std::unique_ptr sw(swb.newStreamWriter());
std::stringstream ss;
int ret=sw->write(value,&ss);
if(ret!=0)
{
std::cout<<"Serialize failed!
";
return false;
}
*body=ss.str();
return true;
}
static bool UnSerialize(const std::string &body,Json::Value *value)
{
Json::CharReaderBuilder crb;
std::unique_ptr cr(crb.newCharReader());
std::string err;
bool ret=cr->parse(body.c_str(),body.c_str()+body.size(),value,&err);
if(ret==false)
{
std::cout<<"UnSerialize failed!
";
return false;
}
return true;
}
};
服务端数据管理模块-视频数据表的设计
在视频共享点播系统中,视频数据和图⽚数据都存储在⽂件中,⽽我们需要在数据库中管理⽤⼾上传 的每个视频信息。

服务端数据管理模块-数据管理类设计
数据管理模块负责统⼀对于数据库中数据的增删改查管理,其他所有模块要进⾏数据的操作都通过数 据管理模块完成。
然⽽,数据库中有可能存在很多张表,每张表中数据⼜有不同,要进⾏的数据操也各不相同,因此咱 们将数据的操作分摊到每⼀张表上,为每⼀张表中的数据操作都设计⼀个类,通过类实例化的对象来 访问这张数据库表中的数据,这样的话当我们要访问哪张表的时候,使⽤哪个类实例化的对象即可。
视频信息在接⼝之间的 传递因为字段数量可能很多,因此使⽤ Json::Value 对象进⾏传递
data.hpp
#ifndef __MY__DATA__
#define __MY__DATA__
#include"util.hpp"
#include
#include
namespace aod
{
#define HOST "127.0.0.1"
#define USER "qjh"
#define PASS ""//这里填登陆密码
#define NAME "aod_system"
static MYSQL *MysqlInit()
{
MYSQL *mysql=mysql_init(NULL);
if(mysql==NULL)
{
std::cout<<"init mysql instance failed!
";
return NULL;
}
if(mysql_real_connect(mysql,HOST,USER,PASS,NAME,0,NULL,0)==NULL)
{
std::cout<<"connect mysql server failed!
";
mysql_close(mysql);
return NULL;
}
mysql_set_character_set(mysql,"utf8");
return mysql;
}
static void MysqlDestroy(MYSQL *mysql)
{
if(mysql!=NULL)
{
mysql_close(mysql);
}
return;
}
static bool MysqlQuery(MYSQL *mysql,const std::string &sql)
{
int ret=mysql_query(mysql,sql.c_str());
if(ret!=0)
{
std::cout<append(video);
}
mysql_free_result(res);
return true;
}
bool SelectOne(int video_id,Json::Value *video)//查询单个-输入视频id,输出信息
{
#define SELECTONE_VIDEO "select * from tb_video where id=%d;"
char sql[1024]={0};
sprintf(sql,SELECTONE_VIDEO,video_id);
_mutex.lock();//lock start 保护查询与保存结果到本地的过程
bool ret=MysqlQuery(_mysql,sql);
if(ret==false)
{
_mutex.unlock();
return false;
}
MYSQL_RES *res=mysql_store_result(_mysql);
if(res==NULL)
{
std::cout<<"mysql store result failed!
";
_mutex.unlock();
return false;
}
_mutex.unlock();// lock end
int num_rows=mysql_num_rows(res);
if(num_rows!=1)
{
std::cout<<"have no data!
";
mysql_free_result(res);
return false;
}
MYSQL_ROW row=mysql_fetch_row(res);
(*video)["id"]=video_id;
(*video)["name"]=row[1];
(*video)["info"]=row[2];
(*video)["video"]=row[3];
(*video)["image"]=row[4];
mysql_free_result(res);
return true;
}
bool SelectLike(const std::string &key,Json::Value *videos)//模糊匹配-输入名称关键字,输出视频信息
{
#define SELECTLIKE_VIDEO "select * from tb_video where name like '%%%s%%';"
char sql[1024]={0};
sprintf(sql,SELECTLIKE_VIDEO,key.c_str());
_mutex.lock();//lock start 保护查询与保存结果到本地的过程
bool ret=MysqlQuery(_mysql,sql);
if(ret==false)
{
_mutex.unlock();
return false;
}
MYSQL_RES *res=mysql_store_result(_mysql);
if(res==NULL)
{
std::cout<<"mysql store result failed!
";
_mutex.unlock();
return false;
}
_mutex.unlock();// lock end
int num_rows=mysql_num_rows(res);
for(int i=0;iappend(video);
}
mysql_free_result(res);
return true;
}
};
}
#endif
服务端业务处理模块-⽹络通信接⼝设计
restful 认识:
- REST 是 Representational State Transfer 的缩写,⼀个架构符合 REST 原则,就称 它为 RESTful 架构
- RESTful 架构可以充分的利⽤ HTTP 协议的各种功能,是 HTTP 协议的最佳实践,正⽂通常 采⽤ JSON 格式
- RESTful API 是⼀种软件架构⻛格、设计⻛格,可以让软件更加清晰,更简洁,更有层次,可 维护性更好.
- restful 使⽤五种 HTTP ⽅法,对应 CRUD(增删改查) 操作
- GET 表⽰查询获取
- POST 对应新增
- PUT 对应修改
- DELETE 对应删除
获取所有视频信息

搜索指定关键字名称视频信息

获取指定视频信息

删除指定视频信息

修改指定视频信息

上传视频信息以及⽂件
因为上传视频信息的时候,会携带有视频⽂件和封⾯图⽚的⽂件上传,⽽这些⽂件数据都是⼆进制 的,⽤ json 不好传输,因此在这⾥使⽤传统的 http 上传⽂件请求格式,⽽并没有使⽤ restful ⻛格。

服务端业务处理模块-业务处理模块类的设计
业务处理模块负责与客⼾端进⾏⽹络通信,接收客⼾端的请求,然后根据请求信息,明确客⼾端端⽤ ⼾的意图,进⾏业务处理,并进⾏对应的结果响应。
在视频共享点播系统中,业务处理主要包含两⼤功能:1、⽹络通信功能的实现;2、业务功能处理的 实现
其中⽹络通信功能的实现借助 httplib 库即可⽅便的搭建 http 服务器完成。
⽽业务处理模块所要完成的业务功能主要有:
- 客⼾端的视频数据和信息上传
- 客⼾端的视频列表展⽰(视频信息查询)
- 客⼾端的视频观看请求(视频数据的获取)
- 客⼾端的视频其他管理(修改,删除)功能
server.hpp
#include"data.hpp"
#include"httplib.h"
namespace aod
{
#define WWWROOT "./www"
#define VIDEO_ROOT "/video/"
#define IMAGE_ROOT "/image/"
//因为httplib基于多线程,因此数据管理对象需要在多线程中访问,为了便于访问定义全局变量
TableVideo *tb_video=NULL;
class Server
{
private:
int _port;//服务器的 监听端⼝
httplib::Server _srv;//用于搭建http服务器
private:
//对应的业务处理接⼝
static void Insert(const httplib::Request &req,httplib::Response &rsp)
{
if(req.has_file("name")==false||
req.has_file("info")==false||
req.has_file("video")==false||
req.has_file("image")==false)
{
rsp.status=400;
rsp.body=R"({"result":false,"reason":"上传的数据信息错误"})";
rsp.set_header("Content-Type","application/json");
return;
}
//请求数据的获取
httplib::MultipartFormData name=req.get_file_value("name");//视频名称
httplib::MultipartFormData info=req.get_file_value("info");//视频简介
httplib::MultipartFormData video=req.get_file_value("video");//视频文件
httplib::MultipartFormData image=req.get_file_value("image");//图片文件
//MultipartFormData {name,content_type,filename,content} 字段名,正文类型,文件名称,内容数据
std::string video_name=name.content;
std::string video_info=info.content;
//视频和封面图片的存储
// ./www/image/变形金刚a.jpg
std::string root=WWWROOT;
std::string video_path=root+VIDEO_ROOT+video_name+video.filename;
std::string image_path=root+IMAGE_ROOT+video_name+image.filename;
if(FileUtil(video_path).SetContent(video.content)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"视频文件存储失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
if(FileUtil(image_path).SetContent(image.content)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"图片文件存储失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
//数据库信息的新增
Json::Value video_json;
video_json["name"]=video_name;
video_json["info"]=video_info;
video_json["video"]=VIDEO_ROOT+video_name+video.filename;// /video/变形金刚robot.mp4
video_json["image"]=IMAGE_ROOT+video_name+image.filename;// /image/变形金刚robot.mp4
if(tb_video->Insert(video_json)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"数据库新增数据失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
rsp.set_redirect("/index.html",303);
return;
}
static void Delete(const httplib::Request &req,httplib::Response &rsp)
{
//1.获取要删除的视频id
int video_id=std::stoi(req.matches[1]);
//2.删除视频以及图片文件
Json::Value video;
if(tb_video->SelectOne(video_id,&video)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"不存在视频信息"})";
rsp.set_header("Content-Type","application/json");
return;
}
std::string root=WWWROOT;
std::string video_path=root+video["video"].asString();
std::string image_path=root+video["image"].asString();
remove(video_path.c_str());
remove(image_path.c_str());
//4.删除数据库信息
if(tb_video->Delete(video_id)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"删除数据库信息失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
return;
}
static void Update(const httplib::Request &req,httplib::Response &rsp)
{
//1.获取要修改的视频信息 1.视频id 2.修改后的信息
int video_id=std::stoi(req.matches[1]);
Json::Value video;
if(JsonUtil::UnSerialize(req.body,&video)==false)
{
rsp.status=400;
rsp.body=R"({"result":false,"reason":"新的视频信息格式解析失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
//2.修改数据库数据
if(tb_video->Update(video_id,video)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"修改数据库信息失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
return;
}
static void SelectOne(const httplib::Request &req,httplib::Response &rsp)
{
//1.获取视频的id
int video_id=std::stoi(req.matches[1]);
//2.在数据库中查询指定视频信息
Json::Value video;
if(tb_video->SelectOne(video_id,&video)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"查询数据库指定视频信息失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
//3.组织响应正文--json格式的字符串
JsonUtil::Serialize(video,&rsp.body);
rsp.set_header("Content-Type","application/json");
return;
}
static void SelectAll(const httplib::Request &req,httplib::Response &rsp)
{
// /video 或者 /video?search="关键字"
bool select_flag=true;//默认所有查询
std::string search_key;
if(req.has_param("search")==true)
{
select_flag=false;//模糊匹配
search_key=req.get_param_value("search");//取出search的值
}
Json::Value videos;
if(select_flag==true)
{
if(tb_video->SelectAll(&videos)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"查询数据库所有视频信息失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
}
else
{
if(tb_video->SelectLike(search_key,&videos)==false)
{
rsp.status=500;
rsp.body=R"({"result":false,"reason":"查询数据库匹配视频信息失败"})";
rsp.set_header("Content-Type","application/json");
return;
}
}
JsonUtil::Serialize(videos,&rsp.body);//序列化后发给客户端
rsp.set_header("Content-Type","application/json");
return;
}
public:
Server(int port):_port(port){}
bool RunModule()//建⽴请求与处理函数的映射关系,设置静态资源根⽬录,启动服务器
{
//1.初始化操作---初始化数据管理模块,创建指定的目录
tb_video=new TableVideo();
FileUtil(WWWROOT).CreateDirectory();
std::string root=WWWROOT;
std::string video_real_path=root+VIDEO_ROOT;// ./www/video/
FileUtil(video_real_path).CreateDirectory();
std::string image_real_path=root+IMAGE_ROOT;// ./www/image/
FileUtil(image_real_path).CreateDirectory();
//2.搭建http服务器,开始运行
//(1)设置静态资源根目录
_srv.set_mount_point("/",WWWROOT);
//(2)添加请求--处理函数映射关系
_srv.Post("/video",Insert);
_srv.Delete("/video/(d+)",Delete);
_srv.Put("/video/(d+)",Update);
_srv.Get("/video/(d+)",SelectOne);
_srv.Get("/video/",SelectAll);
_srv.Get("/video.*", SelectAll);
//3.启动服务器
_srv.listen("0.0.0.0",_port);
return true;
}
};
}
前端界⾯实现
HTML 基础认识
HTML 代码是由标签构成的。我们可以理解不同的标签代表不同的控件元素,前端浏览器拿到 html 代码之后,根据标签之间的关系进⾏解析,得到⼀棵 DOM(Document Object Mode - ⽂档对象 模型的缩写) 树 。
然后根据 DOM 树渲染出不同的控件元素,得到我们所看到的⻚⾯。
第⼀个⻚⾯
hello world
- 标签名 (body) 放到 < > 中
- ⼤部分标签成对出现.为开始标签,为结束标签.
- 少数标签只有开始标签, 称为 "单标签"
- 开始标签和结束标签之间, 写的是标签的内容. (hello)
- 开始标签中可能会带有 "属性". id 属性相当于给这个标签设置了⼀个唯⼀的标识符(⾝份证号码).
HTML 基础标签
标题标签: h1-h6
hello
hello
hello
hello
hello
hello
段落标签: p
段落,
在html中⼀般的回⻋并不起作⽤
把⼀段⽐较⻓的⽂本粘贴到 html 中, 会发现并没有分成段落. 在 html 中使⽤
标签括起⼀个段 落进⾏换⾏。当然也可以在段落内使⽤
标签进⾏段落内的换⾏操作。
图⽚标签: img

超链接标签: a
点击这⾥打开新标签访问百度
表格标签: table
- table 标签: 表⽰整个表格
- tr: 表⽰表格的⼀⾏
- td: 表⽰⼀个单元格
- th: 表⽰表头单元格. 会居中加粗
- thead: 表格的头部区域(注意和 th 区分, 范围是⽐ th 要⼤的)
- tbody: 表格得到主体区域.

列表标签: ol & ul & dl
- 主要使⽤来布局的. 整⻬好看.
- 有序列表[⽤的不多] ol li
- ⾃定义列表[重要] dl (总标签) dt (⼩标题) dd (围绕标题来说明) 上⾯有个⼩标题, ,下⾯有⼏个围 绕着标题来展开的.

表单标签: form
表单是让⽤⼾输⼊信息的重要途径.
分成两个部分:
- 表单域: 包含表单元素的区域. 重点是 form 标签.
- 表单控件: 输⼊框 , 提交按钮等. 重点是 input 标签.
form标签认识
被 form 标签括起来的部分称之为表单域,当点击表单提交按钮时,将会将表单域中所有表单控件数 据提交给指定服务器。
- action :表单动作,或者说当点击表单提交时的请求链接
- method :请求⽅法
- enctype :编码类型,其中 multipart/form-data 常⽤于⽂件上传

input标签的认识

下拉菜单标签: select

option 中定义 selected="selected" 表⽰默认选中.
⽂本域标签: textarea

⽆语义标签: div & span

div 标签, division 的缩写, 含义是 分割
span 标签, 含义是跨度
说⽩了就是两个盒⼦. 常⽤于⽹⻚布局
- div 是独占⼀⾏的, 是⼀个⼤盒⼦.
- span 不独占⼀⾏, 是⼀个⼩盒⼦
html5 语义化标签
div 没有语义. 对于搜索引擎来说没有意义. 为了让搜索引擎能够更好的识别和分析⻚⾯(SEO 优化), HTML 引⼊了更多的 "语义化" 标签. 但是这些标签其实本质上都和 div ⼀样(对于前端开发来说). 然⽽ 对于搜索引擎来说, ⻅到 header 和 article 这种标签就会重点进⾏解析, ⽽ footer 这种标签就可以适当 忽略

- header: 头部
- nav: 导航
- article: 内容
- section: 某个区域
- aside: 侧边栏
- footer: 尾部
多媒体标签: video & audio

video标签:视频
audio标签:⾳频
CSS 基础认识
层叠样式表 (Cascading Style Sheets).
CSS 能够对⽹⻚中元素位置的排版进⾏像素级精确控制, 实现美化⻚⾯的效果. 能够做到⻚⾯的样式和 结构分离.
基本语法规范
选择器 + {⼀条/N条声明}
- 选择器决定针对谁修改 (找谁)
- 声明决定修改啥. (⼲啥)
- 声明的属性是键值对. 使⽤ ; 区分键值对, 使⽤ : 区分键和值

- CSS 要写到 style 标签中
- style 标签可以放到⻚⾯任意位置. ⼀般放到 head 标签内.
- CSS 使⽤ /* */ 作为注释.
- CSS 不区分⼤⼩写, 我们开发时统⼀使⽤⼩写字⺟
选择器的种类
基础选择器: 单个选择器构成的
- 标签选择器
- 类选择器
- id 选择器
- 通配符选择器
复合选择器: 把多种基础选择器综合运⽤起来.
- 后代选择器
- ⼦选择器
- 并集选择器
- 伪类选择器



vue.js 基础认识
安装
使⽤ vue 实现⼀个简单的 hello world 程序
代码解释:
- 创建了⼀个 div, id 为 app
- 在 div 中使⽤ "插值表达式" {{message}} 来指定 div 内部的内容.
- js 中创建了⼀个名为 app 的 Vue 实例. 构造函数中的参数是⼀个对象.
- 参数中的 el 字段是⼀个选择器, 对应到 html 中的具体元素id.
- 参数中的 data 字段是⼀个特定的对象, ⽤来放置数据.
- data 中的 message 属性值, 就对应到 {{message}} 中的内容. Vue 会⾃动解析 {{message}} , 并把 data 中对应的名字为 message 的属性值替换进去.
理解响应式: 修改 message 的值, 界⾯显⽰的内容就会发⽣改变
插值操作: {{}}

遮罩: v-cloak
cloak 意思是 "⽃篷", ⽤来遮罩被渲染之前的插值表达式.
HTML 解析代码的时候是从上往下解析. 如果加载 Vue 的速度⽐较慢, 那么就会在界⾯上看到 {{ }} 这样的内容.

注意: v-cloak 相当于 div 标签的⼀个属性. 这个属性在 Vue 接管 div 之前会存在, 但是 Vue 执⾏之后这个 v-cloak 就会被 Vue 去掉. 此时 display 样式就不再⽣效了.
[v-cloak] 是属性选择器(是 CSS 的基本选择器之⼀). 选择了所有包含 v-cloak 属性的元素.
绑定属性: v-bind
很多标签的属性都是需要动态进⾏设置的. ⽐如标签的href,
事件监听: v-on
v-on 后⾯使⽤ : 连接后续的事件名称.

methods : vue 对象的函数或⽅法都放在其中

v-on:click.prevent :阻⽌元素默认⾏为. ⽐如这⾥则禁止了默认的form表单提交操作
条件显⽰: v-show
v-show , 条件为 true 的时候显⽰元素, 条件为 false 时不显⽰

条件指令: v-if
通过⼀个表达式决定某个元素是否被渲染

v-show 和 v-if 的区别:当把 flag 置为 false 时, v-if 对应的元素不会被构建到 dom 树 中, ⽽ v-show 的元素构建到 dom 树中了, 只是通过 display:none 隐藏了
循环指令: v-for
v-for 可以绑定数据到数组来渲染⼀个标签

双向绑定: v-model
表单是实际开发中和⽤⼾交互的重要⼿段. 通过 v-model 可以将⼀个 vue 数据与标签数据关联起 来,实现⼀荣俱荣⼀损俱损的效果。

通过 v-model 命令就把 input 标签的 value 和 message 关联起来了.
此时, 通过修改输⼊框的内容, app.message 就会发⽣改变.
修改 app.message 的值, 界⾯也会随之发⽣改变.
这个操作就称为 双向绑定
jquery.ajax 基础认识
AJAX 是与服务器交换数据的技术,它在不重载全部⻚⾯的情况下,实现了对部分⽹⻚的更新。 其实简单来说, ajax 就是⼀个 http 客⼾端,可以异步请求服务器。

前端视频展示⻚⾯
index.html
Home
视频列表
02:02
{{video.name}}
20.895
前端视频观看⻚⾯
video.html
视频描述
{{video.info}}
前端页面可以从网上找模板,再加以修改即可
项⽬总结
项⽬名称:视频点播系统
项⽬功能:搭建⼀个共享点播系统,服务器⽀持⽤⼾通过前端浏览器访问服务器,获取展⽰与观看和 操作的界⾯,最终实现视频的上传以及观看和删改查等基础管理功能。
开发环境: Ubuntu 22.04./vim、g++、gdb、makefile
技术特点: http 服务器搭建, restful ⻛格接⼝设计, json 序列化,线程池, html+css+js 基础
项⽬模块:
- 数据管理模块:基于 MYSQL 进⾏数据管理,封装数据管理类进⾏数据统⼀访问
- 业务处理模块:基于 HTTPLIB 搭建 HTTP 服务器,使⽤ restful⻛格 进⾏接⼝设计处理客⼾ 端业务请求
- 前端界⾯模块:基于基础的 HTML+CSS+JS 完成基于简单模板前端界⾯的修改与功能完成。
码云:https://gitee.com/qinjianhao0/project-warehouse/tree/master/SPDB/source










