Vue開發的電影預告webApp介紹

lihaozecq發表於2018-06-15

即將迎來的端午小假期,小夥伴們都準備好怎麼度過了麼?。我每次出去玩都避免不了去看場電影,這次藉此機會向大家介紹下我開發的可以檢視電影預告片的小專案,希望大家可以去測試,瀏覽一波即將上映的電影同時可以幫助我測試一下,指出不足,我都會虛心接受的呦!謝謝大家。

專案演示地址

Vue開發的電影預告webApp介紹

效果圖

Vue開發的電影預告webApp介紹

Vue開發的電影預告webApp介紹

Vue開發的電影預告webApp介紹

Vue開發的電影預告webApp介紹

Vue開發的電影預告webApp介紹

專案介紹

前端是通過vue-cli進行構建專案,後端介面是使用Koa進行編寫的。電影相關資料是使用puppeteer進行爬取並存在mongoDB資料庫中,為減輕頻寬壓力將預告片上傳到七牛雲上。其主要功能包括:

  • 電影列表的展示
  • 電影詳情資訊及預告片播放功能
  • 根據上映情況、分類、評分進行篩選電影
  • 電影熱度前十榜單
  • 搜尋電影功能
  • 使用者的註冊與登入。

未來想完善的功能:

  • 對電影的收藏與喜歡
  • 根據所在地推薦購票地點
  • 使用者資訊相關的操作
  • 電影資料的自動爬取更新
  • 專案web端、小程式端

技術問題

電影上映狀態路由切換問題

電影上映狀態分為正在熱映與即將上映,其中list路由頁是通過引數進行轉換,1為正在上映,2為即將上映。路由配置如下:

{ 
path: '/movie', name: 'movie', component: Movie, children: [ {
path: 'all/:type', name: 'list', component: List
} ]
}複製程式碼

同路由元件引數切換不會再次觸發createdmounted生命週期函式,所以要實現引數切換重新請求資料需要在元件內導航守衛beforeRouteUpdate進行操作。其核心程式碼如下:

beforeRouteUpdate (to, from, next) { 
this.page = 1 this.max_page = 0 this.movies = [] this._getMovies(to.params.type) next()
}複製程式碼

應對不同場合的Card元件

本專案頁面中大量用自己寫的Card元件,在list頁面、搜尋頁面、篩選頁面、榜單頁面等均有使用到。其主要效果如下圖:

Vue開發的電影預告webApp介紹

Vue開發的電影預告webApp介紹

但當在榜單頁面時所有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進行爬取得到的,獲取電影資料總共分為四步:

  1. 利用puppeteer模擬瀏覽器訪問豆瓣網站獲取電影的名字、海報、doubanId、評分存入資料庫。爬取網址是:
    const nowUrl = 'https://movie.douban.com/cinema/nowplaying/beijing/'const comUrl = 'https://movie.douban.com/coming'複製程式碼
  2. 利用豆瓣提供的開放API,通過迴圈資料庫中電影doubanId來獲取到電影詳細的資訊,例如導演、演員、簡介、型別、上映日期等。
  3. 利用puppeteer瀏覽豆瓣電影詳情頁,從而跳轉到預告片頁面爬取預告片的資源,存入資料庫。爬取網址是:
    const url = 'https://movie.douban.com/subject/'複製程式碼
  4. 使用七牛雲提供的NodeSDK將視訊資源上傳到七牛雲床上,並將返回的key值存在資料庫中,通過伺服器CNAME可以訪問七牛雲上的短片。其核心程式碼如下:
    // 上傳函式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)
    }
    }
    }
    })()複製程式碼

利用Decorator修飾器定義Route路由類

本專案是通過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專案地址

Vue開發的電影預告webApp介紹

來源:https://juejin.im/post/5b22553fe51d4558b2776d80

相關文章