cnode总结

cnode社区基本架构

组件:

  • Header头部
  • PostList 列表
  • Article 文章详情页
  • SlideBar 侧边栏
  • UserInfo 用户个人信息
  • pagination 分页组件

起步:

打开终端,选择项目位置。
vue init webpack cnode    建立项目
npm install   安装依赖
npm run dev   启动项目

开始创建组件

  1. 先创建Header组件。
    创建完成以后,在app.vue组件中通过import引入Header组件,然后注入app.vue的components中,再在app.vue的template中渲染。

  2. 创建PostList组件。
    因为这个组件要显示文章列表,可以设置一个正在加载的动画,当数据未返回的时候,显示正在加载的动画,提升用户体验。
    API接口:https://cnodejs.org/api/v1/topics

    - 接口参数:limit、page
    - 拿到的参数分析:
          - 头像:author. avatar_url
          - 回复量/浏览量:reply_count/visit_count
          - 帖子标题:title
    - 需要使用过滤器:
          - 时间:last_reply_at
          - 帖子分类:
               1、top:代表是否置顶
               2、good:代表是否精华
               3、tab:是表示出了置顶和精华之外的其他帖子分类,包括以下这些:
                     - share:分享
                     - ask:问答
                     - job:招聘

要获取到这些数据,我们需要主动向服务器发送请求。这里就可以用到axios这个插件。

什么是axios?

axios是基于promise(诺言)用于浏览器和node.js是http客户端。
axios的作用是什么呢:axios主要是用于向后台发起请求的,还有在请求中做更多是可控功能。
axios有以下特点:
1、从浏览器创建 XMLHttpRequest
2、从 node.js 创建http请求
3、支持Promise API
4、拦截请求和响应
5、转换请求数据和相应数据
6、能取消请求
7、自动转换JSON数据
8、客户端支持防御 XSRF

使用前要先安装 切换到项目目录下,然后:

npm install axios
安装好以后,要在main.js中引入。
一般情况下,全局使用的东西,都在main.js中引入,比如 router、Vue等;

引入之后,要挂载到Vue原型上: 
         Vue.prototype.$http = Axios

$http 是自定义的,随便写。然后我们就可以用 this.$http发送请求了。

我们可以在methods里,定义一个getData函数去获取数据。
 methods:{
        getData(){
            this.$http.get('https://cnodejs.org/api/v1/topics',{
                params:{
                    page:1,
                    limit:20
                }
            })
            .then(res=>{
                this.isLoading = false;  //数据加载成功后,加载动画取消
                console.log(res);
                this.posts = res.data.data;  //拿到数据以后,把数据复制给posts
            })
            .catch(err=>{
                console.log(err);
            })
        }
    }
拿到res之后,我们分析 res.data.data就是我们要的帖子的列表数据。我们可以先定义一个posts数据,以承载res.data.data。然后在li上 v-for="post in posts"以遍历数据,拿到想要的数据。比如头像,标题什么的。


getData函数定义好以后,要执行,什么时候执行呢?
在页面加载之前去执行,这里我们就要用到一个生命周期钩子函数
beforeMount了

beforeMount(){
        // 在页面加载成功之前,显示正在加载的动画
        this.isLoading = true;
        // 在页面加载之前,发请求获取数据
        this.getData();
    }

PostList组件创建好以后,要在app.vue中引入,然后在app.vue的components中注入,注入之后,在app.vue的template中渲染。

<template>
  <div id="app">
    <Header></Header>
    <PostList></PostList>
  </div>
</template>

时间过滤器

因为服务器返回的最终回复时间,格式是 2019-03-12T03:09:11.934Z
而我们需要的时间格式是 几分钟前, 几小时前,几天前。
所以,我们需要一个过滤器,以转换时间格式。
这个过滤器,因为要全局使用,所以我们在main.js中定义。

Vue.filter('formatDate',function(str){
  if(!str) return '';
  var date = new Date(str);
  var time = new Date().getTime()- date.getTime();  //time是当前时间的毫秒数,跟最终回复时间的毫秒数的差值
  if(time<0){
    return '';
  }else if(time/1000 <30){
    return '刚刚'
  }else if(time/1000<60){
    return parseInt(time/1000)+'秒前'
  }else if(time/60000<60){
    return parseInt(time/60000)+'分前'
  }else if(time/3600000<24){
    return parseInt(time/3600000)+'小时前'
  }else if(time/86400000<31){
    return parseInt(time /86400000)+'天前'
  }else if(time/2592000000<12){
    return parseInt(time/2592000000)+'月前'
  }else{
    return parseInt(time/31104000000)+'年前'
  }
})

