整合NUXT前台页面,已经整合redis缓存
服务端渲染技术NUXT
什么是服务端渲染
服务端渲染又称SSR (Server Side Render)是在服务端完成页面的内容,而不是在客户端通过AJAX获取数据。
服务器端渲染(SSR)的优势主要在于:更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
如果你的应用程序初始展示 loading 菊花图,然后通过 Ajax 获取内容,抓取工具并不会等待异步完成后再进行页面内容的抓取。也就是说,如果 SEO 对你的站点至关重要,而你的页面又是异步获取内容,则你可能需要服务器端渲染(SSR)解决此问题。
另外,使用服务器端渲染,我们可以获得更快的内容到达时间(time-to-content),无需等待所有的 JavaScript 都完成下载并执行,产生更好的用户体验,对于那些「内容到达时间(time-to-content)与转化率直接相关」的应用程序而言,服务器端渲染(SSR)至关重要。
什么是NUXT
Nuxt.js 是一个基于 Vue.js 的轻量级应用框架,可用来创建服务端渲染 (SSR) 应用,也可充当静态站点引擎生成静态站点应用,具有优雅的代码结构分层和热加载等特性。
下载压缩包
https://github.com/nuxt-community/starter-template/archive/master.zip
解压
修改package.json
name、description、author(必须修改这里,否则项目无法安装)两个大括号也要去掉
"name": "guli",
"version": "1.0.0",
"description": "谷粒学院前台网站",
"author": "dyk",
修改nuxt.config.js
head: {
title: '谷粒学院 - Java视频|HTML5视频|前端视频|Python视频|大数据视频-自学拿1万+月薪的IT在线视频课程,谷粉力挺,老学员为你推荐',
meta: [{
charset: 'utf-8'
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1'
},
{
hid: 'keywords',
name: 'keywords',
content: '谷粒学院,IT在线视频教程,Java视频,HTML5视频,前端视频,Python视频,大数据视频'
},
{
hid: 'description',
name: 'description',
content: '谷粒学院是国内领先的IT在线视频学习平台、职业教育平台。截止目前,谷粒学院线上、线下学习人次数以万计!会同上百个知名开发团队联合制定的Java、HTML5前端、大数据、Python等视频课程,被广大学习者及IT工程师誉为:业界最适合自学、代码量最大、案例最多、实战性最强、技术最前沿的IT系列视频课程!'
}
],
link: [{
rel: 'icon',
type: 'image/x-icon',
href: '/favicon.ico'
}]
},
在命令提示终端中运行
npm install
NUXT目录结构
(1)资源目录 assets
用于组织未编译的静态资源如 LESS、SASS 或 JavaScript。
(2)组件目录 components
用于组织应用的 Vue.js 组件。Nuxt.js 不会扩展增强该目录下 Vue.js 组件,即这些组件不会像页面组件那样有 asyncData 方法的特性。
(3)布局目录 layouts
用于组织应用的布局组件。
(4)页面目录 pages
用于组织应用的路由及视图。Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。
(5)插件目录 plugins
用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。
(6)nuxt.config.js 文件
nuxt.config.js 文件用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。
安装幻灯片插件
P168中的npm install vue-awesome-swiper下载的是最新4.x版本,视频中用的是3.1.3版本,已下载各位去package.json文件中把对应的组件修改为^3.1.3版本,重新npm install就行
npm install vue-awesome-swiper@3.1.3
配置插件
在 plugins 文件夹下新建文件 nuxt-swiper-plugin.js,内容是
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper/dist/ssr'
Vue.use(VueAwesomeSwiper)
在 nuxt.config.js 文件中配置插件
将 plugins 和 css节点 复制到 module.exports节点下
plugins: [
{
src: '~/plugins/nuxt-swiper-plugin.js',
ssr: false
}
],
css: [
'swiper/dist/css/swiper.css'
],
页面布局
复制静态资源
将静态原型中的css、img、js、photo目录拷贝至assets目录下
将favicon.ico复制到static目录下
修改layouts目录下default.vue,从静态页面中复制首页,修改了原始文件中的资源路径为~/assets/,将主内容区域的内容替换成
default.vue
<template>
<div class="in-wrap">
<!-- 公共头引入 -->
<header id="header">
<section class="container">
<h1 id="logo">
<a href="#" title="谷粒学院">
<img src="~/assets/img/logo.png" width="100%" alt="谷粒学院" />
</a>
</h1>
<div class="h-r-nsl">
<ul class="nav">
<router-link to="/" tag="li" active-class="current" exact>
<a>首页</a>
</router-link>
<router-link to="/course" tag="li" active-class="current">
<a>课程</a>
</router-link>
<router-link to="/teacher" tag="li" active-class="current">
<a>名师</a>
</router-link>
<router-link to="/article" tag="li" active-class="current">
<a>文章</a>
</router-link>
<router-link to="/qa" tag="li" active-class="current">
<a>问答</a>
</router-link>
</ul>
<!-- / nav -->
<ul class="h-r-login">
<li id="no-login">
<a href="/sing_in" title="登录">
<em class="icon18 login-icon"> </em>
<span class="vam ml5">登录</span>
</a>
|
<a href="/sign_up" title="注册">
<span class="vam ml5">注册</span>
</a>
</li>
<li class="mr10 undis" id="is-login-one">
<a href="#" title="消息" id="headerMsgCountId">
<em class="icon18 news-icon"> </em>
</a>
<q class="red-point" style="display: none"> </q>
</li>
<li class="h-r-user undis" id="is-login-two">
<a href="#" title>
<img
src="~/assets/img/avatar-boy.gif"
width="30"
height="30"
class="vam picImg"
alt
/>
<span class="vam disIb" id="userName"></span>
</a>
<a
href="javascript:void(0)"
title="退出"
onclick="exit();"
class="ml5"
>退出</a
>
</li>
<!-- /未登录显示第1 li;登录后显示第2,3 li -->
</ul>
<aside class="h-r-search">
<form action="#" method="post">
<label class="h-r-s-box">
<input
type="text"
placeholder="输入你想学的课程"
name="queryCourse.courseName"
value
/>
<button type="submit" class="s-btn">
<em class="icon18"> </em>
</button>
</label>
</form>
</aside>
</div>
<aside class="mw-nav-btn">
<div class="mw-nav-icon"></div>
</aside>
<div class="clear"></div>
</section>
</header>
<!-- /公共头引入 -->
<nuxt />
<!-- 公共底引入 -->
<footer id="footer">
<section class="container">
<div class>
<h4 class="hLh30">
<span class="fsize18 f-fM c-999">友情链接</span>
</h4>
<ul class="of flink-list">
<li>
<a href="http://www.atguigu.com/" title="尚硅谷" target="_blank"
>尚硅谷</a
>
</li>
</ul>
<div class="clear"></div>
</div>
<div class="b-foot">
<section class="fl col-7">
<section class="mr20">
<section class="b-f-link">
<a href="#" title="关于我们" target="_blank">关于我们</a>|
<a href="#" title="联系我们" target="_blank">联系我们</a>|
<a href="#" title="帮助中心" target="_blank">帮助中心</a>|
<a href="#" title="资源下载" target="_blank">资源下载</a>|
<span>服务热线:010-56253825(北京) 0755-85293825(深圳)</span>
<span>Email:info@atguigu.com</span>
</section>
<section class="b-f-link mt10">
<span>©2018课程版权均归谷粒学院所有 京ICP备17055252号</span>
</section>
</section>
</section>
<aside class="fl col-3 tac mt15">
<section class="gf-tx">
<span>
<img src="~/assets/img/wx-icon.png" alt />
</span>
</section>
<section class="gf-tx">
<span>
<img src="~/assets/img/wb-icon.png" alt />
</span>
</section>
</aside>
<div class="clear"></div>
</div>
</section>
</footer>
<!-- /公共底引入 -->
</div>
</template>
<script>
import "~/assets/css/reset.css";
import "~/assets/css/theme.css";
import "~/assets/css/global.css";
import "~/assets/css/web.css";
export default {};
</script>
首页面index.vue
修改pages/index.vue:
修改了原始文件中的资源路径为~/assets/
<template>
<div>
<!-- 幻灯片 开始 -->
<div v-swiper:mySwiper="swiperOption">
<div class="swiper-wrapper">
<div v-for="banner in bannerList" :key="banner.id" class="swiper-slide" style="background: #040B1B;">
<a target="_blank" :href="banner.linkUrl">
<img :src="banner.imageUrl" :alt="banner.title">
</a>
</div>
</div>
<div class="swiper-pagination swiper-pagination-white"></div>
<div class="swiper-button-prev swiper-button-white" slot="button-prev"></div>
<div class="swiper-button-next swiper-button-white" slot="button-next"></div>
</div>
<!-- 幻灯片 结束 -->
<div id="aCoursesList">
<!-- 网校课程 开始 -->
<div>
<section class="container">
<header class="comm-title">
<h2 class="tac">
<span class="c-333">热门课程</span>
</h2>
</header>
<div>
<article class="comm-course-list">
<ul class="of" id="bna">
<li v-for="course in eduList" :key="course.id">
<div class="cc-l-wrap">
<section class="course-img">
<img
:src="course.cover"
class="img-responsive"
:alt="course.title"
>
<div class="cc-mask">
<a href="#" title="开始学习" class="comm-btn c-btn-1">开始学习</a>
</div>
</section>
<h3 class="hLh30 txtOf mt10">
<a href="#" :title="course.title" class="course-title fsize18 c-333">{{course.title}}</a>
</h3>
<section class="mt10 hLh20 of">
<span class="fr jgTag bg-green" v-if="Number(course.price) === 0">
<i class="c-fff fsize12 f-fA">免费</i>
</span>
<span class="fl jgAttr c-ccc f-fA">
<i class="c-999 f-fA">9634人学习</i>
|
<i class="c-999 f-fA">9634评论</i>
</span>
</section>
</div>
</li>
</ul>
<div class="clear"></div>
</article>
<section class="tac pt20">
<a href="#" title="全部课程" class="comm-btn c-btn-2">全部课程</a>
</section>
</div>
</section>
</div>
<!-- /网校课程 结束 -->
<!-- 网校名师 开始 -->
<div>
<section class="container">
<header class="comm-title">
<h2 class="tac">
<span class="c-333">名师大咖</span>
</h2>
</header>
<div>
<article class="i-teacher-list">
<ul class="of">
<li v-for="teacher in teacherList" :key="teacher.id">
<section class="i-teach-wrap">
<div class="i-teach-pic">
<a href="/teacher/1" :title="teacher.name">
<img :alt="teacher.name" :src="teacher.avatar">
</a>
</div>
<div class="mt10 hLh30 txtOf tac">
<a href="/teacher/1" :title="teacher.name" class="fsize18 c-666">{{teacher.name}}</a>
</div>
<div class="hLh30 txtOf tac">
<span class="fsize14 c-999">{{teacher.career}}</span>
</div>
<div class="mt15 i-q-txt">
<p
class="c-999 f-fA"
>{{teacher.intro}}</p>
</div>
</section>
</li>
</ul>
<div class="clear"></div>
</article>
<section class="tac pt20">
<a href="#" title="全部讲师" class="comm-btn c-btn-2">全部讲师</a>
</section>
</div>
</section>
</div>
<!-- /网校名师 结束 -->
</div>
</div>
</template>
幻灯片插件
<!-- 幻灯片 开始 -->
<div v-swiper:mySwiper="swiperOption">
<div class="swiper-wrapper">
<div
v-for="banner in bannerList"
:key="banner.id"
class="swiper-slide"
style="background: #040b1b"
>
<a target="_blank" :href="banner.linkUrl">
<img :src="banner.imageUrl" :alt="banner.title" />
</a>
</div>
</div>
<div class="swiper-pagination swiper-pagination-white"></div>
<div
class="swiper-button-prev swiper-button-white"
slot="button-prev"
></div>
<div
class="swiper-button-next swiper-button-white"
slot="button-next"
></div>
</div>
<script>
export default {
data () {
return {
swiperOption: {
//配置分页
pagination: {
el: '.swiper-pagination'//分页的dom节点
},
//配置导航
navigation: {
nextEl: '.swiper-button-next',//下一页dom节点
prevEl: '.swiper-button-prev'//前一页dom节点
}
}
}
}
}
</script>
路由
固定路由
使用router-link构建路由,地址是/course
在page目录创建文件夹course ,在course目录创建index.vue
动态路由
如果我们需要根据id查询一条记录,就需要使用动态路由。NUXT的动态路由是以下划线开头的vue文件,参数名为下划线后边的文件名
在pages下的course目录下创建_id.vue
其他的静态页面我都省略了太多了而且删序号好麻烦
首页显示banner数据
在service模块下创建子模块service-cms
执行sql脚本
配置文件
使用代码生成器生成banner代码
在service_cms的pom.xml里面添加
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
<scope>test</scope>
</dependency>
package codedemo;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
/**
* @author
* @since 2018/12/13
*/
public class CodeGenerator {
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir("D:\\ideacode\\guli_parent\\service\\service_cms" + "/src/main/java");
gc.setAuthor("dyk");
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
//UserServie
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ASSIGN_ID); //主键策略
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/gulischool?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("educms"); //模块名
//包 com.atguigu.eduservice
pc.setParent("com.atguigu");
//包 com.atguigu.eduservice.controller
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("crm_banner");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
创建启动类
创建CmsApplication.java
@SpringBootApplication
@ComponentScan(basePackages = {"com.atguigu"})//指定扫描位置
@MapperScan("com.atguigu.educms.mapper.CrmBannerMapper")
public class CmsApplication {
public static void main(String[] args) {
SpringApplication.run(CmsApplication.class,args);
}
}
banner服务接口
banner后台管理接口
banner后台分页查询、添加、修改、删除接口
package com.atguigu.educms.controller;
import com.atguigu.commonutils.ResultVo;
import com.atguigu.educms.entity.CrmBanner;
import com.atguigu.educms.service.CrmBannerService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* <p>
* 首页banner表 后台
* </p>
*
* @author dyk
* @since 2021-09-27
*/
@RestController
@RequestMapping("/educms/banneradmin")
@CrossOrigin
public class BannerAdminController {
@Autowired
private CrmBannerService crmBannerService;
//分页查询banner
@GetMapping("pageBanner/{page}/{limt}")
@ApiOperation(value = "获取Banner分页列表")
public ResultVo pageBanner(@PathVariable long page,@PathVariable long limit){
Page<CrmBanner> crmBannerPage = new Page<CrmBanner>(page, limit);
crmBannerService.page(crmBannerPage,null);
return ResultVo.ok().data("items",crmBannerPage.getRecords()).data("total",crmBannerPage.getTotal());
}
//添加banner
@ApiOperation(value = "新增Banner")
@PostMapping("/addBanner")
public ResultVo addBanner(@RequestBody CrmBanner crmBanner){
boolean save = crmBannerService.save(crmBanner);
if(save){
return ResultVo.ok();
}
else{
return ResultVo.error();
}
}
//通过id获取信息
@ApiOperation(value = "获取Banner")
@GetMapping("get/{id}")
public ResultVo getById(@PathVariable String id){
CrmBanner crmBanner = crmBannerService.getById(id);
return ResultVo.ok().data("item",crmBanner);
}
//修改banner
@PutMapping("update")
@ApiOperation(value = "修改Banner")
public ResultVo updateBanner(@RequestBody CrmBanner crmBanner){
boolean b = crmBannerService.updateById(crmBanner);
if(b){
return ResultVo.ok();
}
else{
return ResultVo.error();
}
}
@DeleteMapping("remove/{id}")
@ApiOperation(value = "删除Banner")
public ResultVo remove(@PathVariable String id){
boolean b = crmBannerService.removeById(id);
if(b){
return ResultVo.ok();
}
else{
return ResultVo.error();
}
}
}
banner前台查询接口
@Autowired
private CrmBannerService bannerService;
//查询所有banner
@GetMapping("getAllBanner")
@ApiOperation(value = "获取首页banner")
public ResultVo getAllBanner(){
List<CrmBanner> list= bannerService.selectAllBanner();
return ResultVo.ok().data("list",list);
}
service
@Override
public List<CrmBanner> selectAllBanner() {
//根据id进行降序排序,显示排列之后的前两条记录
QueryWrapper<CrmBanner> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
queryWrapper.last("limit 2");
List<CrmBanner> list = baseMapper.selectList(null);
return list;
}
首页显示课程名师数据
后端
在service-edu模块的controller下面创建front包
@RestController
@CrossOrigin
@RequestMapping("/eduservice/indexfront")
public class IndexFrontController {
@Autowired
private EduCourseService courseService;
@Autowired
private EduTeacherService teacherService;
//查看前8条热门课程,查询前4条名师
@GetMapping("index")
public ResultVo index(){
QueryWrapper<EduCourse> courseQueryWrapper = new QueryWrapper<EduCourse>();
courseQueryWrapper.orderByDesc("id");
courseQueryWrapper.last("limit 8");
List<EduCourse> courseList = courseService.list(courseQueryWrapper);
QueryWrapper<EduTeacher> eduTeacherQueryWrapper = new QueryWrapper<>();
eduTeacherQueryWrapper.orderByDesc("id");
eduTeacherQueryWrapper.last("limit 4");
List<EduTeacher> teacherList= teacherService.list(eduTeacherQueryWrapper);
return ResultVo.ok().data("eduList",courseList).data("teacherList",teacherList);
}
}
前端
下载axios
npm install axios
封装axios
创建utils文件夹,utils下创建request.js
import axios from 'axios'
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost:9001', // api的base_url
timeout: 20000 // 请求超时时间
})
export default service
创建api文件夹,创建banner.js文件
import request from '@/utils/request'
export default {
//查询前两条banner数据
getListBanner() {
return request({
url: '/educms/bannerfront/getAllBanner',
method: 'get'
})
}
}
<template>
<div>
<!-- 幻灯片 开始 -->
<div v-swiper:mySwiper="swiperOption">
<div class="swiper-wrapper">
<div
v-for="banner in bannerList"
:key="banner.id"
class="swiper-slide"
style="background: #040b1b"
>
<a target="_blank" :href="banner.linkUrl">
<img :src="banner.imageUrl" :alt="banner.title" />
</a>
</div>
</div>
<div class="swiper-pagination swiper-pagination-white"></div>
<div
class="swiper-button-prev swiper-button-white"
slot="button-prev"
></div>
<div
class="swiper-button-next swiper-button-white"
slot="button-next"
></div>
</div>
<!-- 幻灯片 结束 -->
<div id="aCoursesList">
<!-- 网校课程 开始 -->
<div>
<section class="container">
<header class="comm-title">
<h2 class="tac">
<span class="c-333">热门课程</span>
</h2>
</header>
<div>
<article class="comm-course-list">
<ul class="of" id="bna">
<li v-for="course in eduList" :key="course.id">
<div class="cc-l-wrap">
<section class="course-img">
<img
:src="course.cover"
class="img-responsive"
:alt="course.title"
/>
<div class="cc-mask">
<a href="#" title="开始学习" class="comm-btn c-btn-1"
>开始学习</a
>
</div>
</section>
<h3 class="hLh30 txtOf mt10">
<a
href="#"
:title="course.title"
class="course-title fsize18 c-333"
>{{ course.title }}</a
>
</h3>
<section class="mt10 hLh20 of">
<span
class="fr jgTag bg-green"
v-if="Number(course.price) === 0"
>
<i class="c-fff fsize12 f-fA">免费</i>
</span>
<span class="fl jgAttr c-ccc f-fA">
<i class="c-999 f-fA">9634人学习</i>
|
<i class="c-999 f-fA">9634评论</i>
</span>
</section>
</div>
</li>
</ul>
<div class="clear"></div>
</article>
<section class="tac pt20">
<a href="#" title="全部课程" class="comm-btn c-btn-2">全部课程</a>
</section>
</div>
</section>
</div>
<!-- /网校课程 结束 -->
<!-- 网校名师 开始 -->
<div>
<section class="container">
<header class="comm-title">
<h2 class="tac">
<span class="c-333">名师大咖</span>
</h2>
</header>
<div>
<article class="i-teacher-list">
<ul class="of">
<li v-for="teacher in teacherList" :key="teacher.id">
<section class="i-teach-wrap">
<div class="i-teach-pic">
<a href="/teacher/1" :title="teacher.name">
<img :alt="teacher.name" :src="teacher.avatar" />
</a>
</div>
<div class="mt10 hLh30 txtOf tac">
<a
href="/teacher/1"
:title="teacher.name"
class="fsize18 c-666"
>{{ teacher.name }}</a
>
</div>
<div class="hLh30 txtOf tac">
<span class="fsize14 c-999">{{ teacher.career }}</span>
</div>
<div class="mt15 i-q-txt">
<p class="c-999 f-fA">{{ teacher.intro }}</p>
</div>
</section>
</li>
</ul>
<div class="clear"></div>
</article>
<section class="tac pt20">
<a href="#" title="全部讲师" class="comm-btn c-btn-2">全部讲师</a>
</section>
</div>
</section>
</div>
<!-- /网校名师 结束 -->
</div>
</div>
</template>
<script>
import banner from "@/api/banner";
import index from "@/api/index";
export default {
data() {
return {
swiperOption: {
//配置分页
pagination: {
el: ".swiper-pagination", //分页的dom节点
},
//配置导航
navigation: {
nextEl: ".swiper-button-next", //下一页dom节点
prevEl: ".swiper-button-prev", //前一页dom节点
},
},
//banner数组
bannerList: [],
eduList: [],
teacherList: [],
};
},
created() {
//调用查询banner的方法
this.getBannerList();
//调用查询热门课程和名师的方法
this.getHotCourseTeacher();
},
methods: {
//查询热门课程和名师
getHotCourseTeacher() {
index.getIndexData().then((response) => {
this.eduList = response.data.data.eduList;
this.teacherList = response.data.data.teacherList;
});
},
//查询banner数据
getBannerList() {
banner.getListBanner().then((response) => {
this.bannerList = response.data.data.list;
});
},
},
};
</script>
项目集成Redis
在common模块添加依赖
由于redis缓存是公共应用,所以我们把依赖与配置添加到了common模块下面,在common模块pom.xml下添加以下依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
RedisConfig.java
package com.atguigu.servicebase.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurationSelector;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching //开启缓存
public class RedisConfig extends CachingConfigurationSelector {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
在接口中添加redis缓存
由于首页数据变化不是很频繁,而且首页访问量相对较大,所以我们有必要把首页接口数据缓存到redis缓存中,减少数据库压力和提高访问速度。
改造service-cms模块首页banner接口,首页课程与讲师接口类似
Spring Boot缓存注解
缓存@Cacheable
根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
查看源码,属性值如下:
属性/方法名 | 解释 |
---|---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
缓存@CachePut
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
查看源码,属性值如下:
属性/方法名 | 解释 |
---|---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
缓存@CacheEvict
使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
查看源码,属性值如下
属性/方法名 | 解释 |
---|---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
allEntries | 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存 |
beforeInvocation | 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存 |
启动redis服务
./redis-server myredisconfig/redisbak.conf
连接redis服务可能遇到的问题
1.关闭liunx防火墙
2.找到redis配置文件, 注释一行配置
# bind 127.0.0.1
3.修改protected-mode yes
改为
protected-mode no
4.设置为守护线程
daemonize yes
在service-cms模块配置文件添加redis配置
spring.redis.host=124.57.252.81
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
修改CrmBannerServiceImpl,添加redis缓存注解
@Override
@Cacheable(value = "banner",key="'selectIndexList'")
public List<CrmBanner> selectAllBanner() {
//根据id进行降序排序,显示排列之后的前两条记录
QueryWrapper<CrmBanner> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
queryWrapper.last("limit 2");
List<CrmBanner> list = baseMapper.selectList(null);
return list;
}