百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

学成在线 类慕课网 微服务教育网 第12天-讲义-搜索前端 Nuxt.js

bigegpt 2024-08-30 12:32 2 浏览

1搜索前端技术需求

1.1需求描述

采用vue.js开发搜索界面则SEO不友好,需要解决SEO的问题。

1.2了解SEO

总结:seo是网站为了提高自已的网站排名,获得更多的流量,对网站的结构及内容进行调整优化,以便搜索引擎

(百度,google等)更好抓取到更优质的网站的内容。

下图是搜索引擎爬取网站页面的大概流程图:

(搜索引擎的工作流程很复杂,下图只是简单概括)

从上图可以看到SEO是网站自己为了方便spider抓取网页而作出的网页内容优化,常见的SEO方法比如:

1)对url链接的规范化,多用restful风格的url,多用静态资源url;

2) 注意title、keywords的设置。

3)由于spider对javascript支持不好,对于网页跳转用href标签。

。。。

1.3服务端渲染和客户端渲染

采用什么技术有利于SEO?要解答这个问题需要理解服务端渲染和客户端渲染。什么是服务端渲染?

我们用传统的servlet开发来举例:浏览器请求servlet,servlet在服务端生成html响应给浏览器,浏览器展示html 的内容,这个过程就是服务端渲染,如下图:

服务端渲染的特点:

1)在服务端生成html网页的dom元素。

2)客户端(浏览器)只负责显示dom元素内容。

当初随着web2.0的到来,A JAX技术兴起,出现了客户端渲染:客户端(浏览器) 使用A JAX向服务端发起http请求,获取到了想要的数据,客户端拿着数据开始渲染html网页,生成Dom元素,并最终将网页内容展示给用户, 如下图:

S

客户端渲染的特点:

1)在服务端只是给客户端响应的了数据,而不是html网页

2)客户端(浏览器)负责获取服务端的数据生成Dom元素。

两种方式各有什么优缺点? 客户端渲染:

1)缺点

不利于网站进行SEO,因为网站大量使用javascript技术,不利于spider抓取网页。

2)优点

客户端负责渲染,用户体验性好,服务端只提供数据不用关心用户界面的内容,有利于提高服务端的开发效率。

3)适用场景

对SEO没有要求的系统,比如后台管理类的系统,如电商后台管理,用户管理等。

服务端渲染:

1)优点

有利于SEO,网站通过href的url将spider直接引到服务端,服务端提供优质的网页内容给spider。

2)缺点

服务端完成一部分客户端的工作,通常完成一个需求需要修改客户端和服务端的代码,开发效率低,不利于系统的 稳定性。

3)适用场景

对SEO有要求的系统,比如:门户首页、商品详情页面等。

2Nuxt.js介绍

2.1Nuxt.js介绍

移动互联网的兴起促进了web前后端分离开发模式的发展,服务端只专注业务,前端只专注用户体验,前端大量运 用的前端渲染技术,比如流行的vue.js、react框架都实现了功能强大的前端渲染。

但是,对于有SEO需求的网页如果使用前端渲染技术去开发就不利于SEO了,有没有一种即使用vue.js、react的前 端技术也实现服务端渲染的技术呢?其实,对于服务端渲染的需求,vue.js、react这样流行的前端框架提供了服务端渲染的解决方案。

从上图可以看到:

react框架提供next.js实现服务端渲染。

vue.js框架提供Nuxt.js实现服务端渲染。

2.2Nuxt.js工作原理

下图展示了从客户端请求到Nuxt.js进行服务端渲染的整体的工作流程:

1、用户打开浏览器,输入网址请求到Node.js

2、部署在Node.js的应用Nuxt.js接收浏览器请求,并请求服务端获取数据

3、Nuxt.js获取到数据后进行服务端渲染 4、Nuxt.js将html网页响应给浏览器

Nuxt.js使用了哪些技术?

Nuxt.js使用Vue.js+webpack+Babel三大技术框架/组件,如下图:

Babel 是一个js的转码器,负责将ES6的代码转成浏览器识别的ES5代码。

Webpack是一个前端工程打包工具。Vue.js是一个优秀的前端框架。

Nuxt.js的特性有哪些?

基 于 Vue.js

自动代码分层服务端渲染

