我的博客-後臺管理系統 & 前臺系統 & api系統 開發記錄 未完 時刻更新中

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

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能夠由父組件給子組件傳遞值vue

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

如下理解有誤,留做歷史吧,你們能夠不看node

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

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

若是你想把一個對象的全部屬性做爲 prop 進行傳遞,可使用不帶任何參數的 v-bind (即用 v-bind 而不是 v-bind:prop-name)。例如,已知一個 todo 對象:css3

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

而後:nginx

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

將等價於:git

<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 來決定咱們是相對路徑訪問仍是絕對路徑訪問

相關文章
相關標籤/搜索