美文网首页前端学习笔记
我的第一个Vue.js项目----仿cnode社区

我的第一个Vue.js项目----仿cnode社区

作者: _ClariS_ | 来源:发表于2019-09-29 21:19 被阅读0次

    一、环境搭建


    使用vue-cli脚手架搭建Vue项目

    1. 全局安装 npm install -g @vue/cli

    2. 查看版本是否正确 vue --version

    3. 新建一个空文件夹,用Git Bash进入这个文件夹,然后输入命令
      vue create .(该命令表示在当前目录下创建一个vue项目)

    4. 接下来会提示我们选择一些配置选项,比如是否安装Router、Babel、Vuex 等,按需选择即可

    image

    注意:可能我们会遇到命令行下无法 Use arrow keys 的情况,这个时候我们可以使用命令 winpty vue.cmd create . 来解决这个问题

    1. 运行项目在本地预览 npm run serve
    image

    当在浏览器中出现以下界面时,就表示我们的vue项目搭建成功啦

    image

    安装路由

    前提:你在上一步选择配置选项的时候没有选择安装 Router

    1. 安装:npm install --save vue-router
    2. 在router.js中引用:
    import router from 'vue-router'
    Vue.use(router)
    
    1. 在vue实例中注入
    new Vue({
      router, // 注入router
      render: h => h(App)
    }).$mount('#app')
    

    引入 Axios

    1. 安装:npm install axios
    2. 在main.js中引入Axios:import Axios from 'axios'
    3. 在main.js中把Axios挂载到Vue原型上:Vue.prototype.$http = Axios

    二、cnode 社区的基本构架


    将 cnode 社区网页分为以下几个组件

    1. Header 头部
    2. PosltList 列表
    3. Article 文章的详情页
    4. SlideBar 侧边栏
    5. UserInfo 用户个人信息
    6. Psgination 分页组件
    目录结构

    三、开始写代码


    以下只记录了我认为在项目中相对重要的部分

    1. PosltList 组件

    API接口:https://cnodejs.org/api/v1/topics 获取帖子列表

    <script>
    export default {
      name: "PostList",
      data() {
        return {
          isLoading: false, // 默认不显示loading动画
          posts: [] // 代表当前页面的所有内容列表数组
        };
      },
      methods: {
        getData() {
          this.$http
            .get("https://cnodejs.org/api/v1/topics", {
              page: 1,
              limit: 20
            })
            .then(response => {
              this.isLoading = false; // 加载成功之后去除 loading 动画
              console.log(response);
              this.posts = response.data.data;
            })
            .catch(error => {
              // 处理请求失败
              console.log(error);
            });
        }
      },
      beforeMount() { // 在挂载开始之前被调用:相关的 render 函数首次被调用
        this.isLoading = true; // 在页面加载成功之前显示 lodaing 动画
        this.getData(); // 在页面加载之前获取数据
      }
    };
    </script>
    

    若请求成功,则会在控制台打印出如下结果

    [图片上传失败...(image-b11aef-1569763055194)]

    console.log(response)的结果进行分析

    • 头像:author.avatar_url
    • 回复量/浏览量 :reply_count/visit_count
    • 帖子的标题:title
    • 最后回复时间:last_reply_at
    • 帖子分类:
      • top: 代表是否置顶
      • good: 代表是否精华
      • tab 是表示除了置顶和精华之外的其余分区
    • tab 又可分为
      • ­share 分享
      • ­ask 问答
      • ­job 招聘

    filter 的使用

    过滤帖子的类别

    PostList中
    <!-- 帖子的分类 -->
    <span :class="[{put_good:(post.good === true),put_top:(post.top===true),'topiclist-tab':(post.good !== true && post.top !== true)}]">
       <span>{{post | tabFormat}}</span>
    </span>
    
    main.js中
    // 处理帖子分类的文字
    Vue.filter('tabFormat',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 '招聘'
      }
    })
    

    过滤最后回复时间

    PostList中
    <!-- 最后回复时间 -->
    <span class="last_reply">{{post.last_reply_at | formatDate}}</span>
    
    main.js中
    Vue.filter('formatDate',function(string){ // 传入 post.last_reply_at 作为参数,注意它为字符串
        if(!string) return ''
        var date = new Date(string)
        var time = new Date().getTime() - date.getTime() // 现在的时间 - 传入的时间 = 相差的时间(单位:毫秒)
        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/31536000000) + '年前'
        }
    })
    

    2. Article 组件

    API:https://cnodejs.org/api/v1/topic/ + 帖子ID

    Article接收PostList传递的参数post.id,获取参数用 this.$route.params.id

    methods:{
        getArticleData(){
            this.$http.get(`https://cnodejs.org/api/v1/topic/${this.$route.params.id}`)// ES6 语法,字符串拼接
            .then((response)=>{
                this.isLoading = false
                console.log(response)
               this.posts = response.data.data;
            })
            .catch((error)=>{
                console.log(error)
            })
        }
    }
    

    监听路由的变化(解决点击侧边栏title不跳转的问题)

    watch:{ // 监听路由的变化,暂时不是很理解为什么要监听路由的变化
            '$route'(to,from){
                this.getArticleData()
            }
    }
    

    引入外部的markdown.css把变为makdown格式

    /*引入外部CSS时要去掉scoped*/
    <style>
    @import url('../assets/markdown-github.css');
    ...
    </style>
    

    3. Pagination 分页组件

    这里要用到子组件(Pagination)向父组件(PostList)传递数据

    首先要在PostList组件中自定义一个事件 @handleList="renderList"

    <template>
    <!--分页按钮-->
    <Pagination @handleList="renderList"></Pagination><!--自定义事件 handleList-->
    </template>
    
    <script>
    import Pagination from './Pagination'  // 引入组件
    
    export default {
      name: "PostList",
      components:{
        Pagination  // 注册组件
      },
      data() {
        return {
          postPage:1
        }
      },
      methods: {
        getData() {
          this.$http
            .get("https://cnodejs.org/api/v1/topics", {
                params:{ // 注意get请求一定要加params
                    page: this.postPage, // 把请求的参数变为更新后的 postPage
                    limit: 20
                }
            })
            .then(...)
            .catch(...)
        },
        renderList(value){ // 用 value 接收从 Pagination 中传递过来的参数 currentPage
            this.postPage = value
            this.getData()
        }
      }
    </script>
    

    然后在Paginnation组件中传递参数给上面的自定义事件

    <template>
      <div class="pagination">
        <button @click="changeBtn">首页</button>
        <button @click="changeBtn">上一页</button>
        <button v-if="judge" class="pagebtn">......</button>
        <button v-for="btn in pagebtns"
        :class="[{currentPage:btn===currentPage},'pagebtn']"
        @click="changeBtn(btn)">
        {{btn}}
        </button>
        <button @click="changeBtn">下一页</button>
      </div>
    </template>
    
    <script> 
    export default {
      name:"Pagination",
      data(){
          return{
              pagebtns:[1,2,3,4,5,'......'],
              currentPage:1
          }
      },
      methods:{
          changeBtn(page){
              this.currentPage = page // 让currentPage = 当前点击的按钮数字(btn)
              this.$emit('handleList',this.currentPage) // 把currentPage传递给父组件PostList
      }
    }
    </script>
    

    4. SlideBar 侧边栏组件

    使用计算属性

    <li v-for="userinfo in replylimited">   <!--这里使用了计算属性-->
        <router-link :to="{
            name:'post_content',
                params:{
                    id:userinfo.id
                }
            }">
               {{userinfo.title}}
        </router-link>
    </li>
    
    export default {
      name:"SlideBar",
      data() {
        return {
          userinfos: [], // 代表当前页面的所有内容
        }
      }
      computed:{
          topiclimited(){
              if(this.userinfos.recent_topics){
                  return this.userinfos.recent_topics.slice(0,5) // 只截取前5条topic
              }
          },
          replylimited(){
              if(this.userinfos.recent_replies){
                  return this.userinfos.recent_replies.slice(0,5) // 只截取前5条topic
              }
          }
      }
    }
    

    5. App.vue

    <template>
      <div id="app">
        <Header></Header>
        <div class="main">
        <!--注意先后顺序,SlideBar要放在main的上面-->
          <router-view name="SlideBar"></router-view>
          <router-view name="main"></router-view>     
        </div>
      </div>
    </template>
    
    <script> 
    import Header from './components/Header'
    import PostList from './components/PostList'
    import SlideBar from './components/SlideBar'
    export default {
      name:"App",
      components:{
        Header,
        PostList
      }
    }
    </script>
    

    四、项目中 Vue Router 的使用


    router.js 中的内容

    export default new Router({
        mode: 'history',
        base: process.env.BASE_URL,
        routes: [
            {
                name: 'root',
                path: '/',
                components: {
                    main: PostList
                }
            }, 
            {
                name: 'post_content',
                path: '/topic/:id&author=:name', // 接收PostList传过来的id和name
                components: {
                    main: Article,
                    SlideBar: SlideBar
                }
            },
            {
                name: 'user_info',
                path: '/userinfo/:name', // 接收Article传过来的name
                components: {
                    main: UserInfo
                }
            }
        ]
    })
    

    PostList 组件

    <router-link :to="{
           name:'post_content', 
           params:{
              id:post.id,  // 点击PostList时就把id通过路由传给Article
              name:post.author.loginname // 点击PostList时就把name通过路由传递给SlideBar
           }
    }">
      <span>{{post.title}}</span>
    </router-link>
    

    Article

    <router-link :to="{
            name:'user_info',
            params:{
               name:reply.author.loginname // 点击头像把loginname通过路由传递给Userinfo
            }
    }">
      <img :src="reply.author.avatar_url" alt="头像" style="width:30px;height:30px;">
    </router-link>
    

    SlideBar

    <router-link :to="{
          name:'user_info',
          params:{
              name:userinfos.loginname // 点击侧边栏头像把loginname通过路由传递给Userinfo
          }
    }">
      <img :src="userinfos.avatar_url" alt="头像">
    </router-link>
    
    <router-link :to="{
          name:'post_content',
          params:{
             id:userinfo.id  // 点击侧边栏标题把id通过路由传递给Article
          }
    }">
      {{userinfo.title}}
    </router-link>
    

    Userinfo

    <router-link :to="{
          name:'post_content',
          params:{
             id:userinfo.id // 点击用户信息界面的title标题把id通过路由传递给Article
          }
    }">
      {{userinfo.title}}
    </router-link>
    

    五、部署到GitHub


    1. 在GitHub上新建一个空仓库
    image
    1. 在项目根目录中新建一个vue.config.js文件
    image

      并在该文件中设置正确的 publicPath,添加以下内容即可

    module.exports = {
      publicPath: process.env.NODE_ENV === 'production'
        ? '/cnode-demo/' // 这里应该设置为和你的仓库名相同
        : '/'
    }
    
    1. 用GitBash打开该项目,并在命令行中输入npm run build
      此时我们会发现项目中多了一个dist目录
    image
    1. 将项目上传到GitHub上

      如果仅仅只是为了预览项目,那么只需要把刚才生成的dist目录上传到我们第一步中新建的那个仓库中就可以进行预览了,但同时我们也可以将整个项目上传

      注意!!!
      上传项目时最好不要把node_modules文件夹也一起上传,因为这会使我们的项目变得非常大。
      我们应设置忽略上传node_modules,在.gitignore文件中写上一行 /node_modules/,即可防止 node_modules 目录被提交

    2. 预览项目

    将项目上传成功并设置GitHub Pages后,在URL路径中加一个/dist/就可以预览我们的项目啦

    image

    六、效果图


    Header+PostList Article+SlideBar Article评论区 Userinfo

    相关文章

      网友评论

        本文标题:我的第一个Vue.js项目----仿cnode社区

        本文链接:https://www.haomeiwen.com/subject/gkuxuctx.html