强大的路由功能,支持异步数据静态文件服务

ES6/ES7 语法支持

打包和压缩 JS 和 CSS HTML头部标签管理本地开发支持热加载集成ESLint

支持各种样式预处理器: SASS、LESS、 Stylus等等

3Nuxt.js基本使用

3.1创建Nuxt工程

nuxt.js有标准的目录结构,官方提供了模板工程,可以模板工程快速创建nuxt项目。

模板工程地址:https://github.com/nuxt-community/starter-template/archive/master.zip

本项目提供基于Nuxt.js的封装工程,基于此封装工程开发搜索前端,见“资料”–》xc-ui-pc-portal.zip,解压

xc-ui-pc-portal.zip到本项目前端工程目录下。

本前端工程属于门户的一部分,将承载一部分考虑SEO的非静态化页面。

本工程基于Nuxt.js模板工程构建,Nuxt.js使用1.3版本,并加入了今后开发中所使用的依赖包,直接解压本工程即 可使用。

3.2目录结构

本工程的目录结构如下:

‐资源目录
资源目录 assets 用于组织未编译的静态资源如 LESS、SASS 或 JavaScript。
‐组件目录
组件目录 components 用于组织应用的 Vue.js 组件。Nuxt.js 不会扩展增强该目录下 Vue.js 组件,即这些组件不会像页面组件那样有 asyncData 方法的特性。
‐布局目录
布局目录 layouts 用于组织应用的布局组件。该目录名为Nuxt.js保留的,不可更改。
‐中间件目录
middleware 目录用于存放应用的中间件。
‐页面目录
页面目录 pages 用于组织应用的路由及视图。Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。
该目录名为Nuxt.js保留的,不可更改。
‐插件目录
插件目录 plugins 用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。
‐静态文件目录
静态文件目录 static 用于存放应用的静态文件,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下。
举个例子: /static/logo.png 映射至 /logo.png
该目录名为Nuxt.js保留的,不可更改。
‐Store 目 录
store 目录用于组织应用的 Vuex 状态树 文件。 Nuxt.js 框架集成了 Vuex 状态树 的相关功能配置,在 store 目录下创建一个 index.js 文件可激活这些配置。
该目录名为Nuxt.js保留的,不可更改。
‐nuxt.config.js 文 件
nuxt.config.js 文件用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。该文件名为Nuxt.js保留的,不可更改。
‐package.json 文 件
package.json 文件用于描述应用的依赖关系和对外暴露的脚本接口。
该文件名为Nuxt.js保留的,不可更改。

nuxt.js 提供了目录的别名,方便在程序中引用:

3.3页面布局

页面布局就是页面内容的整体结构,通过在layouts目录下添加布局文件来实现。在layouts 根目录下的所有文件都属于个性化布局文件,可以在页面组件中利用 layout 属性来引用。

一个例子:

1、定义:layouts/test.vue布局文件,如下:

注意:布局文件中一定要加 <nuxt/> 组件用于显示页面内容。

<template>

<div>

<div>这里是头</div>

<nuxt/>

<div>这里是尾</div>

</div>

</template>

<script>

export default {

}

</script>

<style>

</style>

2、在pages目录创建user目录,并创建index.vue页面

在 pages/user/index.vue 页面里, 可以指定页面组件使用 test 布局,代码如下:

<template>

<div>

测试页面

</div>

</template>

<script>

export default{ layout:'test'

}

</script>

<style>

</style>

3、测试,请求:http://localhost:10000/user,如果如下:

这里是头测试页面这里是尾

3.4路由

3.4.1基础路由

Nuxt.js 依据目录结构自动生成 vue-router 模块的路由配置。

Nuxt.js根据pages的目录结构及页面名称定义规范来生成路由,下边是一个基础路由的例子:

假设的目录结构如下:

pages/
‐‐| user/
‐‐‐‐‐| index.vue
‐‐‐‐‐| one.vue

那么,Nuxt.js 自动生成的路由配置如下:

router: { routes: [
{
name: 'user',
path: '/user',
component: 'pages/user/index.vue'
},
{
name: 'user‐one',
path: '/user/one',
component: 'pages/user/one.vue'
}
]
}

index.vue代码如下:

