目錄css
喜大普奔,開源地址,請猛戳我html
登陸常見驗證方式是token驗證, 目前打算暫時用session驗證 之後有時間研究token登陸,並添加第三方登陸
//TODO: 一鍵換膚,白天黑夜模式 先佔坑 顏色變量不要寫死在單獨某一個組件裏面,方便之後統一換風格,能夠參考element 等ui庫
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處理
錯誤很清楚的寫明瞭 在setAttribute 時候 key 填入的是逗號,通過糾結的一一對比最終發現這個問題是 在元素的屬性上 有逗號忘記了刪除
你一眼就能看到嗎? 若是能看到 恭喜你 ,不用(像我同樣)再走彎路~ 引起緣由是當初參照的其餘template模板用的是 pug語法,其中每一個屬性是用逗號隔開的 而咱們這裏是html語法 都好必定要刪除,不然 vue會當作屬性填充,切記切記
咱們都知道經過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: [ {}, {}, {} ] } }
此時,咱們將沒法經過父組件傳遞數據過去,
出現這種狀況的場景是,管理系統 博客展現頁面 展現了有多少個目錄,已經獲取了一遍目錄數據, 在新增文章時候還會彈出編輯組件,此時讓須要目錄數據
這種狀況下的解決方案:
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沒有半毛線關係~
不巧的是 博主還算是一個發(沒)散(頭)思(沒)維(鬧)的人,又去了餓了麼 研究一下 回到頂部功能是如何實現的。
此次咱們輕車熟路的就找到了源碼,只是不幸此次點進去是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>
1 入口文件引入bable-core
2 在.babelrc 以下配置,經本人測試,stage-3 若是不配置 則 【擴展運算符】使用會報錯
3 安裝相關依賴
在類中 若是有須要用到內部this 的方法中 須要 在 constructor 中 經過bind 應綁定 this, 由於這些類的方法的調用形式爲 以下圖二調用,所以 若想用this,須要在constructure中 進行 硬綁定
圖一,control對象聲明
圖二 路由調用 相應的control對象的方法的引用
網上最多見的三種查詢方法
1 node-inspector 以前嘗試過 好像最終能夠chrome 進行斷點, 可是仍是偶爾失敗,且麻煩 因此 捨棄
2 好像還能夠經過 --xxx 加相似什麼參數來着, 可是也沒成功 因此 捨棄
3 webstorm 仍是算了吧……
4 咱們說一下 用vscode 調試
默認狀況下直接按 f5
就會呼出調試界面,直接選擇node 便可,也可經過配置,默認狀況下 入口是 app.js
咱們根據須要修改便可。
mongoose find 命令 返回的 數據結構如圖
若是咱們想在find命令後返回的對象裏面添加其定義屬性,
好比 動態的給每個對象添加一個 uid屬性, 咱們直接給對象添加是無效的,
即便當時你手動添加上打印出來能夠看到,可是返回到客戶端 卻沒有這個屬性
由於mongoose內部會檢查你要添加的這個屬性是不是在scheme上,
一說能夠經過 strict: false 讓查詢出的結果可修改,不過測試發現沒有什麼卵用
願意是 mongoose 返回的 對象 其實實在 當前對象的 _doc
屬性 下面
方式一 粗暴添加
因此 咱們能夠 經過 給對對象的_doc屬性下的對象添加自定義屬性便可
方式二 溫柔添加
mongoose 提供啦 toObject()方法 也能夠添加
最終的代碼相似於:
var model = obj.toObject(); model.isBorrow = false; cb(null, model);
場景描述
查詢文章列表接口
須要返回的數據格式以下
{ "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);
方式一
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 來決定咱們是相對路徑訪問仍是絕對路徑訪問