杂篇 - Vue豆瓣系列文章

项目地址 在线演示css

不识庐山真面目,只缘身在此山中。html

大概一个月前,开源了Vue重构豆瓣移动端的项目,效果还能够,收到了不少小伙伴的反馈,话说是要写一些文章的,但迟迟没有动笔,估计小伙伴们等的花都谢了,拖延症是病,须要治疗...vue

接下来开始填坑,这一系列的文章会把在开发中遇到的问题总结下来。这篇文章是第一篇,是一些准备工做,包括从idea到具体项目实现...java

目录结构

万事开头难,当有idea从你脑海中闪过的时候,你第一个想到的应该是:脚手架工具,快速生成基本的项目结构,即刻上手。Vue官方就提供了方便易用的Vue-cli命令行工具,快速,高效,爽! webpack

这个项目自己是基于Vue-cli的webpack模板,简单的输入一条命令vue init webpack my-project,便能生成一个使用webpack做为打包工具,具有热重载,代码检查,css预处理等一系列功能的初始项目。ios

这一系列文章不会讲webpack如何使用?有可能会涉及一些简单的配置项。固然,学习webpack能很好的理解和解决开发中遇到的问题,有须要可自行谷歌。
不过,也没必要担忧,正如上面所说,即便你不了解webpack,你仍然能够用Vue-cli快速开发出高逼格Vue应用。git

来看看咱们的目录结构:github

.
├── build                      // Webpack打包相关配置
├── config                     // 基本环境配置
├── index.html                 // 通用HTML模板
├── package.json               // 相关依赖
├── README.md                  // README
├── src                        // 源码目录
│   ├── App.vue                // 入口页面
│   ├── assets                 // 静态资源目录
│   ├── components             // 全局公用组件目录
│   │   ├── Banner.vue         // 广告横幅组件
│   │   ├── Card.vue           // 卡片组件
│   │   ├── DownloadApp.vue    // 底部app下载组件
│   │   ├── Group.vue          // 小组组件
│   │   ├── HeaderBar.vue      // 顶部导航组件
│   │   ├── List.vue           // 列表组件
│   │   ├── Rating.vue         // 星级评分组件
│   │   ├── Scroller.vue       // 横向滚动组件
│   │   ├── Marking.vue        // 标记组件
│   │   ├── SubNav.vue         // 二级导航组件
│   │   ├── Tags.vue           // 标签组件
│   │   ├── Types.vue          // 项目类型组件
│   │   └── UserBar.vue        // 用户栏组件
│   ├── main.js                // 应用初始化入口文件
│   ├── router                 // 路由目录
│   │   └── index.js           // 路由配置
│   ├── store                  // Vuex全局状态目录
│   │   ├── index.js           // Store根文件
│   │   └── modules            // 模块目录
│   │       ├── activities.js  // 活动相关状态
│   │       ├── book.js        // 书籍相关状态
│   │       ├── group.js       // 小组相关状态
│   │       ├── movie.js       // 电影相关状态
│   │       ├── search.js      // 搜索相关状态
│   │       ├── subject.js     // 主题相关状态
│   │       └── user.js        // 用户相关状态
│   └── views                  // 视图目录
│       ├── BookView.vue       // 书籍视图
│       ├── DetailView.vue     // 首页活动详情视图
│       ├── GroupView.vue      // 小组视图
│       ├── HomeView.vue       // 主页视图
│       ├── LoginView.vue      // 登陆视图
│       ├── MovieView.vue      // 电影视图
│       ├── PagesView.vue      // 综合视图
│       ├── RegisterView.vue   // 注册视图
│       ├── SearchView.vue     // 搜索视图
│       ├── StatusView.vue     // 广播视图
│       ├── SubjectView.vue    // 主题视图
│       └── TalionView.vue     // 覆盖层视图
└── static                     // 静态文件目录

能开始了吗?。客官,莫急!web

首先,咱们要对写的项目有一个总体的印象,哪些东西是公用的?能够抽离为组件的?哪些是一次性组件?这些组件之间是否拥有某种关系?经过怎样的逻辑把他们关联起来?哪些页面是可重用的?又有哪些页面须要同时显示在视图里?路由要怎么划分?项目是否足够的大?要不要统一状态管理......vue-router

从上面结构中能够看出,咱们把一些可重用的或者是一次性组件写在了src/components中。
把彻底不一样或者是关联性不大的页面独立为不一样的视图放在src/views里,这些包含了一个个子组件的视图一样可视为组件。

上面结构中对各个目录和文件作了简单的描述,可能有些不太精确,建议结合代码来看。
关于视图方面,主要想说一下PagesView视图和TalionView视图。TalionView在这里是做为弹出层,覆盖在其余页面之上,拥有较高的在z-index,其内容包括了搜索框和一些快速导航。PagesView视图,又对各个页面作了一次组合,包括了<header-bar>头部导航组件,路由出口<router-view>,还有一个TalionView视图。