<template>
<div>
用户管理首页
</div>
</template>
<script>
export default{ layout:"test"
}
</script>
<style>
</style>

one.vue代码如下:

<template>
<div>
one页面
</div>
</template>
<script>
export default{ layout:"test"
}
</script>
<style>
</style>

分别访问如下链接进行测试:

http://localhost:10000/user http://localhost:10000/user/one

3.4.2嵌套路由

你可以通过 vue-router 的子路由创建 Nuxt.js 应用的嵌套路由。

创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件。

别忘了在父级 Vue 文件内增加假设文件结构如:

pages/
‐‐| user/
‐‐‐‐‐| _id.vue
‐‐‐‐‐| index.vue
‐‐| user.vue

Nuxt.js 自动生成的路由配置如下:

router: { routes: [
{
path: '/user',
component: 'pages/user.vue', children: [
{
path: '',
component: 'pages/user/index.vue', name: 'user'
},
{
path: ':id',
component: 'pages/user/_id.vue', name: 'user‐id'
}
]
}
]
}

将user.vue文件创建到与user目录的父目录下,即和user目录保持平级。

<template>
<div>
用户管理导航,<nuxt‐link :to="'/user/101'">修改</nuxt‐link>
<nuxt‐child/>
</div>
</template>
<script>
export default{ layout:"test"
}
</script>
<style>
</style>

_id.vue页面实现了向页面传入id参数,页面内容如下:

<template>
<div>
修改用户信息{{id}}
</div>
</template>
<script>
export default{ layout:"test", data(){
return {
id:''
}
},
mounted(){
this.id = this.$route.params.id; console.log(this.id)
}
}
</script>
<style>
</style>

测试:http://localhost:10000/user

点击修改:

3.6获取数据

3.6.1asyncData 方法

Nuxt.js 扩展了 Vue.js,增加了一个叫 asyncData 的方法,

方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。 在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用返回的数据一并返回给当前组件。方法来获取数据,Nuxt.js 会将返回的数据融合组件方法

注意:由于对象。

例子:

方法是在组件 初始化 前被调用的,所以在方法内是没有办法通过来引用组件的实例

在上边例子中的user/_id.vue中添加,页面代码如下:

<template>
<div>
修改用户信息{{id}},名称:{{name}}
</div>
</template>
<script>
export default{ layout:'test',
//根据id查询用户信息
asyncData(){ console.log("async方法") return {
name:'黑马程序员'
}
},
data(){ return {
id:''
}
},
mounted(){
this.id = this.$route.params.id;
}
}
</script>
<style>
</style>

此方法在服务端被执行,观察服务端控制台打印输出“async方法”。

此方法返回data模型数据,在服务端被渲染,最后响应给前端,刷新此页面查看页面源代码可以看到name模型数据已在页面源代码中显示。

3.6.2async /await方法

使用async 和 await配合promise也可以实现同步调用,nuxt.js中使用async/await实现同步调用效果。1、先测试异步调用,增加a、b两个方法,并在mounted中调用。

methods:{
a(){
return new Promise(function(resolve,reject){ setTimeout(function () {
resolve(1)
},2000)
})
}, b(){
return new Promise(function(resolve,reject){ setTimeout(function () {
resolve(2)
},1000)
})
}
},
mounted(){
this.a().then(res=>{ alert(res)
console.log(res)
})
this.b().then(res=>{ alert(res) console.log(res)
})
}

2、使用async/await完成同步调用

async asyncData({ store, route }) { console.log("async方法")
var a = await new Promise(function (resolve, reject) { setTimeout(function () {
console.log("1") resolve(1)
},2000)
});
var a = await new Promise(function (resolve, reject) { setTimeout(function () {
console.log("2") resolve(2)
},1000)
});
return {
name:'黑马程序员'
}
},

观察服务端控制台发现是按照a、b方法的调用顺序输出1、2,实现了使用async/await完成同步调用。

3搜索前端开发

3.1搜索页面

3.1.1需求分析

观察服务端控制台发现是按照a、b方法的调用顺序输出1、2,实现了使用async/await完成同步调用。

3搜索前端开发

3.1搜索页面

3.1.1需求分析

