Vue.js技术栈搭建CNODE社区

关键词:

Vue.js文档、Promise、jquery、webpack、npm、vue-­cli、vue-router

一 、cnode社区的基本构架

组件:
Header 头部
PosltList 列表
Article 文章的详情页
SlideBar 侧边栏
UserInfo 用户个人信息
Psgination 分页组件

二 、从头开始

我们先做出大概效果,接收和反馈数据数据

2.1 Header组件

Header组件

首先搭建好vue-­cli,进入编辑器

  • 将demo中要用到的图片和logo都存入assets文件夹中,如图
  • 创建一个Header组件,用img标签引入存储在assets文件中的logo
  • 在li标签分别写出:新手入门、API、关于、注册、登录
  • 将组件引入App.vue,就可以在页面显示
<template>
  <div id="app">
    <Header></Header>
  </div>
</template>

<script>
import Header from './components/header.vue'
export default {
  name: 'App',
  components:{
    Header
  }
}
  • 在加入css,第一个组件基本完成

2.2 PostList组件

PostList组件
  • 在数据还未到来时,我们显示出一个正在加载的logo,用img标签引入一个动态图片,用v-if操作这个动态图的显示
<div class="loading" v-if="isLoading">
         <img src="../assets/loading.gif" alt="">
</div>
  • 在组件data中定义一个空数组,并且让加载logo默认不显示
data(){
      return{
          isLoading: false,
          posts:[]
      }
  }
  • 获取数据用到Axios中的get请求,引入之后将Axios全局挂载到Vue原型上,通过this.$http来对网页进行请求
import Axios from 'axios'
Vue.prototype.$http = Axios;
  • 在methods方法中定义一个方法getData,通过get方法获取数据,通过promise返回请求成功和请求失败的数据,并做对应的事
methods:{
      getData(){
          this.$http.get('https://cnodejs.org/api/v1/topics',{
            params:{
              page:this.postPage,
              limit:20
            }
          })
          .then(res=>{
              this.isLoading = false  //加载成功,去除动画
              this.posts = res.data.data
              // console.log(this.posts)
          })
          .catch(err=>{
            //处理返回失败后的问题
            console.log(err) 
          })
      }
  • 请求成功后给data中定义的posts空数组赋值,如上代码所示

  • 在页面加载之前,触发getData方法,并且在数据没有到来之前,显示加载logo,如代码所示

beforeMount(){
      this.isLoading = true //加载成功之前显示加载动画
      this.getData() //在页面加载之前获取数据
  }
  • 得到数据后,渲染页面用v-for,根据数据结构写出所需要的数据
<div class="posts" v-else>
         <ul>
             <li>
                 <div class="toobar">
                     <span v-for="tab in change_tab" :key="tab.name"
                     @click="changetab(tab)"
                     :class="{tab_style:tab == current_tab}">{{tab}}</span>
                 </div>
             </li>
             <li v-for="post in posts" :key ="post.id">
                 <!--头像-->
                 <router-link :to="{
                   name:'user_info',
                   params:{name:post.author.loginname}
                 }">
                   <img :src="post.author.avatar_url" alt="">
                 </router-link>
                 <!--回复/浏览-->
                 <span class="allcount">
                     <span class="reply_count">{{post.reply_count}}</span>
                     /{{post.visit_count}}
                 </span>
                 <!--帖子的分类-->
                 <span :class="[{put_good:(post.good  == true),put_top:(post.top  == true),
                 'topiclist-tab':(post.good != true && post.top != true)}]">
                   <span>
                     {{post | tabFormatter}}
                   </span>
                </span>
                 <!--标题-->
                 <router-link :to="{
                   name:'post_content',
                   params:{
                     id:post.id,
                     name:post.author.loginname
                   }
                 }">
                   <span>{{post.title}}</span>
                 </router-link>
                 <!--最終回复时间-->
                 <span class="last_reply">{{post.last_reply_at | formatDate}}</span>
             </li>
  • 页面中有帖子分类,所以这里要用到过滤器,我们将过滤器代码写入main.js中以便于各个组件使用
Vue.filter('tabFormatter',function (post) {
  if(post.good == true){
    return '精华'
  }else if(post.top == true){
    return '置顶'
  }else if(post.tab == 'ask'){
    return '问答'
  }else if(post.tab == 'share'){
    return '分享'
  }else{
    return '招聘'
  }
})
  • 通过v-bind动态绑定class控制分类的样式

  • 因为头像和标题都会跳转,所以用router-link来包裹,通过to来跳转到需要的路由和要传统的所需参数

<!--头像-->
                 <router-link :to="{
                   name:'user_info',
                   params:{name:post.author.loginname}
                 }">
                   <img :src="post.author.avatar_url" alt="">
                 </router-link>
