感恩!~~沒想到上篇文章能這麼受你們的喜歡,激動不已。🤩。可是卻也是坐臥不安,這也意味着責任。下篇許多知識點都須要比較深刻的研究和理解,博主也是水平有限,擔憂本身沒法承擔你們的期待。不過終究仍是須要擺正心態,放下情緒,一字一字用心專一,不負本身,也不負社區。與各位小夥伴相互學習,共同成長,以此共勉!javascript
最近業務繁忙,精力有限,雖然我儘可能嚴謹和反覆修訂,但文章也定有疏漏。上篇文章中,許多小夥伴們指出了很多的問題,爲此我也是深表抱歉,我也會虛心接受和糾正錯誤。也很是感激那麼多經過微信或公衆號與我探討的小夥伴,感謝你們的支持和鼓勵。css
你們知道,React 如今已經在前端開發中佔據了主導的地位。優異的性能,強大的生態,讓其沒法阻擋。博主面的 5 家公司,所有是 React 技術棧。據我所知,大廠也大部分以 React 做爲主技術棧。React 也成爲了面試中並不可少的一環。html
中篇主要從如下幾個方面對 React 展開闡述:前端
原本是計劃只有上下兩篇,但是寫着寫着越寫越多,受限於篇幅,也爲了有更好的閱讀體驗,只好拆分出中篇,但願各位童鞋別介意。🙃,另外,下篇還有 Hybrid App / Webpack / 性能優化 / Nginx 等方面的知識,敬請期待。java
建議仍是先從上篇基礎開始哈~有個按部就班的過程:react
小菜雞博客求贊 🙂 bloggit
React 也是現現在最流行的前端框架,也是不少大廠面試必備。React 與 Vue 雖有不一樣,但一樣做爲一款 UI 框架,雖然實現可能不同,但在一些理念上仍是有類似的,例如數據驅動、組件化、虛擬 dom 等。這裏就主要列舉一些 React 中獨有的概念。github
React 的核心流程能夠分爲兩個部分:面試
要了解 Fiber,咱們首先來看爲何須要它?算法
問題: 隨着應用變得愈來愈龐大,整個更新渲染的過程開始變得吃力,大量的組件渲染會致使主進程長時間被佔用,致使一些動畫或高頻操做出現卡頓和掉幀的狀況。而關鍵點,即是 同步阻塞。在以前的調度算法中,React 須要實例化每一個類組件,生成一顆組件樹,使用 同步遞歸 的方式進行遍歷渲染,而這個過程最大的問題就是沒法 暫停和恢復。
解決方案: 解決同步阻塞的方法,一般有兩種: 異步 與 任務分割。而 React Fiber 即是爲了實現任務分割而誕生的。
簡述:
核心:
class Fiber {
constructor(instance) {
this.instance = instance
// 指向第一個 child 節點
this.child = child
// 指向父節點
this.return = parent
// 指向第一個兄弟節點
this.sibling = previous
}
}
複製代碼
鏈表樹遍歷算法: 經過 節點保存與映射,便可以隨時地進行 中止和重啓,這樣便能達到實現任務分割的基本前提;
任務分割,React 中的渲染更新能夠分紅兩個階段:
分散執行: 任務分割後,就能夠把小任務單元分散到瀏覽器的空閒期間去排隊執行,而實現的關鍵是兩個新API: requestIdleCallback
與 requestAnimationFrame
requestIdleCallback
處理,這是個瀏覽器提供的事件循環空閒期的回調函數,須要 pollyfill,並且擁有 deadline 參數,限制執行事件,以繼續切分任務;requestAnimationFrame
處理;// 相似於這樣的方式
requestIdleCallback((deadline) => {
// 當有空閒時間時,咱們執行一個組件渲染;
// 把任務塞到一個個碎片時間中去;
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && nextComponent) {
nextComponent = performWork(nextComponent);
}
});
複製代碼
Tips:
Fiber 其實能夠算是一種編程思想,在其它語言中也有許多應用(Ruby Fiber)。核心思想是 任務拆分和協同,主動把執行權交給主線程,使主線程有時間空擋處理其餘高優先級任務。
當遇到進程阻塞的問題時,任務分割、異步調用 和 緩存策略 是三個顯著的解決思路。
感謝 @Pengyuan 童鞋,在評論中指出了幾個 Fiber 中最核心的理念,感恩!!
在新版本中,React 官方對生命週期有了新的 變更建議:
getDerivedStateFromProps
替換componentWillMount
;getSnapshotBeforeUpdate
替換componentWillUpdate
;componentWillReceiveProps
;其實該變更的緣由,正是因爲上述提到的 Fiber。首先,從上面咱們知道 React 能夠分紅 reconciliation 與 commit 兩個階段,對應的生命週期以下:
reconciliation:
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
commit:
componentDidMount
componentDidUpdate
componentWillUnmount
在 Fiber 中,reconciliation 階段進行了任務分割,涉及到 暫停 和 重啓,所以可能會致使 reconciliation 中的生命週期函數在一次更新渲染循環中被 屢次調用 的狀況,產生一些意外錯誤。
新版的建議生命週期以下:
class Component extends React.Component {
// 替換 `componentWillReceiveProps` ,
// 初始化和 update 時被調用
// 靜態函數,沒法使用 this
static getDerivedStateFromProps(nextProps, prevState) {}
// 判斷是否須要更新組件
// 能夠用於組件性能優化
shouldComponentUpdate(nextProps, nextState) {}
// 組件被掛載後觸發
componentDidMount() {}
// 替換 componentWillUpdate
// 能夠在更新以前獲取最新 dom 數據
getSnapshotBeforeUpdate() {}
// 組件更新後調用
componentDidUpdate() {}
// 組件即將銷燬
componentWillUnmount() {}
// 組件已銷燬
componentDidUnMount() {}
}
複製代碼
使用建議:
constructor
初始化 state;componentDidMount
中進行事件監聽,並在componentWillUnmount
中解綁事件;componentDidMount
中進行數據的請求,而不是在componentWillMount
;getDerivedStateFromProps(nextProps, prevState)
;
public static getDerivedStateFromProps(nextProps, prevState) {
// 當新 props 中的 data 發生變化時,同步更新到 state 上
if (nextProps.data !== prevState.data) {
return {
data: nextProps.data
}
} else {
return null1
}
}
複製代碼
componentDidUpdate
監聽 props 或者 state 的變化,例如:componentDidUpdate(prevProps) {
// 當 id 發生變化時,從新獲取數據
if (this.props.id !== prevProps.id) {
this.fetchData(this.props.id);
}
}
複製代碼
componentDidUpdate
使用setState
時,必須加條件,不然將進入死循環;getSnapshotBeforeUpdate(prevProps, prevState)
能夠在更新以前獲取最新的渲染數據,它的調用是在 render 以後, update 以前;shouldComponentUpdate
: 默認每次調用setState
,必定會最終走到 diff 階段,但能夠經過shouldComponentUpdate
的生命鉤子返回false
來直接阻止後面的邏輯執行,一般是用於作條件渲染,優化渲染的性能。在瞭解setState
以前,咱們先來簡單瞭解下 React 一個包裝結構: Transaction:
setState
: React 中用於修改狀態,更新視圖。它具備如下特色:
異步與同步: setState
並非單純的異步或同步,這其實與調用時的環境相關:
setState
是"異步"的;
setState
的實現中,有一個判斷: 當更新策略正在事務流的執行中時,該組件更新會被推入dirtyComponents
隊列中等待執行;不然,開始執行batchedUpdates
隊列更新;
componentDidUpdate
是在更新以後,此時組件已經不在事務流中了,所以則會同步執行;setState
後立刻從this.state
上獲取更新後的值。setState
實際上是能夠傳入第二個參數的。setState(updater, callback)
,在回調中便可獲取最新值;setState
是同步的,能夠立刻獲取更新後的值;
setTimeout
是放置於定時器線程中延後執行,此時事務流已結束,所以也是同步;批量更新: 在 合成事件 和 生命週期鉤子 中,setState
更新隊列時,存儲的是 合併狀態(Object.assign
)。所以前面設置的 key 值會被後面所覆蓋,最終只會執行一次更新;
函數式: 因爲 Fiber 及 合併 的問題,官方推薦能夠傳入 函數 的形式。setState(fn)
,在fn
中返回新的state
對象便可,例如this.setState((state, props) => newState);
setState
的批量更新的邏輯,傳入的函數將會被 順序調用;注意事項:
setState
,React 會報錯警告,一般有兩種解決辦法:
componentWillUnmount
中標記爲 true,在setState
前進行判斷;HOC(Higher Order Componennt) 是在 React 機制下社區造成的一種組件模式,在不少第三方開源庫中表現強大。
簡述:
用法:
屬性代理 (Props Proxy): 返回出一個組件,它基於被包裹組件進行 功能加強;
function proxyHoc(Comp) {
return class extends React.Component {
render() {
const newProps = {
name: 'tayde',
age: 1,
}
return <Comp {...this.props} {...newProps} /> } } } 複製代碼
function withOnChange(Comp) {
return class extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
}
}
onChangeName = () => {
this.setState({
name: 'dongdong',
})
}
render() {
const newProps = {
value: this.state.name,
onChange: this.onChangeName,
}
return <Comp {...this.props} {...newProps} /> } } } 複製代碼
使用姿式以下,這樣就能很是快速的將一個 Input
組件轉化成受控組件。
const NameInput = props => (<input name="name" {...props} />) export default withOnChange(NameInput) 複製代碼
function withMask(Comp) {
return class extends React.Component {
render() {
return (
<div>
<Comp {...this.props} />
<div style={{
width: '100%',
height: '100%',
backgroundColor: 'rgba(0, 0, 0, .6)',
}}
</div>
)
}
}
}
複製代碼
反向繼承 (Inheritance Inversion): 返回出一個組件,繼承於被包裹組件,經常使用於如下操做:
function IIHoc(Comp) {
return class extends Comp {
render() {
return super.render();
}
};
}
複製代碼
渲染劫持 (Render Highjacking)
function withLoading(Comp) {
return class extends Comp {
render() {
if(this.props.isLoading) {
return <Loading /> } else { return super.render() } } }; } 複製代碼
操做狀態 (Operate State): 能夠直接經過 this.state
獲取到被包裹組件的狀態,並進行操做。但這樣的操做容易使 state 變得難以追蹤,不易維護,謹慎使用。
應用場景:
function withAdminAuth(WrappedComponent) {
return class extends React.Component {
constructor(props){
super(props)
this.state = {
isAdmin: false,
}
}
async componentWillMount() {
const currentRole = await getCurrentUserRole();
this.setState({
isAdmin: currentRole === 'Admin',
});
}
render() {
if (this.state.isAdmin) {
return <Comp {...this.props} />; } else { return (<div>您沒有權限查看該頁面,請聯繫管理員!</div>); } } }; } 複製代碼
function withTiming(Comp) {
return class extends Comp {
constructor(props) {
super(props);
this.start = Date.now();
this.end = 0;
}
componentDidMount() {
super.componentDidMount && super.componentDidMount();
this.end = Date.now();
console.log(`${WrappedComponent.name} 組件渲染時間爲 ${this.end - this.start} ms`);
}
render() {
return super.render();
}
};
}
複製代碼
使用注意:
React.forwardRef
;Redux 是一個 數據管理中心,能夠把它理解爲一個全局的 data store 實例。它經過必定的使用規則和限制,保證着數據的健壯性、可追溯和可預測性。它與 React 無關,能夠獨立運行於任何 JavaScript 環境中,從而也爲同構應用提供了更好的數據同步通道。
核心理念:
大體的數據結構以下所示:
理念實現:
getState
: 獲取 state;dispatch
: 觸發 action, 更新 state;subscribe
: 訂閱數據變動,註冊監聽器;// 建立
const store = createStore(Reducer, initStore)
複製代碼
// 一個普通的 Action
const action = {
type: 'ADD_LIST',
item: 'list-item-1',
}
// 使用:
store.dispatch(action)
// 一般爲了便於調用,會有一個 Action 建立函數 (action creater)
funtion addList(item) {
return const action = {
type: 'ADD_LIST',
item,
}
}
// 調用就會變成:
dispatch(addList('list-item-1'))
複製代碼
// 一個常規的 Reducer
// @param {state}: 舊數據
// @param {action}: Action 對象
// @returns {any}: 新數據
const initList = []
function ListReducer(state = initList, action) {
switch (action.type) {
case 'ADD_LIST':
return state.concat([action.item])
break
defalut:
return state
}
}
複製代碼
注意:
- 遵照數據不可變,不要去直接修改 state,而是返回出一個 新對象,可使用
assign / copy / extend / 解構
等方式建立新對象;- 默認狀況下須要 返回原數據,避免數據被清空;
- 最好設置 初始值,便於應用的初始化及數據穩定;
進階:
<Provider>
: 將 store 經過 context 傳入組件中;connect
: 一個高階組件,能夠方便在 React 組件中使用 Redux;
store
經過mapStateToProps
進行篩選後使用props
注入組件mapDispatchToProps
建立方法,當組件調用時使用dispatch
觸發對應的action
combineReducers()
進行重構合併;dispatch(Action)
便可,下面是三種不一樣的異步實現:
React 中一般使用 類定義 或者 函數定義 建立組件:
在類定義中,咱們可使用到許多 React 特性,例如 state、 各類組件生命週期鉤子等,可是在函數定義中,咱們卻無能爲力,所以 React 16.8 版本推出了一個新功能 (React Hooks),經過它,能夠更好的在函數定義組件中使用 React 特性。
好處:
this
的指向問題;注意:
useEffect
中使用useState
,React 會報錯提示;重要鉤子*:
useState
): 用於定義組件的 State,其到類定義中this.state
的功能;// useState 只接受一個參數: 初始狀態
// 返回的是組件名和更改該組件對應的函數
const [flag, setFlag] = useState(true);
// 修改狀態
setFlag(false)
// 上面的代碼映射到類定義中:
this.state = {
flag: true
}
const flag = this.state.flag
const setFlag = (bool) => {
this.setState({
flag: bool,
})
}
複製代碼
useEffect
):類定義中有許多生命週期函數,而在 React Hooks 中也提供了一個相應的函數 (useEffect
),這裏能夠看作componentDidMount
、componentDidUpdate
和componentWillUnmount
的結合。
useEffect(callback, [source])
接受兩個參數
callback
: 鉤子回調函數;source
: 設置觸發條件,僅當 source 發生改變時纔會觸發;useEffect
鉤子在沒有傳入[source]
參數時,默認在每次 render 時都會優先調用上次保存的回調中返回的函數,後再從新調用回調;useEffect(() => {
// 組件掛載後執行事件綁定
console.log('on')
addEventListener()
// 組件 update 時會執行事件解綁
return () => {
console.log('off')
removeEventListener()
}
}, [source]);
// 每次 source 發生改變時,執行結果(以類定義的生命週期,便於你們理解):
// --- DidMount ---
// 'on'
// --- DidUpdate ---
// 'off'
// 'on'
// --- DidUpdate ---
// 'off'
// 'on'
// --- WillUnmount ---
// 'off'
複製代碼
經過第二個參數,咱們即可模擬出幾個經常使用的生命週期:
componentDidMount
: 傳入[]
時,就只會在初始化時調用一次;const useMount = (fn) => useEffect(fn, [])
複製代碼
componentWillUnmount
: 傳入[]
,回調中的返回的函數也只會被最終執行一次;const useUnmount = (fn) => useEffect(() => fn, [])
複製代碼
mounted
: 可使用 useState 封裝成一個高度可複用的 mounted 狀態;const useMounted = () => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
!mounted && setMounted(true);
return () => setMounted(false);
}, []);
return mounted;
}
複製代碼
componentDidUpdate
: useEffect
每次均會執行,其實就是排除了 DidMount 後便可;const mounted = useMounted()
useEffect(() => {
mounted && fn()
})
複製代碼
其它內置鉤子:
useContext
: 獲取 context 對象
useReducer
: 相似於 Redux 思想的實現,但其並不足以替代 Redux,能夠理解成一個組件內部的 redux:
useContext
的全局性,能夠完成一個輕量級的 Redux;(easy-peasy)useCallback
: 緩存回調函數,避免傳入的回調每次都是新的函數實例而致使依賴組件從新渲染,具備性能優化的效果;
useMemo
: 用於緩存傳入的 props,避免依賴的組件每次都從新渲染;
useRef
: 獲取組件的真實節點;
useLayoutEffect
:
useEffect
相似,只是區別於執行時間點的不一樣。useEffect
屬於異步執行,並不會等待 DOM 真正渲染後執行,而useLayoutEffect
則會真正渲染後才觸發;自定義鉤子(useXxxxx
): 基於 Hooks 能夠引用其它 Hooks 這個特性,咱們能夠編寫自定義鉤子,如上面的useMounted
。又例如,咱們須要每一個頁面自定義標題:
function useTitle(title) {
useEffect(
() => {
document.title = title;
});
}
// 使用:
function Home() {
const title = '我是首頁'
useTitle(title)
return (
<div>{title}</div>
)
}
複製代碼
SSR,俗稱 服務端渲染 (Server Side Render),講人話就是: 直接在服務端層獲取數據,渲染出完成的 HTML 文件,直接返回給用戶瀏覽器訪問。
先後端分離: 前端與服務端隔離,前端動態獲取數據,渲染頁面。
痛點:
首屏渲染性能瓶頸:
SEO 問題: 因爲頁面初始狀態爲空,所以爬蟲沒法獲取頁面中任何有效數據,所以對搜索引擎不友好。
最初的服務端渲染,便沒有這些問題。但咱們不能返璞歸真,既要保證現有的前端獨立的開發模式,又要由服務端渲染,所以咱們使用 React SSR。
原理:
條件: Node 中間層、 React / Vue 等框架。 結構大概以下:
開發流程: (此處以 React + Router + Redux + Koa 爲例)
一、在同個項目中,搭建 先後端部分,常規結構:
二、server 中使用 Koa 路由監聽 頁面訪問:
import * as Router from 'koa-router'
const router = new Router()
// 若是中間也提供 Api 層
router.use('/api/home', async () => {
// 返回數據
})
router.get('*', async (ctx) => {
// 返回 HTML
})
複製代碼
// 前端頁面路由
import { pages } from '../../client/app'
import { matchPath } from 'react-router-dom'
// 使用 react-router 庫提供的一個匹配方法
const matchPage = matchPath(ctx.req.url, page)
複製代碼
四、經過頁面路由的配置進行 數據獲取。一般能夠在頁面路由中增長 SSR 相關的靜態配置,用於抽象邏輯,能夠保證服務端邏輯的通用性,如:
class HomePage extends React.Component{
public static ssrConfig = {
cache: true,
fetch() {
// 請求獲取數據
}
}
}
複製代碼
獲取數據一般有兩種狀況:
const data = await matchPage.ssrConfig.fetch()
複製代碼
// 頁面路由
class HomePage extends React.Component{
public static ssrConfig = {
fetch: {
url: '/api/home',
}
}
}
// 根據規則匹配出對應的數據獲取方法
// 這裏的規則能夠自由,只要能匹配出正確的方法便可
const controller = matchController(ssrConfig.fetch.url)
// 獲取數據
const data = await controller(ctx)
複製代碼
五、建立 Redux store,並將數據dispatch
到裏面:
import { createStore } from 'redux'
// 獲取 Clinet層 reducer
// 必須複用前端層的邏輯,才能保證一致性;
import { reducers } from '../../client/store'
// 建立 store
const store = createStore(reducers)
// 獲取配置好的 Action
const action = ssrConfig.action
// 存儲數據
store.dispatch(createAction(action)(data))
複製代碼
renderToString
將 React Virtual Dom 渲染成 字符串:import * as ReactDOMServer from 'react-dom/server'
import { Provider } from 'react-redux'
// 獲取 Clinet 層根組件
import { App } from '../../client/app'
const AppString = ReactDOMServer.renderToString(
<Provider store={store}> <StaticRouter location={ctx.req.url} context={{}}> <App /> </StaticRouter> </Provider>
)
複製代碼
七、將 AppString 包裝成完整的 html 文件格式;
八、此時,已經能生成完整的 HTML 文件。但只是個純靜態的頁面,沒有樣式沒有交互。接下來咱們就是要插入 JS 與 CSS。咱們能夠經過訪問前端打包後生成的asset-manifest.json
文件來獲取相應的文件路徑,並一樣注入到 Html 中引用。
const html = ` <!DOCTYPE html> <html lang="zh"> <head></head> <link href="${cssPath}" rel="stylesheet" /> <body> <div id="App">${AppString}</div> <script src="${scriptPath}"></script> </body> </html> `
複製代碼
import serialize from 'serialize-javascript'
// 獲取數據
const initState = store.getState()
const html = ` <!DOCTYPE html> <html lang="zh"> <head></head> <body> <div id="App"></div> <script type="application/json" id="SSR_HYDRATED_DATA">${serialize(initState)}</script> </body> </html> `
ctx.status = 200
ctx.body = html
複製代碼
Tips:
這裏比較特別的有兩點:
使用了
serialize-javascript
序列化 store, 替代了JSON.stringify
,保證數據的安全性,避免代碼注入和 XSS 攻擊;使用 json 進行傳輸,能夠得到更快的加載速度;
const hydratedEl = document.getElementById('SSR_HYDRATED_DATA')
const hydrateData = JSON.parse(hydratedEl.textContent)
// 使用初始 state 建立 Redux store
const store = createStore(reducer, hydrateData)
複製代碼
函數式編程是一種 編程範式,你能夠理解爲一種軟件架構的思惟模式。它有着獨立一套理論基礎與邊界法則,追求的是 更簡潔、可預測、高複用、易測試。其實在現有的衆多知名庫中,都蘊含着豐富的函數式編程思想,如 React / Redux 等。
常見的編程範式:
函數式編程的理念:
純函數(肯定性函數): 是函數式編程的基礎,可使程序變得靈活,高度可拓展,可維護;
優點:
條件:
new Date()
或者Math.randon()
等這種不可控因素;咱們經常使用到的許多 API 或者工具函數,它們都具備着純函數的特色, 如split / join / map
;
函數複合: 將多個函數進行組合後調用,能夠實現將一個個函數單元進行組合,達成最後的目標;
扁平化嵌套: 首先,咱們必定能想到組合函數最簡單的操做就是 包裹,由於在 JS 中,函數也能夠當作參數:
f(g(k(x)))
: 嵌套地獄,可讀性低,當函數複雜後,容易讓人一臉懵逼;xxx(f, g, k)(x)
結果傳遞: 若是想實現上面的方式,那也就是xxx
函數要實現的即是: 執行結果在各個函數之間的執行傳遞;
reduce
,它能夠按數組的順序依次執行,傳遞執行結果;pipe
,用於函數組合:// ...fs: 將函數組合成數組;
// Array.prototype.reduce 進行組合;
// p: 初始參數;
const pipe = (...fs) => p => fs.reduce((v, f) => f(v), p)
複製代碼
使用: 實現一個 駝峯命名 轉 中劃線命名 的功能:
// 'Guo DongDong' --> 'guo-dongdong'
// 函數組合式寫法
const toLowerCase = str => str.toLowerCase()
const join = curry((str, arr) => arr.join(str))
const split = curry((splitOn, str) => str.split(splitOn));
const toSlug = pipe(
toLowerCase,
split(' '),
join('_'),
encodeURIComponent,
);
console.log(toSlug('Guo DongDong'))
複製代碼
好處:
const log = curry((label, x) => {
console.log(`${ label }: ${ x }`);
return x;
});
const toSlug = pipe(
toLowerCase,
log('toLowerCase output'),
split(' '),
log('split output'),
join('_'),
log('join output'),
encodeURIComponent,
);
複製代碼
Tips:
一些工具純函數可直接引用
lodash/fp
,例如curry/map/split
等,並不須要像咱們上面這樣本身實現;
數據不可變性(immutable): 這是一種數據理念,也是函數式編程中的核心理念之一:
const
。使用const
建立一個對象後,它的屬性仍然能夠被修改;Object.freeze
: 凍結對象,但freeze
仍沒法保證深層的屬性不被串改;immutable.js
: js 中的數據不可變庫,它保證了數據不可變,在 React 生態中被普遍應用,大大提高了性能與穩定性;
trie
數據結構:
避免不一樣函數之間的 狀態共享,數據的傳遞使用複製或全新對象,遵照數據不可變原則;
避免從函數內部 改變外部狀態,例如改變了全局做用域或父級做用域上的變量值,可能會致使其它單位錯誤;
避免在單元函數內部執行一些 反作用,應該將這些操做抽離成更獨立的工具單元;
高階函數: 是指 以函數爲參數,返回一個新的加強函數 的一類函數,它一般用於:
函數式編程的好處:
總結:
Tips:
其實咱們很難也不須要在面試過程當中去完美地闡述出整套思想,這裏也只是淺嘗輒止,一些我的理解而已。博主也是初級小菜鳥,停留在表面而已,只求對你們能有所幫助,輕噴🤣;
我我的以爲: 這些編程範式之間,其實並不矛盾,各有各的 優劣勢。
理解和學習它們的理念與優點,合理地 設計融合,將優秀的軟件編程思想用於提高咱們應用;
全部設計思想,最終的目標必定是使咱們的應用更加 解耦顆粒化、易拓展、易測試、高複用,開發更爲高效和安全;
有一些庫能讓你們很快地接觸和運用函數思想:
Underscore.js
/Lodash/fp
/Rxjs
等。
到此,想必你們會發現已經開始深刻一些理論和原理層面了,並不像上篇那麼的淺顯易懂了。但這也是個必經之路,不可能永遠停留在 5分鐘掌握的技術 上。再也不停留在語言的表面,而是理解更深刻的原理,模式,架構,因果,你就會忽然發現你成爲高級軟件工程師了。😁。
但願各位小夥伴能沉下心來,一些理論、概念雖然枯燥,但反覆琢磨後再本身實踐嘗試下,就能有本身的理解。
當你開始面試高級工程師時,面試官便再也不重點關注你會不會寫stopPropagation
或者會不會水平居中了,而是更在意你本身的思考和研究能力了。表現出本身深刻理解研究的成果,定會讓面試官另眼相看。
Tips:
聯繫我直接郵件 159042708@qq.com 或關注下面公衆號加我微信詳聊哈。
博主真的寫得很辛苦,再不 star 下,真的要哭了。~ github。🤑