<template>
<div>
<Header />
<nuxt/>
<Footer />
</div>
</template>
<script>
import Footer from '../components/Footer.vue' import Header from '../components/Header.vue' export default {
components: { Header, Footer
}
}
</script>
<style>
</style>

3.1.3Nginx代理配置

搜索页面中以/static开头的静态资源通过nginx解析,如下:

/static/plugins:指向门户目录下的plugins目录。

/static/css:指向门户目录下的的css目录

修改Nginx中www.xuecheng.com虚拟主机的配置:

#静态资源,包括系统所需要的图片,js、css等静态资源location /static/img/ {
alias	F:/develop/xc_portal_static/img/;
}
location /static/css/ {
alias	F:/develop/xc_portal_static/css/;
}
location /static/js/ {
alias	F:/develop/xc_portal_static/js/;
}
location /static/plugins/ {
alias	F:/develop/xc_portal_static/plugins/;
add_header Access‐Control‐Allow‐Origin http://ucenter.xuecheng.com; add_header Access‐Control‐Allow‐Credentials true;
add_header Access‐Control‐Allow‐Methods GET;
}

配置搜索Url,下图是Nginx搜索转发流程图:

用户请求/course/search时Nginx将请求转发到nuxt.js服务,nginx在转发时根据每台nuxt服务的负载情况进行转 发,实现负载均衡。

本教程开发环境Nuxt.js服务和www.xuecheng.com虚拟机主在同一台计算机,使用同一个nginx,配置如下:

#前端门户课程搜索
location ^~ /course/search {
proxy_pass http://dynamic_portal_server_pool;
}
#后端搜索服务
location /openapi/search/ {
proxy_pass http://search_server_pool/search/;
}
#分类信息
location /static/category/ {
proxy_pass http://static_server_pool;
}
#前端动态门户
upstream dynamic_portal_server_pool{ server 127.0.0.1:10000 weight=10;
}
#后台搜索(公开api)
upstream search_server_pool{
server 127.0.0.1:40100 weight=10;
}

其它配置:

#开发环境webpack定时加载此文件location ^~ / webpack_hmr {
proxy_pass http://dynamic_portal_server_pool/ webpack_hmr;
}
#开发环境nuxt访问_nuxt location ^~ /_nuxt/ {
proxy_pass http://dynamic_portal_server_pool/_nuxt/;
}

在静态虚拟主机中添加:

#学成网静态资源server {
listen	91; server_name localhost;
#分类信息
location /static/category/ {
alias F:/develop/xuecheng/static/category/;
}
...

3.1.4搜索页面

创建搜索页面如下:

3.1.4搜索页面

创建搜索页面如下:

//配置文件

let config = require('~/config/sysConfig') import querystring from 'querystring' import * as courseApi from '~/api/course' export default {

head() { return {

title: '传智播客‐一样的教育,不一样的品质',

meta: [

{charset: 'utf‐8'},

{name: 'description', content: '传智播客专注IT培训,Java培训,Android培训,安卓培训,PHP培

训,C++培训,网页设计培训,平面设计培训,UI设计培训,移动开发培训,网络营销培训,web前端培训,云计算大数据培训, 全栈工程师培训,产品经理培训。'},

{name: 'keywords', content: this.keywords}

],

link: [

{rel: 'stylesheet', href: '/static/plugins/normalize‐css/normalize.css'},

{rel: 'stylesheet', href: '/static/plugins/bootstrap/dist/css/bootstrap.css'},

{rel: 'stylesheet', href: '/static/css/page‐learing‐list.css'}

]

}

},

<script>

//配置文件

let config = require('~/config/sysConfig') import querystring from 'querystring' import * as courseApi from '~/api/course' export default {

head() { return {

title: '传智播客‐一样的教育,不一样的品质',

meta: [

{charset: 'utf‐8'},

{name: 'description', content: '传智播客专注IT培训,Java培训,Android培训,安卓培训,PHP培 训,C++培训,网页设计培训,平面设计培训,UI设计培训,移动开发培训,网络营销培训,web前端培训,云计算大数据培训, 全栈工程师培训,产品经理培训。'},

{name: 'keywords', content: this.keywords}

],

link: [

{rel: 'stylesheet', href: '/static/plugins/normalize‐css/normalize.css'},

{rel: 'stylesheet', href: '/static/plugins/bootstrap/dist/css/bootstrap.css'},

{rel: 'stylesheet', href: '/static/css/page‐learing‐list.css'}

]

}

},

async asyncData({ store, route }) { return {

courselist: {},

first_category:{},

second_category:{}, mt:'',

st:'',

grade:'',

keyword:'', total:0,

imgUrl:config.imgUrl

}

},

data() { return {

courselist: {}, first_category:{}, second_category:{}, mt:'',

st:'',

grade:'',

keyword:'', imgUrl:config.imgUrl, total:0,//总记录数page:1,//页码page_size:12//每页显示个数

}

},

watch:{//路由发生变化立即搜索search表示search方法'$route':'search'

},

methods: {

//分页触发handleCurrentChange(page) {

},

//搜索方法search(){

//刷新当前页面

window.location.reload();

}

}

}

