即將迎來的端午小假期,小夥伴們都準備好怎麼度過了麼😄。我每次出去玩都避免不了去看場電影,此次藉此機會向你們介紹下我開發的能夠查看電影預告片的小項目,但願你們能夠去測試,瀏覽一波即將上映的電影同時能夠幫助我測試一下,指出不足,我都會虛心接受的呦!謝謝你們。javascript
前端是經過vue-cli進行構建項目,後端接口是使用Koa進行編寫的。電影相關數據是使用puppeteer進行爬取並存在mongoDB數據庫中,爲減輕帶寬壓力將預告片上傳到七牛雲上。其主要功能包括:html
將來想完善的功能:前端
電影上映狀態分爲正在熱映與即將上映,其中list路由頁是經過參數進行轉換,1爲正在上映,2爲即將上映。路由配置以下:vue
{
path: '/movie',
name: 'movie',
component: Movie,
children: [
{
path: 'all/:type',
name: 'list',
component: List
}
]
}複製代碼
同路由組件參數切換不會再次觸發created
、mounted
生命週期函數,因此要實現參數切換從新請求數據須要在組件內導航守衛中beforeRouteUpdate
進行操做。其核心代碼以下:java
beforeRouteUpdate (to, from, next) {
this.page = 1
this.max_page = 0
this.movies = []
this._getMovies(to.params.type)
next()
}複製代碼
本項目頁面中大量用本身寫的Card組件,在list頁面、搜索頁面、篩選頁面、榜單頁面等均有使用到。其主要效果以下圖:git
但當在榜單頁面時全部Card組件前都須要有排名,因此能夠經過擴展組件的props
實現,新增一個rank屬性,當爲true時則將排名展現出來,其代碼以下:github
<p class="text" v-if="rank" :class="'rank-' + index">{{index}}</p>複製代碼
props: {
movie: Object,
index: Number,
rank: {
type: Boolean,
default: false
}
}複製代碼
電影相關數據信息是使用doubanApi結合puppeteer進行爬取獲得的,獲取電影數據總共分爲四步:web
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方式去定義路由,更利於開發與維護。例如:vue-cli
// 利用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呦!謝謝!