最近剛好在研究i18Next, 遇到了一個小坑, 特此紀錄。
首先新增了一隻 i18n.js 提供給Component使用:
import i18next from 'i18next'; import XHR from 'i18next-xhr-backend'; import LanguageDetector from 'i18next-browser-languagedetector'; i18next .use(XHR) .use(LanguageDetector) .init({ fallbackLng: ["en","zh-TW"], ns: ['common'], defaultNS: 'common', debug: false, interpolation: { escapeValue: false }, load: 'currentOnly', backend: { "loadPath": "/public/locales/{{lng}}/{{ns}}.json" } }); export default i18next
利用Provider 注入i18n
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { I18nextProvider } from 'react-i18next'; import i18n from 'i18n/i18n.js'; import { createStore, applyMiddleware } from 'redux'; import reducers from './reducers'; import RouterMain from './router/RouterMain'; import App from './App' import 'css/main.sass'; const createStoreWithMiddleware = applyMiddleware()(createStore); ReactDOM.render( <I18nextProvider i18n={ i18n }> <App/> </I18nextProvider> , document.getElementById('root') );
在我的元件中使用 translate 函數, 會發現render函數持續被呼叫。因此我在componentWillReceiveProps中觀察props的變化,發現props中的i18n有個tReady屬性會一直被watch , 因此我自己新增一個stopDetectTranslateReady函式來處理,或許有更好的處理方法,不過我只是為了測試i18n,因此沒有特別修改,如果有更好的解法也請大家不吝指教。
import React, { Component } from 'react'; import { Link } from 'react-router-dom' import { translate } from 'react-i18next'; import {stopDetectTranslateReady} from 'helpers' class NavBar extends Component { constructor(props){ super(props); } shouldComponentUpdate(nextProps){ return stopDetectTranslateReady(nextProps); } componentWillReceiveProps(props){ console.log('componentWillReceiveProps', props) } render() { const { t } = this.props; return ( <nav> <div className="flex flex1 flex-center"> <div className="flex flex1"></div> <div className="flex flex5 flex-center"> <h2 style={{fontWeighjt: '300'}}>{t('appName')}</h2> </div> <div className="flex flex1 flex-right"> <i className="fas fa-bars font-lg" style={{marginRight: '10px'}}></i> </div> </div> <div className="flex flex4 menu-list"> <Link to="/reservation" className="menu-link"> <i className="far fa-check-circle"></i> <span>{t('navbar.reservation')}</span> </Link> <Link to="/favorite" className="menu-link"> <i className="far fa-heart"></i> <span>{t('navbar.mycamp')}</span> </Link> <Link to="/" className="menu-link"> <i className="far fa-thumbs-up"></i> <span>{t('navbar.famous')}</span> </Link> <Link to="/" className="menu-link"> <i className="far fa-snowflake"></i> <span>{t('navbar.equipment')}</span> </Link> <Link to="/" className="menu-link"> <i className="fas fa-user-astronaut"></i> <span>{t('navbar.about')}</span> </Link> </div> </nav> ) } } const mobileMenuContentStyle = { justifyContent: 'flex-end' } export default translate()(NavBar);
stopDetectTranslateReady函式:
export const stopDetectTranslateReady = nextProps => { if(nextProps.tReady){ return true; } return false; }
JWT JSON Web Token 驗證簡述
JSON Web Token, 或JWT (jot) 簡單來說, 是一種傳送驗證授權的標準。 為什麼我們需要JWT? 從前我們在驗證使用者身份後,可能會將使用者資訊存在於session當中,這種做法有幾種缺點:
- 造成Server記憶體負擔
- 如果需將Application掛載於多台Server上,必須要同步所有的Server的session
- 無法處跨平台授權問題
於是,JWT誕生了。因為Http 的請求是無狀態的(stateless)因此我們必須將sessiont儲存在Server Side. 我們利用JWT的技術標準,由Server Side產生一組token給予Client Side, 再由Client Side保存好,並在每一次Request將token帶入。 這間接解決了cookie的缺點,不易被竄改。
談到這我們就可以來說明token的優點:
安全性
如上述所說明, 除了token沒機會被修改以外, 我們也可以要求token的時效性來提高安全性。
擴充性
我們可以利用token來分享權限給予第三方使用者
解決跨域問題
傳統cookie在登入的時候必須在所有domain寫入,但token只要實作相同的驗證機制即可
說了這麼多,我們還是要來談論,如何實作JWT機制
JWT主要分為三個部分:
- header (標頭)
- payload(訊息內容)
- signature(簽章)
最終組成一個Token:
{header}.{payload}.{signature}
Header的內容為 JSON物件:
{ "alg": "HS256", "typ": "JWT" }
Header 主要是說明JWT將用何種演算法建立。
Payload的內容也是JSON物件:
{ "sub": "1234567890", "name": "John Doe", "admin": true }
Payload主要承載使用者需要的資訊,官方的HandBook有說明沒有硬性規定哪些必要屬性。
但仍舊可以歸納出幾個特別的要求:
- iss: issuer,帶有機密資料的字串或是URI,可明確指出是由誰發行此JWT
- sub: subject,帶有機密資料的字串或是URI,說明JWT的內容
- aud: audience,帶有機密資料的字串或是URI或是陣列,指出JWT的受眾
- exp: expiration,代表特定時間的數字,依循POSIX定義的"seconds since epoch"格式,明確指出JWT的過期時間
- nbf: not before,與exp相反,依循POSIX定義的"seconds since epoch"格式,明確指出JWT的的開始生效日期
- iat: issue at (time),與exp和nbf格式相同,為JWT的發行時間
- jti: JWT id,識別此JWT的唯一字串
JSON Web Signatures 大概是JWT 最重要的特性之一,先前說過JWT是由Header.Payload.Signature組成,
大概會是類似下面的範例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. (header) eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.(payload) TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ(signature)
依據JWT 規範,有數種簽章演算法可用,因此會有多種不同的解譯。 JWT要求單一演算法以支援相同實作
- HMAC using SHA-256,在JWT中稱作HS256
- RSASSA PKCS1 v1.5 using SHA-256,在JWT稱作RS256
- ECDSA using P-256 and SHA-256,在JWT稱作ES256
JWA為JSON Web Algorithms的縮寫,還有許多演算法就不在此詳列。
HMAC-based的簽章演算法範例如下:
const encodedHeader = base64(utf8(JSON.stringify(header))); const encodedPayload = base64(utf8(JSON.stringify(payload))); const signature = base64(hmac(`${encodedHeader}.${encodedPayload}`,secret, sha256)); const jwt = `${encodedHeader}.${encodedPayload}.${signature}`;
Binary Search in Javascript
const binarySearch = (arr, target) => { // arr 必須排序, 否則要實作 arr.sort( (a,b) => a - b); if(!arr || arr.length === 0){ return -1; } const len = arr.length; let start = 0, end = arr.length -1; while(start + 1 < end){ let mid = start + parseInt((end - start)/2); //找出中位數 if (arr[mid] === target) { return mid; } else if (arr[mid] < target){ start = mid; } else{ end = mid; } } if(arr[start] === target) { return start; } if(arr[end] === target) { return end; } return -1; } (function(){ let result = binarySearch([1,2,3,4,5],5); console.log(result) //4 })()
解題練習 – subsets
最近開始練習解題,決定將每次的練習記錄下來
有關subsets的處理幾乎都是以深度優先為第一考量
解題的方式不外乎:
- 排序
- 刪除重複
- 遞迴
由於subsets是tree的概念, 我們會向下搜尋,
因此必須以pos作為一個基準點, 每次遞迴就往下層尋找。
每次的遞迴把我們搜尋到的 subset 存入我們的result陣列。
list則是提供一個暫存的容器。當我們搜尋完某個支線後,
要爬回上一層節點,所以必須remove(splice)最後一個元素。
以下是javascript的程式碼,僅供參考,如有錯誤還請不吝指正。
const subsetHandler = function(result, list, nums, pos){ result.push([].concat(list)); //將子集放入result for(let i = pos; i < nums.length; i ++){ list.push(nums[i]); //子集的容器 subsetHandler(result,list,nums,i+1); list.splice(list.length-1,1); //刪除最後的元素 } };
const subsets = function(nums) { const len = nums.length; let result = []; let list = []; nums = nums.sort((a,b) => a - b); //先排序 subsetHandler(result,list,nums,0); return result; };
Webpack Uglify.js 不支援ES6語法
記錄一下前幾天踩的雷:
在terminal使用webpack指令做打包動作一切正常,但使用webpack -p做打包出現了Unexpected token (>) 的錯誤問題
google了一下發現原來是unglifyjs不支援es6語法,因此我寫的arrow function造成壓縮失敗。 只要安裝babel-preset-env
yarn add babel-preset-env --save -dev
並在.babelrc中設定:
{
"presets": ["env"]
}
重新執行webpack -p之後即可正常打包。
淺談React with Redux (1) – Redux 概念
由於筆者沒使用過Flux, 所以在這邊不會特別介紹Flux,也不會做相關的比較,在此先跟大家道歉。
為什麼要用Redux?
這個問題很簡單,親手開發一個單純的React專案。 當我們的組件越來越多,包的子組件越來越多時,我們很有可能會遭遇到state或props難以管理的問題。 這裡我們就要借助一個名為"Redux"的Pattern來簡化這些複雜的工作。 也因此,我們Redux不是只能應用在React身上。
Redux的三大定律
- 單一資料流
- state只能讀取
- 使用純函數做資料修改
單一資料流– 所有的state皆儲存於一個JavaScript的物件中
state只能讀取– 我們不能修改state的資料,修改資料唯一的方式就是觸發一個action
純函數做資料修改– 我們利用reducer來處理state與action,reducer就是一個很單純的function
看到這裡, Redux的初學者應該還是不懂,到底什麼是action , 什麼是reducer。先來看看圖表吧。
圖表來源
我們先來談談Action, 到底什麼是Action呢?根據React Redux的官網描述:
Actions are plain JavaScript objects. Actions must have a type
property that indicates the type of action being performed.
亦即Action是一個純js物件,物件必須帶有一個屬性叫做type,舉例來說:
{ type: 'Select_Book', payload: 'Harry Potter' }
就是一個Action, payload就是由外部(例如: UI 觸發)傳入的新state。
可是單純一個物件並沒有任何作用,因此我們需要一個ActionCreator作為窗口,用來建立Action物件。
所以我們可以定義一個函數叫做:
function selectBook(bookName){ return { type: 'Select_Book', payload: { selectedBook: bookName } }; }
現在我們可以來談談Reducer了,Redux的命名也是由Reducer來的。 Action定義了要執行的行為,但並未描述state的變化,因此我們要借助reducer來處理state。 在Redux中,我們會把所有的資料儲存在唯一的物件中,我們當然可以只寫一個reducer來處理所有的action,但隨著專案規模越來越大的時候,reducer會變得相當擁腫,這時我們就需要拆分數個reducer來降低複雜度。
首先我們會創建一個物件來初始化state
const initialState = [];
reducer事實上也是一個純函數,所以我們可以這樣寫:
function bookReducer(state = initialState, action){ switch(action.type){ case: 'Select_Book': return Object.assign({}, state, { selectedBook: action.payload.selectedBook, }); } return state; }
reducer主要接收兩個參數,一個是action,另一個是state。 它的工作就是根據action的定義來修改state。
回傳使用Object.assign這個方法是因為我們要確保每次的變化都是獨立的。
說到這邊,讀者應該已經大致瞭解action,reducer的作用了。
至於action要如何跟reducer結合,這我們留待下次再討論吧。
p.s 由於筆者是前端初心者, 本身為後端開發人員, 我盡量以新手的觀點來撰寫本文以求每個人都可以輕易上手。
如有問題請提出, 有錯誤希望各位高手不吝指教,謝謝。