</script>

3.1.5测试

重启Nginx,请求:http://www.xuecheng.com/course/search,页面效果如下:

3.2查询全部

3.2.1需求分析

初次进入页面,没有输入任何查询条件,默认查询全部课程,分页显示。

3.2.2API方法

在api目录创建本工程所用的api方法类,api方法类使用了public.js等一些抽取类:

/api/public.js抽取axios 的基础方法

/api/util.js工具类

/con?g/sysCon?g.js系统配置类,配置了系统参数变量

创建course.js,作为课程相关业务模块的api方法类。

import http from './public' import qs from 'qs'
let config = require('~/config/sysConfig') let apiURL = config.apiURL
let staticURL = config.staticURL
if (typeof window === 'undefined') { apiURL = config.backApiURL staticURL = config.backStaticURL
}
/*搜索*/
export const search_course = (page,size,params) => { let querys = qs.stringify(params);
return http.requestQuickGet(apiURL+"/search/course/list/"+page+"/"+size+"?"+querys);
}

3.2.3搜索方法

实现思路如下:

1、用户请求本页面到达node.js

2、在asyncData方法中向服务端请求查询课程 3、asyncData方法执行完成开始服务端渲染

在asyncData中执行搜索,代码如下:

async asyncData({ store, route }) {//服务端调用方法
//搜索课程
let page = route.query.page; if(!page){
page = 1;
}else{
page = Number.parseInt(page)
}
console.log(page);
//请求搜索服务,搜索服务
let course_data = await courseApi.search_course(page,2,route.query); console.log(course_data)
if (course_data && course_data.queryResult ) { let keywords = ''
let mt='' let st='' let grade=''
let keyword=''
let total = course_data.queryResult.total if( route.query.mt){
mt = route.query.mt
}
if( route.query.st){ st = route.query.st
}
if( route.query.grade){ grade = route.query.grade
}
if( route.query.keyword){ keyword = route.query.keyword
}
return {
courselist: course_data.queryResult.list,//课程列表keywords:keywords,
mt:mt,
st:st,
grade:grade, keyword:keyword, page:page, total:total, imgUrl:config.imgUrl
}
}else{
return { courselist: {},
first_category:{}, second_category:{}, mt:'',
st:'',
grade:'',
keyword:'', page:page, total:0,
imgUrl:config.imgUrl
}
}
}

3.2.5 页面

在页面中展示课程列表。

<div class="content‐list">
<div class="recom‐item" v‐for="(course, index) in courselist">
<nuxt‐link :to="'/course/detail/'+course.id+'.html'" target="_blank">
<div v‐if="course.pic">
<p><img :src="imgUrl+'/'+course.pic" width="100%" alt=""></p>
</div>
<div v‐else>
<p><img src="/img/widget‐demo1.png" width="100%" alt=""></p>
</div>
<ul >
<li class="course_title"><span v‐html="course.name"></span></li>
<li style="float: left"><span v‐if="course.charge == '203001'">免费</span>
<span v‐if="course.charge == '203002'">¥{{course.price | money}}</span>
<!‐‐ <em> ? </em>‐‐> <!‐‐<em>1125人在学习</em>‐‐></li>
</ul>
</nuxt‐link>
</div>
<li class="clearfix"></li>
</div>

3.3分页查询

3.3.1服务端代码

...
//分页
//当前页码
if(page<=0){
page = 1;
}
//起始记录下标
int from = (page ‐1) * size; searchSourceBuilder.from(from); searchSourceBuilder.size(size);
...

