课程简介

本课程主要讲解 React 的基础知识及应用案例,包括 props、state、生命周期函数等,样式和 Flex 布局,React Native 内置的 API 和 UI 组件介绍、路由、状态管理 Redux,如何使用第三方组件,JS 跟原生代码之间的通信、动画、调试、静态代码检查 Flow,测试 Jest & Enzyme,打包发布和热更新等。

学完本课程可以系统掌握 React Native 生态系统各个方面的知识,可以完全从零开始开发并发布上线一款能同时运行在 iOS 和 Android 上的 APP,进入 APP 开发之路,向大前端职业生涯迈进一大步。

实践案例包括:

  • 一个简单的图片搜索引擎
  • 一个阅读类 APP
  • 一个神秘的 APP

作者简介

李焱,2010 年毕业于北京大学信息管理系,获得信息管理学士学位及计算机软件学士学位;毕业后曾在酷我音乐、人人游戏、腾讯等互联网公司从事 Web 开发及游戏开发;2015 年先后担任成都鱼说科技大前端技术总监和 CTO 职位;2017 年初创办漫极客科技,致力于人工智能产品开发。

课程内容

导读:课程概要

我使用 React / React Native + Redux + Webpack 开发两年有余,期间既有发现新功能时的惊喜,也有踩坑时的痛苦,希望将这两年多的所学所思整理成文,让新人少踩坑,早入 React / React Native 之门。

内容主要包括:

  • React 基础知识,包括 JSX、Component、props vs state、生命周期函数等;
  • React Native 提供的基础 API 和 UI 组件;
  • React Native 里如何组件化地写样式;
  • router;
  • 状态管理之 Redux;
  • 如何使用第三方组件;
  • JS 与原生代码之间的通信;
  • 动画;
  • 静态代码检查;
  • 测试;
  • 打包发布上架 APPStore;
  • 热更新。

最后会安排一个完整的实战项目,覆盖 React Native 绝大部分知识点。课程代码会放在 GitHub 上。

关于作者

李焱,网名 magicly,2010 年毕业于北大,获信息管理学士学位和计算机软件学士学位,毕业之后先后在酷我音乐、人人游戏、腾讯等知名互联网公司工作,之后在成都一家创业公司担任 CTO,带过一年多的团队。

从前到后,网页设计、微信公众号、小程序、后端接口、数据库设计优化、运维脚本、数据统计分析等都做过,使用过 Java、JS、Scala、Python、Bash、C++、Rust 等语言,算不上精通,但也远非只会写 Hello World

最近关注大前端(如 Node.js、React、React Native、Electron、Web Workers、WebAssembly、WebGL、Three.js、D3.js 等)及 深度学习算法 等。

欢迎关注 微博,或者扫描关注微信公众号:

jsforfun

适用于什么人群

  • 有一定 Web 前端基础,会使用 React 开发 WebAPP,想开发原生 APP 的 Web 前端开发人员。
  • 做 Android 想要顺便做一下 iOS 的开发人员, 反之亦然。
  • Android 和 iOS 都会做,想使用 React Native 来提高自己的竞争力和开发效率的人员。

如果既不会 React,又没做过原生 APP 开发,建议先看 React课程。

为什么是 React Native

React Native 开源以来受到了极大的关注,目前 GitHub 上有 55000+ stars,背后有 Facebook 这样的大公司支持,也做出了有大量用户的产品,国内也有很多公司在用。开发效率高于原生应用,运行效率高于 H5 混合式开发,综合素质很强。

阿里也开源了一款类似的框架 Weex。 但是 Weex 开源时间短,用的人还比较少,整体上没有 RN 成熟。

React Native 还能用么

为什么会问这个问题呢?因为今年发生了两件“大事”,很多人都说不要用 React / React Native 了。那我们先来看看这两件“大事”到底对 React / React Native 生态系统有什么影响,是否还可以继续使用 React / React Native 呢。

苹果下架热更新应用

今年三月份,很多 iOS 开发者收到了苹果的警告邮件,说他们的 APP 里使用了“热更新”技术,需要在下一个版本里面去掉,否则会面临被下架 APPStore 的风险。

enter image description here

然后 6 月份的时候,苹果动了真格,在中国区下架了将近 3 万款 APP。一时间各大社区都炸了锅,包括 JSPatch、React Native、Weex、Cocos2d-x、白鹭引擎等都在讨论自己是不是中枪了。结果发现,真正受影响的是 JSPatch、RolloutIO,而 React Native 等并没有受到影响,而有部分开发者的 React Native APP 被下架最后发现也是因为用了某些第三方库,而这些库用到了 JSPatch。为什么都是热更新,差别这么大呢?

先来看看什么是“热更新”。下面的图(或者类似的图)大家应该见过吧。

enter image description here

enter image description here

简单来说,热更新就是 APP 在通过 APPStore 下载后,在启动的时候去检查是否有版本更新,有的话直接下载更新资源,绕过 APPStore 而达到直接更新 APP 的方式。这样做有什么好处呢,当然:

  • 加快发布速度,可以快速修复 bug,或者做一些运营活动。
  • 更新包很小,便于用户快速升级。更新包可能 1M,而整个 APP 可能 100M。
  • 强制要求用户升级。
  • 等苹果审核过后,通过热更新增加“功能”,如微信或支付宝支付,这样可以避免被苹果抽成 30%。

都是热更新,JSPatch 为什么就被下架了,而 React Native 等还好好的。

简单来说就是 JSPatch 对所有 Objective-C 的 API 进行了映射,允许开发者在 JS 端调用任意原生代码,这就很危险啦。万一热更新服务器被黑客攻击,或者更新的时候受到中间人攻击,那基本上黑客就可以使用任意的系统接口了。同样这样也绕过了苹果的审核,完全可以在发布之后把 APP 通过热更新换成另外一个 APP。

而给开发者的警告邮件里面也提到了:

这包括将任意参数传递给诸如 dlopen()、dlsym()、respondingToSelector:、performSelector:、method_exchangeImplementations()等动态方法的代码,以及运行远程脚本以便更改应用程序行为或基于下载的脚本调用 SPI。即使远程资源不是故意恶意的,它也可能很容易地通过中间人(MiTM)攻击被劫持,这可能对应用程序的用户造成严重的安全隐患。

而 React Native、Weex 等都没有这些问题,甚至苹果的开发者协议 3.3.2 节明确写明了是可以下载 JS 执行的。

解释型的代码只能运行在所有脚本、代码和解释器打包在应用内且不是下载的情况下。唯一的例外是前述代码是脚本,且代码下载后运行于苹果内置的 WebKit 或 JavascriptCore 框架内,且该脚本和代码不改变应用提供的主要特性和功能以及主要目的,并与 App Store 上提交的应用描述一致。

