個人博客-后台管理系統 & 前台系統 & api系統 開發記錄 未完 時刻更新中


喜大普奔,開源地址,請猛戳我

1 登錄

登錄常見驗證方式是token驗證, 目前打算暫時用session驗證   以后有時間研究token登錄,並添加第三方登錄

2 css變量的處理

//TODO: 一鍵換膚,白天黑夜模式 先占坑
顏色變量不要寫死在單獨某一個組件里面,方便以后統一換風格,可以參考element 等ui庫

3 數據接口處理

a 首先所有的接口地址寫在一個文件中,,切url接口地址最好不要寫死,這樣有兩個好處
第一  方便查找我們所有用到的接口
第二 后台接口便於修改,比如初期階段后台接口可能較少,訪問直接以 '/login' '/entry' 這樣的形式訪問,等以后台想把某一部分用戶登錄相關的接口 訪問時統一在前面加上’/v1' 如果我們接口調用散落在各個文件里那就非常難以修改

b 利用 NODE_ENV 區分 當前的 baseUrl     當前前端webpackdevserver地址為 127.0.0.1:8080
例如 開發環境時我的 baseUrl 為 ‘’,所有接口的訪問的為當前ip當前接口,模擬數據可以用 axios-mock-adapter  (與axios配合簡單方便) --- 這是模擬數據方式一
這種數據訪問地址為 127.0.0.1:8080  ->    127.0.0.1:8080

例如 我想用json-server,或者express 自己起一個server 做模擬數據服務器,地址為127.0.0.1:3005 開發環境時我的 baseUrl 則就改為 ‘127.0.0.1:3005 ’,
這種數據訪問地址為 127.0.0.1:8080 -> 127.0.0.1:3005 存在跨域
所以需要在webpack-dev-config.js中加入


proxyTable: {
  '/': {
    target: 'http://127.0.0.1:3005',

  }
}

也就是將所以8080的訪問 代理到3005上
小總結
so~ axios-mock-adapter  優點 使用簡單 不存在跨域   缺點 不能動態根據前端傳遞參數不同返回不同結果,自己起的模擬服務器當然你自己想怎么干就怎么干了,上天都沒人管你~

c 利用axios的全局配置功能,
添加baseurl, -- 可以方便修改根路徑
添加請求、響應攔截器  首先這是符合 統一入口思想的,very good~~~   如此以來我們可以對所有的請求統一處理 比如打印請求日志,過濾關鍵key,等等   對統一返回也可以 根據某些錯誤碼進行全局warn處理

4一個逗號引發的血案~

錯誤很清楚的寫明了 在setAttribute 時候 key 填入的是逗號,經過糾結的一一對比最終發現這個問題是 在元素的屬性上 有逗號忘記了刪除

你一眼就能看到嗎? 如果能看到 恭喜你 ,不用(像我一樣)再走彎路~

引發原因是當初參照的其他template模板用的是 pug語法,其中每個屬性是用逗號隔開的
而我們這里是html語法 都好一定要刪除,否則 vue會當做屬性填充,切記切記

5再看父子組件的傳值(這一條之前理解稍微有誤,可以跳過)

我們都知道通過props可以由父組件給子組件傳遞值

<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>

以下理解有誤,留作歷史吧,大家可以不看

需要注意的是以上只適合傳輸字符串 而不能傳輸對象!

需要注意的是以上只適合傳輸字符串 而不能傳輸對象!

如果你想把一個對象的所有屬性作為 prop 進行傳遞,可以使用不帶任何參數的 v-bind (即用 v-bind 而不是 v-bind:prop-name)。例如,已知一個 todo 對象:

todo: {
  text: 'Learn Vue',
  isComplete: false
}

然后:

<todo-item v-bind="todo"></todo-item>

將等價於:

<todo-item
  v-bind:text="todo.text"
  v-bind:is-complete="todo.isComplete"
></todo-item>
>

子類接受屬性時:

props: ['text', 'is-complete']

闊是!如果父組件data里面有一個目錄屬性 是數組結構

data () {
    return {
        cagalogs: [
        {},
        {},
        {}
        ]
    }
}

此時,我們將無法通過父組件傳遞數據過去,

出現這種情況的場景是,管理系統 博客展示頁面 展示了有多少個目錄,已經獲取了一遍目錄數據, 在新增文章時候還會彈出編輯組件,此時讓需要目錄數據

這種情況下的解決方案:

  • 通過 props -----------> 走不通
  • 通過 vuex --------------> 不推薦,因為只是兩個組件使用,不想都放到全局,避免常駐內存
  • 存放到 localstorage + mixin --------> 暫時采用此方案

mixin.js

import urls from '@/config/urls
import {storageKey, setStorage, getStorage} from '@/utils/storage'
export default {
  data () {
    return {
      catalogs: []
    }
  },
  methods: {
    async getCatalogs () {
      let catalogs = getStorage(storageKey.CATALOGS)
      if (catalogs) {
        this.catalogs = catalogs
        return
      }
      const res = await this.axios.get(urls.catalogList)
      if (res.data.status === 0) {
        setStorage(storageKey.CATALOGS, res.data.data)
        this.catalogs = res.data.data
      }
    }
  }
}

雖然最終數據都是取的 localstorage 下的 數據 , 但 兩個組件實際上是 自己維護了自己的 catalogs 這個 屬性。

前台博客記錄

如何找到其他網站的源碼實現

主界面封面部分參考了 搜車大無線團隊博客,其中有一個功能 封面 有一個下箭頭,點擊一下 實現滾屏到博客正文


直觀發現此處是通過href錨點實現滾動,然而自己實現時發現這樣並沒有滾動動畫,但是他們是怎么做到的呢,開始我一直以為肯定是用了某個css3動畫實現,但是根本找不到任何css相關的樣式



如何確定是不是用js實現的呢?



如圖點擊js便可跳轉到相應js文件,並發現了用animate的地方,由此我們得出了原來此處是用js,根本和css沒有半毛線關系~


如何找到其他網站的源碼實現2


不巧的是 博主還算是一個發(沒)散(頭)思(沒)維(鬧)的人,又去了餓了么 研究一下 回到頂部功能是如何實現的。



這次我們輕車熟路的就找到了源碼,只是不幸這次點進去是vue的源碼,所以並沒有什么卵用, 我們 發現 這個div 的類名 為 page-component-up 嗯,我們有理由相信 源碼是通過給這個類名添加點擊事件實現的,(誰給你的自信?)

so~ ,我們從github 下載element 源碼, vscode 打開,全文搜索(ctrl shift f),這個類名, 在 component.tpl 文件下找到了源碼 歐耶~