3.3.2前端代码

使用Element-UI的el-pagination分页插件:

<div style="text‐align: center">
<el‐pagination background
layout="prev, pager, next" @current‐change="handleCurrentChange"
:total="total"
:page‐size="page_size"
:current‐page="page" prev‐text="上一页" next‐text="下一页">
</el‐pagination>
</div>

定义分页触发方法:

methods:{
//分页触发handleCurrentChange(page) {
this.page = page this.$route.query.page = page
let querys = querystring.stringify(this.$route.query) window.location = '/course/search?'+querys;
}
...

3.4按分类搜索

3.4.1需求分析

1、通过一级分类搜索

2、选择一级分类后将显示下属的二级分类

3、选择二分类进行搜索

4、选择一级分类的全部则表示没有按照分类搜索

5、选择一级分类的全部时二级分类不显示

3.4.2API方法

课程分类将通过页面静态化的方式写入静态资源下,通过/category/category.json可访问,通过

www.xuecheng.com/static/category/category.json即可访问。

category.json的内容如下:

我们需要定义api方法获取所有的分类在/api/course.js中添加:

/*获取分类*/
export const sysres_category = () => {
return http.requestQuickGet(staticURL+"/static/category/category.json");
}

3.4.3在asyncData中查询分类

进入搜索页面将默认显示所有一级分类,当前如果已选择一级分类则要显示所有一级分类及该一级分类下属的二级 分类。

在asyncData方法中实现上边的需求,代码如下:

async asyncData({ store, route }) {//服务端调用方法
//搜索课程
let page = route.query.page; if(!page){
page = 1;
}else{
page = Number.parseInt(page)
}
console.log(page);
//请求搜索服务,搜索服务
let course_data = await courseApi.search_course(page,2,route.query); console.log(course_data)
//查询分类
let category_data = await courseApi.sysres_category() if (course_data && course_data.queryResult ) {
//全部分类
let category = category_data.category//分部分类
let first_category = category[0].children//一级分类let second_category=[]//二级分类
let keywords = '' let mt=''
let st='' let grade=''
let keyword=''
let total = course_data.queryResult.total if( route.query.mt){
mt = route.query.mt
}
if( route.query.st){ st = route.query.st
}
if( route.query.grade){ grade = route.query.grade
}
if( route.query.keyword){ keyword = route.query.keyword
}
//遍历一级分类
for(var i in first_category){ keywords+=first_category[i].name+' '
if(mt!=''&& mt == first_category[i].id){
//取出二级分类
second_category = first_category[i].children;
// console.log(second_category) break;
}
}
return {
courselist: course_data.queryResult.list,//课程列表first_category: first_category,
second_category: second_category, keywords:keywords,
mt:mt,
st:st, grade:grade, keyword:keyword, page:page, total:total,
imgUrl:config.imgUrl
}
}else{
return { courselist: {},
first_category:{}, second_category:{}, mt:'',
st:'',
grade:'',
keyword:'', page:page, total:0,
imgUrl:config.imgUrl
}
}
}

3.3.4页面

在页面显示一级分类及二级分类,需要根据当前是否选择一级分类、是否选择二分类显示页面内容。

<ul>
<li>一级分类:</li>
<li v‐if="mt!=''"><nuxt‐link class="title‐link" :to="'/course/search? keyword='+keyword+'&grade='+grade">全部</nuxt‐link></li>
<li class="all" v‐else>全部</li>
<ol>
<li v‐for="category_v in first_category">
<nuxt‐link class="title‐link all" :to="'/course/search?keyword='+keyword+'&mt=' + category_v.id" v‐if="category_v.id == mt">{{category_v.name}}</nuxt‐link>
<nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + category_v.id" v‐else>{{category_v.name}}</nuxt‐link>
</li>
</ol>
<!‐‐<ol>
<li>数据分析</li>
<li>机器学习工程</li>
<li>前端开发工程</li>
</ol>‐‐>
</ul>
<ul>
<li>二级分类:</li>
<li v‐if="st!=''"><nuxt‐link class="title‐link" :to="'/course/search? keyword='+keyword+'&mt='+mt+'&grade='+grade">全部</nuxt‐link></li>
<li class="all" v‐else>全部</li>
<ol v‐if="second_category.length>0">
<li v‐for="category_v in second_category">
<nuxt‐link class="title‐link all" :to="'/course/search?keyword='+keyword+'&mt='+mt+'&st='
+ category_v.id" v‐if="category_v.id == st">{{category_v.name}}</nuxt‐link>
<nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt='+mt+'&st=' + category_v.id" v‐else>{{category_v.name}}</nuxt‐link>
</li>
<!‐‐ <li>大数据</li>
<li>云计算</li>‐‐>
</ol>
<!‐‐<a href="#" class="more">更多 ∨</a>‐‐>
</ul>