douban_TalionView.gif

为何要这么作呢?
综合项目全部视图来看,除了登陆注册页面外,其余页面的布局大体相同:顶部导航+中间内容视图+覆盖层。这在无形中经过视图将此项目分红了两大模块:登陆注册类无通用样式模块,和具有通用公共样式的普通模块。

固然这样划分的另外一个基础是对路由的配置。接下来,咱们来看看路由,进行进一步解释。

路由

先瞄一眼咱们的路由配置:

routes: [
    {                           // [1]
      path: '/',
      redirect: '/pages/'
    },
    {                           // [2]
      path: '/pages',
      component: PagesView,
      children: [
        {
          path: '',
          redirect: '/pages/home'
        },
        {
          path: 'home',
          name: 'HomeView',
          component: HomeView
        },
        {
          path: 'movie',
          name: 'MovieView',
          component: MovieView
        },
        ......
        {
          path: 'detail/:id',
          name: 'DetailView',
          component: DetailView
        }
      ]
    },
    {                           // [3]
      path: '/pages/:classify/subject/:id',
      name: 'SubjectView',
      components: {
        default: PagesView,
        subject: SubjectView
      }
    },
    {                           // [4]
      path: '/search',
      name: 'SearchView',
      components: {
        default: PagesView,
        search: SearchView
      }
    },
    {                           // [5]
      path: '/login',
      name: 'LoginView',
      component: LoginView
    },
    {                           // [6]
      path: '/register',
      name: 'RegisterView',
      component: RegisterView
    },
    {                           // [7]
      path: '*',
      redirect: '/pages/'
    }
  ]

路由的匹配规则是自上而下的,第一条[1]规则对路由进行了重定向。接着,第二条[2],对应了组件PagesView,其中又包含了若干条子路由。这至关于上面描述的第一类模块,拥有统一的视图,子路由的路由出口为本组件PagesView中定义的<router-view>。而这一个聚合的拥有子路由和子视图的模块对应的路由出口为入口文件App.vue中定义的无名路由出口<router-view class="view"></router-view>

能够看到在App.vue中还有两个具名的路由出口:name="subject"name="search"subject出口对应的路由为[3],是电影,图书等的详情页面,是否是以为能够把它划分到[2]中的子路由中?彻底能够,但这里没有这么作,为了将视图和路由对应起来,拥有一个好看直观的路径,因此就抽了出来,单独配置了,是否是有点蛇精病...回正题,为了同时(同级)展现多个视图,而不是嵌套展现,咱们配置了components,将PagesViewSubjectView同时展现在视图中。search路由也作了一样的处理。

5为登陆注册路由,只在视图中展现了各自对应的单个组件,再也不受约束。自由发挥...

最后一条路由[7],至关于404页面,在以上全部路由都不匹配的状况下,重定向到404页面,这里又把它重定向到了主页,是否是很机智...

状态管理

数据驱动应用,当咱们的应用愈来愈大时,对数据的操控将会愈来愈复杂。那么,咱们到底须要不须要集中式的状态管理(Vuex)?

Flux 架构就像眼镜:您自会知道何时须要它。 --Dan Abramov

对于组件来讲,若是,数据自己高度隶属于本组件,那么,直接封装在组件里将是一个好的选择。当父子组件之间共享数据,咱们也能经过props和自定义事件解决。即便组件愈来愈多,视图愈来愈多,若是彼此之间并没有太大联系,咱们也没有必要使用Vuex

稍微吐槽一下本项目:除了用户模块和搜索模块的状态以外,其余的状态是没有必要必定使用Vuex的。电影数据,图书数据,小组数据等是一次性数据,何时访问就何时呈现,顶多会经过路由传递一些必要的参数。便是说,其余视图不依赖这些数据,它自己也不须要来自其余视图的状态,而且这些状态并不影响全局状态。这个时候你没必要使用Vuex。但你这里使用了,做何解释呢?的确,Vuex很酷,我想试试。没别的理由。我能清楚明了的查看每个组件的状态,掌控全局,这的确很酷。

douban_vuex_store.gif

吐槽完了!回正题。如今假设咱们的项目足够大了(接着更新说不定就大了哦-_-!)。组件,视图多的数不过来,而且他们之间的关系错综复杂。整个应用也须要记录和维护更多的复杂的全局状态。如今咱们有了Vuex,咱们把共享状态抽取出来,以一个全局单例模式管理。咱们的应用构成一张巨大的蜘蛛网,而不论网的哪个节点都能访问状态。描述为星型拓扑结构更容易理解一些吧。

为了防止单一的状态集中到一个文件中而变得冗余,难以维护,咱们进一步的将状态分割成了模块,这些模块和视图一一对应,这样代码就变得更结构化,且很是容易维护了。

