All Projects → xingbofeng → Douban Movie

xingbofeng / Douban Movie

Licence: mit
🎥The douban-movie Application built with webpack + vue + vuex + vue-router + iView.

Projects that are alternatives of or similar to Douban Movie

Vue Wechat
用Vue.js开发微信app
Stars: ✭ 1,653 (+1024.49%)
Mutual labels:  vue-cli, vuex, vue-router
Eleme
restructure..
Stars: ✭ 1,635 (+1012.24%)
Mutual labels:  vue-cli, vuex, vue-router
Xyy Vue
🎨基于vue+vue-router+vuex+axios+webpack开发的个人Demo《Qu约》
Stars: ✭ 1,316 (+795.24%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Wechat
🔥 基于Vue2.0高仿微信App的单页应用
Stars: ✭ 1,832 (+1146.26%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Cli Multi Page
基于vue-cli模板的多页面多路由项目,一个PC端页面入口,一个移动端页面入口,且有各自的路由, vue+webpack+vue-router+vuex+mock+axios
Stars: ✭ 145 (-1.36%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Taobao
😀史诗级😀巨仿淘宝手机端(vue2.0以上+vue-router+axios+vuex+webpack+betterscroll等)
Stars: ✭ 85 (-42.18%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Cli Multipage Bootstrap
vue-cli-multipage-bootstrap demo with vue2+vue-router+vuex+bootstrap+markdown for learning vue2.0
Stars: ✭ 105 (-28.57%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Admin
基于Vue2、element ui、vue-cli、vuex、vue-router、axios 、echarts后台管理系统demo. 权限管理,用户管理,菜单管理。无限级菜单,下拉树形选择框
Stars: ✭ 1,135 (+672.11%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Mobile Cli
🚀 Vue移动端多页应用脚手架
Stars: ✭ 112 (-23.81%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Meizi
vue最新实战项目,vue2 + vuex + webpack + es6 干货多多,新手福利
Stars: ✭ 1,476 (+904.08%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Shoppingcart
ShoppingCart (Ecommerce) 🛒 Application using Vuejs, + Node.js + Express + MongoDB 🚀🤘
Stars: ✭ 141 (-4.08%)
Mutual labels:  vue-cli, vuex, vue-router
Basix Admin
Get Free and Premium Vue.js Bootstrap v4 Admin Dashboard Templates
Stars: ✭ 138 (-6.12%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Awesome Template
☀基于[email protected]的[email protected] 项目模板;集成各种常用组件、轮子、最佳实践;
Stars: ✭ 70 (-52.38%)
Mutual labels:  vue-cli, vuex, vue-router
Egg Vue Webpack Boilerplate
Egg Vue Server Side Render (SSR) / Client Side Render (CSR)
Stars: ✭ 1,302 (+785.71%)
Mutual labels:  vue-cli, vuex, vue-router
Vuejs Fujun
vue/vue-router/vuex/axios 仿肤君试用小程序
Stars: ✭ 68 (-53.74%)
Mutual labels:  vue-cli, vuex, vue-router
Vuejs 2 Na Pratica
Curso totalmente prático de Vue.js da Webschool
Stars: ✭ 101 (-31.29%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Plan
使用vue+vue-router+vuex+boostrap实现计划表系统
Stars: ✭ 33 (-77.55%)
Mutual labels:  vue-cli, vuex, vue-router
Pretty Vendor
[零食商贩] - 基于vue全家桶 + koa2 + sequelize + mysql 搭建的移动商城应用
Stars: ✭ 57 (-61.22%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Mmplayer
🎵 基于 Vue 的在线音乐播放器(PC) Online music player
Stars: ✭ 1,632 (+1010.2%)
Mutual labels:  vue-cli, vuex, vue-router
Vue Todos
vue最新实战项目教程,从零开始,一步一个脚印,循序渐进。跟着我一起学习vue吧!
Stars: ✭ 1,659 (+1028.57%)
Mutual labels:  vue-cli, vuex, vue-router

douban-movie

downloads-image npm-image travis-image appveyor-image codeship-image david-dm-image david-dm-dev-image

It is the douban-movie Application built with webpack + vue + vuex + vue-router + iView.

中文文档

because the limit of open API is 40 times / minute.I recommended you clone this project to you own local environment.

Enter GitHub to see code!

Thanks for you support, waiting for your issue, pr, star or follow!I will release more interesting project in the future!

Online

Click Here

Or you can clone this project to you own local environment, then enjoy this project online:

git clone https://github.com/xingbofeng/douban-movie.git

cd douban-movie

yarn install

yarn run server

Then open your browser, and go to http://localhost:3000/ to enjoy it!

Development

git clone https://github.com/xingbofeng/douban-movie.git

cd douban-movie

yarn install 

yarn run dev

Then open your browser, and go to http://localhost:8080/ to enjoy it!

Preview

Technology stack

  • vue + vuex+ vue-router vue based project
  • webpack + webpack-dev-server + http-proxy-middleware dev environment we use webpack-dev-server and http-proxy-middleware.
  • express + http-proxy-middleware online we use express and http-proxy-middleware
  • iView UI components library
  • vue-lazyload help us lazyload images
  • rem + flex + grid responsive layout in mobile
  • yarn package manager.
  • postman test our interface

Functions

src/containers/Home.vue

  • [x] hot-movie, comming-soon, top250 and us-box.
  • [x] horizontal scrolling.
  • [x] preview the score of the movie.

src/containers/Search.vue

You should input some word which is you want to search, then press Enter and begin to search, or you can click the button of search.

  • [x] search.
  • [x] save the hot search record.

src/containers/More.vue

  • [x] preview the score of the movie.
  • [x] loading when you scroll.
  • [x] save the data you accessd to vuex.

src/containers/MovieDetail.vue

  • [x] score of the movie.
  • [x] information of the movie.
  • [x] actors list.
  • [x] the plot.
  • [x] save the data you accessd to vuex.

src/containers/Tah.vue

  • [x] turn the page.
  • [x] lazyload images.
  • [x] preview the information of the movie.
  • [x] cache your browsing.

Directory

|
|—— build 
|—— config
|—— server
| |—— index.js : the entry of the server.
| |—— static/ : static files after packaging.
| |__ index.html : the entry of this application.
|
|——src : dev resources.
| |—— assets : images
| |—— components/
| |    |____ Common/ : reusable components
| |    |____ ... : other components of the own page.
| |
| |—— router/
| |    |____ index.js : the entry of router.
| |    |____ server.js : export ajax function.
| |    |____ serverConfig.js : export the server detail.
| |    |____ routes/ : every page's router, changing the state of `vuex` at its lifecycle function.
| |
| |—— store : vuex
| |—— App.vue : douban-movieSPA
| |__ main.js : the entry of douban-movieSPA
|
|__ static : static files

What did I learnt in this project?

How to save data in vuex?

{
  [`${A.id}`]: A,
  ...store.state
}

see the codes of /src/router/routes.

beforeEnter: (to, before, next) => {
  const currentMovieId = to.params.currentMovieId;
  if (store.state.moviedetail.currentMovie[`${currentMovieId}`]) {
    store.commit(types.LOADING_FLAG, false);
    next();
    return;
  }
  store.commit(types.LOADING_FLAG, true);
  currentMovie(currentMovieId).then((currentMovieDetail) => {
    // 成功则commit后台接口的数据,并把NET_ERROR的数据置空,并把加载中的状态置为false。
    const id = currentMovieDetail.id;
    store.commit(types.CURRENT_MOVIE, {
      [`${id}`]: currentMovieDetail,
      ...store.state.moviedetail.currentMovie,
    });
    store.commit(types.LOADING_FLAG, false);
    store.commit(types.NET_STATUS, '');
    document.title = `${currentMovieDetail.title} - 电影 - 豆瓣`;
  }).catch((error) => {
    document.title = '出错啦 Oops… - 豆瓣';
    store.commit(types.NET_STATUS, error);
    store.commit(types.LOADING_FLAG, false);
  });
  next();
}

How to turn the page and load?

We set a state named currentPage,everytime we change this state, the page will rerender.

see the codes of /src/containers/Tag.vue.

computed: {
  ...mapState({
    tagData(state) {
      return state.tag.tagData[`${this.$route.params.currentTagId}`];
    },
  }),

  subjects() {
    return this.tagData.subjects.slice(
      (this.currentPage - 1) * 10,
      this.currentPage * 10,
    );
  },
},

methods: {
  ...mapActions(['getMoreTagData']),
  changePage(flag) {
    const currentTagId = this.$route.params.currentTagId;
    const { start, count } = this.tagData;
    // 第一页不能往前翻页,最后一页不能往后翻页。
    if ((this.currentPage === 1 && flag === 'reduce') ||
      (this.currentPage === Math.ceil(this.tagData.total / 10) && flag === 'add')
    ) {
      return;
    }
    if (flag === 'add') {
      this.currentPage = this.currentPage + 1;
      // 每次请求十条数据
      this.getMoreTagData({
        tag: currentTagId,
        count: 10,
        start: count + start,
      });
      // 需要使用localStorge保存当前的页码信息,再次进入可以有这个页码信息。
      const doubanMovieCurrentPage = JSON.parse(window.localStorage.doubanMovieCurrentPage);
      window.localStorage.doubanMovieCurrentPage = JSON.stringify({
        ...doubanMovieCurrentPage,
        [`${currentTagId}`]: this.currentPage,
      });
    } else {
      this.currentPage = this.currentPage - 1;
    }
    window.scrollTo(0, 0);
  },

How to scroll and load?

like the waterfall layout,when user scroll to some location , we request the data form back-end.

see the codes of src/containers/More.vue

handleScroll() {
  // 函数的作用是滚动加载电影详情信息
  // 判断是否为请求后台中的状态,如果是则返回
  const { start, count, total } = this.currentSeeMore;
  if (!this.requestFlag) {
    return;
  }
  // 不同浏览器top展现会不一致
  let top = window.document.documentElement.scrollTop;
  if (top === 0) {
    top = document.body.scrollTop;
  }
  const clientHeight = document.getElementById('app').clientHeight;
  const innerHeight = window.innerHeight;
  const proportion = top / (clientHeight - innerHeight);
  // 但如果已把所有数据加载完毕了,则不请求
  if (proportion > 0.6 && (start + count) < total) {
    this.getMoreData({
      count,
      start: start + count,
      title: this.$route.params.title,
    });
    this.requestFlag = false;
  }
}

How to throttle when user scroll?

To implementation throttle of scrolling, we set a flag.when flag === true, we return the scroll function.

see the codes of src/containers/More.vue.

scrolling() {
  // scrolling函数用于作函数节流
  if (this.scrollFlag) {
    return;
  }
  this.scrollFlag = true;
  setTimeout(() => {
    this.handleScroll();
    this.scrollFlag = false;
  }, 20);
}

404 and loading

set two states in vuex.

see the codes of src/App.vue

<template>
  <div id="app">
    <net-error
      v-if="netStatus"
      :netStatus="netStatus"
    />
    <loading
      v-else-if="!netStatus && loadingFlag"
    />
    <router-view v-else></router-view>
  </div>
</template>

How to change the asynchronous state?

We often use universal-router in Reactproject.in that case, we can dispatch an action to change the state of redux, when we entry/change the router, and we use async/await fuction.

Like this code of React:

async action({ store, params }) {
  // 判断store里的id和当前id是否一致,若一致,则不请求后台
  console.log("chapter")
  const chapterInfos = store.getState().home.chapterInfos;
  if (Object.keys(chapterInfos).length === 0 ||
    chapterInfos.subject.id !== parseInt(params.chapter, 10)) {
    await store.dispatch(chapter(params.chapter));
  }
}

And in this project, I had imitated it!

see the codes of /src/router/routes

beforeEnter: (to, before, next) => {
  document.title = '电影 - 豆瓣';
  if (Object.keys(store.state.home.homeData).length !== 0) {
    store.commit(types.LOADING_FLAG, false);
    next();
    return;
  }
  store.commit(types.LOADING_FLAG, true);
  Promise.all([
    hotMovie(8, 0),
    commingSoon(8, 0),
    top250(8, 0),
    usBox(8, 0),
  ]).then((homeData) => {
    // 成功则commit后台接口的数据,并把NET_ERROR的数据置空,并把加载中的状态置为false。
    store.commit(types.HOME_DATA, homeData);
    store.commit(types.LOADING_FLAG, false);
    store.commit(types.NET_STATUS, '');
  }).catch((error) => {
    document.title = '出错啦 Oops… - 豆瓣';
    store.commit(types.NET_STATUS, error);
    store.commit(types.LOADING_FLAG, false);
  });
  next();
}

Ajax

import serverConfig from './serverConfig';

const Ajax = url => new Promise((resolve, reject) => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.send(null);
  xhr.onreadystatechange = () => {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(`错误: ${xhr.status}`);
      }
    }
  };
});

// 影院热映
export const hotMovie = (count, start) =>
  Ajax(`${serverConfig}/v2/movie/in_theaters?count=${count}&start=${start}`);
// 即将上映
export const commingSoon = (count, start) =>
  Ajax(`${serverConfig}/v2/movie/coming_soon?count=${count}&start=${start}`);
// top250
export const top250 = (count, start) =>
  Ajax(`${serverConfig}/v2/movie/top250?count=${count}&start=${start}`);
// 北美票房榜
export const usBox = (count, start) =>
  Ajax(`${serverConfig}/v2/movie/us_box?count=${count}&start=${start}`);
// 当前电影详情信息
export const currentMovie = currentMovieId =>
  Ajax(`${serverConfig}/v2/movie/subject/${currentMovieId}`);
// 当前标签详情信息
export const getTagData = (tag, count, start) =>
  Ajax(`${serverConfig}/v2/movie/search?tag=${tag}&count=${count}&start=${start}`);

How to set proxy?

In dev environment we use webpack-dev-server and http-proxy-middleware, and online we use express and http-proxy-middleware.

proxyTable: {
  '/v2': {
    target: 'http://api.douban.com',
    changeOrigin: true,
    pathRewrite: {
      '^/v2': '/v2'
    }
  }
},
var express = require('express');
var proxy = require('http-proxy-middleware');

var app = express();
app.use('/static', express.static('static'));
app.use('/v2', proxy({
  target: 'http://api.douban.com', 
  changeOrigin: true, 
  headers: {
    Referer: 'http://api.douban.com'
  }
}
));

app.get('/*', function (req, res) {
  res.sendFile(__dirname + '/index.html');
});
app.listen(3000);

Responsive layout in mobile

Use rem! In this project 1rem = 100px!

The browser run the codes following,change the font-size of the document.

<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
(function (doc, win) {
  var docEl = doc.documentElement,
    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
    recalc = function () {
      var clientWidth = docEl.clientWidth > 750 ? 360 : docEl.clientWidth ;
      if (!clientWidth) return;
      docEl.style.fontSize = clientWidth / 750 * 100 + 'px';
    };
  if (!doc.addEventListener) return;
  doc.addEventListener('DOMContentLoaded', recalc, false);
  if (docEl.clientWidth > 750) return;
  win.addEventListener(resizeEvt, recalc, false);
})(document, window);

Chinese document referencing my friend ShanaMaid!

Support

Thanks for you support,being glad for your star, pr, follow and issue.

When you see bugs.You can mail to me! [email protected] !

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].