可见,苹果仍然允许运行于 WebKit 之上的 HTML 5 页面以及运行于 JavascriptCore 之上的诸如 React Native 和 Weex 之类的 JS-Native 框架,无论这些页面和 JS 文件是远程的还是本地的。

另外,去年用 React Native 开发的一块 App 一直以来也没有受到影响,现在还在 APPStore 里面。当然如果大家还是有所担心的话,可以不用 React Native 去做热更新。 除了热更新,React Native 还有很多值得学习使用的地方,能大大提高开发效率。

若想要了解更多细节,读者可查看其他资料。

React 开源协议

前段时间 React 的开源协议问题被大肆报道,百度发文要求全面停止使用 React,Apache 也要求所有旗下项目禁止使用带 Facebook BSD+PATENTS License 的项目,并限时完成替代。不过后来 Facebook 终于顶不住压力更换了许可证:

https://www.infoq.com/news/2017/09/facebook-react-mit。

其实呢,这件事看看热闹就可以了,同时可以顺便了解一下开源许可证及一些专利知识。

Hello React Native

我们还是“俗”一下,来一个 ReactNative 版本的 Hello World 吧。

首先需要安装 Node.js,如果还没有安装的请访问 Node.js,下载对应平台版本。 Mac 下直接安装后面好像会遇到权限的问题,建议使用 https://github.com/creationix/nvm 安装。 安装好 Node 后,建议安装 nrm,然后选择访问速度最快的 npm registry,这样能为后面安装第三方 npm 包节省大量时间。

开发 Native 应用,需要配置比较多的环境。比如开发 iOS,需要有 Mac 电脑,然后安装 XCode; 如果是开发 Android,需要安装 JDK、Android Studio 等,整个配置非常费时。好在今年3月份,Facebook 开发了 Create React Native App, 这个工具是仿照 Create React App 设计的,大大简化了初始开发流程,只需要如下:

npm install -g create-react-native-appcreate-react-native-app HelloReactNativecd HelloReactNativenpm start

然后这个工具会在你的电脑上启动一个 server,并且打印出一个二维码。你只需要在手机上装 Expo APP(iOS 和 Android 都可以),让手机和电脑在同一个 WiFi 下,然后打开 Expo APP。

Expo

扫描二维码,即可在手机上看到第一个 React Native APP。 然后修改 App.js 文件,保存后手机上就能立马看到效果!多亏了热更新!

const App = () => {  return <View style={styles.container}>    <Text>Hello React Native!</Text>  </View>}const styles = StyleSheet.create({  container: {    flex: 1,    backgroundColor: '#fff',    alignItems: 'center',    justifyContent: 'center',  },});export default App;
Hello React Native

摇一摇手机,会出现菜单。

expo菜单
第01课:React 基础知识

React Native 大部分的知识点还是 React 那一套,包括 JSX、Props、State、Lifecycle 等。 如果会 React 的话,可以说 React Native 已经会了一大半了,毕竟 React 的口号是:

Learn Once, Write Anywhere

React 简介

在几年前开发 Web 页面的标准思路还是 HTML 写内容、CSS 控制样式、JavaScript 控制逻辑和动态效果,三者要分开,目录结构也是按文件类型分开的。然而随着 Web 基础的飞速发展,浏览器性能越来越好,支持的功能越来越多,前端工程化越来越强,Web 开发已经从原来的写页面变成了开发 Web APP!以前按照文件类型组织项目的方式也已经越来越满足不了现在复杂的需求了。

随着 React、Ember、Angular 2 等框架的出现以及 W3C 本身也提出的 web components 技术,Web 开发已经进入了组件化的开发时代,思路、工具以及开发方式等都要有重大转变,才能适应目前日益复杂和快速多变的 Web 开发工作。

2013 年 Facebook 在 JSConfUS 大会上宣布开源 ReactReact 提出的 Virtual DOM 技术以及组件化开发思想大大方便开发大型 Web 应用,开源后迅速走红,得到广大开发者的喜爱,逐渐形成了完善的 React 生态圈。截止目前(2017-11-08 13:57),React 在 github 上有 80446 个 stars。

下面是我两年多学习使用 React 的经验总结,希望能对大家有所帮助。

  • 函数就是组件,组件就是函数;
  • 每一个组件只做一件事;
  • 显示组件和逻辑组件分开。

接下来的章节会反复强调这几个基本思路。

开始学习 React 之前,强烈建议先看一下这篇关于 React 开发思想的文章,思想上转化过来,后面会事半功倍。

下面简单介绍一下 React 的基础知识,如 JSX、Stateless Component、Props、State、Lifecycle 和事件等,了解了这些,就可以进入 React Native 的开发了。

没有用过 React 的读者建议可以先看看我的另一门 React学习课程 里的 30分钟入门介绍,然后再学习下面的知识点细节。

JSX

JSX 即 JavaScript Extension,是在 JavaScript 语言上做的扩展,是为了在 JavaScript 里面更方便地书写 HTML 标签等而开发。JSX 不同于其他框架采用的模板,它本质上就是 Javascript,所以功能跟 JavaScript 一样完整,远比其他任何模板语言都灵活方便,而且也不用单独学一门模板语言。

以下即是最简单的一段 JSX 代码。

const hi = <h1>Hello, JSX!</h1>
嵌入 JS 表达式

JSX 里面可以很容易地嵌入 JS 表达式,比如:

const hi = <h1>Hello, JSX! 1 + 1 == {1 + 1}. today is {new Date()}</h1>
JSX 也是表达式

而 JSX 本身也是一个表达式,会被 Babel 等编译成普通的 JS 对象,所以任何可以使用对象的地方都可以使用 JSX,比如 if 判断、for 循环里面、赋值给变量,作为函数参数传递或者返回值返回。比如:

function getGreeting(user) {  if (user) {    return <h1>Hello, {formatName(user)}!</h1>;  }  return <h1>Hello, Stranger.</h1>;}
指定属性

JSX跟 HTML 很像(其实就是为了方便使用 HTML 才发明了 JSX),所以支持指定属性。比如:

const img = <img src={user.photo} alt="this is a photo." />

注意,不要使用引号把 {} 包起来。

子元素

JSX 可以包含子元素(就跟 HTML 标签一样),如果没有子元素可以直接用 /> 来结束标签。

const element = (  <div>    <h1 className="hi">Hello!</h1>    <h2>Good to see you here.</h2>  </div>);

注意 JSX 其实是 JS,所以各种命名等都习惯用 JS 常用的 camelCase 风格,并且由于 class 是 JS 的关键字,所以标签里面要用 className。

JSX 的内部表示

Babel 会把 JSX 编译成 React.createElement() 调用,如下两者是完全等同的:

const element = (  <h1 className="greeting">    Hello, world!  </h1>);
const element = React.createElement(  'h1',  {className: 'greeting'},  'Hello, world!');

React.createElement() 会做一些检查(如 props 类型等)来减少 bug,然后生产如下的对象:

// Note: this structure is simplifiedconst element = {  type: 'h1',  props: {    className: 'greeting',    children: 'Hello, world'  }};

这些对象叫 “React elements”,可以把它看成是对想要展现在屏幕上的内容描述。React 读取这些对象,然后用它们构造 DOM 节点,并保持状态一致。

如果没有子元素的话,可以直接使用闭标签, 比如:

<img src="x.jpg" alt="a pic" />

会被翻译成:

React.createElement(  'img',  {src: 'x.jpg', alt: 'a pic'},  null)

你可以使用 在线 Babel 编译器 来查看 JSX 具体编译成什么样的 JS。

必须 import React

因为 JSX 是编译成 React.createElement,所以 React 必须在 JSX 文件中被 import,否则会编译报错。比如下面的例子,虽然没有直接引用 React,但是也需要 import 进来。这是初学者常犯的一个错误,经常出现在 stateless component 文件中, 因为 container component 里面需要显示 extends React.Component

import React from 'React';import CustomButton from './CustomButton';function WarningButton() {  // return React.createElement(CustomButton, {color: 'red'}, null);  return <CustomButton color="red" />;}

自定义的组件必须大写开头

小写字母开头的组件是 built-in 的组件,如 <div> 或者 <span> 等。如果定义了一个小写字母开头的组件,那也可以, 不过在用在 JSX 之前,必须赋值给一个大写字母开头的变量。感觉有点奇怪, 所以不建议这样用,记住定义组件的时候要有大写字母开头就可以了。

import React from 'react';// Wrong! This is a component and should have been capitalized:function hello(props) {  // Correct! This use of <div> is legitimate because div is a valid HTML tag:  return <div>Hello {props.toWhat}</div>;}function HelloWorld() {  // Wrong! React thinks <hello /> is an HTML tag because it's not capitalized:  return <hello toWhat="World" />;  // 可以这样修复  //const Hello = hello;  //return <Hello toWhat="World" />;}

最好是定义的时候就定义成大写字母开头的:

import React from 'react';// Correct! This is a component and should be capitalized:function Hello(props) {  // Correct! This use of <div> is legitimate because div is a valid HTML tag:  return <div>Hello {props.toWhat}</div>;}function HelloWorld() {  // Correct! React knows <Hello /> is a component because it's capitalized.  return <Hello toWhat="World" />;}

JSX 的 props

JSX 中 props 的值可以是很多种类型。

JS 表达式
<MyComponent foo={1 + 2 + 3 + 4} />
字符串

下面两种写法是一样的, 当然我们一般用第一种,因为简单啊!

<MyComponent message="hello world" /><MyComponent message={'hello world'} />
不指定值默认是 true

下面两者等价,不过官方说不推荐第一种,因为容易与 ES6 object shorthand 的写法 {autocomplete} 表达的是 {autocomplete: autocomplete} 混淆,官方提供这种写法只是为了跟 HTML 保持一致。

<MyTextBox autocomplete /><MyTextBox autocomplete={true} />
展开 object

如果父组件传递的 props 想全部传递给子组件,用 ... 展开 object 操作符会很方便,这个也是 ES6 的新特性 Spread syntax。

function App1(props) {  return <Greeting firstName={props.firstName} lastName={props.lastName} />;}function App2(props) {  return <Greeting {...props} />;}<App1 props={firstName: 'Ben', lastName: 'Hector'} /><App2 props={firstName: 'Ben', lastName: 'Hector'} />

不过这个功能要慎用,我见过很多人不管父组件传递过来的 props 包含多少个属性,统统都直接 {...props} 传递给子组件,而其实父组件传递过来的 props 可能有 10 个属性,而子组件只需要 1 个。而且这样写也看不出来子组件具体需要那几个参数,所以如果不是特别多的话,最好还是显示地写出来传递的 props 值。记住:

Explicit is better than implicit.。

子组件

JSX 中在开标签和闭标签之间的内容会通过 props.children 传递给组件,值的类型有很多种。

字符串
<ComponentA>Hello, React!</ComponentA>

上述写法 ComponentA 中可以通过 props.children 拿到字符串 Hello, React!。字符串是 unescaped 的,所以 <> 需要转义之后传递 &lt;&gt;。JSX 会把下面这些空白去掉:

  • 每行开始和结尾的空白。
  • 空行会去掉。
  • 挨着标签的换行会去掉。
  • 字符串中间的很多换行会合并成一个空格。
  • 字符串中间的空格不会去除,不过 HTML 显示的时候本身多个空格只会显示一个。

所以下面这几种都是一样的, 大家可以合理利用空白来增强代码的阅读性。

    <div>Hello World</div>    <div>      Hello            World    </div>    <div>      Hello      World    </div>    <div>      Hello World    </div>
JSX组件

跟 HTML 一样,JSX 组件可以嵌套,不同种类的子组件也可以混用,比如:

<MyContainer>  Here is a list:  <ul>    <li>Item 1</li>    <li>Item 2</li>  </ul>  <ComponentA>sth...</ComponentA>  <ComponentB /></MyContainer>
JS表达式

比如:

<MyComponent>foo</MyComponent><MyComponent>{'foo'}</MyComponent><MyComponent>{1 + 2 * 3}</MyComponent>

这种方式用来循环渲染列表的时候很适合,如:

function Item(props) {  return <li>{props.message}</li>;}function TodoList() {  const todos = ['finish doc', 'submit pr', 'nag dan to review'];  return (    <ul>      {todos.map((message) => <Item key={message} message={message} />)}    </ul>  );}
函数

JS 里面函数是第一等公民,也是可以像数字、字符串一样传递的。当然传递一个函数拿来显示会很奇怪(试一下,发现不会渲染,除非调用 toString() 转化为String),所以一般传递过去的函数会被调用进行一些转换,转化为 React 可以渲染的东西。

// Calls the children callback numTimes to produce a repeated componentfunction Repeat(props) {  let items = [];  for (let i = 0; i < props.numTimes; i++) {    items.push(props.children(i));  }  return <div>{items}</div>;}function ListOfTenThings() {  return (    <Repeat numTimes={10}>      {(index) => <div key={index}>This is item {index} in the list</div>}    </Repeat>  );}
Booleans、Null、Undefined 会被忽略

falsenullundefined 以及 true 都是合法的子元素,但是不会被渲染出来。下面这些 JSX 渲染出来对的都是一样的: 空元素!

<div /><div></div><div>{false}</div><div>{null}</div><div>{undefined}</div><div>{true}</div>

这样做条件渲染就比较方便了,下面这样写的话只有 showHeadertrue 的时候 <Header /> 才会被渲染:

<div>  {showHeader && <Header />}  <Content /></div>

注意有些所谓的 falsy value,比如 0 是会被 React 渲染的。所以下面的例子中,如果 messages 是空的话,是会显示 0 的。

<div>  {props.messages.length &&    <MessageList messages={props.messages} />  }</div>

可以把 && 之前的表达式变成 boolean 来修复:

<div>  {props.messages.length > 0 &&    <MessageList messages={props.messages} />  }</div>

如果确实需要显示 falsenullundefined 以及 true 的话,需要转化为 String

value is {String(true)}value is {'' + null}value is {`${undefined}`}

具体可参考如下资料:

  • https://facebook.github.io/react/docs/introducing-jsx.html。
  • https://facebook.github.io/react/docs/jsx-in-depth.html。

Stateless Component

组件化开发思想第一条是:函数就是组件,组件就是函数。要自定义组件很简单,直接定义函数就可以了。

import React from 'react';const ComponentA = (props) => {  return <h1>ComponentA, welcome {props.name}</h1>}const App = () => {  const componentA1 = <ComponentA name="magicly" />  const componentA2 = ComponentA({ name: "magicly" });  console.log(typeof componentA1, typeof componentA2, typeof ComponentA);  console.log(componentA1, componentA2);  return (    <div className="App">      <componentA1 />      {componentA1}      {componentA2}    </div>  );}export default App;

可以看到 ComponentA 是函数,所以 ComponentA({ name: "magicly" }) 是函数调用,所以 componentA1 其实就是函数调用的结果,类型是 object。而 <ComponentA name="magicly" /> 其实也是调用了函数,所以两种写法结果是一样的。

但是我把 componentA1componentA2 打印出来,发现两个 object 并不完全一致:

componentA1的type是function ComponentA(props)componentA2的type是"h1"

而实际渲染出来的 DOM 结构则完全一样。

还要注意,因为 componentA1componentA2 已经是 React 组件实例了,所以再写 <componentA1 /><componentA2 /> 没有意义,相当于是在一个 object 上做函数调用,是会报错的,不过 React 没有报错,只是什么都没有输出而已。而如果你直接写 {componentA1()} 则会得到报错 TypeError: componentA1 is not a function,从这里可以看出 <ComponentA /> 这种方式确实不完全等同于 ComponentA()

当然,按照习惯,我们还是用 JSX 的语法,写成 HTML 标签的形式:

<ComponentA name="magicly" />

Stateless Component vs Class Component

React 里面有两种定义 Component 的方式,除了前面说的函数式定义方式,另外一种是基于 class 的:

class ComponentB extends React.Component {  render() {    return <h1>ComponentB, welcome {this.name}</h1>  }}

那应该选哪种方式呢?每个人都有自己的 taste,但我的建议是:优先使用 Stateless Component! stateless 顾名思义就是没有 state,实际上用函数定义的 component 除了没有 state,还没有任何 react 组件的生命周期方法,也没有 this!就是一存粹天真的函数,如此而已。所以简单直接,代码还可以少敲几行!

best practice 是:

用 Stateless 组件定义一堆小组件并包含样式。然后在要用到 state 或者生命周期函数的时候再用 class 组件包一层,只写逻辑,而把渲染样式等转发给 stateless 组件。

如果你用 redux 等来管理状态了,那么理论上而言,你是不会需要写这些包装组件的,因为 react-redux 自己会包装一层,也就是说再也不需要 class 定义的组件了。

注意,我说的是如果你用 redux 的话应该是不用 class 组件的,我并没有说为了不用 class 组件,你要用 redux 哈!

下次再看到 class 定义的组件, 请考虑一下重构成 stateless component,如果你觉得实在没法重构,请出门散散步,回来继续思考!

具体可参考资料:https://facebook.github.io/react/docs/components-and-props.html。

State & Lifecycle

组件化开发思想第二条是:样式跟状态分离。现在我们来实现一个时钟应用:只是显示当前时间!

显示

用 stateless 组件,so easy:

const Clock = (props) => {  return <h1>{new Date().toLocaleTimeString()}</h1>}
state

stateless 组件和 props 都是静态的,只能显示出固定的内容,如果我们要显示动态内容(每一秒的时间都不一样呢),需要用到 state。所以,我们转化为 class 组件。

class Clock2 extends React.Component {  constructor(props) {    super(props);// constructor 里第一行必须是这行,否则会报错    this.state = {      time: new Date().toLocaleTimeString(), // 这里是唯一一处直接复制给 this.state 的,其他地方请用 this.setState({...})    }  }  render() {    return <h1>{this.state.time}</h1>  }}
添加定时器

然而上述代码还是静态的,只能显示 constructor 执行那一秒时的时间,我们需要用 setInterval 起一个定时器,每秒去更新时间。

在哪里起定时器呢? 应该是在 DOM 节点被渲染的时候,这个叫 Mounting。 同样,在不需要 Clock 组件的时候,我们也需要清除定时器,否则会出现内存泄露。那应该在哪里清除呢?应该在 DOM 节点被卸载的时候, 叫 Unmounting

React 提供了这两个时间点的回调函数给我们用。

class Clock2 extends React.Component {  constructor(props) {    super(props);// constructor 里第一行必须是这行,否则会报错    this.state = {      time: new Date().toLocaleTimeString(), // 这里是唯一一处直接复制给 this.state 的,其他地方请用 this.setState({...})    }  }  componentDidMount() {    console.log('componentDidMount');    // timer 不需要拿来渲染, 所以不要把 timer 让在 this.state 里    this.timer = setInterval(() => {      this.setState({        time: new Date().toLocaleTimeString(),      }) // 注意,如果直接修改 this.state.time = new Date().toLocaleTimeString() 是不行的, 不会渲染页面!    })  }  componentWillUnmount() {    console.log('componentDidMount');    clearInterval(this.timer);  }  render() {    return <h1>{this.state.time}</h1>  }}

一定要注意代码里面的几条注释!

重构:显示和状态分离

我们之前说了,用 stateless 组件来负责显示,class 组件来负责状态和逻辑,代码可以改为:

import React from 'react';const Clock = (props) => {  return <h1>{props.time}</h1>}class ClockContainer extends React.Component {  constructor(props) {    super(props);// constructor 里第一行必须是这行,否则会报错    this.state = {      time: new Date().toLocaleTimeString(), // 这里是唯一一处直接复制给 this.state 的,其他地方请用 this.setState({...})    }  }  componentDidMount() {    console.log('componentDidMount');    // timer 不需要拿来渲染, 所以不要把 timer 让在 this.state 里    this.timer = setInterval(() => {      this.setState({        time: new Date().toLocaleTimeString(),      }) // 注意,如果直接修改 this.state.time = new Date().toLocaleTimeString() 是不行的, 不会渲染页面!    })  }  componentWillUnmount() {    console.log('componentDidMount');    clearInterval(this.timer);  }  render() {    return <Clock time={this.state.time} />  }}export default ClockContainer;

这样我们就把负责静态内容显示的组件拆为 Clock,而用 ClockContainer 专门负责逻辑:每秒更新一次状态。

肯定会有人说,这完全就是多此一举把代码搞复杂了啊。 是的!在这个简单的例子里面确实没必要,但是如果在实战中,可能 Clock 这个负责显示的组件会很复杂,有颜色、有布局、有不同的形状吗,甚至可能还会调用很多其他更小的组件,这样拆分后在修改 Clock 的时候就完全不用管 ClockContainer。 同样,如果有一天你想把显示逻辑改为精确到毫秒,你也只用修改 ClockContainer 而完全不用动 Clock

当然,如果你的实际项目就是像 demo 这么简单的话,我觉得你应该使用 jQuery 甚至直接用原生 JS 而不用 React 的啊! I really mean it!

具体可参考如下资料:

  • Presentational and Container Components。
  • State and Lifecycle。

事件 & this 的问题

在 React 里面处理事件,跟在 HTML DOM 节点上很像,区别有两点:

  • React 里用 camelCase 而不是 lowercase;
  • JSX 里传递函数,而不是 string。

比如,HTML 里这样写:

<button onclick="clickme()">  Click me!</button>

在 React 里要这样写:

<button onClick={clickme}>  Click me!</button>

注意上面 clickme 后面没有括号哦,react 里面是传递函数本身,如果加了()就变成了函数调用咯。

另外,在 HTML 里一般可以用 return false 的方式来阻止默认行为,在 React 里面不行,必须用 preventDefault

HTML 里:

<a href="#" onclick="console.log('clicked'); return false">  Click me!</a>

React 里:

const handleClick = e => {  e.preventDefault();  console.log('clicked');}<a href="#" onClick={handleClick}>  Click me!</a>

其中 e 是 React 封装的 SyntheticEvent,所以可以不用考虑浏览器兼容性问题。 God bless!

this 问题

我们在 30分钟简介 中提到过这个问题,再来看看代码。

class Toggle extends React.Component {  constructor(props) {    super(props);    this.state = {isToggleOn: true};    // 如果没有这行,在 button 被 click 然后执行 handleClick 的时候 this 是 undefined    this.handleClick = this.handleClick.bind(this);  }  handleClick() {    this.setState(prevState => ({      isToggleOn: !prevState.isToggleOn    }));  }  render() {    return (      <button onClick={this.handleClick}>        {this.state.isToggleOn ? 'ON' : 'OFF'}      </button>    );  }}

所以经常看到很多人写的 constructor 里面一堆 bind:

  this.function1 = this.function1.bind(this);  this.function2 = this.function2.bind(this);  this.function3 = this.function3.bind(this);  ......

解决方法是用 箭头函数,因为 箭头函数 自己没有绑定 this,所以它里面的 this 是自己被定义时候的 this,即组件本身。

箭头函数可以写在 JSX 上,比如:

    return (      <button onClick={(e) => this.handleClick(e)}>        {this.state.isToggleOn ? 'ON' : 'OFF'}      </button>

也可以用 JS 的一个新特性,所谓的 property initializer syntax,如下:

class Toggle extends React.Component {  constructor(props) {    super(props);    this.state = {isToggleOn: true};  }  handleClick = () => {    this.setState(prevState => ({      isToggleOn: !prevState.isToggleOn    }));  }  render() {    return (      <button onClick={this.handleClick}>        {this.state.isToggleOn ? 'ON' : 'OFF'}      </button>    );  }}

总结

这一章我们介绍了 React 的基础知识,有了这些准备,就可以开始 React Native 学习之旅了。 Let's go!

第02课:React Native 基础知识

这一部分我们主要介绍 React Native 的大多数基础知识, 包括:

  • React Native 里面如何写样式和布局;
  • 显示内容;
  • 处理用户输入和单击;
  • 发起网络请求。

学完本部分内容,可以开发一个简单的 APP 了。

显示内容

Web 领域最基础的显示组件如 divph1ullispanimg 等在 React Native 里都有类似对应的显示组件,我们先来介绍几个用得比较多的。

View

类似于 div,主要拿来包其他的显示组件,如果不指定 widthheight 或者设置 flex,则显示大小由包的子组件决定,具体可参考这里。

Text

类似于 span,主要用来包一段文字。 注意 React Native 里面不能像 Web 里一样直接写文字,必须把文字包含在 Text 组件里面。 还要注意,Text 的宽度会撑满父元素宽导致换行(有点类似块元素),这样说来其实 Text 更像是 p,具体可参考这里。下面的两个 Text 会显示在两行。

    <Text style={{backgroundColor: 'green'}}>Hello React Native!</Text>    <Text style={{backgroundColor: 'yellow'}}>line 2!</Text>
Image

相当于 Web 里面的 img,具体可参考这里。 下面是常用的三种使用 Image 的方式,第一种是静态图片,也就是图片会打包的 APP 里一起发布,因此打包的时候是知道图片大小的,所以不需要指定图片宽和高。后面两种都是动态指定图片,所以需要指定宽和高,不然渲染不出来,一般用于 APP 发布之后从服务器下载图片资源,这样 APP 打包会小很多。

    <View>      <Image        source={require('./lzl.jpeg')}      />      <Image        style={{ width: 250, height: 250 }}        source={{ uri: 'http://e.hiphotos.baidu.com/baike/pic/item/ac6eddc451da81cb6de1cb4d5a66d0160924312e.jpg' }}      />      <Image        style={{ width: 66, height: 58 }}        source={{ uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==' }}      />    </View>
列表

类似于 Web 里的 ulli,主要用来显示一列数据。

ScrollView 其参考资料如下:

  • https://facebook.github.io/react-native/docs/using-a-scrollview.html。
  • https://facebook.github.io/react-native/docs/scrollview.html。

包的子组件可以是不一样的, 并且支持横向滑动。如果 ScrollView 只有一个子组件, 可以通过 maximumZoomScaleminimumZoomScale 属性来支持双指缩放。

    <ScrollView>      <Text style={{ backgroundColor: 'green' }}>Hello React Native!</Text>      <Text style={{ backgroundColor: 'yellow' }}>line 2!</Text>      <ScrollView maximumZoomScale={2} minimumZoomScale={0.5}>        <Image          style={{ width: 250, height: 250 }}          source={{ uri: 'http://e.hiphotos.baidu.com/baike/pic/item/ac6eddc451da81cb6de1cb4d5a66d0160924312e.jpg' }}        />      </ScrollView>      <Image        style={{ width: 250, height: 250 }}        source={{ uri: 'http://e.hiphotos.baidu.com/baike/pic/item/ac6eddc451da81cb6de1cb4d5a66d0160924312e.jpg' }}      />      <Image        style={{ width: 250, height: 250 }}        source={{ uri: 'http://e.hiphotos.baidu.com/baike/pic/item/ac6eddc451da81cb6de1cb4d5a66d0160924312e.jpg' }}      />      <Image        style={{ width: 250, height: 250 }}        source={{ uri: 'http://e.hiphotos.baidu.com/baike/pic/item/ac6eddc451da81cb6de1cb4d5a66d0160924312e.jpg' }}      />    </ScrollView>

要注意,ScrollView 适合于子组件数量不多的情况,因为即使子组件没有在屏幕里面,也会被渲染出来,所以数量太多的话会有内存问题。这种场景适合使用 FlatListSectionList

FlatList & SectionList

FlatList 适合于渲染结构比较类似的数据,并且可以支持很长很长的数据,因为它只会渲染当前显示在屏幕里面的那部分元素,而不像 ScrollView 那样把所有数据都渲染出来。FlatList 有两个props,一个是data用于传递数据,一个是 renderItem 用于将 data 里的每一个元素渲染成格式化的组件。

<FlatList          data={[            {key: 'Devin'},            {key: 'Jackson'},            {key: 'James'},            {key: 'Joel'},            {key: 'John'},            {key: 'Jillian'},            {key: 'Jimmy'},            {key: 'Julie'},          ]}          renderItem={({item}) => <Text>{item.key}</Text>}        />

另外 SectionList 用于渲染需要将数据做逻辑划分并且每个部分有个标题的列表,比如通讯录里面按照字母划分成不同的 section

<SectionList          sections={[            {title: 'D', data: ['Devin']},            {title: 'J', data: ['Jackson', 'James', 'Jillian', 'Jimmy', 'Joel', 'John', 'Julie']},          ]}          renderItem={({item}) => <Text>{item}</Text>}          renderSectionHeader={({section}) => <Text>{section.title}</Text>}        />

参考资料:

  • https://facebook.github.io/react-native/docs/using-a-listview.html。
  • https://facebook.github.io/react-native/docs/flatlist.html。
  • https://facebook.github.io/react-native/docs/sectionlist.html。

ListView

老版本的 React Native 用 ListView 组件渲染列表,不过它会一次性渲染所有数据,所以有内存问题,现在已经 DEPRECATED 了,具体可参考这里。

今年 三月份Facebook发布了新的组件,就是前面讲的 FlatListSectionList,直接用这两个就好了。

样式

在 React Native 里写样式不需要另外的语言或者语法,就只需要用 JavaScript。每个核心组件都接受一个 prop: stylestyle 的使用方式跟 Web 里面使用 CSS 基本一样,不同的是 RN 里面因为是用 JS 写的,需要用 camelCase,如 backgroundColor,具体可参考这里。

style prop 可以是一个简单的 JS 对象,也可以是一个对象数组,后面的会把前面的样式覆盖掉,可以这样来继承样式。

import React, { Component } from 'react';import { StyleSheet, Text, View } from 'react-native';export default class LotsOfStyles extends Component {  render() {    return (      <View>        <Text style={styles.red}>just red</Text>        <Text style={styles.bigblue}>just bigblue</Text>        <Text style={[styles.bigblue, styles.red]}>bigblue, then red</Text>        <Text style={[styles.red, styles.bigblue]}>red, then bigblue</Text>      </View>    );  }}const styles = StyleSheet.create({  bigblue: {    color: 'blue',    fontWeight: 'bold',    fontSize: 30,  },  red: {    color: 'red',  },});
设置宽和高

通过给 style prop 设置固定的 widthheight 就可以实现。注意,RN 中不需要带单位(如 px),值代表的是 density-independent pixels,具体可参考这里。

import React, { Component } from 'react';import { View } from 'react-native';export default class FixedDimensionsBasics extends Component {  render() {    return (      <View>        <View style={{width: 50, height: 50, backgroundColor: 'powderblue'}} />        <View style={{width: 100, height: 100, backgroundColor: 'skyblue'}} />        <View style={{width: 150, height: 150, backgroundColor: 'steelblue'}} />      </View>    );  }}

这种方式主要用来设置那些不管屏幕多大都是固定大小的组件。如果需要组件大小根据屏幕大小而变化的话,需要用 flex

Flex

如果只有一个组件设置为 flex,则它会占据所有可用的空间。如果有多个组件平级,可以通过 flex 值的大小,按权重来分配组件占据的屏幕大小,flex 值越大占据的空间(宽或者高)越大,具体可参考这里。

注意:如果一个组件设置为 flex: 1,但是它的父组件既没有设置 flex: 1 又没有显示地设置 widthheight,则它占用空间为 0,即不会显示。

import React, { Component } from 'react';import { View } from 'react-native';export default class FlexDimensionsBasics extends Component {  render() {    return (      <View style={{flex: 1}}>        <View style={{flex: 1, backgroundColor: 'powderblue'}} />        <View style={{flex: 2, backgroundColor: 'skyblue'}} />        <View style={{flex: 3, backgroundColor: 'steelblue'}} />      </View>    );  }}

flex 其实是 Web 领域比较新的布局方式。

不过要注意, RN里 flexDirection 默认是 column 而不是 row

输入

APP 用户主要通过打字或者手势(如点击)来进行输入, 我们来看看两个主要的输入组件。

TextInput

相当于 Web 里面的 <input type="text" />onChangeText 属性接受一个函数,在输入内容变化的时候会被调用。

      <TextInput        style={{ width: 200, height: 40 }}        placeholder="输入sth"        onChangeText={text => Alert.alert('change: ' + text)}      />

参考资料:

  • https://facebook.github.io/react-native/docs/handling-text-input.html。
  • https://facebook.github.io/react-native/docs/textinput.html。
Button

相当于 Web里的 button。通过 color 属性指定颜色,通过 onPress 属性指定被点击后的回调函数。

<Button  onPress={() => { Alert.alert('You tapped the button!')}}  title="Press Me"  color="red"/>

参考资料:

  • https://facebook.github.io/react-native/docs/handling-touches.html。
  • https://facebook.github.io/react-native/docs/button.html。

网络请求

Fetch

RN 里面实现了 Web 里面的 Fetch API,可以很方面地发起网络请求。如果没有用过 fetch 的话,可以参考 MDN 的文档 Using Fetch。

发起网络请求

很简单, 直接传递一个 url 就可以了。

fetch('https://mywebsite.com/mydata.json')

fetch 的第二个参数是一个可选的对象,可以在里面指定请求方法(如 POST)、请求头、body 等。

fetch('https://mywebsite.com/endpoint/', {  method: 'POST',  headers: {    'Accept': 'application/json',    'Content-Type': 'application/json',  },  body: JSON.stringify({    name: 'magicly',    age: '25',  })})

完整的参数列表可以参考 Fetch Request文档。

处理响应

网络请求是一个典型的异步操作,所以 fetch 返回的是一个 Promise,比写回调方便很多。

function getMoviesFromApiAsync() {    return fetch('https://facebook.github.io/react-native/movies.json')      .then((response) => response.json())      .then((responseJson) => {        return responseJson.movies;      })      .catch((error) => {        console.error(error);      });  }

也可以直接使用 ES2017 里的 async/await 语法:

async function getMoviesFromApi() {    try {      let response = await fetch('https://facebook.github.io/react-native/movies.json');      let responseJson = await response.json();      return responseJson.movies;    } catch(error) {      console.error(error);    }  }

注意,现在 iOS 默认会阻止没有用 SSL 加密的请求,所以如果访问的接口是 HTTP 的话,需要添加 App Transport Security 例外。如果事先知道要访问哪些域名的接口,那只添加这些域名为例外会更安全,但是如果不能提前知道,那可能需要完全禁用 ATS。从 2017 年 1 月开始,禁用 ATS 在 提交审核的时候需要专门说明理由,更多内容可以参考 苹果的文档。

参考资料:https://facebook.github.io/react-native/docs/network.html。

使用其他网络库

RN 里面其实也实现了 XMLHttpRequest接口 的。这意味着你可以使用第三方库比如 frisbee 或者 axios,或者直接使用 XMLHttpRequest 接口。

const request = new XMLHttpRequest();request.onreadystatechange = (e) => {  if (request.readyState !== 4) {    return;  }  if (request.status === 200) {    console.log('success', request.responseText);  } else {    console.warn('error');  }};request.open('GET', 'https://mywebsite.com/endpoint/');request.send();

注意,不管是 fetch 还是 XMLHttpRequest,都没有跨域一说,因为这是原生应用而不是 web,所以根本就没有 CORS 一说。

WebSocket

RN 也是支持 WebSocket 的,拿来做实时应用(如 IM 或者游戏)很方便。如果真的要用 WS 开发实时应用的话, 强烈推荐一下 socket.io。

const ws = new WebSocket('ws://host.com/path');ws.onopen = () => {  // connection opened  ws.send('something'); // send a message};ws.onmessage = (e) => {  // a message was received  console.log(e.data);};ws.onerror = (e) => {  // an error occurred  console.log(e.message);};ws.onclose = (e) => {  // connection closed  console.log(e.code, e.reason);};

第一个 APP

有了前面的知识准备,我们现在来做一个简单的 APP:一个简单的图片“搜索引擎”。当然搜索引擎可不是在一个 APP 里能做出来的,这让我想起以前在网上论坛看到有人问:

我会 PHP,请问几天能做出一个百度那样的东西来呢?

下面有个牛人回复:

根本不用 PHP,5 分钟就可以做出来。写一个 HTML 页面,然后用 iframe 嵌入 baidu.com 就可以了。

我们今天就用类似的方式去做,不要觉得这很 low,互联网发展早期有一类工具叫“元搜索引擎”,就是调用多家搜索引擎的数据,然后整合返回给用户的。

获取数据

首先选择一家图片搜索引擎,国内当然是 百度图片 啦,不然用 Google 的话还得让用户想着怎么翻Q。

我们调选一张风景图,例如:

enter image description here

然后鼠标点击右键选择显示网页源码

enter image description here

然后对照着 DOM 结构分析,找到了数据部分:

enter image description here

这时候发现,图片数据的信息(包括 URL、宽和高等)就在 imgData 这个对象里,我们只需要用字符串截取出这段,然后用 JSON.parse 解析成 Javascript Object 就可以访问 thumbURLwidthheight 等字段了。

      const start = "app.setData('imgData', ";      const end = ']}';      const startIndex = html.indexOf(start);      const endIndex = html.indexOf(end, startIndex);      const dataStr = html.slice(startIndex + start.length, endIndex + end.length);      const data = JSON.parse(dataStr.replace(/'/g, '"')); // 这里是因为 data:image/jpeg 那部分百度用的是单引号',不是合法的 json

显示图片

接下来就只需要把前面获取到的图片显示出来就可以了。显示图片用 Image 组件:

          <Image            style={{ width: 200, height: 200 }}            source={{ uri: 'http://e.hiphotos.baidu.com/baike/pic/item/ac6eddc451da81cb6de1cb4d5a66d0160924312e.jpg' }}          />

这里要注意两点,一个是 source 里面传递的是一个 object,里面是 uri 不是 url 哦。 第二个是一定要记得设置 widthheight,否则是显示不出来的。

因为我们获取到的是很多张图片,所以用列表方式显示。由前面的介绍得知可以用 ScrollViewFlatList

        <ScrollView>          {            this.state.isLoading ? <ActivityIndicator />              :              <FlatList                data={this.state.imgs}                renderItem={({ item }) => {                  const img = item;                  if (!img.thumbURL) {                    return null;                  }                  return <Image                    style={{ width: this.width, height: this.width * img.height / img.width }}                    source={{ uri: img.thumbURL }}                  />                }}                keyExtractor={(item, index) => {                  return item.thumbURL;                }}              />          }        </ScrollView>

获取用户输入

如果我们只能显示默认的风景图照片,那怎么能叫搜索引擎呢。所以用 TextInput 和接收用户输入,用 Button 来触发搜索。

            <View style={{ flex: 1, flexDirection: 'row', }}>              <TextInput                style={{ width: this.width - 100, height: 40, borderColor: 'black', borderWidth: 1, }}                placeholder="汉源湖"                onChangeText={text => this.setState({ text })}              />              <Button                onPress={this.buttonHandler}                title="搜索"                color="#841584"              />            </View>

完整结果

下图就是我们的成果啦。

搜索汉源湖图片
搜索轿顶山图片

App.js 完整代码如下:

import React from 'react';import { StyleSheet, ActivityIndicator, Dimensions, SectionList, FlatList, ScrollView, Alert, Button, Text, View, Image, TextInput } from 'react-native';class App extends React.Component {  constructor(props) {    super(props);    this.state = {      text: '',      isLoading: true,      imgs: [],    }    this.width = Dimensions.get('window').width;    this.height = Dimensions.get('window').height;  }  componentDidMount() {    this.searchHandler();  }  async getImgsFromBaidu(query) {    try {      let response = await fetch(`https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&hs=0&xthttps=111111&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=${query}&oq=${query}&rsp=-1`);      let html = await response.text();      const start = "app.setData('imgData', ";      const end = ']}';      const startIndex = html.indexOf(start);      const endIndex = html.indexOf(end, startIndex);      const dataStr = html.slice(startIndex + start.length, endIndex + end.length);      const data = JSON.parse(dataStr.replace(/'/g, '"'));      return data    } catch (error) {      Alert.alert('error: ' + error)      console.error(error);    }  }  async searchHandler(query = '汉源湖') {    query = query || '汉源湖';// default parameter只处理undefined的情况    this.setState({      isLoading: true,    });    const data = await this.getImgsFromBaidu(query);    const imgs = data.data.map(e => {      delete e.base64;      return e;    }).filter(e => !!e.thumbURL);    this.setState({      isLoading: false,      imgs,    })  }  buttonHandler = () => {    this.searchHandler(this.state.text);  }  render() {    return (      <View >        <ScrollView>          <View style={{ height: 20 }} />          <View>            <View style={{ flex: 1, flexDirection: 'row', }}>              <TextInput style={{                width: 300,                height: 40,                borderColor: 'black',                borderWidth: 1,              }}                placeholder="汉源湖"                onChangeText={text => this.setState({ text })}              />              <Button                onPress={this.buttonHandler}                title="搜索"                color="#841584"              />            </View>          </View>          {            this.state.isLoading ? <ActivityIndicator />              :              <FlatList                data={this.state.imgs}                renderItem={({ item }) => {                  const img = item;                  if (!img.thumbURL) {                    return null;                  }                  return <Image                    style={{ width: this.width, height: this.width * img.height / img.width }}                    source={{ uri: img.thumbURL }}                  />                }}                keyExtractor={(item, index) => {                  return item.thumbURL;                }}              />          }        </ScrollView>      </View>    );  }}const styles = StyleSheet.create({  container: {    flex: 1,    backgroundColor: 'white',    alignItems: 'center',    justifyContent: 'center',  },});export default App;

RN 开发 APP 还是蛮快的嘛,而且这段代码支持 Android 和 iOS 哦。

单击这里查看完整代码。

接下来,我们详细看看 RN 里面支持哪些 CSS 的样式和布局。

第03课:样式和布局
第04课:React Native 内置 API 和组件介绍
第05课:导航 Navigation
第06课:使用 Native 代码之 iOS
第07课:使用 Native 代码之 Android
第08课:状态管理 Redux
第09课:动画
第10课:项目实战一
第11课:Debugging
第12课:静态代码检查 Flow
第13课:测试 Jest
第14课:性能分析与优化
第15课:打包发布 & 热更新
第16课:项目实战二

阅读全文: http://gitbook.cn/gitchat/column/5a17c2e113c02f4a35ca5a7d

React Native 移动开发入门与实战相关推荐

  1. React Native移动开发实战-4-Android平台的适配

    打开Android开发工具Android Studio,选择菜单 Open an existing AndroidStudio project,打开ch04项目的android文件夹,如图5.8所示. ...

  2. 视频教程-React 全家桶从入门到实战到源码-其他

    React 全家桶从入门到实战到源码 上市公司前端开发工程师,专注于 React 技术栈,对 React 全家桶从 react-router 路由到 Redux 状态管理工具再到 webpack 打包 ...

  3. 【苹果家庭推iiMessage】React Native举行开发仍是iOS,用原生的代码实现类似webview的页面

    推荐内容IMESSGAE相关 作者推荐内容 iMessage苹果推软件 *** 点击即可查看作者要求内容信息 作者推荐内容 1.家庭推内容 *** 点击即可查看作者要求内容信息 作者推荐内容 2.相册 ...

  4. 《Android 开发入门与实战(第二版)》——6.6节配置改变

    本节书摘来自异步社区<Android 开发入门与实战(第二版)>一书中的第6章,第6.6节配置改变,作者eoe移动开发者社区 组编 , 姚尚朗 , 靳岩,更多章节内容可以访问云栖社区&qu ...

  5. React Native组件开发指南

    React Native的组件开发一直处在一个比较尴尬的处境.在官方未给予相关示例与脚手架的情况下,社区中依然诞生了许许多多的React Native组件.因为缺少示例与规范,很多组件库仅含有一个in ...

  6. Android之Windows下搭建React Native Android开发环境(差不多搞了一天)

    Android之Windows下搭建React Native Android开发环境               穷逼买不起mac,但是他们都说React Native Android只能在mac下面 ...

  7. BDD敏捷开发入门与实战

    BDD敏捷开发入门与实战 1.BDD的来由 2003年,Dan North首先提出了BDD的概念,并在随后开发出了JBehave框架.在Dan North博客上介绍BDD的文章中,说到了BDD的想法是 ...

  8. android开发入门与实践_我的新书《Android App开发入门与实战》已经出版

    前言 工作之余喜欢在CSDN平台上写一些技术文章,算下时间也有两三年了.写文章的目的一方面是自己对技术的总结,另一方面也是将平时遇到的问题和解决方案与大家分享,还有就是在这个平台上能和大家共同交流. ...

  9. Android自定义控件开发入门与实战(1)绘图基础

    今天从leader那里拿到了启舰大神写的<自定义控件开发入门与实战>这本书,据说看完了,至少写起自定义view也不会慌. 最重要的是多练,所以这本书基本设计到的我没有涉及过的控件开发(之前 ...

最新文章

  1. C语言 字符串和字符串数组动态分配及赋值
  2. Linux 之七 SSH、SSL、OpenSSH、OpenSSL、LibreSSL
  3. js进阶 13-8 jquery如何实现侧边栏
  4. 波特率_不同波特率CAN总线系统如何进行数据收发
  5. Amazon Alexa 新里程碑: 50000 个功能、 20000 种设备、 3500 个品牌
  6. 数据结构与算法-- 二叉树中和为某一值的路径
  7. 啪啪三国2获取服务器信息,啪啪三国2怎么快速的获得资源?
  8. IE10 访问 ASP.NET 站点的问题
  9. JavaScript Break 和 Continue 语句
  10. R︱Softmax Regression建模 (MNIST 手写体识别和文档多分类应用)
  11. [新整理] CAD高级模拟考题
  12. 拥有PMP/ITIL/Prince2证书,你将享受这些国家福利
  13. java 多线程高级,java 多线程高级(传统多线程)
  14. PayPal集成标准版案例(asp.net)关键源码
  15. 百度收录静态html吗,百度收录越多,网站排名就越高吗?
  16. 服务器的ip端口加密协议混淆,Obfsproxy - 混淆/加密端口数据
  17. 华清远见嵌入式开发工程师2022
  18. 计算机中的英语六级作文万能模板,英语六级作文万能模板句子
  19. Swift5.1 语言参考(三) 类型
  20. iphone13支持双卡双待吗 苹果13是5g吗

热门文章

  1. Linux服务器上设置全局代理访问外网并验证
  2. suse linux 11 xdm图形化,suse xdm 设置
  3. Logstash的grok正则匹配自定义
  4. 工程制图——尺寸标注
  5. C语言求斜边程序,用C语言编写勾股定理求斜边
  6. 2022-2028全球与中国WiFi拦截器市场现状及未来发展趋势
  7. 形容计算机专业的诗句,描写技术精湛的诗句
  8. 事件抽取文献整理(2018)
  9. 屏幕关闭原因以及p-sensor
  10. 快速求一个字符串的非空子串(不相同)的数量