基于Vue,Vue-router,Vuex的简书网站模仿

860 查看

在这个教程里面,我会通过一系列的代码和图片来学习怎么使用vue-router,以及vuex。本文假设读者手里有关于vue-router和Vuex的文档,并且对Vue-router和Vuex有一定的了解。
没有文档也没关系,我这里有关于 Vue-router文档以及Vuex介绍,可以配合本文进行学习。由于才开始学习Vue,如有不当
之处,还请大家帮我斧正!!

首先看下这个网站的截图

这里是网站的源码下载地址 Github Repo
这里是Demo地址,在线感受vue的魅力

搭建项目

项目结构

本项目才用Vue-cli脚手架自动生成,这是一个Vue生态系统中一个伟大创举。这意味着我们不需要手动构建我们的项目,而它就可以很快地为我们生成。如图所示:

  • components:包含所有的页面组件

  • vuex:包含vuex相关文件(action.js, store.js)

  • static:静态文件存放(图片和图标库等)

  • index.html:渲染的页面

  • main.js:应用入口点,包含根实例

  • App.vue:主页面组件

项目流程:

  • 安装Vue-cli(要有node与npm)

npm i -g vue-cli
  • 创建一个webpack项目,并且下载依赖

vue init webpack vue-demo-jianshu
cd vue-demo-jianshu
npm i
  • 安装Vue-router和Vuex

npm install vue-router vuex --save
  • 热加载打开页面(生产的时候运行npm run build)

npm run dev

项目的框架已经搭建好了,接下来就是为项目添砖加瓦。

添砖加瓦第一步(初始化main.js与App.vue)

首先分析页面结构(专题页面结构和首页一样,就不在单独赘述了,写到哪儿分析到哪儿)

  • App.vue: 绿色框内的和黄色框内的就是每个页面都存在的,故写在App.vue里

  • home.vue: 紫色区域部分,由于文章区内文章会进行变化,故把文章区域单独写成组件

  • Article.vue: 棕色框部分

main.js部分

