項目地址: https://github.com/mirrorhanyu/WeGit
掃碼體驗
小程序項目按照官方推薦的代碼組織結(jié)構(gòu)
WeGit
├── .editorconfig
├── .eslintrc
├── global.d.ts
├── package.json
├── project.config.json
├── tsconfig.json
├── config
│ ├── dev.js
│ ├── index.js
│ └── prod.js
└── src
├── app.scss
├── app.tsx
├── common.scss
├── index.html
├── actions
├── components
│ ├── activity
│ ├── common
│ ├── footer
│ ├── search
│ ├── trending
├── constants
├── pages
├── reducers
├── store
├── types
├── utils
└── wemark
生命周期和事件回調(diào)函數(shù)
Taro 遵循 React 語法規(guī)范搓幌,同樣是采用組件化思想迷郑,并保持了一致的組件生命周期懂更。
同時最蕾,小程序每個頁面指定頁面的初始數(shù)據(jù)、生命周期回調(diào)憨降、事件處理函數(shù)等,詳細(xì)看參考官方文檔。
React 基礎(chǔ)
在我們開始之前咐容,我們可以簡單的回顧一遍 React 基本概念。
元素 (Elements)
我們知道蚂维,在 React 應(yīng)用中
Elements are the smallest building blocks of React apps.
翻譯過來戳粒,element 就是 React 中最小的單元。 它描述了屏幕上應(yīng)該展現(xiàn)什么內(nèi)容虫啥。
const element = <h1>Hello, world</h1>;
element 是 immutable(不可變的)蔚约,定義好后就決定了長什么模樣。
如果把一個不斷變化的 Component (比如一個倒計時的組件)看作一個不斷放映的電影涂籽,element 就是其中的一幀苹祟,描述那一秒屏幕上該展示什么。
想要在屏幕上展現(xiàn)一個element ,用 ReactDOM.render(element, document.getElementById('root’)) 去更新就可以了树枫。
組件化 (Components)
組件化的思想是指把所看到的 UI直焙,拆分為獨立的,可以復(fù)用的小組件砂轻。
Component 可以理解成一個函數(shù)奔誓,輸入就是 props,輸出就是上面提到的 elements搔涝。比如:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
如果采用 ES6 的語法:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
當(dāng)我們想要渲染一個 Component 的時候厨喂,
const element = <Welcome name=“Han"/>;
ReactDOM.render(
element,
document.getElementById('root')
);
{name: 'Han'} 就是輸入的 props,<h1>Hello, Han</h1> 就是輸出庄呈,render函數(shù)會渲染在對應(yīng)的節(jié)點上
React 只會更新需要更新的節(jié)點蜕煌。
Props
React 相當(dāng)之靈活,但是有一個非常嚴(yán)格的規(guī)則:
All React components must act like pure functions with respect to their props.
React 要求我們不能改變 props诬留,必須是一個純函數(shù) (pure)斜纪。輸入不改變的話,如何做到改變輸出故响,從而使 UI 動態(tài)改變呢傀广?
于是我們就有了,
State
State 與 props 類似彩届,但是 state 是私有的伪冰,并且完全受控于當(dāng)前組件。
State 允許 React 組件隨用戶操作樟蠕、網(wǎng)絡(luò)響應(yīng)或者其他變化而動態(tài)更改輸出內(nèi)容贮聂。
具體可以參考這個例子
需要注意的三點:
- setState() 去修改 state
- setState() 異步的,出于性能考慮寨辩,React 可能會把多個 setState() 調(diào)用合并成一個調(diào)用吓懈。
- setState() 合并是淺合并
因為State是私有的,如果想要傳遞 State靡狞,只能是”自上而下”的通過props作為輸入?yún)?shù)向子組件傳遞耻警。這種通常會成為單向數(shù)據(jù)流。
那么甸怕,如果兩個組件都需要一個同樣的數(shù)據(jù)源呢甘穿,或者兄弟組件會通信呢?
通常梢杭,我們可以可以將它提升至這些組件的最近共同父組件中温兼。但是,還有別的方法嗎武契?
Flux
Flux 是 facebook 提出的一種網(wǎng)頁端設(shè)計架構(gòu)(Architecture)
Flux 核心理念是實現(xiàn)了一種單向的數(shù)據(jù)流募判。簡單來說荡含,就是當(dāng)用戶進(jìn)行操作的時候,會從組件 dispatch 一個 action届垫,這個 action 流向 store释液,store 基于 action 對狀態(tài)進(jìn)行改動,然后 store 又觸發(fā)組件基于新的狀態(tài)重新渲染敦腔。
這樣的實現(xiàn)均澳,可以幫助視圖和業(yè)務(wù)邏輯的分離恨溜。業(yè)務(wù)邏輯控制中心數(shù)據(jù)源符衔,視圖基于數(shù)據(jù)源渲染。
<img src="https://user-gold-cdn.xitu.io/2019/8/8/16c6fc480d9f696b?w=1300&h=708&f=png&s=141989" width="600" alt="flux-unidirectional-data-flow"/>
Redux
Redux 是基于 Flux 思想的一種實現(xiàn)糟袁。
Redux 的三個基本原則(Three Principles)
- 單一數(shù)據(jù)源
整個應(yīng)用的 state 被儲存在一棵 object tree 中判族,并且這個 object tree 只存在于唯一一個 store 中。 - State 是只讀的
唯一改變 state 的方法就是觸發(fā) action项戴,action 是一個用于描述已發(fā)生事件的普通對象形帮。 - 使用純函數(shù)來執(zhí)行修改
為了描述 action 如何改變 state tree ,你需要編寫 reducers周叮。
Redux 的單項數(shù)據(jù)流是這樣的:
- 調(diào)用 store.dispatch(action)
- Redux store 把當(dāng)前的 state 樹和 action 傳給 reducer 函數(shù)做計算下一個 state辩撑。
- 根 reducer 應(yīng)該把多個子 reducer 輸出合并成一個單一的 state 樹。
- Redux store 保存了根 reducer 返回的完整 state 樹仿耽。
同時合冀,為了從 Redux state 樹中讀取部分?jǐn)?shù)據(jù),并通過 props 來把這些數(shù)據(jù)提供給要渲染的組件项贺, React Redux 庫提供了 connect()方法君躺。
connect 可接受如下參數(shù)
- mapStateToProps 這個函數(shù)來指定如何把當(dāng)前 Redux store state 映射到展示組件的 props 中
- mapDispatchToProps() 方法接收 dispatch() 方法并返回期望注入到展示組件的 props 中的回調(diào)方法
小程序首頁數(shù)據(jù)是如何展示的
首先 connect 方法,mapStateToProps()开缎,mapDispatchToProps()
@connect(({trending}) => ({
trending
}), (dispatch) => ({
fetchDevelopers(since, language) {
dispatch(fetchDevelopers(since, language))
},
fetchRepositories(since, language) {
dispatch(fetchRepositories(since, language))
},
fetchLanguages() {
dispatch(fetchLanguages())
}
}))
在 Component 的 componentDidMount 生命周期回調(diào)函數(shù)里獲取 Trending language(火熱的開發(fā)語言)
this.props.fetchLanguages()
dispatch 會分發(fā)對應(yīng)的 action 出去
export const fetchLanguages = () => {
return async (dispatch) => {
try {
dispatch({ type: FETCH_TRENDING_LANGUAGES_PENDING})
const response = await Taro.request({
url: 'https://example.com/trending-languages',
method: 'GET'
})
dispatch({ type: FETCH_TRENDING_LANGUAGES_FULFILLED, payload: [ ...response.data] })
} catch (e) {
dispatch({ type: FETCH_TRENDING_LANGUAGES_REJECTED, payload: e })
}
}
}
對應(yīng)的 reducer 會處理收到當(dāng)前狀態(tài)樹和 action棕叫,從而計算下一個 state
export default function activity(state = {} as ActivityState, action) {
switch (action.type) {
case FETCH_ACTIVITIES_PENDING:
return {
...state, isActivitiesUpdated: false
}
case FETCH_ACTIVITIES_REJECTED:
return {
...state, isActivitiesUpdated: true
}
case FETCH_ACTIVITIES_FULFILLED:
return {
...state,
isActivitiesUpdated: true,
activities: action.payload,
username: action.addition.username,
maxPagination: action.addition.maxPage,
currentPagination: action.addition.currentPagination
}
}
}
還記得 connect 里的 mapStateToProps 嗎?
當(dāng) state 變化奕删,Component 里的 props 也會改變俺泣,從而使得 Component 重新渲染。
taro build
在本地開發(fā)的時候
yarn dev:weapp //taro build --type weapp —watch 會編譯預(yù)覽及打包完残,并會監(jiān)聽文件修改
yarn build:weapp //taro build --type weapp 不會監(jiān)聽文件修改伏钠,但會對代碼進(jìn)行壓縮打包
實測過程中,build:weapp 把生成的小程序從1000+kb 壓縮到 ~700kb
但是 build 之后可能會有些許區(qū)別坏怪,需要自行 End to end 測試
比如贝润,build 出來的小程序 Taro response 中,header 中的 Max-Page 會解析成 max-page
持續(xù)優(yōu)化的
- [ ] 項目中 component 拆分可以更細(xì)致
- [ ] actions 重復(fù)代碼
- [ ] sass 管理
- [ ] connect 計算屬性
- [ ] 測試