五月天青色头像情侣网名,国产亚洲av片在线观看18女人,黑人巨茎大战俄罗斯美女,扒下她的小内裤打屁股

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

RN從0到1系統(tǒng)精講與小紅書(shū)APP實(shí)戰(zhàn)(2023版)萬(wàn)山春色歸

2023-03-19 14:42 作者:bili_28881155651  | 我要投稿


ReactNative介紹

RN從0到1系統(tǒng)精講與小紅書(shū)APP實(shí)戰(zhàn)

下栽地止https://lexuecode.com/6659.html


React Native 是一個(gè)由 Facebook 于 2015 年 9 月發(fā)布的一款開(kāi)源的 JavaScript 框架,它可以讓開(kāi)發(fā)者使用 JavaScript 和 React 來(lái)開(kāi)發(fā)跨平臺(tái)的移動(dòng)應(yīng)用。它既保留了 React 的開(kāi)發(fā)效率,又同時(shí)擁有 Native 應(yīng)用的良好體驗(yàn),加上 Virtual DOM 跨平臺(tái)的優(yōu)勢(shì),實(shí)現(xiàn)了真正意義上的:Learn Once,Write Anywhere.


注:非高清 logo,這不是原子結(jié)構(gòu)模型嗎?暗示 React (Native)是萬(wàn)惡之源?

React Native 的特點(diǎn)

跨平臺(tái)

React Native 使用了 Virtual DOM(虛擬 DOM),只需編寫(xiě)一套代碼,便可以將代碼打包成不同平臺(tái)的 App,極大提高了開(kāi)發(fā)效率,并且相對(duì)全部原生開(kāi)發(fā)的應(yīng)用來(lái)說(shuō),維護(hù)成本也相對(duì)更低。


上手快

相比于原生開(kāi)發(fā),JavaScript 學(xué)習(xí)成本低、語(yǔ)法靈活。允許讓 Web 開(kāi)發(fā)者更多地基于現(xiàn)有經(jīng)驗(yàn)開(kāi)發(fā) App。React Native 只需使用 JavaScript 就能編寫(xiě)移動(dòng)原生應(yīng)用,它和 React 的設(shè)計(jì)理念是一樣的,因此可以毫不夸張地說(shuō):你如果會(huì)寫(xiě) React,就會(huì)寫(xiě) React Native!


原生體驗(yàn)

由于 React Native 提供的組件是對(duì)原生 API 的暴露,雖然我們使用的是 JavaScript 語(yǔ)言編寫(xiě)的代碼,但是實(shí)際上是調(diào)用了原生的 API 和原生的 UI 組件。因此,體驗(yàn)和性能足以媲美原生應(yīng)用。


熱更新

React Native 開(kāi)發(fā)的應(yīng)用支持熱更新,因?yàn)?React Native 的產(chǎn)物是 bundle 文件,其實(shí)本質(zhì)上就是 JS 代碼,在 App 啟動(dòng)的時(shí)候就會(huì)去服務(wù)器上獲取 bundle 文件,我們只需要更新 bundle 文件,從而使得 App 不需要重新前往商店下載包體就可以進(jìn)行版本更新,開(kāi)發(fā)者可以在用戶無(wú)感知的情況下進(jìn)行功能迭代或者 bug 修復(fù)。但是值得注意的是,AppStore 禁止熱更新的功能中有調(diào)用私有 API、篡改原生代碼和改變 App 的行為。


React Native 原理

JavaScriptCore

JavaScriptCore 是 JavaScript 引擎,通常會(huì)被叫做虛擬機(jī),專門(mén)設(shè)計(jì)來(lái)解釋和執(zhí)行 JavaScript 代碼。在 React Native 里面,JavaScriptCore 負(fù)責(zé) bundle 產(chǎn)出的 JS 代碼的解析和執(zhí)行。

JS Engine

React Native 需要一個(gè) JS 的運(yùn)行環(huán)境,因?yàn)?React Native 會(huì)把應(yīng)用的 JS 代碼編譯成一個(gè) JS 文件(x x.bundle),React Native 框架的目標(biāo)就是解釋運(yùn)行這個(gè) JS 腳本文件,如果是 Native 拓展的 API,則直接通過(guò) bridge 調(diào)用 Native 方法,最基礎(chǔ)的比如繪制 UI 界面,映射 Virtual DOM 到真實(shí)的 UI 組件中。


綠色的是我們應(yīng)用開(kāi)發(fā)的部分,我們寫(xiě)的代碼基本上都是在這一層。

藍(lán)色代表公用的跨平臺(tái)的代碼和工具引擎,一般我們不會(huì)動(dòng)藍(lán)色部分的代碼。

黃色代表平臺(tái)相關(guān)的 bridge 代碼,做定制化的時(shí)候會(huì)添加修改代碼。

紅色代表系統(tǒng)平臺(tái)的功能,另外紅色上面有一個(gè)虛線,表示所有平臺(tái)相關(guān)的東西都通過(guò) bridge 隔離開(kāi)來(lái)了,紅色部分是獨(dú)立于 React Native 的。

脫離 React Native,純?cè)耸侨绾闻c JS 交互的?來(lái)看下 iOS 里面是如何實(shí)現(xiàn)的。

在 Native 創(chuàng)建一個(gè) JS 上下文:

// 創(chuàng)建一個(gè)ctx的JS上下文 JSContent *ctx = [[JSContent alloc] init]; // 創(chuàng)建一個(gè)變量name [ctx evaluateScript:@"var name = 'Hellen'"]; // 創(chuàng)建一個(gè)方法 [ctx evaluateScript:@"var hello = function(name) { return 'hello ' + name }"];

Native 調(diào)用 JavaScript 方法:

// 通過(guò)ctx上下文對(duì)象,獲取到hello方法 JSValue *helloFUnction = ctx[@"hello"]; // 運(yùn)行js方法 JSValue *greetings = [helloFunction callWithArguments:@[@"bytedancers"]; // hello bytedancers

所以,JavaScript 代碼只要將變量暴露在 JS 上下文全局,Native 就能獲取到,并運(yùn)行 JS 的代碼。

JavaScript 調(diào)用 Native,首先需要在 Native 端,將一個(gè)變量暴露在 JS 上下文全局,在 JavaScript 全局變量里面就能獲取到并執(zhí)行這個(gè)方法:

ctx[@"createdByNative"] = ^(NSString *name) { // do something return someResult }


RN從0到1系統(tǒng)精講與實(shí)戰(zhàn)小紅書(shū)APP - 實(shí)戰(zhàn)

Navigator使用和封裝
點(diǎn)擊查看官方文檔

0.44版本后Navigator已經(jīng)從react-native庫(kù)中移除,如需導(dǎo)入可按如下操作:

// install
$npm install React-native-deprecated-custom-components --save

// import API
import CustomerComponents, {Navigator} from 'react-native-deprecated-custom-components';
實(shí)際項(xiàng)目中對(duì)于單頁(yè)面應(yīng)用,我們可以把Navigator封裝成一個(gè)組件,把各頁(yè)面當(dāng)作Navigator的一個(gè)個(gè)場(chǎng)景轉(zhuǎn)換,在頁(yè)面中實(shí)現(xiàn)跳轉(zhuǎn),返回,動(dòng)畫(huà)等的各種操作時(shí)只需要調(diào)用相應(yīng)方法即可。

class APP extends Component {
constructor(props) {
super(props);
this._renderScene = this._renderScene.bind(this);
this.state = {};
}

/* eslint-disable */
_renderScene(route, navigator) {
let Component = route.component;
return (
<Component
{...route}
navigator={navigator}
passProps={route.passProps}
callback={route.callback}
/>
);
}

render() {
return (
<View style={{ flex: 1 }}>
<Navigator
ref="navigator"
renderScene={this._renderScene}
configureScene={(route) => ({
...route.sceneConfig || Navigator.SceneConfigs.HorizontalSwipeJump,
gestures: route.gestures
})}
initialRoute={{
component: Login
}}
/>
<LoadingView isVisible={this.props.showLoading} />
</View>
)
}
}
除了場(chǎng)景轉(zhuǎn)換等操作,還可以在這個(gè)組件中集成控制App全局的一些操作,比如說(shuō),Loading的設(shè)置,網(wǎng)絡(luò)狀態(tài)檢查等設(shè)置,在各頁(yè)面就無(wú)須再單獨(dú)設(shè)置。盡量在一個(gè)地方里面實(shí)現(xiàn)控制app的一些相近的默認(rèn)操作

實(shí)際頁(yè)面中跳轉(zhuǎn)或其他操作:

_jumpPage() {
const { navigator } = this.props;
if (navigator) {
navigator.push({
component: TabBarList, //next route
sceneConfig: Navigator.SceneConfigs.FloatFromBottomAndroid, // animated config
callback: () => {} //callback
passProps: { //transfer parameters
tabs: 'home',
activeTab: 'home',
onPressHandler: this.props.goToPage
}
});
}
}
React Navigation理解和使用
點(diǎn)擊查看官方文檔

react-native 0.44版本之前路由控制使用的Navigator雖然非常穩(wěn)定,基本沒(méi)出現(xiàn)過(guò)什么BUG,但是跳轉(zhuǎn)效果一直被人詬病,跳轉(zhuǎn)時(shí)候的動(dòng)畫(huà)和原生App的效果相比,非常明顯差一等,在0.44版本后Facebook推薦使用react-navigation庫(kù)來(lái)實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn),tab轉(zhuǎn)換,側(cè)邊欄滑動(dòng)等功能。

react-navigation主要包括導(dǎo)航,底部tab,頂部tab
,側(cè)滑等,功能很強(qiáng)大,而且體驗(yàn)接近原生。接下來(lái)會(huì)一一介紹:

導(dǎo)航 -> StackNavigator
底部或者頂部tab -> TabNavigator
關(guān)于側(cè)滑DrawerNavigator的使用,筆者不在本文介紹,但可以看這篇附帶Demo的推薦博客

StackNavigator
StackNavigator在功能上就是相當(dāng)于原來(lái)使用Navigator,但是他有著不一樣的實(shí)現(xiàn)和非常好的跳轉(zhuǎn)體驗(yàn),使用上也非常簡(jiǎn)單,其實(shí)也就是三部曲:

路由配置(頁(yè)面注冊(cè)):
const routeConfigs = {
Login: { screen: Login },
TabBar: { screen: TabBarContainer },
Feedback: { screen: Feedback },
};
默認(rèn)場(chǎng)景配置:
const stackNavigatorConfig = {
initialRouteName: 'Login',
navigationOptions: {
headerBackTitle: null,
headerTintColor: 'white',
showIcon: true,
swipeEnabled: false,
animationEnabled: false,
headerStyle: {
backgroundColor: '#f2f2f2'
}
},
mode: 'card',
paths: 'rax/: Login',
headerMode: 'float',
transitionConfig: (() => ({
screenInterpolator: CardStackStyleInterpolator.forHorizontal // android's config about jump to next page
})),
onTransitionStart: () => {},
onTransitionEnd: () => {}
};
容器生成與初始化:
const Nav = StackNavigator(routeConfigs, stackNavigatorConfig);
export default class QQDrawerHome extends Component {
render() {
return(
<Nav/>
);
}
}
這樣就簡(jiǎn)單完成了路由的配置,開(kāi)發(fā)時(shí)只需要把新頁(yè)面添加到注冊(cè)對(duì)象routeConfigs中,StackNavigator會(huì)對(duì)里面的的注冊(cè)頁(yè)面和注冊(cè)時(shí)使用的KEY值形成對(duì)應(yīng)關(guān)系,當(dāng)你在頁(yè)面時(shí)跳轉(zhuǎn)時(shí),只需要這樣:

_jumpPage() {
const { navigation } = this.props;
if (navigation) {
const { navigation } = this.props;
navigation.navigate('TabBar');
}
}
帶參數(shù)跳轉(zhuǎn)時(shí):

_jumpPage() {
const { navigation } = this.props;
if (navigation) {
const { navigation } = this.props;
navigation.navigate('TabBar', {
visible: false,
title: '首頁(yè)'
});
}
}
在下個(gè)頁(yè)面就可以拿到參數(shù)并設(shè)置頭部或其他參數(shù):

static navigationOptions = ({ navigation }) => {
const { state } = navigation;
const { title } = state.params;
return {
title: title,
};
};
其他reset,setParams等操作將可以學(xué)著本文后面封裝到組件中去使用,當(dāng)然你也可以直接在頁(yè)面跳轉(zhuǎn)函數(shù)中重置路由,就像這樣:

const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Login'})
]
})
this.props.navigation.dispatch(resetAction)
TabNavigator
0.44版本之前我們實(shí)現(xiàn)Tab頁(yè)面通常都選擇使用框架react-native-tab-navigator或者react-native-scrollable-tab-view,現(xiàn)在0.44版本后react-navigation庫(kù)中推薦使用TabNavigator,同樣的使用方式,類似StackNavigator三部曲:

const routeConfigs = {
Message:{
screen:QQMessage,
navigationOptions: {
tabBarLabel: '消息',
tabBarIcon: ({ tintColor }) => (
<Image
source={require('./notif-icon.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>),
}
},
Contact:{
screen:QQContact,
navigationOptions: {
tabBarLabel: '聯(lián)系人',
tabBarIcon: ({ tintColor }) => (
<Image
source={require('./notif-icon.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>),
}
},
};

const tabNavigatorConfig = {
tabBarComponent:TabBarBottom,
tabBarPosition:'bottom',
swipeEnabled:false,
animationEnabled:false,
lazy:true,
initialRouteName:'Message',
backBehavior:'none',
tabBarOptions:{
activeTintColor:'rgb(78,187,251)',
activeBackgroundColor:'white',
inactiveTintColor:'rgb(127,131,146)',
inactiveBackgroundColor:'white',
labelStyle:{
fontSize:12
}
}
}

export default TabNavigator(routeConfigs, tabNavigatorConfig);
關(guān)于使用TabNavigator的一些注意點(diǎn)和當(dāng)前問(wèn)題:

如你甚至未使用StackNavigator,而想直接使用TabNavigator,還是用其他第三方框架吧,他和StackNavigator是配套使用的,你必須保證TabNavigator存在于StackNavigator中,TabNavigator才能良好工作。
當(dāng)你當(dāng)前頁(yè)面使用了TabNavigator,那么TabNavigator所形成的容器組件應(yīng)該是當(dāng)前頁(yè)面的頂層組件,否則報(bào)錯(cuò),將會(huì)無(wú)法獲取到tab中的router數(shù)組。
關(guān)于嵌套使用TabNavigator,即在TabNavigator的一個(gè)screen中再次使用了TabNavigator形成頁(yè)面,安卓平臺(tái)下無(wú)法渲染子組件,頁(yè)面空白,且內(nèi)層Tab基本失效,或者你的內(nèi)層Tab容器使用其他第三方框架如react-native-tab-view等類似框架,問(wèn)題依然存在,關(guān)于此問(wèn)題可關(guān)注公關(guān)BUG#1796。
StackNavigator路由的集中封裝
此部分集成了一部分Redux知識(shí),建議可以看一下redux官方文檔了解一下redux。StackNavigator本身就集成了Redux來(lái)進(jìn)行路由數(shù)據(jù)的管理,如你想要將你自己的redux管理集成到StackNavigator中,官方同樣提供接口addNavigationHelpers,這里我們關(guān)注的是如何把reset,setParams等Navigator中的Action直接封裝到組件中形成頁(yè)面調(diào)用接口。

以下是筆者的封裝組件,類似之前封裝Navigator組件封裝集中管理組件的思路代碼,我們把StackNavigator同樣封裝為一個(gè)組件作為管理中心

......

const AppNavigator = StackNavigator(RouteConfigs, stackNavigatorConfig);// eslint-disable-line

class MainContainer extends Component {
constructor(props) {
super(props);
this.resetRouteTo = this.resetRouteTo.bind(this);
this.resetActiveRouteTo = this.resetActiveRouteTo.bind(this);
this.backTo = this.backTo.bind(this);
this.setParamsWrapper = this.setParamsWrapper.bind(this);
this.state = {};
}

resetRouteTo(route, params) {
const { dispatch } = this.props;
if (dispatch) {
dispatch(
NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: route, params: params })],
})
);
}
}

resetActiveRouteTo(routeArray, activeIndex) {
const { dispatch } = this.props;
if (dispatch) {
const actionsArray = [];
for (let i = 0; i < routeArray.length; i++) {
actionsArray.push(NavigationActions.navigate({ routeName: routeArray[i] }));
}

const resetAction = NavigationActions.reset({
index: activeIndex,
actions: actionsArray,
});
dispatch(resetAction);
}
}

backTo(key) {
const { dispatch } = this.props;
if (dispatch) {
dispatch(
NavigationActions.reset({
key: key
})
);
}
}

setParamsWrapper(params, key) {
const { dispatch } = this.props;
if (dispatch) {
const setParamsAction = NavigationActions.setParams({
params: params,
key: key,
});
dispatch(setParamsAction);
}
}

render() {
const { dispatch, navigationState, screenProps } = this.props;
return (
<View
style={{ flex: 1 }}
onStartShouldSetResponder={() => dismissKeyboard()}
>
<StatusBar barStyle="light-content" />
<AppNavigator
navigation={addNavigationHelpers({
dispatch: dispatch,
state: navigationState,
resetRouteTo: (route, params) => this.resetRouteTo(route, params),
resetActiveRouteTo: (routeArray, activeIndex) => this.resetActiveRouteTo(routeArray, activeIndex),
backTo: (key) => this.backTo(key),
setParamsWrapper: (params, key) => this.setParamsWrapper(params, key)
})}
screenProps={screenProps}
/>
<Loading isVisible={true} mode="alipay" />
</View>
);
}
}

const mapStateToProps = (state) => {
const newNavigationState = state.navReducer;
if (state.screenProps) {
newNavigationState.params = {
...state.params,
...state.screenProps
};
}
return {
navigationState: newNavigationState,
screenProps: state.screenProps
};
};

export default connect(mapStateToProps)(MainContainer);

......
其中綁定navReducer文件的數(shù)據(jù),可參考redux和react-navigation官網(wǎng)文檔,此文不再列出

這樣封裝后,各頁(yè)面使用reset,setParams等操作時(shí),就可以像以前一樣直接使用相關(guān)操作,如重置路由:


_jumpPage() {
const { navigation } = this.props;
if (navigation) {
navigation.resetRouteTo('TabBar', { title: '首頁(yè)', selectedTab: 'home' });
}
}

狀態(tài)分析
前幾天剛好看到一篇文章前端狀態(tài)管理請(qǐng)三思,覺(jué)得挺有意思的,原文作者利用狀態(tài)機(jī)的思想,預(yù)先設(shè)想好所有狀態(tài)和狀態(tài)的遷移,優(yōu)雅的管理頁(yè)面登錄狀態(tài)避免過(guò)多變量的使用。本文參考作者的思想和代碼,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的登錄頁(yè)面。狀態(tài)分析如下:

初始登錄頁(yè)面是展示登錄的表單(login form)
當(dāng)提交(submit)數(shù)據(jù)過(guò)程后,頁(yè)面變?yōu)榈却龜?shù)據(jù)響應(yīng)狀態(tài)(loading)

數(shù)據(jù)響應(yīng)有兩種狀態(tài),成功(success)頁(yè)面跳轉(zhuǎn)到首頁(yè);失?。╢ailure)頁(yè)面提示錯(cuò)誤
當(dāng)?shù)卿洺晒?,只有先退出登錄(logout)之后才能重新登錄
當(dāng)?shù)卿浭?,重新提交(submit)回到加載狀態(tài)(loading)


logout之后回到login form狀態(tài)

依舊是模仿掘金app登錄頁(yè)面的一個(gè)實(shí)現(xiàn)效果:

定義狀態(tài)機(jī)
const machine = {
states: {
'login form': {
submit: 'loading'
},
loading: {
success: 'profile',
failure: 'error'
},
profile: {
viewProfile: 'profile',
logout: 'login form'
},
error: {
submit: 'loading'
}
}
}
復(fù)制代碼實(shí)現(xiàn)一個(gè)狀態(tài)控制函數(shù),返回下一個(gè)狀態(tài)
const stateTransformer = function(currentState, stepUp) {
let nextState
if (machine.states[currentState][stepUp]) {
nextState = machine.states[currentState][stepUp]
}
console.log(`${currentState} + ${stepUp} --> ${nextState}`)
return nextState || currentState
}
復(fù)制代碼我們把狀態(tài)控制的變量存儲(chǔ)在redux中,定義一個(gè)簡(jiǎn)單的auth模塊如下,stateChanger純函數(shù)用于控制currentState的狀態(tài)遷移,每次操作結(jié)果返回進(jìn)行狀態(tài)變換
export default {
namespace: 'auth',
state: {
currentState: 'login form'
},
reducers: {
stateChanger(state, {stepUp}) {
return {
...state,
currentState: stateTransformer(state.currentState, stepUp)
}
}
},
effects: dispatch => ({
async loginByPhoneNumber(playload, state) {
dispatch.auth.stateChanger({stepUp: 'submit'})
let {data} = await api.auth.loginByPhoneNumber(playload)
if (data.s === 0) {
dispatch.auth.stateChanger({stepUp: 'success'})
saveData('juejin_token', data.token)
} else {
dispatch.auth.stateChanger({stepUp: 'failure'})
Toast.info('用戶名或密碼錯(cuò)誤', 2)
}
}
})
}
復(fù)制代碼那么在組件中,我們很容易寫(xiě)一個(gè)控制狀態(tài)變化的組件

render() {
let {currentState} = this.props
return (
<>
{(() => {
switch (currentState) {
case 'loading':
return (
//加載中展示組件
)
case 'profile':
return <Redirect to={'/'} />//返回首頁(yè)
default:
return (
//登錄表單
)
}
})()}
</>
)
}
復(fù)制代碼具體配置補(bǔ)充
為了配合項(xiàng)目的用戶登錄驗(yàn)證,我們重新搭建一個(gè)本地服務(wù),在react配置路由的代理轉(zhuǎn)發(fā),具體地,在根目錄下新建文件src/setupProxy.js,將/api開(kāi)頭請(qǐng)求轉(zhuǎn)發(fā)到服務(wù)器
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(proxy('/api', {target: 'http://localhost:8989/', changeOrigin: true}))
}
復(fù)制代碼services/api定義數(shù)據(jù)接口
export async function loginByPhoneNumber({phoneNumber, password}) {
return post('/api/auth/type/phoneNumber', {
body: {
phoneNumber,
password
}
})
}
復(fù)制代碼后端實(shí)現(xiàn)一個(gè)簡(jiǎn)單的中間件路由
const Koa = require('koa')
const router = require('./router')
router.post('/auth/type/phoneNumber', async (ctx, next) => {
var {phoneNumber, password} = await parse.json(ctx.req)
if (phoneNumber === '15111111111' && password === '123456') {
let token = generateToken({uid: phoneNumber, password})
ctx.response.body = JSON.stringify({
s: 0,
m: `賬號(hào)登錄成功錯(cuò)誤`,
d: '',
token
})
} else {
ctx.response.body = JSON.stringify({s: 1, m: '賬號(hào)信息錯(cuò)誤', d: ''})
}
})

RN從0到1系統(tǒng)精講與小紅書(shū)APP實(shí)戰(zhàn)(2023版)萬(wàn)山春色歸的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
丽江市| 蒙自县| 都昌县| 建水县| 盐亭县| 凌海市| 荥阳市| 衡阳市| 革吉县| 乌拉特中旗| 灌南县| 开鲁县| 沂水县| 龙岩市| 共和县| 延长县| 郁南县| 资溪县| 咸宁市| 洛南县| 丰顺县| 东丰县| 大庆市| 兴城市| 泽普县| 皮山县| 梅州市| 罗江县| 平乡县| 南川市| 南投县| 桐城市| 内丘县| 抚州市| 宁德市| 莱阳市| 博野县| 揭阳市| 涟源市| 青铜峡市| 大连市|