│   ├── store                  // Vuex全局状态目录
│   │   ├── index.js           // Store根文件
│   │   └── modules            // 模块目录
│   │       ├── activities.js  // 活动相关状态
│   │       ├── book.js        // 书籍相关状态
│   │       ├── group.js       // 小组相关状态
│   │       ├── movie.js       // 电影相关状态
│   │       ├── search.js      // 搜索相关状态
│   │       ├── subject.js     // 主题相关状态
│   │       └── user.js        // 用户相关状态

不要为了用vuex而用vuex

若是应用确实不大,只是有一些多层嵌套组件,兄弟组件之间传递状态的需求,使用事件总线global event bus就ok啦!若是须要构建一个中大型应用,为了更好地管理状态,上Vuex。至于,不要为了用vuex而用vuex,只要你以为用着爽,拿来用又何妨!

HTTP请求

Ajax库的选择是比较自由的。起初,项目使用的是vue-resource,其提供的jsonp方法用着比较爽,因为项目的不少数据来自豆瓣的api,直接上jsonp简单方便,跨域什么的不考虑。你很机智嘛!vue-resource不支持服务端渲染,官方也再也不推荐vue-resource。但这个不影响它的使用,若是你喜欢,接着用也没有问题。这里我将vue-resource的版本released出来:The last version of vue-resource

花了一点时间,迁移到了Superagent,准备作一些有趣的事情。和axios基本相同,很明显axios更火,axios的文章也更多一点,有的选未必很差。Superagent是在写express时接触的,先后端通用,做者杀马特TJ,用了一段时间,还不错。如今拿来和vue一块用,更亲切。
Superagent的api简单易用,能知足大多数应用需求,轻量级,支持ssr,可扩展性强。Superagent和axios的核心都不支持jsonp,这里采用了Superagent的插件superagent-jsonp

咱们能够这么使用:

import request from 'superagent'
import jsonp from 'superagent-jsonp'

request
    .get('https://api.douban.com/v2/event/list?loc=108288&start=' +
       state.skip + '&count=3')
    .use(jsonp)
    .end((err, res) => {
        if (!err) {
          commit({
            type: 'loadMore',
            res: res.body.events
          })
        }
    })

有关于Superagent的更多问题能够阅读官方文档或参考本项目源码。

表单验证

登陆注册部分只简单的在先后端作了基本检验,更严格的表单验证,没有作。社区里表单验证的插件也不少,vee-validate比较好用,就很少说了,若是有须要能够仔细阅读文档。这里先挖个坑,我想再造个轮子,试一试...
我暂时总结了一些关于表单验证的关键词,算是占个坑吧!

冗余,远程,时机,分离,集中式管理,重用,可配置,顺序,自定义,字段消毒,随机补充规则,优雅

我去!要求不少嘛...

API

数据来源,分两个部分,一个是豆瓣的api:Douban Api V2,另外一个是mock的一些数据:Douban Backend。采用express手写完成。跨域问题,上面已经介绍,在不能操控的豆瓣数据上,使用的是jsonp。部署在heroku上的mock数据服务,彻底可控,采用cors,借助cors模块完成。固然,方法不少,你的地盘,你作主。

发布到服务器

在构建成功以后,咱们将dist/中的文件发布到咱们已经准备好的服务器上。
在发布中会遇到两个常见的问题:

第一:npm run build后,项目部署到服务器,访问页面空白?
因为项目是用vue-cli构建,咱们并无对项目作任何配置,尽管默认配置下它工做的很好。看控制台,报错,一连串的404,文件找不到。js没加载上,vue没有初始化,页面天然空白。
解决方法:把congif/目录下index.jsbuildassetsPublicPath改成 ./ ,而后从新npm run build。

第二:assetsPublicPath配置正确,页面依旧空白?
查看路由配置信息:mode: 'history'启用了History模式。使用 history 模式时,URL 就像正常的 url,不过这种模式要玩好,还须要后台配置支持,vue-router文档给出了不一样服务器的简单配置:HTML5 History 模式

发布到github pages上:
github pages用来展现项目再好不过了。不过,咱们没办法作服务器配置,HTML5 History 模式要舍弃了,页面的滚动行为的体验差点也不要紧,关于这两点有后续文章。
npm run build后经过下面git命令发布:

git add -f dist
git commit -am 'Update live demo'
git subtree push --prefix dist origin gh-pages

最后

这是第一篇,比较杂乱一些,写了这么长,但愿对你有所帮助。若是你在查看这个项目,或阅读本文章时有什么问题能够直接联系我或到项目地址提交英文issue,欢迎各位小伙伴前来斧正,不胜感激......项目,系列文章将会持续更新,拖延症也在治疗中...

本文首发于简书 
做者:jeneser
Github:https://github.com/jeneser
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证

相关文章
相关标签/搜索