当用户点击分类时立即执行搜索,实现思路如下:

1)点击分类立即更改路由。

2)通过监听路由,路由更改则刷新页面。

1)创建搜索方法

search(){
//刷新当前页面window.location.reload();
}

2)定义watch

通过vue.js的watch可以实现监视某个变量,当变量值出现变化时执行某个方法。 实现思路是:

1、点击分类页面路由更改

2、通过watch监视路由,路由更改触发search方法与methods并行定义watch:

watch:{//路由发生变化立即搜索search表示search方法'$route':'search'
},

3.5按难度等级搜索

3.5.1需求分析

用户选择不同的课程难度等级去搜索课程。

3.5.2API方法

使用 search_course方法完成搜索。

3.5.3页面

按难度等级搜索思路如下:

1)点击难度等级立即更改路由。

2)通过监听路由,路由更改则立即执行search搜索方法。按难度等级搜索页面代码如下:

<ul>
<li>难度等级:</li>
<li v‐if="grade!=''">
<nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade='">全部
</nuxt‐link>
</li>
<li class="all" v‐else>全部</li>
<ol>
<li v‐if="grade=='200001'" class="all">初级</li>
<li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200001'">初级</nuxt‐link></li>
<li v‐if="grade=='200002'" class="all">中级</li>
<li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200002'">中级</nuxt‐link></li>
<li v‐if="grade=='200003'" class="all">高级</li>
<li v‐else><nuxt‐link class="title‐link" :to="'/course/search?keyword='+keyword+'&mt=' + mt+'&st='+st+'&grade=200003'">高级</nuxt‐link></li>
</ol>
</ul>

3.6高亮显示

3.6.1服务端代码

修改service的搜索方法,添加高亮设置:

...
//定义高亮
HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.preTags("<font class='eslight'>"); highlightBuilder.postTags("</font>"); highlightBuilder.fields().add(new HighlightBuilder.Field("name")); searchSourceBuilder.highlighter(highlightBuilder);
...
//解析高亮字段
for(SearchHit hit:searchHits){
CoursePub coursePub = new CoursePub();
//源文档
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//课程id
String id = (String) sourceAsMap.get("id"); coursePub.setId(id);
//取出name
String name = (String) sourceAsMap.get("name");
//取出高亮字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields(); if(highlightFields.get("name")!=null){
HighlightField highlightField = highlightFields.get("name"); Text[] fragments = highlightField.fragments();
StringBuffer stringBuffer = new StringBuffer(); for(Text text:fragments){
stringBuffer.append(text);
}
name = stringBuffer.toString();
}
coursePub.setName(name);
....

3.6.2前端代码

在search/index.vue中定义eslight样式:

<style>
.eslight{ color: red;
}
...

4集成测试

4.1需求分析

本次集成测试的目的如下:

1、测试课程发布与CMS接口是否正常。

2、测试课程发布与ES接口是否正常。

3、测试课程从创建到发布的整个过程。

4.2准备环境

1、启动MySQL、MongoDB

2、启动ElasticSearch、RabbitMQ 3、启动Eureka Server

4、启动CMS、课程管理服务、搜索服务。

5、启动Nginx、系统管理前端、教学管理前端、Nuxt.js。

相关推荐

Java 泛型大揭秘:类型参数、通配符与最佳实践

引言在编程世界中,代码的可重用性和可维护性是至关重要的。为了实现这些目标,Java5引入了一种名为泛型(Generics)的强大功能。本文将详细介绍Java泛型的概念、优势和局限性,以及如何在...

K8s 的标签与选择器:流畅运维的秘诀