<!--标题-->
                 <router-link :to="{
                   name:'post_content',
                   params:{
                     id:post.id,
                     name:post.author.loginname
                   }
                 }">
                   <span>{{post.title}}</span>
                 </router-link>
  • 在roouter文件夹下的index.js文件中引入vue-router,然后定义路由名字,接收传递来的参数,引入其他组件,代码如下:
import Vue from 'vue'
import Router from 'vue-router'
import PostList from '../components/PostList'
import Article from '../components/Article'
import SlideBar from '../components/SlideBar'
Vue.use(Router)

export default new Router({
  routes: [
    {
      name:'root',
      path:'/',
      components:{
        main:PostList
      }
    },
    {
      name:'post_content',
      path:'/topic/:id&author=:name',
      components:{
        main:Article,
        slidebar:SlideBar
      }
    }
  ]
})
  • 并且还要在父组件中引入router
import Vue from 'vue'
import App from './App'
import router from './router'
import Axios from 'axios'
Vue.prototype.$http = Axios;

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
  • 最后在App.vue组件中由router-view组件来接收,通过name的值来判定需要显示的组件
<template>
  <div id="app">
    <Header></Header>
    <div class="main">
      <router-view name="slidebar"></router-view>
      <router-view name="main"></router-view>
    </div>
  </div>
</template>

<script>
import Header from './components/header.vue'
// import PostList from './components/PostList.vue'
// import UserInfo from './components/UserInfo.vue'
export default {
  name: 'App',
  components:{
    Header
  }
}

2.3 大同小异

UserInfo组件:

UserInfo组件

SlideBar组件:
SlideBar组件

Article组件:
上部分
Article组件上部分

下部分
Article组件下部分

后面的Article组件、SlideBar组件、UserInfo组件写法都是一样的,有跳转的用router.link包裹,传递路由名字和参数,遇到需要遍历的就用v-for遍历,需要注意的有两个地方,如下

  • 第一个是在Article父组件中,因为写完后会发现点击下图中两个区域(SlideBar组件)页面没有变化,路由的参数虽然变了,但并不会跳转。所以我们要在Article组件中用watch监听路由的变化以便进行跳转


    SlideBar组件

注意看api参数:

第一个

第二个

监听路由代码:

watch:{
    '$route'(to,from){
      this.getArticleData()
    }
  }
  • 第二个是在SlideBar组件中,因为只需要获取五条数据,所以要在计算属性中写两个方法,然后在模板中用v-for来渲染,例:v-for="list in topcilimitby5"
computed:{
        topcilimitby5(){
          if(this.userinfo.recent_topics){
            return this.userinfo.recent_topics.slice(0,5);
          }
        },
        replylimitby5(){
          if(this.userinfo.recent_replies){
            return this.userinfo.recent_replies.slice(0,5);
          }
        }
      }

2.4 Psgination组件

Psgination组件

直接甩代码更明了:

<template>
<div class="pagination">
  <button @click="changeBtn">首页</button>
  <button @click="changeBtn">上一页</button>
  <button v-if="jduge" class="pagebtn">......</button>
  <button v-for="btn in pagebtns" :key="btn.page"
  :class="[{currentPage:btn == currentPage},'pagebtn']"
  @click="changeBtn(btn)">
      {{btn}}
  </button>
  <button @click="changeBtn">下一页</button>
</div>
</template>

<script>
import $ from 'jquery'
export default {
    name:'Pagination',
    data(){
        return{
            pagebtns:[1,2,3,4,5,'......'],
            currentPage:1,
            jduge:false
        }
    },
    methods:{
        changeBtn(page){
            // console.log(page.target)
            if(page == '......') return
          if(typeof page != 'number'){
            switch(page.target.innerText){
              case '上一页':
                $('button.currentPage').prev().click();
                // console.log( $('button.currentPage'))
                break;
              case '下一页':
                $('button.currentPage').next().click();
                break;
              case '首页':
                this.pagebtns = [1,2,3,4,5,'......'];
                this.changeBtn(1);
                break;
              default:
                break;
            }
            return;
          }
            this.currentPage = page
            if(page>4){
              this.jduge = true;
            }else{
              this.jduge = false;
            }
            if(page == this.pagebtns[4]){
                this.pagebtns.shift()
                this.pagebtns.splice(4,0,this.pagebtns[3]+1)
            }else if(page == this.pagebtns[0] && page != 1){
                this.pagebtns.unshift(this.pagebtns[0]-1)
                this.pagebtns.splice(5,1)
            }
            this.$emit('handleList',this.currentPage);
        }
    }
}
</script>
  • 在data中定义pagebtns,如:pagebtns:[1,2,3,4,5,'......'],在模板中由v-for来遍历,然后在定义个changBtn方法,点击时实现需要的功能,此段需要引入jquery,用switch判定点击上一页、下一页、首页该做什么事

  • 最后,因为Psgination组件是定义在PostList组件中,所以是子组件向父组件传递数据,所以这里需要用到,自定义事件,用Vue中的方法this.$emit('handleList',this.currentPage)
    再在PostList组件中定义出handleList方法即可,在给出我的PostList组件最后的代码:

<template>
 <div class="PostList">
     <!--在数据未返回的时候,显示这个正在加载的gif-->
     <div class="loading" v-if="isLoading">
         <img src="../assets/loading.gif" alt="">
     </div>
     <div class="posts" v-else>
         <ul>
             <li>
                 <div class="toobar">
                     <span v-for="tab in change_tab" :key="tab.name"
                     @click="changetab(tab)"
                     :class="{tab_style:tab == current_tab}">{{tab}}</span>
                 </div>
             </li>
             <li v-for="post in posts" :key ="post.id">
                 <!--头像-->
                 <router-link :to="{
                   name:'user_info',
                   params:{name:post.author.loginname}
                 }">
                   <img :src="post.author.avatar_url" alt="">
                 </router-link>
                 <!--回复/浏览-->
                 <span class="allcount">
                     <span class="reply_count">{{post.reply_count}}</span>
                     /{{post.visit_count}}
                 </span>
                 <!--帖子的分类-->
                 <span :class="[{put_good:(post.good  == true),put_top:(post.top  == true),
                 'topiclist-tab':(post.good != true && post.top != true)}]">
                   <span>
                     {{post | tabFormatter}}
                   </span>
                </span>
                 <!--标题-->
                 <router-link :to="{
                   name:'post_content',
                   params:{
                     id:post.id,
                     name:post.author.loginname
                   }
                 }">
                   <span>{{post.title}}</span>
                 </router-link>
                 <!--最終回复时间-->
                 <span class="last_reply">{{post.last_reply_at | formatDate}}</span>
             </li>
             <li>
               <Pagination @handleList="renderList"></Pagination>
             </li>
         </ul>
     </div>
 </div>
</template>

<script>
import Pagination from './Pagination'
export default {
  name: 'PostList',
  data(){
      return{
          isLoading: false,
          posts:[],
          postPage:1,
          change_tab:['全部','精华','分享','问答','招聘'],
          current_tab:'全部',
          url:'https://cnodejs.org/api/v1/topics'
      }
  },
  components:{
    Pagination
  },
  methods:{
      getData(){
          this.$http.get(this.url,{
            params:{
              page:this.postPage,
              limit:20
            }
          })
          .then(res=>{
              this.isLoading = false  //加载成功,去除动画
              this.posts = res.data.data
              // console.log(this.posts)
          })
          .catch(err=>{
            //处理返回失败后的问题
            console.log(err) 
          })
      },
      renderList(value){
        this.postPage =value
        this.getData()
      },
    changetab(tab){
      if(tab == '全部'){
        console.log(this.url)
        this.url = 'https://cnodejs.org/api/v1/topics?tab=all'
      }else if(tab == '精华'){
        console.log(this.url)
        this.url = 'https://cnodejs.org/api/v1/topics?tab=good'
      }else if(tab == '分享'){
        console.log(this.url)
        this.url = 'https://cnodejs.org/api/v1/topics?tab=share'
      }else if(tab == '问答'){
        console.log(this.url)
        this.url = 'https://cnodejs.org/api/v1/topics?tab=ask'
      }else{
        this.url = 'https://cnodejs.org/api/v1/topics?tab=job'
      }
      this.current_tab = tab
      this.getData()
    }
  },
  beforeMount(){
      this.isLoading = true //加载成功之前显示加载动画
      this.getData() //在页面加载之前获取数据
  }
}
</script>

三、大结局

写到这里,模拟cnode社区基本写完了,后面便是一些自己想要的功能,可以自行添加-----------The early bird catches the worm

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • # 传智播客vue 学习## 1. 什么是 Vue.js* Vue 开发手机 APP 需要借助于 Weex* Vu...
    再见天才阅读 3,525评论 0 6
  • 相关概念 混合开发和前后端分离 混合开发(服务器端渲染) 前后端分离后端提供接口,前端开发界面效果(专注于用户的交...
    他爱在黑暗中漫游阅读 2,765评论 4 45
  • 所有人都称赞大雨之后的彩虹 却忽视大雪之后的星空 大概彩虹无论在哪里都会被聚焦 而星星 永远闪闪烁烁
    heim_dn阅读 192评论 0 1
  • 那时的我不知不觉脑海里面就有她了吧。我也不知道为什么,谈不上喜欢,更谈不上爱。只是感觉她很特别吧。 上课了,我记得...
    她最近好吗阅读 181评论 0 0