在main.js中我们引入Vue,App,Vue-router,并且创建了一个Vue的实例(因为在router这行引入了App组件router.start(App, '#app'),上main.js代码:

import Vue from 'vue'
import App from './App'
import VueRouter from 'vue-router'

import home from './components/Home.vue'
import topic from './components/Topic.vue'
import article from './components/Article.vue'
import bonus from './components/Bonus.vue'
import login from './components/Login.vue'
import topic_article from './components/Topic_article.vue'

//注册VueRouter这个插件
Vue.use(VueRouter)

const router = new VueRouter()

//路由map
router.map({
    '/home': {
        component: home,
        subRoutes: {
            '/article': {
                component: article
            }
        }
    },
    '/topic': {
        component: topic,
        subRoutes: {
            'topic_article': {
                component: topic_article
            }
        }
    },
    '/bonus': {
        component: bonus
    },
    '/login': {
        component: login
    }
})
//路由重定向(访问不存在的页面时,重定向到这个页面)
router.redirect({
    '*':'/home/article'
})

router.start(App,'app')//启动一个启用了路由的应用。创建一个 App 的实例并且挂载到元素 'app'
router.go('/home/article')//为了让页面打开的时候能看见文章,我把它导航到新路由

App.vue部分

App.vue里面我们把两个侧边栏(绿色框和黄色框)写在这里边,因为这两个总是一直存在的

<template>
<div class="container">
  <div class="sidebar">
    <ul class="dropdown">
      <li :class="{active: show === 'home'}">
        <a @click="show = 'home'" v-link="'/home/article'"><i class="fa fa-home"></i><span>&nbsp;&nbsp;首页</span></a>
      </li>
      <li :class="{active: show === 'topic'}">
        <a @click="show = 'topic'" v-link="'/topic/topic_article'"><i class="fa fa-th"></i><span>&nbsp;&nbsp;专题</span></a>
      </li>
      <li><a href="#"><i class="fa fa-mobile"></i><span>&nbsp;&nbsp;下载手机应用</span></a></li>
    </ul>
    <ul class="nav-user">
      <li><a href=""><i class="fa fa-font"></i><span>&nbsp;&nbsp;显示模式</span></a></li>
      <li><a v-link="'/login'"><i class="fa fa-sign-in"></i><span>&nbsp;&nbsp;登录</span></a></li>
    </ul>
  </div>
  <div class="home">
    <router-view transition = 'display' transition-mode = 'out-in'></router-view>
  </div>
  <div class="rightbar">
    <nav>
      <a v-link="'/login'"><i class="fa fa-sign-in"></i>登录</a>
      <a href="#"><i class="fa fa-user"></i>注册</a>
    </nav>
  </div>
</div>
</template>

添砖加瓦第二步(home.vue和article.vue)

home.vue部分

先讲home.vue,home.vue里面要放紫色框内(除了棕色框)的内容,分析这个界面紫色框内的左侧边栏是不变的,故可以写死,顶部导航也是不变的,也写死。关键就是在中部导航栏(热门,新上榜那块),棕色框内的内容会根据中部导航栏选中不同内容进行改变。那么该怎么实现这个呢?

首先写好需要写死的地方,如下图代码所示:

<template>
<div>
    <div class="showbar">
        <div class="cover-image"></div>
        <div class="text" style="text-shadow:1px 1px 1px #000000">
            <h1>简书</h1>
            <h3>交流故事,沟通想法</h3>
            <p>一个基于内容分享的社区</p>
            <a href="#"><i class="fa fa-home"></i>提笔写篇文章</a>
        </div>
    </div>
    <div class="article-page">
        <nav>
            <span class="nav-text fir"><a href="#">发现</a></span>
            <span class="nav-text"><a v-link="'../bonus'">2015精选</a></span>
            <span class="search clearfloat">
                <span class="input">
                    <input type="search" placeholder="搜索">
                </span>
                <span class="search-icon"><i class="fa fa-search"></i></span>
            </span>                    
        </nav>
        <div class="article-list">
            <ul class="btn-group">
                <li :class="{active: show === 'hot'}">
                    <a @click="displayArticle('hot')"
                       v-link="'/home/article'" 
                    >热门</a></li>
                <li :class="{active: show === 'new'}">
                    <a @click="displayArticle('new')"
                       v-link="'/home/article'" 
                    >新上榜</a></li>
                <li :class="{active: show === 'daily'}">
                    <a @click="displayArticle('daily')"
                       v-link="'/home/article'" 
                    >日报</a></li>
                <li :class="{active: show === 'weekhot'}">
                    <a @click="show = 'weekhot'"
                       v-link="'/home/article'" 
                    >七日热门</a></li>
                <li :class="{active: show === 'monthhot'}">
                    <a @click="show = 'monthhot'"
                       v-link="'/home/article'" 
                    >三十日热门</a></li>
                <li :class="{active: show === 'reward'}">
                    <a @click="show = 'reward'"
                       v-link="'/home/article'" 
                    >有奖活动</a></li>
                <li :class="{active: show === 'publish'}">
                    <a @click="show = 'publish'"
                       v-link="'/home/article'" 
                    >简书出版</a></li>
                <li :class="{active: show === 'video'}">
                    <a @click="show = 'vedio'"
                       v-link="'/home/article'" 
                    >简书播客</a></li>
                <li :class="{active: show === 'hotnews'}">
                    <a @click="show = 'hotnews'"
                       v-link="'/home/article'" 
                    >时事热闻</a></li>
                <li :class="{active: show === 'choice'}">
                    <a @click="show = 'choice'"
                       v-link="'/home/article'" 
                    >专题精选</a></li>

            </ul>
            <router-view></router-view>
        </div>
    </div>
</div>
</template>

不知道你看懂代码了没,没看懂不要紧,我来给你讲解,重点分析导航栏部分,为了让导航栏标签被选中时改变背景色,这里我使用了:class="{active: show === 'hot'}" ,这是vue里v-bind:class的简写,如果你问我show从哪里来,我会告诉你show从天上来,哈哈,不开玩笑,show的来源也就是今天的另一个重点Vuex。看代码:
首先是store.js(我的理解是管理数据和操作数据的地方)

store.js部分

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
    articles:{
        fir: {
            author:'徐丹妮',
            title:'我不是学霸,只是比你努力一点而已',
            time:'大约6小时之前',
            read:'8498',
            comment:'248',
            like:'2342',
            pay:'2',
            src:'url(../../static/vue-demo-hot.jpg)'
        },
        sec: {
            author:'共央君',
            title:'爱美的女生们,六款超高性价比的变美神器你都有了吗?',
            time:'大约8小时之前',
            read:'2623',
            comment:'34',
            like:'191',
            pay:'2',
            src:'url(../../static/vue-demo-hot_1.jpg)'
        },
        thi: {
            author:'姜肥东',
            title:'毕业9年,我才学懂的道理',
            time:'大约6天之前',
            read:'6498',
            comment:'38',
            like:'242',
            pay:'2',
            src:'url(../../static/vue-demo-hot_2.jpg)'
        }
    },
    show:'hot'
}
const mutations = {
    DISPLAY_ARTICLE (state, show) {
        const article = {
            hot: {
                fir: {
                    author:'徐丹妮',
                    title:'我不是学霸,只是比你努力一点而已',
                    time:'大约6小时之前',
                    read:'8498',
                    comment:'248',
                    like:'2342',
                    pay:'2',
                    src:'url(../../static/vue-demo-hot.jpg)'
                },
                sec: {
                    author:'共央君',
                    title:'爱美的女生们,六款超高性价比的变美神器你都有了吗?',
                    time:'大约8小时之前',
                    read:'2623',
                    comment:'34',
                    like:'191',
                    pay:'2',
                    src:'url(../../static/vue-demo-hot_1.jpg)'
                },
                thi: {
                    author:'姜肥东',
                    title:'毕业9年,我才学懂的道理',
                    time:'大约6天之前',
                    read:'6498',
                    comment:'38',
                    like:'242',
                    pay:'2',
                    src:'url(../../static/vue-demo-hot_2.jpg)'
                }
            },
            new: {
                fir: {
                    author:'阿俊',
                    title:'Learn by doing',
                    time:'大约6小时之前',
                    read:'3398',
                    comment:'258',
                    like:'232',
                    pay:'88',
                    src:'url(../../static/vue-demo-new.jpg)'    
                },
                sec: {
                    author:'阿猫',
                    title:'Learn by doing',
                    time:'大约6小时之前',
                    read:'3398',
                    comment:'258',
                    like:'232',
                    pay:'88',
                    src:'url(../../static/vue-demo-new.jpg)'    
                },
                thi: {
                    author:'阿狗',
                    title:'Learn by doing',
                    time:'大约6小时之前',
                    read:'3398',
                    comment:'258',
                    like:'232',
                    pay:'88',
                    src:'url(../../static/vue-demo-new.jpg)'    
                }
            },
            daily:{
                fir: {
                    author:'尸叔',
                    title:'如何让你的自拍,惊爆朋友圈?看我是怎么设计的',
                    time:'大约2小时之前',
                    read:'3750',
                    comment:'70',
                    like:'190',
                    pay:'0',
                    src:'../../static/vue-demo-daily.jpg'                    
                },
                sec: {
                    author:'尸爸',
                    title:'如何让你的自拍,惊爆朋友圈?看我是怎么设计的',
                    time:'大约2小时之前',
                    read:'3750',
                    comment:'70',
                    like:'190',
                    pay:'0',
                    src:'../../static/vue-demo-daily.jpg'
                },
                thi: {
                    author:'尸姐',
                    title:'如何让你的自拍,惊爆朋友圈?看我是怎么设计的',
                    time:'大约2小时之前',
                    read:'3750',
                    comment:'70',
                    like:'190',
                    pay:'0',
                    src:'../../static/vue-demo-daily.jpg'
                }
            }
        }
        state.show = show
        state.articles = article[show]
    }
}
export default new Vuex.Store({
    state,
    mutations
})