帖子类型判断过滤器


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



 <!-- 帖子的分类 -->
//动态绑定class,class分为3中 top,good,还有其他。
 <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>
  1. 创建Article组件
    API:https://cnodejs.org/api/v1/topic/:id
    我们怎么进入Article组件呢,肯定是点击了某个帖子进入的,点击的时候把帖子的id传递给API,一起发送到服务器,请求到数据,拿到数据后,渲染到页面。
    点击帖子,跳转到帖子详情,就需要用到路由了。
  <!-- 标题 -->
                    <router-link :to="{
                        name:'post_content',
                        params:{
                            id:post.id
                        }
                    }">
                         <span>{{post.title}}</span>
                    </router-link>

// router/index.js里
export default new Router({
  routes: [
    {
      name:'root',
      path:'/',
      components:{
        main:PostList
      }
    },
    {
      name:'post_content',    //路由的名字
      path:'/topic/:id' ,     //这里的id 就是PostList组件里的帖子标题被点击的时候,传递过来的参数
      components: {            //path要指向那个组件呢? 需要在components里定义
        main:Article          //这里用到了Article组件,需要先引入这个组件
      }
    }
  ]
})


//定义好router-link之后,是要跳转的,这个跳转之后的页面显示到哪里呢?  由  router-view决定
所以我们在 app.vue里这样写:
<template>
  <div id="app">
    <Header></Header>
    <div class="main">
       <router-view name="main"></router-view>
    </div>
  </div>
</template>

4.创建UserInfo组件
API:https://cnodejs.org/api/v1/user/:loginname
创建之后,把路由写入 index.js

{
      name:'user_info',
      path:'/user/:loginname',
      components:{
        main:UserInfo
      }
    }
在Article组件里,点击头像和用户名的时候,可以跳转到UserInfo组件。所以需要router-link做跳转
<router-link :to="{
          name:'user_info',
          params:{
            loginname:reply.author.loginname
          }
    }">
     <img :src="reply.author.avatar_url" alt="头像">
     <span>{{reply.author.loginname}}</span>
  </router-link>
  1. SlideBar组件
    侧边栏的API和UserInfo组件的API是一样的。
    当我们点击某一个帖子的时候,侧边栏组件需要跟Article组件同时出现,所以,需要在post_content 这个路由里的components里,再加一个SlideBar组件。
{
      name:'post_content',    //路由的名字
      path:'/topic/:id' ,     //这里的id 就是PostList组件里的帖子标题被点击的时候,传递过来的参数
      components: {            //path要指向那个组件呢? 需要在components里定义
        main:Article,          //这里用到了Article组件,需要先引入这个组件
        SlideBar:SlideBar
      }
    }

路由里添加之后,在app.vue里,需要在template里做出渲染。

<template>
  <div id="app">
    <Header></Header>
    <div class="main">
      <router-view name="SlideBar"></router-view>
      <router-view name="main"></router-view>
    </div>
  </div>
</template>

还有就是,因为跟UserInfo组件的API是一样的。所以请求数据的时候,需要传一个 loginname参数,这个参数哪里来呢?
因为SlideBar组件是我们点击帖子标题的时候,就要出现的,所以 loginname参数需要我们在点击帖子标题的时候就要传出去,之前点击帖子标题的时候已经传了一个id,所以现在需要loginname参数跟id一起传出去

  <!-- 标题 -->
                    <router-link :to="{
                        name:'post_content',
                        params:{
                            id:post.id,
                            loginname:post.author.loginname
                        }
                    }">
                         <span>{{post.title}}</span>
                    </router-link>

SlideBar组件的渲染,需要向https://cnodejs.org/api/v1/user/:loginname发送请求。这个API是需要loginname参数的,而在PostList组件里,点击帖子标题的时候,已经把这个loginname参数传递到 index.js的post_content路由了,所以,我们可以通过this.$route.params.loginname拿到这个参数。
这样就可以发出请求了。
侧边栏做好以后

