好久以前就想寫一個博客應用.
在一開始想要直接用express
和ejs
模板直接寫, 可是暑假一開始的時候不當心入了react的坑, 因此就一不作二不休直接用react
寫. 那既然用了react
, 不寫個單頁應用也過意不去了...(不
前先後後寫了將近兩個星期, 如今看來這實際上是一個很容易的應用. 可是鑑因而第一次用react
, 對於nodejs
也不是特別熟悉, 因此走了很多冤枉路. 其中也有不少次以爲想放棄, 不過最終仍是寫下來了. 雖然仍是有很多瑕疵, 不過也算給本身一個交代吧.
我的博客網站的地址爲: harryfyodor.tk
下面會從幾個方面把我整個編寫的過程的一些經驗記錄下來, 主要是記錄本身的學習過程, 編寫中遇到的困難以及解決, 以及一些學習/複習資料的整理分享. 也但願給各位和我同樣的初學者一點點借鑑的經驗.
(我的感受比較好的學習方式就是本身看資料(文檔和博客)而後寫本身的一個小應用, 而不是跟着某一個教程從頭至尾過一遍, 雖然不得不認可, 後者學起來的感受很爽, 並且也更清晰. 可是很差的地方就是技術棧不徹底匹配的時候就會很頭疼...)
下面是目錄:css
前端 (包括react
, redux
, css-module
等)html
後端 (包括express
, mongodb
)前端
先後端 (包括fetch
, jwt
)node
其餘 (包括webpack
, 優化等)react
下面直接進入正題.webpack
react
的學習根據官方文檔, 主要是理解一下幾個方面的內容:git
構建模塊的方法. 用了推薦es6
的class
的方法而非createClass
.es6
如何導入導出模塊. es6
的import
和export
.github
jsx
的編寫. 不是必要的, 官網推薦. 感受其中用上es6
模板字符串, map
方法會很方便不少~web
props
, state
, refs
的相關概念以及使用. 單向數據流中, 父組件給子組件傳遞數據經過props
, 子組件給父組件傳遞數據用回調函數. 後者的實現是經過父組件把一個函數傳到子組件中, 這個函數裏面有能夠有this.setState
(收到子組件的數據後馬上渲染), 而後子組件調用傳進來的函數, 經過這個函數把參數傳給父組件.
掌握connect
組件的使用方法. 把state
數據和dispatch
傳進組件中.
生命週期. 在這個博客SPA應用裏用了componentDidMount
和componentWillReceiveProps
, 前者可用於初始化的渲染以及異步請求的發起, 後者用於接收到新的數據時的再次渲染, 把異步的結果渲染出來. 所以一個組件(涉及到異步)是這樣工做的: 調用組件
-> 組件渲染以前發起異步請求
-> 第一次渲染,沒有數據的頁面
-> 接收到異步發回來的數據
-> 從新渲染, 有數據的頁面
. 代碼以下, 基本也是按照這種數據流向的方法來的(不知道是否是最好的方案, 可是感受很方便).
class Archive extends React.Component { constructor(props) { super(props); this.displayName = 'Archive'; this.state = { articles: [] } } componentDidMount() { this.props.actions.getTitles({ type: "ARCHIVE" }) } componentWillReceiveProps(nextProps) { this.setState({ articles: nextProps.getTitles.articles }) } render() { return ( <div className={style.archive}> {/*這裏是相關的渲染articles的操做, 注意要把[]的狀況也考慮到*/} </div> ) }
}
export default Archive
配合好setState
和生命週期, 運用好父子組件之間的數據傳遞可以很好地完成各類異步渲染.
理解redux
主要是要理解action
, reducer
, middleware
等概念. 我的感受redux
的官方文檔簡直精彩, 例子也很豐富, 很是值得學習. 這個SPA
博客裏的action
大部分是爲了ajax獲取後端數據服務的. 下面選取了其中的一組, 功能是獲取單獨的文章. 對應不一樣相應狀態有不一樣的action
. 這樣就能夠把異步的每個狀態記錄下來, 使得數據的流向更加清晰. 具體有關異步請求的相關的內容能夠看個人上一篇文章.
export const singleRequest = () => { return { type: SINGLE_REQUEST } } export const singleSuccess = (article) => { return { type: SINGLE_SUCCESS, article: article } } export const singleFailure = () => { return { type: SINGLE_FAILURE } } export const getSingle = (day, title) => { return dispatch => { dispatch(singleRequest()) return fetch('/api/single', { method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json" }, body: JSON.stringify({ day: day, title: title }) }) .then(checkHttpStatus) .then(res => res.json()) .then(res => { if(res.ok) { dispatch(singleSuccess(res.article)) } else { dispatch(singleFailure()) } }) } }
至於在reducer
中, 初始化state
用了幾個標識. 好比下面的例子中, 初始化的reducer state
包含了isFetching
, isFetched
, fetchFailure
這些標誌異步進行的當前狀態的信息. 傳入props
以後能夠很方便地進行異步請求前後的設置. 比方說一個異步要在另外一個異步以後, 就能夠經過讀取這幾個數值完成. (第二個異步必定要在第一個的isFetched
爲true
的時候才能發起)
const initialState = { isFetching: false, isFetched: false, fetchFailure: false, articles: [], count: 1 }
在整個應用中須要用到中間件, 在應用中用了thunk
還有logger
.
在博客應用中css
的引入用的是css-modules
, 阮一峯大神的這篇文章講得算是完整了, 感興趣能夠看一下~ 固然有些部分也仍是用了css in js
的方法, 直接把css
寫到js
裏面, 主要是考慮到一些操做的方便, 好比點擊以後某一個標籤display
改變之類的.
說來慚愧, 後端大部分都是"抄"的, 以前看的一個教程是用express
和ejs
寫的博客應用, 然後端的操做大部分都比較接近. 主要就是根據接口路由處理數據, 發送數據, 經過數據庫api
(這裏是mongodb
)讀取數據庫數據. 因此最後寫出來的和我本來看的那個教程有很大的類似之處. 我看的教程是這一個, 很是棒, 感謝做者!!>o<!!
關於express
我的感受比較重要的是處理配置以及路由兩個方面的問題.
前者須要靠本身慢慢摸索, 好比要處理json
須要用到bodyParser
模塊, webpack
一些中間件的配置等等, 能夠拿redux
官網還有上面提到的教程來參考一下.
後者主要是要了解express
提供的各類方法, 以及一些有關res和req的相關操做等等.
有關數據庫的操做我也是參考上面的教程的...(oh..)基本對數據庫的增刪查改要掌握. 更多有關mongo
的api
原理等能夠去看官網介紹.
因爲目標不是專業後端, 因此後端作得比較粗糙. 不足之處有不少, 好比沒有擁抱es6
(明明前端已經擁抱), 好比還在若無其事地寫着臭名昭著的回調金字塔等等等. nodejs
須要增強.
有關登陸和登出開始找了不少相關的實現方法, 在這篇文章的推薦下看了JWT實現方式. 簡單來講就是前端把密碼post
到後端, 後端生成一個token
而後發送到前端去. 前端把收到的token
保存在localStorage
中. 每次須要獲取一些保密的信息或者須要作一些修改的時候, 把這個token
寫在請求的headers
裏. 後端收到數據以後就會先驗證一下token
是否正確, 正確才容許操做.
headers: { "Content-Type": "application/json", "Accept": "application/json", "Authorization": `Bearer ${token}` }
關於先後端交互這一點能夠參考我寫的上一篇文章. 後端把api
暴露出來給前端, 前端經過ajax
進行數據的交互, 並把獲取到的數據渲染出來. 操做上沒有難度, 只是要注意異步操做中redux
要用中間件.
有關中間件還要去看點高階函數的基礎知識, 否則沒法正確理解.
沒有怎麼認真地看webpack
的東西, 都是順手操起來直接用的...
說實話, 第一次開始看chrome
的devtool
的network
的時候, 我被嚇得不輕...一個bundle
文件5m
大, PC
端打開以後真的是不忍直視. 後來上網找了一些webpack
打包優化的方向, 在這裏記錄一下:
webpack
的config文件裏面不能有cheap-module-eval-source-map
之類的devtool
, 真的很大很大...
plugin
若是不是必要的話也請刪去吧. 不過有兩個plugin
能夠在生產環境中用一下, 第一個是UglifyJsPlugin
, 用於壓縮文件. 第二個是CommonsChunkPlugin
, 這個具體下一點解釋.
適當分塊. CommonsChunkPlugin
用於把bundle分塊, 把能夠放在緩存的, 經常使用的, 體積比較大的壓縮到vendor
裏面(好比react
等). 後來又把babel-polyfill
分開另外加載了. 以前有看到code split
, 就是直到須要用到該ui
組件的時候纔去加載, 想法好像不錯, 不過感受改動會比較大因此最後沒有作.
最後的文件大小其實也仍是不小, 可是有了很好的改善. 關於前端優化也是一個重要的話題.
博客應用寫做用的是markdown
. 本來想找一個現成的, 可是死活找不到合適的...最後直接用marked
強行假裝markdown
編輯器...其實這很不安全, 但目前也沒有什麼辦法...(draft.js
貌似能夠?)
整體來講, 這個博客其實實現起來沒有特別高的難度, 可是對於初學者來講感受真的挺不容易的. 以前聽過這樣一句話--不要同時學幾樣東西, 其實還真的有點道理...可是對於一些最佳實踐, 自己就要結合在一塊兒才能發揮其最大的做用, 不一塊兒學又怎麼能行呢?(所以就陷入了大坑).
這個博客不完善的地方太多了, 特別是有關安全方面的問題.不過如今仍是先關注着前端吧.
但願這篇文章可以給你一點點幫助.
最後上代碼博客代碼
(本人是初學者, 若是有什麼說得不對, 很差的地方歡迎指出來, 感激涕零!~互相學習!~)