getters获取数据

这里面我采用的是模拟数据的方式(一个人没有后台 QAQ),可以看到show来自于state,我们在home.vue页面通过vuex的getters来获取数据show,代码如下:

import { displayArticle} from '../vuex/actions'
export default{
    vuex: {
        getters: {
            show: state => state.show
        },
        actions: {
            displayArticle
        }
    }
}

actions.js部分

在这里还引入一个action,它是做什么的呢?你答对了,这个displayArticle是用来分发事件的函数,它将更换文章区域内容的事件dispatch出去,然后在store.js里面完成内容的更换,下面是actions.js代码:

export const displayArticle = ({ dispatch },show) => {
    dispatch('DISPLAY_ARTICLE',show)
}

是不是感觉vuex很简单。中部导航栏我只给前三个弄了正确的点击事件,如需体验更多效果,自己动手,丰衣足食!!233
接下来是文章区域(棕色框部分)的代码:

Article.vue

<template>
    <ul>
        <li class='list' v-for="article in articles">
            <p class="list-top"><a href="#" class="author"><span>{{ article.author }}</span></a><span class="time"> - {{ article.time}}</span></p>
            <h2 class="title"><a href="#">{{ article.title }}</a></h2>
            <span class="small-text">阅读 {{article.read}} -</span>
            <span class="small-text">评论 {{article.comment}} -</span>
            <span class="small-text">喜欢 {{article.like}} -</span>
            <span class="small-text">打赏 {{article.pay}}</span>
            <span class="image" 
                :style="{background:article.src,backgroundSize:'100%',backgroundRepeat:'no-repat'}">
            </span>
        </li>
    </ul>
</template>
<script>
    export default {
        vuex: {
            getters: {
                articles: state => state.articles
            }
        }
    }
</script>

Article.vue和home.vue里获取数据的方式一样,由于未知文章数量,这里采用vue的列表渲染(是不是感觉很方便,有了vue,妈妈再也不用担心我的学习啦)。简书的首页到这里就写完成了,由于电脑越写越卡,这篇就先发了,接下来开第二篇文章,简书的专题页面和推荐页面,如果你发现本文章的错误之除,还请您斧正。
参考文章:用 Vuex 构建一个笔记应用 Vue构建单页应用最佳实战