image.png

当我们点击侧边栏里,作者最近话题和最近回复的时候,发现无法跳转。
这是因为,当前页面的路由是:
http://localhost:8080/#/topic/5bd4772a14e994202cd5bdb7&author=alsotang
而我们随便点击一个title后,路由变成了
http://localhost:8080/#/topic/5c4c929a595cbd1e95088b3a&author=zhennann
不难发现,前后只是参数不一样,前边的path是一样的。这个时候点击的时候,走的是跟当前页面是同一个路由。同一个路由内是检测不到变化的。
所以,我们需要在Article组件里,定义一个检测路由变化执行的函数

beforeMount(){
      this.isLoading = true;
      this.getArticleData();
    },
//watch这里用来检测路由的变化,并执行getArticleData()函数
    watch:{
      '$route'(to,from){
        this.getArticleData();
      }
    }
  1. Pagination组件
    这个分页组件,是显示在首页的,所以,我们可以定义了这个组件之后,可以在PostList组件里边引入,并渲染。
<template>
    <div class="pagination">
                <!-- 如果changeBtn不传递参数,会把默认的原生的event对象传递进去 -->
        <button @click="changeBtn">首页</button>
        <button @click="changeBtn">上一页</button>
        <button v-if="judge" class="pagebtn">......</button>
        <button v-for="btn in pagebtns" :key="btn.id" @click="changeBtn(btn)"
        :class="[{currentPage:btn == currentPage},'pagebtn']">
            {{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,
            judge:false
        }
    },
    methods:{
        changeBtn(page){
            // 如果点击的是首页,上一页,下一页这3个按钮的话,传递进来的就不是数字了,而是event对象
            if(typeof page != 'number'){
                switch(page.target.innerText){
                    case '上一页':
                    // 当点击上一页的时候,当前备选按钮的上一个按钮,触发一次点击
                    $('button.currentPage').prev().click();
                    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;
            //这里的判断目的是,当被点击页码大于4的时候,要在上一页后边出现一个'......'按钮。
            if(page > 4){
                this.judge = true;
            }
            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);   //然后数组最后一个元素删除
            }
            // 子组件给父组件(PostList),传递参数,参数传递过去后,PostList根据传递的页数去渲染页面
            this.$emit('handle',this.currentPage);
        }
    }
}
</script>


<style scoped>
.pagination {
    margin-top: 5px;
    margin-bottom: 20px;
    background-color: white;
    padding: 6px 20px;
    border-radius: 5px;
    /*box-shadow: 0px 2px 9px #888888;*/
    border: 1px solid #888888;
  }

  button {
    background-color: #fff;
    border: 1px solid #ddd;
    color: #778087;
    border-radius: 3px;
    outline: none;
    height: 21px;
    cursor: pointer;
    padding: 0 2px;
    width: 55px;
    height: 29px;
  }

  .pagebtn {
    position: relative;
    bottom: 1px;
    width: 40px;
    margin: 0 4px;
  }

  .currentPage {
    color: white;
    background-color: #1f1b1b;

  }
</style>

需要注意的是,Pagination组件,是放在PostList组件里渲染的,是它的子组件,当我们点击分页按钮,比如第3页的时候,需要把这个页码传递给PostList组件,然后PostList组件根据这个页码作为参数,去重新发送请求,获取到第3页的数据去重新渲染页面。

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

推荐阅读更多精彩内容

  • 基于Vue的一些资料 内容 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 element★...
    尝了又尝阅读 1,137评论 0 1
  • UI组件 element - 饿了么出品的Vue2的web UI工具套件 Vux - 基于Vue和WeUI的组...
    鲁大师666阅读 43,358评论 5 97
  • UI组件 element- 饿了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的组件库 m...
    35eeabfa0772阅读 3,246评论 7 12
  • UI组件 element - 饿了么出品的Vue2的web UI工具套件 Vux - 基于Vue和WeUI的组件库...
    卞卞村长L阅读 1,687评论 0 8
  • Vue2.0+组件库总结 UI组件 element - 饿了么出品的Vue2的web UI工具套件 Vux - 基...
    szch阅读 1,944评论 1 52