【Vue全家桶+SSR+Koa2全栈开发】项目搭建过程 整合 学习目录(持续更新中)
寫在開(kāi)頭
大家好,這里是lionLoveVue,基礎(chǔ)知識(shí)決定了編程思維,學(xué)如逆水行舟,不進(jìn)則退。金三銀四,為了面試也還在慢慢積累知識(shí),Github上面可以直接查看所有前端知識(shí)點(diǎn)梳理,github傳送門,覺(jué)得不錯(cuò),點(diǎn)個(gè)Star★,好運(yùn)連連,Offer終究鼠于你,持續(xù)更新中。另外,也可以關(guān)注微信公眾號(hào):小獅子前端Vue,源碼以及資料今后都會(huì)放在里面。Vue基礎(chǔ)
全局安裝vue/cli腳手架
管理員身份運(yùn)行cmd,執(zhí)行如下代碼:
npm install -g @vue/cli或
yarn global add @vue/cli命令行輸入vue ui 打開(kāi)可視化界面
使用默認(rèn)default配置
用vscode打開(kāi)項(xiàng)目,執(zhí)行如下命令
npm/cnpm run serve訪問(wèn):http://localhost:8080/ 查看是否配置成功
自定義指令
推薦閱讀:官方文檔-自定義指令
除了核心功能默認(rèn)內(nèi)置的指令 (v-model 和 v-show),Vue 也允許注冊(cè)自定義指令。注意,在 Vue2.0 中,代碼復(fù)用和抽象的主要形式是組件。然而,有的情況下,你仍然需要對(duì)普通 DOM 元素進(jìn)行底層操作,這時(shí)候就會(huì)用到自定義指令。
組件
父子組件的通信方式
父組件傳子組件,是在子組件使用props,然后再父組件,通過(guò):=綁定變量
子組件傳父組件,是在子組件使用自定義事件,例如:
//子組件配置 @click="$emit('patch(xxx)')" //父組件配置 @patch="func"如果是跨組件,不是父子組件關(guān)系,就使用 Vuex。
slot
插槽在組件抽象設(shè)計(jì)中的應(yīng)用:
原本組件不能嵌入html內(nèi)容,但通過(guò)父子組件傳遞插槽即可實(shí)現(xiàn)
//父組件 slot="a" //子組件 <slot name="a"></slot>Vuex基礎(chǔ)
Vuex簡(jiǎn)單來(lái)說(shuō)就是多個(gè)組件共享數(shù)據(jù),但是組件是不能直接操縱數(shù)據(jù)的,如下圖所示,我們的數(shù)據(jù)放在紫色部分state里面(沒(méi)有放在data里),操作數(shù)據(jù)源由紅色部分Mutations完成,什么時(shí)候操控?cái)?shù)據(jù)有黃色部分Actions來(lái)控制,這里就是由用戶來(lái)操縱組件來(lái)觸發(fā) Actions ,最后,由 Actons 提交 Commit 通知改變數(shù)據(jù)源,來(lái)完成組件視圖的更新渲染。
安裝Vuex
npm/cnpm i vuexKoa2基礎(chǔ)
koa-generator
管理員身份打開(kāi)cmd,運(yùn)行如下代碼,全局安裝
npm install -g koa-generator使用git bash,在文件夾內(nèi)創(chuàng)建項(xiàng)目
koa2 -e project_name-e 表示使用ejs,不加和 node 一樣
接下來(lái),執(zhí)行安裝命令
安裝&更新 fsevents 包
npm install --update-binaryrun app命令:
DEBUG=koa2_learn:* npm start執(zhí)行后輸出 node bin/www 說(shuō)明服務(wù)端已經(jīng)跑起來(lái)了
訪問(wèn) http://localhost:3000/ 查看頁(yè)面效果
我們查看 package.json ,發(fā)現(xiàn)啟動(dòng)腳本是通過(guò) npm scripts ,如果你想要自啟動(dòng)的話,執(zhí)行如下命令:
對(duì)于上述執(zhí)行指令,dev 和 prd 必須使用 npm run xxx
與此同時(shí),我們?cè)?index.js 全局打印一下,加入如下代碼:
在 git bash 中執(zhí)行如下命令
npm run dev說(shuō)明我們改變服務(wù)端腳本,服務(wù)能夠自啟動(dòng)了
Koa2中間件
圖片來(lái)自Koa2官方,大致意思就是服務(wù)端接受客戶端的 Request,經(jīng)過(guò)服務(wù)端一些流程,然后 Response 返回給瀏覽器,其中每一個(gè)環(huán),都是一個(gè)中間件。進(jìn)來(lái)的時(shí)候會(huì)經(jīng)過(guò)某個(gè)中間件,出去的時(shí)候也會(huì)經(jīng)過(guò),這樣的機(jī)制就可以讓中間件引用順序和代碼執(zhí)行順序不一致。
mongoose基礎(chǔ)
mongodb概念&安裝
mongodb屬于非關(guān)系型數(shù)據(jù)庫(kù),與mysql相對(duì)的(mysql是關(guān)系型數(shù)據(jù)庫(kù))
mongodb里面沒(méi)有 table表,只有 Collections
原本mysql一行叫做 row,而在mongodb中叫做 document,原本一列叫做 cloumn,而現(xiàn)在叫做 fields
windows 10 安裝教程:
推薦閱讀:Windows 平臺(tái)安裝 MongoDB
注意
安裝Install MongoDB Compass 不勾選,否則可能要很長(zhǎng)時(shí)間都一直在執(zhí)行安裝,MongoDB Compass是一個(gè)圖形界面管理工具,后面會(huì)去下載一個(gè)圖形界面管理工具Robo3T。
其它選項(xiàng)就默認(rèn)選擇 next 即可
找到下面位置下的兩個(gè) .exe 都點(diǎn)擊運(yùn)行一下好了… (啟動(dòng)服務(wù)應(yīng)該是下面那一個(gè))
檢測(cè)是否安裝成功
訪問(wèn):http://127.0.0.1:27017/ 是否出現(xiàn)如下界面:
圖形界面管理工具 Robo3T 安裝與使用
官網(wǎng)地址
Studio windows下載地址
Robo 3T下載
進(jìn)入安裝頁(yè)面后選擇 easy 版本 ,然后選擇你喜歡的主題風(fēng)格,是明亮還是黑暗風(fēng)格
進(jìn)入圖形化界面后,新建一個(gè) connection,選擇默認(rèn)端口 27017,保存
mongoose 中文文檔
參考:mongoose 4.5中文文檔
在之前配置好的 Koa2 文件夾內(nèi)(我的是koa2_learn文件夾) 打開(kāi) bash 界面,輸入如下命令,安裝 mongoose
npm i mongoose連接數(shù)據(jù)庫(kù)
接下來(lái)就是配置相關(guān)了
在項(xiàng)目根目錄下創(chuàng)建 名為 dbs 的文件夾
新建一個(gè) config.js 文件,配置如下代碼
在 dbs 目錄下創(chuàng)建 名為 models 的文件夾
新建一個(gè) person.js 文件,配置如下代碼(此時(shí)的文件名對(duì)應(yīng)著之后我們的“表名”,也就是 Collections)
然后在 app.js 中進(jìn)行導(dǎo)入
const mongoose = require('mongoose') const dbConfig = require('./dbs/config')在 app.js 默認(rèn)注釋 routes 后面,添加第三行連接代碼
// routes app.use(index.routes(), index.allowedMethods()) app.use(users.routes(), users.allowedMethods()) mongoose.connect(dbConfig.dbs,{useNewUrlParser:true })基本配置就結(jié)束了,現(xiàn)在最好是重啟一下koa服務(wù),ctrl+c 退出,然后輸入如下命令:
npm run dev出現(xiàn)如下界面,說(shuō)明配置是沒(méi)有問(wèn)題的,連接成功!
接下來(lái)就是嘗試寫一個(gè)api試試,在 users.js 文件內(nèi)配置如下代碼
頭部導(dǎo)入模型
//導(dǎo)入模型 const Person = require('../dbs/models/person')自定義api接口
router.post('/addPerson',async function(ctx){//新建一個(gè)模型的實(shí)例const person = new Person({name: ctx.request.body.name,age: ctx.request.body.age})let codetry {await person.save()code=0} catch (error) {code=-1}ctx.body={code} })調(diào)用接口。 curl 是一個(gè)shell 命令, -d命令表示是 post 請(qǐng)求 ,接下來(lái)是數(shù)據(jù)項(xiàng),最后是api接口地址
curl -d 'name=chocolate&age=20' http://localhost:3000/users/addPerson
此時(shí),打開(kāi)Robo 3T,刷新一下我們的數(shù)據(jù)庫(kù),就能發(fā)現(xiàn)多了一個(gè) dbs
查看一下,是否有對(duì)應(yīng)寫入數(shù)據(jù):
上述過(guò)程,基本上把我們的后端服務(wù)和數(shù)據(jù)庫(kù)進(jìn)行了打通,后續(xù)我們只需要套著之前的例子來(lái)就好了,接下來(lái)再展示寫一個(gè)api,因?yàn)樯衔氖窃黾硬僮?#xff0c;也就是寫操作,下文我們?cè)賮?lái)個(gè)讀操作吧。
依舊是在 users.js 中加入如下代碼
//數(shù)據(jù)庫(kù)讀取api router.post('/getPerson',async function(ctx){const result = await Person.findOne({name:ctx.request.body.name})const results = await Person.find({name:ctx.request.body.name})ctx.body = {code: 0,result,results} })打開(kāi) bash ,執(zhí)行如下命令:
curl -d 'name=chocolate' http://localhost:3000/users/getPerson發(fā)現(xiàn),會(huì)返回給我們json數(shù)據(jù)包,證明api接口實(shí)現(xiàn)
最后,附上更新和刪除api,親測(cè)有效嗷
更多知識(shí)點(diǎn)請(qǐng)參考:mongoose 4.5中文文檔
Redis基礎(chǔ)
cookie 和 session 引入
這里就要扯到 cookie 和 session的相關(guān)知識(shí)點(diǎn)了,劃重點(diǎn)啦!
服務(wù)端的程序如何去識(shí)別客戶端的狀態(tài),大家知道,http是無(wú)狀態(tài)的,推薦閱讀:如何學(xué)好前端,白嫖知識(shí)
比如現(xiàn)在有個(gè)用戶A,它訪問(wèn)了服務(wù)器程序,那服務(wù)器程序如何知道下一次再訪問(wèn)的時(shí)候還是A呢?
因此,對(duì)于這一塊就要用到非常重要的概念,session,當(dāng)然,這個(gè)session可不是瀏覽器的,而是服務(wù)器的,它是用來(lái)存儲(chǔ)用戶的信息的。
那么,服務(wù)器的 session是如何保持在客戶端呢?
這個(gè)時(shí)候呢,又要引出另一個(gè)非常重要的概念,瀏覽器中的 cookie 。
綜上所述,cookie 和 session 的關(guān)系是:服務(wù)端用 session 來(lái)保存用戶的狀態(tài),然后客戶端用 cookie 來(lái)保存 session ,服務(wù)器端把 session 種植到 cookie 中,然后下次訪問(wèn)時(shí),cookie 會(huì)攜帶著 session ,進(jìn)而達(dá)到一個(gè)身份認(rèn)證的效果。
redis概念&安裝
上文我們知道了cookie 和 session 在瀏覽器和服務(wù)器端的作用,那么與我們r(jià)edis 有什么關(guān)系呢?
想一想,既然是認(rèn)證的功能,那我們服務(wù)端的session 應(yīng)該存在哪呢,也許你會(huì)想著放入服務(wù)器端存儲(chǔ),放入內(nèi)存當(dāng)中,這確實(shí)是一種方式,沒(méi)有問(wèn)題,但當(dāng)應(yīng)用程序很大的時(shí)候,session 容量特別大的時(shí)候呢?例如某寶這樣的呢?此時(shí)內(nèi)存肯定不夠用了,這個(gè)時(shí)候就需要一個(gè)容器來(lái)存儲(chǔ)大容量的 session 了,此時(shí)存儲(chǔ)數(shù)據(jù)庫(kù) redis就發(fā)揮了它的作用了!
關(guān)于數(shù)據(jù)庫(kù)的話,上文也提到了mongodb,我們是不是也可以用這個(gè)呢?
這個(gè)的話,雖然也是一種方式,但我們要考慮讀寫性能問(wèn)題,因?yàn)?redis是快速讀寫類型的數(shù)據(jù)庫(kù),使用靈活方便,是 key-value 型的
為了更加了解redis,這里我就引用官方介紹了:
-
REmote DIctionary Server(Redis) 是一個(gè)由Salvatore Sanfilippo寫的key-value存儲(chǔ)系統(tǒng)。
-
Redis是一個(gè)開(kāi)源的使用ANSI C語(yǔ)言編寫、遵守BSD協(xié)議、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫(kù),并提供多種語(yǔ)言的API。
-
它通常被稱為數(shù)據(jù)結(jié)構(gòu)服務(wù)器,因?yàn)橹?#xff08;value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型。
安裝教程
參考:redis安裝教程
參考:windows下Redis的安裝和配置–圖文教程
在 koa 的 bash界面,執(zhí)行如下命令,完成 koa 與 redis 的連接
npm i koa-generic-session koa-redis redis-server.exe redis.windows.conf啟動(dòng) redis 服務(wù)
一、[9980] 21 Apr 02:57:05.611 # Creating Server TCP listening socket 127.0.0.1:6379: bind: No error
解決方法:在命令行中運(yùn)行
redis-cli.exe 127.0.0.1:6379>shutdown二、Redis (error) NOAUTH Authentication required.解決方法
127.0.0.1:6379>auth "123456" 127.0.0.1:6379>shutdown not connected>exit然后重新運(yùn)行 redis-server.exe redis.windows.conf ,啟動(dòng)成功!
參考:Redis啟動(dòng)報(bào)錯(cuò):Creating Server TCP listening socket 127.0.0.1:6379: bind: No error
Nuxt.js基礎(chǔ)
Nuxt.js 是一個(gè)基于Vue.js的通用應(yīng)用框架,預(yù)設(shè)了利用Vue.js開(kāi)發(fā)服務(wù)端渲染的應(yīng)用所需要的各種配置。
基于Vue 2做的,包括Vue-Router,支持Vuex、Vue Server Render、vue-meta
Nuxt.js工作流
下圖源自Nuxt官網(wǎng),簡(jiǎn)單介紹一下它的工作流程
更多內(nèi)容,請(qǐng)參考:nuxtjs 英文官方文檔
參考:nuxtjs 中文文檔
從瀏覽器發(fā)出一個(gè)請(qǐng)求,到最終服務(wù)端渲染完成,關(guān)于Nuxt的生命周期如下:
- Incoming Request 瀏覽器發(fā)送一個(gè)請(qǐng)求
- 服務(wù)端檢查是否有 nuxtServerInit 配置項(xiàng),有的話就會(huì)執(zhí)行這個(gè)函數(shù),其中包含一個(gè)標(biāo)注: Store action 用來(lái)操作 vuex
- 下一個(gè)環(huán)節(jié)就是中間件 middleware ,與路由相關(guān),做任何你想要的功能
- 預(yù)驗(yàn)證 validate() 可以配合高級(jí)動(dòng)態(tài)路由,做一些驗(yàn)證,比如是否允許跳轉(zhuǎn)某個(gè)頁(yè)面
- asyncData() & fetch() 獲取數(shù)據(jù),前一個(gè)是用來(lái)渲染vue component,即 vue組件的,后一個(gè)通常用來(lái)修改 vuex,即 Store的
- 有了數(shù)據(jù),模板后,最后一步就是 Render 渲染了,方式是 SSR
Nuxt.js 安裝
如果沒(méi)有安裝vue cli的話,先全局安裝一下
npm/cnpm install -g @vue/cli-init vue init nuxt-community/koa-template nuxt_learn(這里填入你自己的文件名)安裝完成后,依次執(zhí)行如下命令
cd nuxt_learn npm install # Or yarn npm install --update-binary npm install ajv@^6(可選項(xiàng),如果有報(bào)warn,就裝一下) npm run dev如果安裝過(guò)程網(wǎng)絡(luò)有問(wèn)題,報(bào)錯(cuò)了,可以試試用 cnpm或者
nrm use cnpmeslint版本問(wèn)題: 需要更新eslint版本
解決:執(zhí)行如下代碼:
執(zhí)行完如下步驟后,最后一步,打開(kāi)服務(wù):
npm run devbabel編譯版本有問(wèn)題: 解決辦法是升級(jí)backpack-core@0.3.0到backpack-core@0.7.0
npm install backpack-core@0.7.0 --save-dev解決上述問(wèn)題后,編譯能成功,但是客戶端依舊運(yùn)行報(bào)錯(cuò)
解決辦法是:在nuxt.config.js里找到eslint-loader將ctx.isClient改成ctx.Client就可以運(yùn)行了
參考:解決vue init nuxt-community/koa-template項(xiàng)目,安裝報(bào)錯(cuò)問(wèn)題
解決完上述問(wèn)題后,執(zhí)行如下指令:
npm run dev
訪問(wèn):http://localhost:3000/ 出現(xiàn)如下界面,代表配置成功 ??ヽ(°▽°)ノ?
附:微信公眾號(hào):【小獅子前端】 回復(fù)【nuxt-learn】即可獲取本節(jié)源碼
知識(shí)拓展
當(dāng)使用 SSR 時(shí), mounted 只在瀏覽器端渲染,而在服務(wù)器端不會(huì)渲染 :瀏覽器通過(guò)axios請(qǐng)求的數(shù)據(jù),只有 created 在開(kāi)啟SSR時(shí)會(huì)執(zhí)行。
SSR原理:① 服務(wù)器端將編譯好的內(nèi)容(模板)下發(fā)(包括樣式、內(nèi)容、數(shù)據(jù)) ② 把異步獲取的數(shù)據(jù)響應(yīng)給瀏覽器端(把交互交給瀏覽器來(lái)完成)
SSR原理深入
我們打開(kāi)頁(yè)面源代碼,查看一下:
服務(wù)器端渲染完頁(yè)面后給瀏覽器端的html分了幾個(gè)部分,第一個(gè)是樣式 style ,第二個(gè)是模板內(nèi)容,例如上圖中圈中的藍(lán)色部分,第三個(gè)是服務(wù)端拿到的數(shù)據(jù)結(jié)果,例如上圖中圈中的紅色部分,為什么服務(wù)端拿到的數(shù)據(jù)給到瀏覽器端呢?
這里我們得思考一個(gè)SSR的工作原理 了
如果不給數(shù)據(jù)的話,就是一個(gè)靜態(tài)html模板,一個(gè)靜態(tài)的內(nèi)容,沒(méi)有任何交互,那交互是在哪完成的呢?
交互是在瀏覽器端完成的,也就是說(shuō)瀏覽器端會(huì)有一個(gè)入口,進(jìn)行預(yù)編譯,但不會(huì)再渲染頁(yè)面了,因?yàn)榉?wù)器端已經(jīng)在頁(yè)面渲染過(guò)一次了。它要做的是創(chuàng)建一個(gè)虛擬的編譯結(jié)果(可以理解為虛擬dom), 和服務(wù)器端傳過(guò)來(lái)的結(jié)果進(jìn)行對(duì)比,如果有區(qū)別,它會(huì)重新請(qǐng)求數(shù)據(jù)。在nuxt項(xiàng)目中都是一套文件,沒(méi)有特別指定是在瀏覽器端運(yùn)行還是服務(wù)端運(yùn)行,也就是SSR常說(shuō)的同構(gòu),瀏覽器端編譯虛擬dom,也依賴于 vue 文件,因此模板是有的,而編譯這個(gè)dom,需要的是額外的數(shù)據(jù),此數(shù)據(jù)是服務(wù)器端渲染之前請(qǐng)求而來(lái)的數(shù)據(jù),如果數(shù)據(jù)不同步在瀏覽器端,編譯出來(lái)的結(jié)果必然和服務(wù)器端編譯結(jié)果不一致。
綜上,服務(wù)器端異步獲取的數(shù)據(jù)會(huì)同步在瀏覽器端,作對(duì)比,如果對(duì)比一致的話,瀏覽器端就會(huì)對(duì)對(duì)應(yīng)的dom結(jié)點(diǎn)注冊(cè)事件,達(dá)到交互作用。
更多內(nèi)容,請(qǐng)參考:nuxtjs 英文官方文檔
參考:nuxtjs 中文文檔
以上,就是我們項(xiàng)目搭建所需的基礎(chǔ)知識(shí)啦,已經(jīng)整理完結(jié)啦,當(dāng)然還有許多地方?jīng)]有提及,讀者可以去查閱一些官方文檔來(lái)補(bǔ)充更多知識(shí),下文我們將逐步深入全棧開(kāi)發(fā)過(guò)程記錄,制作不易,點(diǎn)贊收藏評(píng)論,一鍵三連一波~
項(xiàng)目搭建開(kāi)始
配置環(huán)境
- node
- vue
- npm
- webpack
- nuxt
創(chuàng)建項(xiàng)目
npm install -g npx npx create-nuxt-app loveVueApp使用 npx 可以幫助我們更快速的搭建項(xiàng)目環(huán)境,在你需要?jiǎng)?chuàng)建項(xiàng)目文件夾的地方 shift+鼠標(biāo)右鍵 打開(kāi) powershell 窗口,執(zhí)行上述命令,然后按照下列圖片選擇對(duì)應(yīng)的模塊:
安裝完成后,會(huì)提示你執(zhí)行如下命令:
這里我們就執(zhí)行開(kāi)發(fā)環(huán)境的指令就好了
cd loveVueApp npm run dev另附上,生產(chǎn)環(huán)境的指令:
cd loveVueApp npm run build npm run start訪問(wèn):http://localhost:3000/ 會(huì)有一個(gè)nuxtjs的圖標(biāo)出現(xiàn),代表環(huán)境搭配成功 ??ヽ(°▽°)ノ?
此時(shí),再 ctrl+c 退出當(dāng)前服務(wù)
執(zhí)行如下命令,重新安裝一下,防止版本問(wèn)題等
npm install --update-binary重裝之后,再跑一次服務(wù),看能不能打開(kāi)界面
npm run dev增加 babel-node 處理(使用ES6的import指令問(wèn)題)
創(chuàng)建好我們的基本項(xiàng)目后,我們還需要對(duì)我們所需要的模塊進(jìn)行更改。
例如,對(duì)應(yīng)目標(biāo)文件的第一行代碼:
改為如下代碼
于是,就會(huì)出現(xiàn)報(bào)錯(cuò),無(wú)法識(shí)別
在上文講解 nuxtjs基礎(chǔ) 我們是可以直接用 import 操作的,但當(dāng)我們使用官方腳手架時(shí),是沒(méi)有處理這個(gè) bug 的。下面來(lái)解釋一下為什么會(huì)出現(xiàn)這個(gè)問(wèn)題:
當(dāng)我們使用命令 npm run dev 的時(shí)候,其實(shí)是用了 npm 的 script 的方法,如下圖所示:
可見(jiàn)使用了 node,并且不支持 import 操作,上文是因?yàn)槭褂昧?babel 進(jìn)行了處理,在這里官方腳手架時(shí)使用 node 來(lái)啟動(dòng)服務(wù),沒(méi)有經(jīng)過(guò) babel 處理,因此就不會(huì)識(shí)別 import 指令了。
解決上述問(wèn)題
上文提到了是因?yàn)闆](méi)有 babel處理,執(zhí)行服務(wù)程序,那就自己加上去咯,具體如下:
在 dev 和 start 后面添加 --exec babel-node
或者直接使用下面代碼覆蓋你的 scripts
"scripts": {"dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server --exec babel-node","build": "nuxt build","start": "cross-env NODE_ENV=production node server/index.js --exec babel-node","generate": "nuxt generate"},然后,在根目錄下創(chuàng)建一個(gè) .babelrc 的配置文件,然后給它指定一個(gè)指令集
{"presets":["es2015"] }有了代碼還不行,還要再安裝插件,執(zhí)行如下命令
npm install babel-preset-es2015 npm install babel-cli -S好了,有了 babel-node 后我們就能啟動(dòng)服務(wù)程序了,改成了我們常用的 ES6語(yǔ)法了
npm run dev訪問(wèn):http://localhost:3000/ 會(huì)有一個(gè)nuxtjs的圖標(biāo)出現(xiàn),代表環(huán)境搭配成功 ??ヽ(°▽°)ノ?
解決 sass 導(dǎo)致編譯出錯(cuò)問(wèn)題
這個(gè)問(wèn)題也依舊是腳手架帶來(lái)的問(wèn)題,解決方式就是安裝幾個(gè)插件,執(zhí)行命令如下:
npm install sass-loader node-sassPS:可能會(huì)有下述 warn
npm install eslint@^(版本號(hào)) //(注意,這里是當(dāng)你出現(xiàn)對(duì)應(yīng)warn才執(zhí)行)我的是這種警告,因此依葫蘆畫瓢,執(zhí)行下述命令
輔助工具安裝
- MongoDB
- redis
- Robo 3T
(上文基礎(chǔ)部分有介紹安裝教程,可以往回看一看對(duì)應(yīng)模塊 ↑)
遠(yuǎn)程倉(cāng)庫(kù)常用指令(整理)
-
查看分支:git branch
-
查看項(xiàng)目的分支們(包括本地和遠(yuǎn)程) git branch -a
-
創(chuàng)建分支:git branch <name>
-
切換分支:git checkout <name>
-
創(chuàng)建+切換分支:git checkout -b <name>
-
合并某分支到當(dāng)前分支:git merge <name>
-
提交 git commit -m ‘簡(jiǎn)介’
-
git push -u origin master
首頁(yè)開(kāi)發(fā)
需求分析
- 模板設(shè)計(jì)(解決復(fù)用問(wèn)題)
- 組件設(shè)計(jì)(如何拆分組件)
如何節(jié)省網(wǎng)絡(luò)請(qǐng)求?
上述流程圖共進(jìn)行了兩次請(qǐng)求,另外,異步獲取數(shù)據(jù)時(shí),還會(huì)有閃一下,影響用戶體驗(yàn),同時(shí)浪費(fèi)一次網(wǎng)絡(luò)請(qǐng)求。解決辦法就是當(dāng)瀏覽器去請(qǐng)求服務(wù)器的時(shí)候,當(dāng)瀏覽器去請(qǐng)求文檔的時(shí)候,服務(wù)端 ip已經(jīng)知道了,那個(gè)時(shí)候就可以拿到對(duì)應(yīng)的城市,立即返回?cái)?shù)據(jù)給瀏覽器。做法就是通過(guò) vuex 來(lái)同步狀態(tài),然后通過(guò) SSR 異步請(qǐng)求就能得到數(shù)據(jù)。
類似于上文流程圖,也是可以通過(guò) vuex 來(lái)做
屬于靜態(tài)模塊,不需要我們異步獲取數(shù)據(jù),直接用 SSR 下發(fā)一下就ok。
- 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)(依賴于數(shù)據(jù)和組件)
pois表 城市推薦數(shù)據(jù)庫(kù)
- 接口設(shè)計(jì)(與數(shù)據(jù)結(jié)構(gòu)相對(duì)應(yīng))
首頁(yè)開(kāi)發(fā)Header-城市定位服務(wù)設(shè)計(jì)
根據(jù) Header 布局情況來(lái)看的話,我們?cè)赾omponents文件夾下創(chuàng)建如下幾個(gè)文件夾和文件(水印遮住的是 README)
導(dǎo)入css,然后打開(kāi) nuxt.config.js 進(jìn)行配置
項(xiàng)目開(kāi)發(fā)可能遇到問(wèn)題
重要提醒!
本篇文章能詳細(xì)概述的地方有限,所以下文將會(huì)以講重點(diǎn)部分為主,其余部分請(qǐng)參考本參考源碼,已經(jīng)開(kāi)放,開(kāi)發(fā)過(guò)程中按照分支進(jìn)行開(kāi)發(fā),然后合并到 master 分支
倉(cāng)庫(kù)の傳送門(戳一戳)
收藏 star 一波,??ヽ(°▽°)ノ?
關(guān)于項(xiàng)目中SMTP服務(wù)功能配置問(wèn)題
如下圖,所示,我們首先登陸自己的QQ,打開(kāi)郵箱,然后去打開(kāi)下面兩項(xiàng)服務(wù)
在 server/dbs/config.js 文件內(nèi)更改為你自己的授權(quán)密鑰和郵箱即可
引入 mongoose
npm install mongoose配置 users 接口
npm install koa-router koa-redis nodemailer配置 axios
npm install axios配置 passport
npm install koa-passport passport-local關(guān)于項(xiàng)目中登錄注冊(cè)接口問(wèn)題
passport 包內(nèi)有 isAuthenticated()方法,因此在文檔源碼中沒(méi)有定義過(guò)。
passport會(huì)把用戶的信息對(duì)象放到 session 對(duì)象里面去,也就是 passport 會(huì)存儲(chǔ)在 session 中,例如如下,如果是登錄狀態(tài)的話,那么session會(huì)有 passport,而 passport 會(huì)有 user
//判斷是否是登錄狀態(tài)if (ctx.isAuthenticated()) {const {username, email} = ctx.session.passport.userctx.body={user:username,email}}- users.js
server文件夾 index.js文件中 加載相關(guān)包
import mongoose from 'mongoose' import bodyParser from 'koa-bodyparser' import session from 'koa-generic-session' import Redis from 'koa-redis' import json from 'koa-json' import dbConfig from './dbs/config' import passport from './interface/utils/passport' import users from './interface/users' import geo from './interface/geo' import search from './interface/search' import categroy from './interface/categroy' import cart from './interface/cart'加載指令:
npm install mongoose koa-bodyparser koa-generic-session koa-redis koa-jsonCannot read property ‘post’ of undefined 問(wèn)題解決
注冊(cè)那一個(gè)模塊那里,我一點(diǎn)發(fā)送驗(yàn)證碼,然后就報(bào)了這個(gè)錯(cuò)誤,一開(kāi)始以為接口寫錯(cuò)了,還測(cè)試了一下,原來(lái)是因?yàn)?axios 沒(méi)有在 nuxt 腳手架中配置
Cannot read property 'name' of undefinednuxt.config.js 文件中配置如下:
module.exports = {modules: ['@nuxtjs/axios',],axios: {// proxyHeaders: false} }解決之后,發(fā)送驗(yàn)證碼,如下結(jié)果,有郵件發(fā)送過(guò)來(lái) ??ヽ(°▽°)ノ?
關(guān)于 win10 redis 報(bào)錯(cuò) Permission denied
同樣也是注冊(cè)頁(yè)面,當(dāng)我們注冊(cè)后,點(diǎn)擊同意協(xié)議并注冊(cè),發(fā)現(xiàn)無(wú)法跳轉(zhuǎn)到登錄界面,而提示 已注冊(cè) ,看了半天,提示服務(wù)錯(cuò)誤,于是一開(kāi)打redis命令窗,報(bào)了這個(gè)錯(cuò)誤,原來(lái)是默認(rèn)設(shè)置了只可讀不可寫,按如下方式增加權(quán)限,解決了問(wèn)題 ??ヽ(°▽°)ノ?
解決:去Redis的rdb文件查看文件屬性,增加寫入權(quán)限
參考:Redis在Windows下的坑
關(guān)于win10 mongodb 導(dǎo)入.bat文件問(wèn)題
網(wǎng)上說(shuō)用如下指令,但是我還是沒(méi)有導(dǎo)入成功,說(shuō)是不存在這指令,但之前數(shù)據(jù)庫(kù)都導(dǎo)入了 users
mongoimport -d student -c areas areas.dat于是,我就打開(kāi)了 Studio 3T 來(lái)試著導(dǎo)入,還算是成功了吧
我的做法是直接在左邊那個(gè) Collections 文件夾直接右鍵 選擇 Add 操作,然后導(dǎo)入 .dat文件,不過(guò)導(dǎo)完之后會(huì)有后綴出現(xiàn),于是我就重命名了一下。雖然看起來(lái)有點(diǎn)傻傻的做法,但也算是解決了吧 ??ヽ(°▽°)ノ?
關(guān)于 geo服務(wù) 接口簽名
http://cp-tools.cn/sign
關(guān)于定位服務(wù)及切換城市 geo bug解決
在城市定位 geo 分支那里,報(bào)了如下兩個(gè)錯(cuò)誤,可讓我焦頭爛額啊,終于,找到了解決辦法,特來(lái)寫此模塊,記錄一下 ??ヽ(°▽°)ノ?
① 報(bào)錯(cuò)Classic mode for store/ is deprecated and will be removed in Nuxt 3.
② Error occurred when calling nuxtServerInit: socket hang up
對(duì)第一個(gè)問(wèn)題出現(xiàn)這種錯(cuò)誤的原因是:nuxt3版本中已經(jīng)移除了對(duì)原始vuex這種編程,出現(xiàn)這種錯(cuò)誤的代碼如下:
解決的辦法如下: store 目錄下的每個(gè) .js 文件會(huì)被轉(zhuǎn)換成為狀態(tài)樹(shù)指定命名的子模塊 (當(dāng)然,index 是根模塊)。
(以下代碼親測(cè)有效,直接覆蓋你的代碼即可)
首先是目錄結(jié)構(gòu)修改,最新版的已經(jīng)不需要另外加一個(gè) models 文件夾了。
index.js 文件:
export const actions = {async nuxtServerInit({commit},{req,app}) {const {status,data: {province,city}} = await app.$axios.get('/geo/getPosition')commit('geo/setPosition',status===200?{city,province}:{city:'',province:''}) const {status:status2, data: {menu}} = await app.$axios.get('/geo/menu')commit('home/setMenu',status2===200?menu:[])} }geo.js 文件無(wú)需修改
對(duì)第二個(gè)問(wèn)題,多半是因?yàn)槟銓懞玫?geo 相關(guān)接口沒(méi)有在serve/index.js中進(jìn)行導(dǎo)入和配置使用(我就是因?yàn)檫@個(gè))
import geo from './utils/geo' app.use(geo.routes()).use(geo.allowedMethods())具體導(dǎo)入位置如下:
參考:美團(tuán)項(xiàng)目 — 定位服務(wù)及切換城市5
參考:nuxtjs中使用SSR開(kāi)發(fā)關(guān)于前端vuex請(qǐng)求后臺(tái)的問(wèn)題
關(guān)于搜索框請(qǐng)求次數(shù)問(wèn)題
我們當(dāng)然不能沒(méi)輸入一個(gè)字符,就請(qǐng)求一次接口,因此我們需要弄一個(gè)延時(shí)函數(shù)
npm install lodash引入庫(kù)
import _ from 'lodash'關(guān)于切換城市模塊優(yōu)化與實(shí)現(xiàn)
首先明確一載入切換城市界面,哪個(gè)是要進(jìn)行請(qǐng)求的,那就是省份(第一級(jí)),此時(shí)可以使用ssr進(jìn)行服務(wù)端渲染,頁(yè)面載入后也跟著進(jìn)行載入,但這里就直接使用vue中的Mouted生命函數(shù)完成這里的axios請(qǐng)求 ↓
mounted: async function() {let self = this;let {status,data: { province }} = await self.$axios.get("/geo/province");if (status === 200) {self.province = province.map(item => {return {value: item.id,label: item.name};});}}聯(lián)動(dòng)邏輯,使用watch監(jiān)聽(tīng)用戶選擇了什么省份
//監(jiān)聽(tīng)pvalue值,當(dāng)省份發(fā)生改變的時(shí)候,可選城市也要跟著改變(聯(lián)動(dòng))watch:{pvalue:async function(newPvalue){let self=this;let {status,data:{city}}=await self.$axios.get(`/geo/province/${newPvalue}`)if(status===200){self.city=city.map(item=>{return {value:item.id,label:item.name}})self.cvalue=''}}},注意select下拉框的設(shè)計(jì),如果你不設(shè)置 label 顯示的還是 value 的值,但是如果你設(shè)置了 label 那輸入框顯示的就是 label 的值,但是你 v-model 拿的還是你 value 的值,所以在省份中復(fù)制也是將 id 賦值給 value 的,所以參數(shù)正確!
會(huì)二級(jí)聯(lián)動(dòng),三級(jí)聯(lián)動(dòng)還會(huì)難嗎?畢竟現(xiàn)在你只需要重點(diǎn)思考邏輯上的代碼,而 DOM 結(jié)構(gòu)E-UI已經(jīng)隨隨便便完成了,只剩下你關(guān)聯(lián)他們的代碼而已
參考:美團(tuán)項(xiàng)目 — 切換城市 7
通過(guò)項(xiàng)目挖掘知識(shí)點(diǎn)(整理)
koa2中的ctx是什么?
為了試圖搞明白,用console.log將它輸出
{ request:{ method: 'GET',url: '/',header:{ host: 'localhost:3000',connection: 'keep-alive','cache-control': 'max-age=0','upgrade-insecure-requests': '1','user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','accept-encoding': 'gzip, deflate, sdch, br','accept-language': 'zh-CN,zh;q=0.8' } },response:{ status: 200,message: 'OK',header:{ 'content-type': 'text/plain; charset=utf-8','content-length': '25' } },app: { subdomainOffset: 2, proxy: false, env: 'development' },originalUrl: '/',req: '<original node req>',res: '<original node res>',socket: '<original node socket>' }可見(jiàn)它主要包括 request 和 response 兩部分。
ctx是context的縮寫中文一般叫成上下文,這個(gè)在所有語(yǔ)言里都有的名詞,可以理解為上(request)下(response)溝通的環(huán)境,所以koa中把他們兩都封裝進(jìn)了ctx對(duì)象,koa官方文檔里的解釋是為了調(diào)用方便,ctx.req=ctx.request,ctx.res=ctx.response,類似linux系統(tǒng)中的軟連接?最終執(zhí)行還是request和response對(duì)象 body是http協(xié)議中的響應(yīng)體,header是指響應(yīng)頭ctx.body = ctx.res.body = ctx.response.bodyKoa 提供一個(gè) Context 對(duì)象,表示一次對(duì)話的上下文(包括 HTTP 請(qǐng)求和 HTTP 回復(fù))。通過(guò)加工這個(gè)對(duì)象,就可以控制返回給用戶的內(nèi)容。
Context.response.body 屬性就是發(fā)送給用戶的內(nèi)容。
const Koa = require('koa'); const app = new Koa();const main = ctx => {ctx.response.body = 'Hello World'; };app.use(main); app.listen(3000);上面代碼中,main函數(shù)用來(lái)設(shè)置 ctx.response.body 。然后,使用 app.use 方法加載main函數(shù)。
你可能已經(jīng)猜到了,ctx.response代表 HTTP Response。同樣地,ctx.request代表 HTTP Request。
運(yùn)行這個(gè) demo,訪問(wèn) http://127.0.0.1:3000 ,現(xiàn)在就可以看到"Hello World"了。
參考:koa2中的ctx是什么?
koa2中 query 和 querystring
在 koa 中,GET請(qǐng)求獲取請(qǐng)求數(shù)據(jù)request對(duì)象中的 query 方法和 querystring 方法, query方法返回的是格式化好的參數(shù),querystring 方法返回的是請(qǐng)求字符串。
koa2如何獲取get方式的路由參數(shù),比如xxx?name=123 獲取name值
ctx.request.query // => {name: '123'} ctx.query // => {name: '123'}ctx.request 是 Koa 請(qǐng)求對(duì)象。可以通過(guò) querystring 獲取請(qǐng)求路徑中的 query 字符串,通過(guò) query 獲取格式化好的參數(shù)。
ctx.query 是 route.query 的別名。
閱讀過(guò):nuxt - nuxtServerInit & 頁(yè)面渲染前的store處理 & context
koa2 使用passport權(quán)限認(rèn)證中間件
參考:koa2 使用passport權(quán)限認(rèn)證中間件
序列化和反序列化
更多內(nèi)容參考:了解護(hù)照序列化反序列化
故名思議就是將結(jié)構(gòu)化的對(duì)象轉(zhuǎn)換為字節(jié)序列,反之就叫做反序列化。
為什么要序列化和反序列化?
內(nèi)存當(dāng)中的對(duì)象是結(jié)構(gòu)化的,當(dāng)你需要將這個(gè)對(duì)象在網(wǎng)絡(luò)當(dāng)中傳輸?shù)臅r(shí)候,或者要保存到文件或者數(shù)據(jù)庫(kù)當(dāng)中的時(shí)候,你就需要將它序列化成字節(jié)流,便于處理。
而反過(guò)來(lái),傳輸過(guò)去之后,或者從文件和數(shù)據(jù)庫(kù)里讀取出來(lái)的時(shí)候,又要重新構(gòu)建恢復(fù)出原來(lái)的對(duì)象。
類比就是,比如我們打電話,電線不能直接傳聲音,需要把聲音轉(zhuǎn)換為電流,過(guò)去再把電流轉(zhuǎn)換為聲音。
為什么utils/axios.js 要?jiǎng)?chuàng)建一個(gè)實(shí)例
以下內(nèi)容參考: nuxt.js 官方中文文檔
如果您的項(xiàng)目中直接使用了 node_modules 中的 axios ,并且使用 axios.interceptors 添加攔截器對(duì)請(qǐng)求或響應(yīng)數(shù)據(jù)進(jìn)行了處理,確保使用 axios.create 創(chuàng)建實(shí)例后再使用。否則多次刷新頁(yè)面請(qǐng)求服務(wù)器,服務(wù)端渲染會(huì)重復(fù)添加攔截器,導(dǎo)致數(shù)據(jù)處理錯(cuò)誤。
import axios from 'axios' const myaxios = axios.create({// ... }) myaxios.interceptors.response.use(function (response) {return response.data }, function (error) {// ... })結(jié)尾
如若本文有瑕疵需修改的地方,請(qǐng)?zhí)岢鰜?lái),謝謝您的貢獻(xiàn)!
歡迎關(guān)注微信公眾號(hào):小獅子前端Vue
謝謝您的支持!??ヽ(°▽°)ノ?
學(xué)如逆水行舟,不進(jìn)則退總結(jié)
以上是生活随笔為你收集整理的【Vue全家桶+SSR+Koa2全栈开发】项目搭建过程 整合 学习目录(持续更新中)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 电脑死机故障解决方法全面汇总
- 下一篇: 什么是蛮力攻击,您能做什么来防止这些攻击