Введение в веб-приложение предварительного просмотра фильмов, разработанное Vue

база данных внешний интерфейс Vue.js Семь Ниуюн
Введение в веб-приложение предварительного просмотра фильмов, разработанное Vue

Предстоящий праздник Праздника лодок-драконов, готовы ли вы его провести 😄. Каждый раз, когда я иду играть, я не могу не посмотреть фильм. На этот раз я пользуюсь этой возможностью, чтобы представить вам то, что я разработалПосмотрите небольшой проект для трейлеров к фильмам, я надеюсь, что каждый может пройти тест, просмотреть волну предстоящих фильмов и помочь мне протестировать его, и я смиренно приму это, если укажу на недостатки! Спасибо вам всем.

Демонстрационный адрес проекта


визуализация





Введение в проект

Фронтенд — это сборка проекта через vue-cli, а бэкенд-интерфейс написан с помощью Koa. Используются данные, связанные с фильмомpuppeteerСканирование выполняется и сохраняется в базе данных mongoDB, а трейлер загружается в облако Qiniu для снижения нагрузки на пропускную способность. К его основным функциям относятся:

  • Отображение списков фильмов
  • Детали фильма и воспроизведение трейлера
  • Фильтровать фильмы по выпуску, категории, рейтингу
  • 10 самых популярных фильмов
  • Функция поиска фильмов
  • Регистрация пользователя и вход.

Особенности, которые будут улучшены в будущем:

  • Любимые и любимые фильмы
  • Рекомендуемые места для покупки билетов в зависимости от вашего местоположения
  • Операции, связанные с информацией о пользователе
  • Автоматическое сканирование и обновление данных фильмов
  • Веб-сторона проекта, сторона апплета

техническая проблема

Проблема переключения маршрутизации статуса выпуска фильма

Статус выпуска фильма делится на горячий выпуск и предстоящий выпуск, в которых страница маршрутизации списка преобразуется с помощью параметров, 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()
}

Компоненты открытки на разные случаи жизни

На этой странице проекта используется большое количество компонентов карты, написанных мной, которые используются на странице списка, странице поиска, странице фильтра, странице списка и т. д. Его основной эффект заключается в следующем:



Но когда он находится на странице списка, все компоненты карты должны иметь ранжирование, чтобы вы могли расширить возможности компонента.propsДля этого добавьте атрибут ранга, когда он равен 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Смоделируйте браузер для доступа к веб-сайту Douban, чтобы получить название фильма, постер, doubanId, а также оценку и сохранить ее в базе данных. URL-адрес сканирования:

    const nowUrl = 'https://movie.douban.com/cinema/nowplaying/beijing/'
    const comUrl = 'https://movie.douban.com/coming'

  2. Используя открытый API, предоставленный Douban, можно получить подробную информацию о фильме, такую ​​как режиссер, актер, введение, жанр, дата выпуска и т. д., путем распространения doubanId фильма в базе данных.
  3. использоватьpuppeteerПросмотрите страницу сведений о фильме Douban, чтобы перейти на страницу трейлера, чтобы просканировать ресурсы трейлера и сохранить их в базе данных. URL-адрес сканирования:

    const url = 'https://movie.douban.com/subject/'
    

  4. Используйте NodeSDK, предоставленный Qiniu Cloud, для загрузки видеоресурсов в Qiniu Cloud Bed и сохранения возвращенного значения ключа в базе данных.Через сервер CNAME вы можете получить доступ к клипам в Qiniu Cloud. Его основной код выглядит следующим образом:

    // 上传函数
    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.Маршрут, соответствующий параметру декоратора, выполняет метод соответствующего экземпляра класса. код реализации выглядит следующим образом:

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'})

Суммировать

В целом проект относительно простой, а недочетов много.Продолжу улучшать проект в дальнейшем.Надеюсь, что друзья смогут выдвинуть свои недочеты и свои предложения. Кроме того, это мой первый раз, когда я пишу статью, у меня ограниченные навыки и я не могу писать глубокие знания, поэтому я должен использовать свой собственный проект в качестве моей первой работы. Надеюсь, вы, ребята, можете терпеть меня. Наконец, если вы чувствуете, что проект неплох, не скупитесь на свою звезду! Спасибо!

Адрес проекта GitHub