大家好,我是编程小6,很高兴遇见你,有问题可以及时留言哦。
即将迎来的端午小假期,小伙伴们都准备好怎么度过了么😄。我每次出去玩都避免不了去看场电影,这次借此机会向大家介绍下我开发的可以查看电影预告片的小项目,希望大家可以去测试,浏览一波即将上映的电影同时可以帮助我测试一下,指出不足,我都会虚心接受的呦!谢谢大家。
前端是通过vue-cli进行构建项目,后端接口是使用Koa进行编写的。电影相关数据是使用puppeteer进行爬取并存在mongoDB数据库中,为减轻带宽压力将预告片上传到七牛云上。其主要功能包括:
未来想完善的功能:
电影上映状态分为正在热映与即将上映,其中list路由页是通过参数进行转换,1为正在上映,2为即将上映。路由配置如下:
{
path: '/movie',
name: 'movie',
component: Movie,
children: [
{
path: 'all/:type',
name: 'list',
component: List
}
]
}
同路由组件参数切换不会再次触发created
、mounted
生命周期函数,所以要实现参数切换重新请求数据需要在组件内导航守卫中beforeRouteUpdate
进行操作。其核心代码如下:
beforeRouteUpdate (to, from, next) {
this.page = 1
this.max_page = 0
this.movies = []
this._getMovies(to.params.type)
next()
}
本项目页面中大量用自己写的Card组件,在list页面、搜索页面、筛选页面、榜单页面等均有使用到。其主要效果如下图:
但当在榜单页面时所有Card组件前都需要有排名,所以可以通过扩展组件的props
实现,新增一个rank属性,当为true时则将排名展示出来,其代码如下:
<p class="text" v-if="rank" :class="'rank-' + index">{{index}}</p>
props: {
movie: Object,
index: Number,
rank: {
type: Boolean,
default: false
}
}
电影相关数据信息是使用doubanApi结合puppeteer进行爬取得到的,获取电影数据总共分为四步:
const nowUrl = 'https://movie.douban.com/cinema/nowplaying/beijing/'
const comUrl = 'https://movie.douban.com/coming'
const url = 'https://movie.douban.com/subject/'
// 上传函数
const uploadToQiniu = async (url, key) => {
return new Promise((resolve, reject) => {
bucketManager.fetch(url, bucket, key, function (err, respBody, respInfo) {
if (err) {
reject(err)
} else {
if (respInfo.statusCode == 200) {
resolve({key})
} else {
reject(respBody)
}
}
})
})
}
// 循环数据库中数据将上传后返回的keuy值存在数据库
;(async () => {
const movies = await Movie.find({
$or: [
{videoKey: {$exists: false}},
{videoKey: null},
{videoKey: ''}
]
})
for (let i = 0; i < movies.length; i++) {
let movie = movies[i]
if (movie.video && !movie.videoKey) {
try {
let videoData = await uploadToQiniu(movie.video, nanoid() + '.mp4')
let posterData = await uploadToQiniu(movie.poster, nanoid() + '.jpg')
let coverData = await uploadToQiniu(movie.cover, nanoid() + '.jpg')
const arr = []
for (let i = 0; i < movie.images.length; i++) {
let { key } = await uploadToQiniu(movie.images[i], nanoid() + '.jpg')
if (key) {
arr.push(key)
}
}
movie.images = arr
for (let j = 0; j < movie.casts.length; j++) {
if (!movie.casts[j].avatar) continue;
let { key } = await uploadToQiniu(movie.casts[j].avatar, nanoid() + '.jpg')
if (key) {
movie.casts[j].avatar = key
}
}
if (videoData.key) {
movie.videoKey = videoData.key
}
if (posterData.key) {
movie.posterKey = posterData.key
}
if (coverData.key) {
movie.coverKey = coverData.key
}
await movie.save()
} catch (error) {
console.log(error)
}
}
}
})()
本项目是通过koa-router进行拦截请求,并进行数据库相关操作,由于接口数量较多,所以可以采用Decorator方式去定义路由,更利于开发与维护。例如:
// 利用Decorator修饰类的行为
@controller('api/client/movie')export class movieController {
@get('/get_all') // 获取符合条件的电影条数
@required({
query: ['page_size', 'page']
})
async getAll (ctx, next) {
const { page_size, page, type } = ctx.query
const data = await getAllMovies(page_size, page, type)
ctx.body = {
code: 0,
errmsg: '',
data
}
}
......
}
如果想让上述代码有效,需要在项目运行时将修饰器函数定义好,并且载入koa-router中间件,符合修饰器参数的路由则执行相关类实例的方法,其Route类实现代码如下:
export class Route {
constructor (app, apiPath) {
this.app = app
this.apiPath = apiPath
this.router = new Router()
}
/** * 遍历routerMap,得到请求路径和方法,路径和controller装饰器的参数拼接 * 通过koa-router实例调用请求方法(请求路径, 对应的路由中间件) * 通过koa实例载入router中间件 */
init () {
glob.sync(path.resolve(__dirname, this.apiPath, './**/*.js')).forEach(require)
for (let [conf, controllers] of routerMap) {
controllers = toArray(controllers)
const prefixPath = conf.target[symbolPrefix]
prefixPath && (prefixPath = normalizePath(prefixPath))
const routerPath = prefixPath + conf.path
this.router[conf.method](routerPath, ...controllers)
}
this.app.use(this.router.routes()).use(this.router.allowedMethods())
}
}
// 将path统一成 '/xxx'
const normalizePath = path => path.startsWith('/')? path : `/${path}`
// 将路由类,请求路径以及方法,装饰器对应的方法存入routerMap中
export const router = conf => (target, key, desc) => {
conf.path = normalizePath(conf.path)
routerMap.set({
target,
...conf
}, target[key])
}
// 将path挂载到路由类的prototyp上,实例上可以访问
export const controller = path => target => (target.prototype[symbolPrefix] = path)
export const get = path => router({ path, method: 'get'})
项目总体来说较为简单,而且有很多不足的地方,之后我也会一直完善项目,希望小伙伴们可以提出不足,以及自己的建议。还有这是我第一次写文章,水平有限,写不出深层次的知识,只好拿自己项目作为处女作😂。希望各位小伙伴多多包涵。最后,如果感觉项目还不错的,不要吝啬你的star呦!谢谢!
GitHub项目地址