<style>
  .page-component__scroll {
    height: calc(100% - 80px);
    margin-top: 80px;

    .el-scrollbar__wrap {
      overflow-x: auto;
    }
  }

  .page-component {
    box-sizing: border-box;
    height: 100%;
  
    &.page-container {
      padding: 0;
    }

    .page-component__nav {
      width: 240px;
      position: fixed;
      top: 0;
      bottom: 0;
      margin-top: 80px;
      transition: padding-top .3s;

      .el-scrollbar__wrap {
        height: 100%;
      }

      &.is-extended {
        padding-top: 0;
      }
    }

    .side-nav {
      height: 100%;
      padding-top: 50px;
      padding-bottom: 50px;
      padding-right: 0;

      & > ul {
        padding-bottom: 50px;
      }
    }

    .page-component__content {
      padding-left: 270px;
      padding-bottom: 100px;
      box-sizing: border-box;
    }

    .content {
      padding-top: 50px;

      > {
        h3 {
          margin: 55px 0 20px;
        }

        table {
          border-collapse: collapse;
          width: 100%;
          background-color: #fff;
          font-size: 14px;
          margin-bottom: 45px;
          line-height: 1.5em;

          strong {
            font-weight: normal;
          }

          td, th {
            border-bottom: 1px solid #d8d8d8;
            padding: 15px;
            max-width: 250px;
          }

          th {
            text-align: left;
            white-space: nowrap;
            color: #666;
            font-weight: normal;
          }

          td {
            color: #333;
          }

          th:first-child, td:first-child {
            padding-left: 10px;
          }
        }

        ul:not(.timeline) {
          margin: 10px 0;
          padding: 0 0 0 20px;
          font-size: 14px;
          color: #5e6d82;
          line-height: 2em;
        }
      }
    }

    .page-component-up {
      background-color: #fff;
      position: fixed;
      right: 100px;
      bottom: 150px;
      size: 40px;
      border-radius: 20px;
      cursor: pointer;
      transition: .3s;
      box-shadow: 0 0 6px rgba(0,0,0, .12);

      i {
        color: #409EFF;
        display: block;
        line-height: 40px;
        text-align: center;
        font-size: 18px;
      }

      &.hover {
        opacity: 1;
      }
    }
    .back-top-fade-enter,
    .back-top-fade-leave-active {
      transform: translateY(-30px);
      opacity: 0;
    }
  }

  @media (max-width: 768px) {
    .page-component {
      .page-component__nav {
        width: 100%;
        position: static;
        margin-top: 0;
      }
      .side-nav {
        padding-top: 0;
        padding-left: 50px;
      }
      .page-component__content {
        padding-left: 10px;
        padding-right: 10px;
      }
      .content {
        padding-top: 0;
      }
      .content > table {
        overflow: auto;
        display: block;
      }
      .page-component-up {
        display: none;
      }
    }
  }
</style>
<template>
  <el-scrollbar class="page-component__scroll" ref="componentScrollBar">
  <div class="page-container page-component">
    <el-scrollbar class="page-component__nav">
      <side-nav :data="navsData[lang]" :base="`/${ lang }/component`"></side-nav>
    </el-scrollbar>
    <div class="page-component__content">
      <router-view class="content"></router-view>
      <footer-nav></footer-nav>
    </div>
    <transition name="back-top-fade">
      <div
        class="page-component-up"
        :class="{ 'hover': hover }"
        v-show="showBackToTop"
        @mouseenter="hover = true"
        @mouseleave="hover = false"
        @click="toTop">
        <i class="el-icon-caret-top"></i>
      </div>
    </transition>
  </div>
  </el-scrollbar>