在Kubernetes的世界里,**标签(Label)和选择器(Selector)**并不是最炫酷的技术,但却是贯穿整个集群管理与运维流程的核心机制。正是它们让复杂的资源调度、查询、自动化运维变得...

哈希Hash算法:原理、应用(哈希算法 知乎)

原作者:Linux教程,原文地址:「链接」什么是哈希算法?哈希算法(HashAlgorithm),又称为散列算法或杂凑算法,是一种将任意长度的数据输入转换为固定长度输出值的数学函数。其输出结果通常被...

C#学习:基于LLM的简历评估程序(c# 简历)

前言在pocketflow的例子中看到了一个基于LLM的简历评估程序的例子,感觉还挺好玩的,为了练习一下C#,我最近使用C#重写了一个。准备不同的简历:image-20250528183949844查...

55顺位,砍41+14+3!季后赛也成得分王,难道他也是一名球星?

雷霆队最不可思议的新星:一个55号秀的疯狂逆袭!你是不是也觉得NBA最底层的55号秀,就只能当饮水机管理员?今年的55号秀阿龙·威金斯恐怕要打破你的认知了!常规赛阶段,这位二轮秀就像开了窍的天才,直接...

5分钟读懂C#字典对象(c# 字典获取值)

什么是字典对象在C#中,使用Dictionary类来管理由键值对组成的集合,这类集合被称为字典。字典最大的特点就是能够根据键来快速查找集合中的值,其键的定义不能重复,具有唯一性,相当于数组索引值,字典...

c#窗体传值(c# 跨窗体传递数据)

在WinForm编程中我们经常需要进行俩个窗体间的传值。下面我给出了两种方法,来实现传值一、在输入数据的界面中定义一个属性,供接受数据的窗体使用1、子窗体usingSystem;usingSyst...

C#入门篇章—委托(c#委托的理解)

C#委托1.委托的定义和使用委托的作用:如果要把方法作为函数来进行传递的话,就要用到委托。委托是一个类型,这个类型可以赋值一个方法的引用。C#的委托通过delegate关键字来声明。声明委托的...

C#.NET in、out、ref详解(c#.net framework)

简介在C#中,in、ref和out是用于修改方法参数传递方式的关键字,它们决定了参数是按值传递还是按引用传递,以及参数是否必须在传递前初始化。基本语义对比修饰符传递方式可读写性必须初始化调用...

C#广义表(广义表headtail)

在C#中,广义表(GeneralizedList)是一种特殊的数据结构,它是线性表的推广。广义表可以包含单个元素(称为原子),也可以包含另一个广义表(称为子表)。以下是一个简单的C#广义表示例代...

「C#.NET 拾遗补漏」04:你必须知道的反射

阅读本文大概需要3分钟。通常,反射用于动态获取对象的类型、属性和方法等信息。今天带你玩转反射,来汇总一下反射的各种常见操作,捡漏看看有没有你不知道的。获取类型的成员Type类的GetMembe...

C#启动外部程序的问题(c#怎么启动)

IT&OT的深度融合是智能制造的基石。本公众号将聚焦于PLC编程与上位机开发。除理论知识外,也会结合我们团队在开发过程中遇到的具体问题介绍一些项目经验。在使用C#开发上位机时,有时会需要启动外部的一些...

全网最狠C#面试拷问:这20道题没答出来,别说你懂.NET!

在竞争激烈的C#开发岗位求职过程中,面试是必经的一道关卡。而一场高质量的面试,不仅能筛选出真正掌握C#和.NET技术精髓的人才,也能让求职者对自身技术水平有更清晰的认知。今天,就为大家精心准备了20道...

C#匿名方法(c#匿名方法与匿名类)

C#中的匿名方法是一种没有名称只有主体的方法,它提供了一种传递代码块作为委托参数的技术。以下是关于C#匿名方法的一些重要特点和用法:特点省略参数列表:使用匿名方法可省略参数列表,这意味着匿名方法...

C# Windows窗体(.Net Framework)知识总结

Windows窗体可大致分为Form窗体和MDI窗体,Form窗体没什么好细说的,知识点总结都在思维导图里面了,下文将围绕MDI窗体来讲述。MDI(MultipleDocumentInterfac...