</template>
<script>
  import bus from '../../bus';
  import navsData from '../../nav.config.json';
  import throttle from 'throttle-debounce/throttle';

  export default {
    data() {
      return {
        lang: this.$route.meta.lang,
        navsData,
        hover: false,
        showBackToTop: false,
        scrollTop: 0,
        showHeader: true,
        componentScrollBar: null,
        componentScrollBoxElement: null
      };
    },
    watch: {
      '$route.path'() {
        // 觸發偽滾動條更新
        this.componentScrollBox.scrollTop = 0;
        this.$nextTick(() => {
          this.componentScrollBar.update();
        });
      }
    },
    methods: {
      renderAnchorHref() {
        if (/changelog/g.test(location.href)) return;
        const anchors = document.querySelectorAll('h2 a,h3 a');
        const basePath = location.href.split('#').splice(0, 2).join('#');

        [].slice.call(anchors).forEach(a => {
          const href = a.getAttribute('href');
          a.href = basePath + href;
        });
      },

      goAnchor() {
        if (location.href.match(/#/g).length > 1) {
          const anchor = location.href.match(/#[^#]+$/g);
          if (!anchor) return;
          const elm = document.querySelector(anchor[0]);
          if (!elm) return;

          setTimeout(_ => {
            this.componentScrollBox.scrollTop = elm.offsetTop;
          }, 50);
        }
      },
      toTop() {
        this.hover = false;
        this.showBackToTop = false;
        this.componentScrollBox.scrollTop = 0;
      },

      handleScroll() {
        const scrollTop = this.componentScrollBox.scrollTop;
        this.showBackToTop = scrollTop >= 0.5 * document.body.clientHeight;
        if (this.showHeader !== this.scrollTop > scrollTop) {
          this.showHeader = this.scrollTop > scrollTop;
        }
        if (scrollTop === 0) {
          this.showHeader = true;
        }
        if (!this.navFaded) {
          bus.$emit('fadeNav');
        }
        this.scrollTop = scrollTop;
      }
    },
    created() {
      bus.$on('navFade', val => {
        this.navFaded = val;
      });
      window.addEventListener('hashchange', () => {
        if (location.href.match(/#/g).length < 2) {
          document.documentElement.scrollTop = document.body.scrollTop = 0;
          this.renderAnchorHref();
        } else {
          this.goAnchor();
        }
      });
    },
    mounted() {
      this.componentScrollBar = this.$refs.componentScrollBar;
      this.componentScrollBox = this.componentScrollBar.$el.querySelector('.el-scrollbar__wrap');
      this.throttledScrollHandler = throttle(300, this.handleScroll);
      this.componentScrollBox.addEventListener('scroll', this.throttledScrollHandler);
      this.renderAnchorHref();
      this.goAnchor();
      document.body.classList.add('is-component');
    },
    destroyed() {
      document.body.classList.remove('is-component');
    },
    beforeDestroy() {
      this.componentScrollBox.removeEventListener('scroll', this.throttledScrollHandler);
    }
  };
</script>

后台 node api 系統

1如何使用es6語法

1 入口文件引入bable-core

2 在.babelrc 如下配置,經本人測試,stage-3 如果不配置 則 【擴展運算符】使用會報錯

3 安裝相關依賴

2繼承使用問題 this問題

在類中 如果有需要用到內部this 的方法中  需要 在 constructor 中 通過bind 應綁定 this,
因為這些類的方法的調用形式為 如下圖二調用,因此 若想用this,需要在constructure中 進行 硬綁定

圖一,control對象聲明

圖二 路由調用 相應的control對象的方法的引用

3通過 vscode 進行斷點調試

網上最常見的三種查詢方法

1 node-inspector 之前嘗試過 好像最終可以chrome 進行斷點, 但是還是偶爾失敗,且麻煩 所以 舍棄

2 好像還可以通過 --xxx 加類似什么參數來着, 但是也沒成功 所以 舍棄

3 webstorm 還是算了吧……

4 我們說一下 用vscode 調試

默認情況下直接按 f5 就會呼出調試界面,直接選擇node 即可,也可通過配置,默認情況下 入口是 app.js
我們根據需要修改即可。

4給 mongoose find 命令 返回的 數據 添加額外屬性的兩種方式

mongoose find 命令 返回的 數據結構如圖

如果我們想在find命令后返回的對象里面添加其定義屬性,

比如 動態的給每一個對象添加一個 uid屬性, 我們直接給對象添加是無效的,

即使當時你手動添加上打印出來可以看到,但是返回到客戶端 卻沒有這個屬性

因為mongoose內部會檢查你要添加的這個屬性是否是在scheme上,

一說可以通過 strict: false 讓查詢出的結果可修改,不過測試發現沒有什么卵用

願意是 mongoose 返回的 對象 其實實在 當前對象的 _doc 屬性 下面

方式一 粗暴添加

所以 我們可以 通過 給對對象的_doc屬性下的對象添加自定義屬性即可

方式二 溫柔添加

mongoose 提供啦 toObject()方法 也可以添加

最終的代碼類似於:

 var model = obj.toObject();
 model.isBorrow = false;
cb(null, model);

5 循環+異步引起的

場景描述

查詢文章列表接口
需要返回的數據格式如下

{
                "id": "0920892e-1512-401a-994e-5406a14aca0b",
                "title": "Vue2 + Nodejs  + WebSocket 完成你畫我猜多人在線游戲",
                "summary": "使用 websocket + vue2 即可完成一個很有意思的在線游戲作品。 你畫我猜,相信大家對這個游戲都很熟悉。 我用Vue2 + mint-ui + nodejs + websocket 實現了你畫我猜這個游戲。 建議移動端打開效果更佳(可掃下方二維碼),PC端需要使用谷歌開發者模式,然后使用移動調試工具,才可以正常使用(主要是一些touch事件,pc不支持)。  大家可以拉上一兩個人,來開個房間試試看,體驗體驗效果。 http://yd.diamondfsd.com   主要實現了以下這些功能  大廳功能  個人信息顯示 頂部顯示個人昵稱,可以修改 暫時不支持上傳頭像,頭像用昵稱第一個",
                "createTime": 1487680940000,
                "updateTime": 1487686388000,
                "catalogId": "1d16334c-3231-4232-9adc-e57e5d74552e",
                "banner": "",
                "tagNames": "你畫我猜手機游戲",
                "catalogName": "技術分享",
                "tags": [{
                    "id": "a0997aea-2a58-431c-8dad-f88843515587",
                    "name": "你畫我猜手機游戲"
                }

這里面的大部分字段都在aritcle這個表中,我們通過__db.article.find()__ 即可查出來所有文章

其中 catalogName 這個字段 在__catalog__表中 並沒有在__article__表中,因此我們需要通過第一次查詢__article__表出來的結果遍歷得到 catalogId,然后再去查詢 __catalog__表,

async _addCatalogName (articleArr) {
    let arr = []
    articleArr.forEach ( async (article,idx) => {
        let catalog_id = article.catalog_id
        let ret = await ArticleModel.getCatalogNameById(catalog_id)
        let name = ret[0].name
        let ob = article.toObject()
        ob.catalog_name = name
        arr.push(ob)
    })
    console.log(arr)
    return arr
}

我們觀察打印出來的結果 就會發現靈異現象,明明有10個數據,但是最外層竟然顯示的零個,

更詭異的是 當我們 再一次調用 arr.push(1)

length是11,但是只能看到一個,意不意外?

解決辦法:

偉大的阮老師早就為我們想好啦解決辦法

so~ 我們最終的解決辦法

    for (let article of articleArr) {
        let query_ret = await ArticleModel.getCatalogNameById(article.catalog_id)
        // console.log(obj)
        let name = query_ret[0].name
        let article_copy = article.toObject()
        article_copy.catalog_name = name
        arr.push(article_copy)
      }
    console.log(arr)
    return arr

完美~

補充一下,一下這種方式再此處不合適我們,因為我們不能保證異步執行的順序,也就無法正確的添加catalogname 到對應的對象上

    let promises = articleArr.map((arrticle) => ArticleModel.getCatalogNameById(arrticle.catalog_id));

    let results = await Promise.all(promises);
    console.log(results);

5 開發過程中調試解決跨域問題的兩種方式

方式一

1 開發過程中 以相對路徑訪問,如 this.get('/v1/userinfo')
2 通過 webpack-dev-server  配置 代理
![](http://images2017.cnblogs.com/blog/821507/201802/821507-20180201103959734-763359013.png)
3 上線時通過nginx 反向代理即可

方式二

1 開發過程中 以相對路徑訪問,如 this.get('http://host:port/v1/userinfo')
2 服務端 配置相應的跨域配置即可
![](http://images2017.cnblogs.com/blog/821507/201802/821507-20180201104319765-1304935666.png)
3 此時請求資源頭部可以看到服務端的設置
![](http://images2017.cnblogs.com/blog/821507/201802/821507-20180201104446296-1641890297.png)

補充
前端通過axios訪問時 頁面訪問路徑都以相對路徑方式寫 如 this.get('/v1/userinfo')
通過動態配置 baseurl 來決定我們是相對路徑訪問還是絕對路徑訪問


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM