Yongjz's home

走在成为全栈程序猿的路上


  • 首页

  • 归档

express源码解析

发表于 2016-03-29   |  

express源码解析(转)

express是nodejs平台上一个非常流行的框架,4.2.0是最新的版本,相比3.x版本优化了代码和api,去除了connect模块,自己实现了一个router组件,实现http请求的顺序流程处理,去除了很多绑定的中间件,使代码更清晰。

1.使用express

如何使用express在官网有很好的讲解,只用experssjs实例app的几个函数,就可以构建构建web程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var express = require('express');
var logger = require('morgan');

var app = express();

//app.engine('html', require('ejs').renderFile);
app.use('/public',express.static(__dirname + '/public'));
app.use(logger());
app.get('/', function(req, res){
res.send('Hello World');
});

var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});

上面是一个简单的web程序,返回浏览器hello world,就几个步骤,获取express实例对象,加入需要的中间件,加入路由响应,启动服务器,很简单吧,相比java,.net的框架轻量了很多,而且不需要单独架设web服务器,利用nodejs的异步非阻塞机制,可以大大提高网站的并发量。

1.1中间件

app.use 加入中间件,所谓中间件其实就是java,.net平台MVC框架都会有的filter。
app.use(["path"],function(req,res,next){}) 有两个参数,path代表route路径,可选,为空表示匹配所有路径,后面是回调函数,需要添加一个next参数,执行时,框架将传入一个next函数,调用它启动下一个中间件,下面是一个中间件的示例

1
2
3
4
5
6
7
8
9
app.use('/public',express.static(__dirname + '/public'));
app.use(logger());
app.use(function(req, res, next){
console.log('hello middleware');
next();
});
app.get('/', function(req, res){
res.send('Hello World');
});

我们添加了一个自定义的中间件,打印出 hello middleware 从上面我们可以看出app.use把中间件加入一个栈中,http request将触发整个中间件链条,并依次执行(通过 next() 函数实现),功能类似于filter,但其作用却大于filter,它可以动态地给req,res添加内容。margon是一个日志记录的包,记录每个request的信息。express.static()是express保留的唯一个内置中间件,对/pulic路径下的route导向静态资源文件,不调用next。这样中间件就可以实现对指定或所有路径request和response的处理。

1.2 app.get()/app.VERB()

app.get有两个功能,第一次看express文档时都会很疑惑,app.get可以获取app.set设置的全局变量,也可以设置路由的处理函数,下面是get实现的源码,对js不是很熟悉的人会很纠结,代码里找不到get函数啊,app.get和app[‘get’]的方式都可以定义对象的函数,下面是其实现的源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>application.js
/**
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
*/
methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);

this.lazyrouter();

var route = this._router.route(path);
route[method].apply(route, [].slice.call(arguments, 1));
return this;
};
});

methods是一个数组,存储了http所有请求的类型,在method模块里定义,除了基本的get、port请求外,还有多达十几种请求,可能是为了兼容新的http标准吧。app[method]中,method==’get’且只有一个参数,则执行set,执行的是获取变量的功能,否则,执行app.get('path',function(req,res){})中path对应的回调函数,执行route组件的get方法(实现方式和这里一样),将route和回调存储进一个栈中。http请求触发执行,app.get也将产生一条路由中间件,执行后返回浏览器html页面。

module.exports = [ 'get', 'post', 'put', 'head', 'delete', 'options', 'trace', 'copy', 'lock', 'mkcol', 'move', 'purge', 'propfind', 'proppatch', 'unlock', 'report', 'mkactivity', 'checkout', 'merge', 'm-search', 'notify', 'subscribe', 'unsubscribe', 'patch', 'search' ];


2.了解express4.2的结构

下面是express4.2的文件结构图:

img

  1. express.js和application.js是主要的框架文件,暴露了express的api。
  2. router文件夹下为router组件,负责中间件的插入和链式执行,具体讲解在下一章节。
  3. middleware下的init.js和query.js为两个中间件,init.js的作用是初始化request,response,看一下代码就能明白:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
exports.init = function(app){
return function expressInit(req, res, next){
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
req.res = res;
res.req = req;
req.next = next;

req.__proto__ = app.request;
res.__proto__ = app.response;

res.locals = res.locals || Object.create(null);

next();
};
};

query.js中间件的作用是格式化url,将url中的rquest参数剥离,储存到req.query中:

1
2
3
4
5
6
7
8
9
10
11
module.exports = function query(options){
return function query(req, res, next){
if (!req.query) {
req.query = ~req.url.indexOf('?')
? qs.parse(parseUrl(req).query, options)
: {};
}

next();
};
};
  1. request.js和response.js, 提供了一些方法丰富request和response实例的功能,在init.js中初始化了http的req和res实例。req.\__proto__ = app.request;res.\__proto__ = app.response;
  2. view.js提供模板渲染引擎的封装,通过res.render()调用引擎渲染网页,具体请看第五章

3.Router组件

Router组件由三个文件组成,index.js为主文件,route.js主要功能是路由处理,layer保存中间件的数据结构,Router组件实例化后的对象如下图所示,stack为中间件栈:这是第一章里代码执行时的对象结构图,我们可以看到route存储了五个中间件,包含两个默认的query和expressInit组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{ [Function: router]
params: {},
_params: [],
caseSensitive: false,
strict: false,
stack:
[ { keys: [], regexp: /^\/?(?=/|$)/i, handle: [Function: query] },
{ keys: [],
regexp: /^\/?(?=/|$)/i,
handle: [Function: expressInit] },

{ keys: [],
regexp: /^\/public\/?(?=/|$)/i,
handle: [Function: staticMiddleware] },

{ keys: [], regexp: /^\/?(?=/|$)/i, handle: [Function: logger] },
{ keys: [], regexp: /^\/?(?=/|$)/i, handle: [Function] } ]

}

下面是Route的实例,stack为其http.verb的method和响应函数对, 如下图所示,”/“为一条路由的路径,接受method为get的http请求。

1
2
3
4
{ path: '/',
stack: [ { method: 'get', handle: [Function] } ],
methods: { get: true }
}
  • index.js主要处理中间件的执行,包括中间的插入,错误处理,执行(handle)等
  • route.js主要处理路由信息,每条路由都会生成一个Route实例,通过index.js里的proto.route(path)方法可以创建一个path对应的Route实例,并封装在layer中,加入中间件栈。另外Route.get (Route[‘get’]) 方法也是在这里动态生成的。
  • layer.js是中间件的存储结构。

看router.stack的最后一条,发现它的handle是一个无名的function,看了源码你就会知道,这个无名funtion就是路由’/‘对应的处理函数,每条路由都会作为一个中间件加入栈中。

我们每次调用app.get()就新建了一个Route实例(见1.2节代码),调用链条为app['get']=>router.Route['get']。

如下代码,调用Route['get'],Route中将会将get标示加入self.methods中,防止重复定义,然后生成一个数据项加入self.stack,数据项{ method: 'get', handle: [Function] }含method标示和路由处理函数fn。

在1.2节代码中,app.get()函数将Route实例封装在layer中,作为一个中间件加入栈中,当触发执行时,会将处理函数fn取出执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>route.js
methods.forEach(function(method){
Route.prototype[method] = function(){
var self = this;
var callbacks = utils.flatten([].slice.call(arguments));

callbacks.forEach(function(fn) {
if (typeof fn !== 'function') {
var type = {}.toString.call(fn);
var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
throw new Error(msg);
}

debug('%s %s', method, self.path);

if (!self.methods[method]) {
self.methods[method] = true;
}

if (!self.stack) {
self.stack = [];
}
else if (typeof self.stack === 'function') {
self.stack = [{ handle: self.stack }];
}

self.stack.push({ method: method, handle: fn });
});
return self;
};

4.中间件触发流程

4.1主要过程

中间件触发通过以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>express.js
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};

mixin(app, proto);
mixin(app, EventEmitter.prototype);

app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}

express模块返回一个app作为http.createServer()的回调函数,这样一个http请求将触发执行app.handle()执行中间件,下面我们看看app.handle()的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
app.handle = function(req, res, done) {
var env = this.get('env');

this._router.handle(req, res, function(err) {
if (done) {
return done(err);
}

// unhandled error
if (err) {
// default to 500
.........
return;
}

// 404
debug('default 404');
res.statusCode = 404;
res.setHeader('Content-Type', 'text/html');
if ('HEAD' == req.method) return res.end();
res.end('Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl) + '\n');
});
};

app.handle()调用了router组件的handle(req,res,fn)函数执行中间件,链式执行完所有中间件后,done函数是定义的错误处理函数,在htpp.createServer(function(res,req,done)中传入,下面将讲述express的核心route组件。

4.2 router组件

router组件主要有三个文件组成,index.js和route.js是其主要逻辑部分,layer.js作为中间件封装的数据结构。

下面的代码是route生成http.verb的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
methods.forEach(function(method){
Route.prototype[method] = function(){
var self = this;
var callbacks = utils.flatten([].slice.call(arguments));

callbacks.forEach(function(fn) {
if (typeof fn !== 'function') {
var type = {}.toString.call(fn);
var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
throw new Error(msg);
}

debug('%s %s', method, self.path);

if (!self.methods[method]) {
self.methods[method] = true;
}

if (!self.stack) {
self.stack = [];
}
else if (typeof self.stack === 'function') {
self.stack = [{ handle: self.stack }];
}

self.stack.push({ method: method, handle: fn });
});
//console.log(self)
return self;
};

1.2节中,application.js里的methods.each调用的就是这里生成http.verb处理函数,Reoute实例化的时候就生成了对应http.verb的处理函数(Route[‘method’])。

代码里可以看出,http.verb可以一次添加多个处理函数,形式为function(req,res,next)或者function(req,res).

下面是router组件构建的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  var proto = module.exports = function(options) {
options = options || {};


function router(req, res, next) {
router.handle(req, res, next);
}


// mixin Router class functions
router.__proto__ = proto;


router.params = {};
router._params = [];
router.caseSensitive = options.caseSensitive;
router.strict = options.strict;
router.stack = [];

return router;
};

router是一个对象构造函数,router.__proto__ = proto引入了整个proto的所有函数,包括use,handle等待相关中间件操作函数,定义了中间件储存的数组,配置等。

router完全可以作为一个对象定义为 var router ={},源码里,发现router没有进行实例化,所以这个构造函数式没有必要的。

下面我们看看router是如何触发链式执行的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 proto.handle = function(req, res, done) {
.............

// middleware and routes

var stack = self.stack;

// request-level next
var parent = req.next;
done = wrap(done, function(old, err) {
req.next = parent;
old(err);
});
req.next = next;

.............

next();


function next(err) {
.........
}
}

这里主要展示了中间件执行的过程,每调用一次next()就会有一个中间件触发,并再一次调用next(),看next里的代码:

1
2
3
if (route) {
return layer.handle(req, res, next);
}

layer是一个保存中间件路径,处理函数的数据结构,想详细了解请看源码,上面的代码表示如果路径和这个中间件配置的路径匹配,则执行其回调,next就是我们第一章开头讲的,下一个中间件触发的函数,现在应该知道这个函数从哪来了吧?

递归执行next,直到执行完为止,执行done,但是我们会发现done一直就是null,不知道这是不是老的nodejs遗留下来的问题,createServer的回调现在不会传入第三个参数了。

1
2
3
4
var layer = stack[idx++];
if (!layer) {
return done(err);
}

下面看看route.js 是如何运行的吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#router/index.js
proto.route = function(path){
var route = new Route(path);

var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));

layer.route = route;

this.stack.push(layer);
return route;
};

这个函数把一条路由和它的route.dispatch作为一个中间件加入了栈中,并返回一个Route实例,Route实例包含了路由处理的各种方法和信息,其中route.dipatch也是其原型函数,用来处理相同路由的不同http.verb,下面我们看看这个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 Route.prototype.dispatch = function(req, res, done){
var self = this;
var method = req.method.toLowerCase();

if (method === 'head' && !this.methods['head']) {
method = 'get';
}

req.route = self;

// single middleware route case
if (typeof this.stack === 'function') {
this.stack(req, res, done);
return;
}

var stack = self.stack;
if (!stack) {
return done();
}
//这里进行递归地链式调用,遍历所有的处理函数
var idx = 0;
(function next_layer(err) {
if (err && err === 'route') {
return done();
}

var layer = stack[idx++];
if (!layer) {
return done(err);
}

if (layer.method && layer.method !== method) {
return next_layer(err);
}

var arity = layer.handle.length;
if (err) {
if (arity < 4) {
return next_layer(err);
}

try {
layer.handle(err, req, res, next_layer);
} catch (err) {
next_layer(err);
}
return;
}

if (arity > 3) {
return next_layer();
}

try {
layer.handle(req, res, next_layer);
} catch (err) {
next_layer(err);
}
})();
};

这个函数很长,主要过程可以简单叙述一下,也是通过next_layer函数,链式访问一条路由的post,get等方法的回调函数,根据req.method来判断请求类型,执行相应处理函数,不同http.verb可以执行不同回调,也就是说,express一条路由可以响应多种类型的请求。但是注意到,这样回调函数应该写成function(req, res, next){ ..... next()}。
讲了很多,估计大家都昏头了,下面的流程图会很清晰的让大家知道整个过程。

5.View的实现

4.x版本的render和3.x版本不一样,这里以回调的方式进行render,而不在内部调用res.send()示例:

1
2
3
4
5
6
7
8
res.render('index', function(err, html){
....
res.send(html);
});

res.render('user', { name: 'Tobi' }, function(err, html){
// ...
});

res.render()的实现比中间件简单很多,总体来说,经过三次封装,进行了一些配置,调用链条为res.render() => app.render() =>view.render()=> require("jade")/reqiure("ejs").render(),首先看app.engine,将jade或ejs模板引擎的render函数存入了engines数组中

1
2
3
4
5
6
app.engine = function(ext, fn){
if ('function' != typeof fn) throw new Error('callback function required');
if ('.' != ext[0]) ext = '.' + ext;
this.engines[ext] = fn;
return this;
};

app.defaultConfiguration()(app初始化的一个函数),把View的构造函数保存。

1
2
// default configuration
this.set('view', View);

app.render()将其取出并调用,初始化一个View实例,并执行‘view.render()’渲染模板,注意初始化函数将engines传入了View实例,里面保存了模板引擎的render函数。

1
2
3
4
5
6
7
8
9
10
11
view = new (this.get('view'))(name, {
defaultEngine: this.get('view engine'),
root: this.get('views'),
engines: engines
});
.....
try {
view.render(opts, fn);
} catch (err) {
fn(err);
}

view.render()执行的便是模板引擎的render函数,fn为渲染完成后的回调函数。

1
2
3
View.prototype.render = function(options, fn){
this.engine(this.path, options, fn);
};

转自: https://gist.github.com/dlutwuwei/3faf88d535ac81c4e263

使用ES6语法重构React组件

发表于 2016-03-27   |  

使用ES6语法重构React组件

在Airbnb React/JSX Style Guide中,推荐使用ES6语法来编写react组件。下面总结一下使用ES6 class语法创建组件和以前使用React.createClass方法来创建组件的不同。

创建组件

ES6 class创建的组件语法更加简明,也更符合javascript。内部的方法不需要使用function关键字。

React.createClass

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';

const MyComponent = React.createClass({
render: function() {
return (
<div>以前的方式创建的组件</div>
);

}
});

export default MyComponent;

React.Component(ES6)

1
2
3
4
5
6
7
8
9
10
11
import React,{ Component } from 'react';

class MyComponent extends Component {
render() {
return (
<div>ES6方式创建的组件</div>
);
}
}

export default MyComponent;

props propTypes and getDefaultProps

  1. 使用React.Component创建组件,需要通过在constructor中调用super()将props传递给React.Component。另外react 0.13之后props必须是不可变的。
  2. 由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外。所以propTypes要写在组件外部。
  3. 对于之前的getDefaultProps方法,由于props不可变,所以现在被定义为一个属性,和propTypes一样,要定义在class外部。

React.createClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from 'react';

const MyComponent = React.createClass({
propTypes: {
nameProp: React.PropTypes.string
},
getDefaultProps() {
return {
nameProp: ''
};
},
render: function() {
return (
<div>以前的方式创建的组件</div>
);

}
});

export default MyComponent;

React.Component(ES6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React,{ Component } from 'react';

class MyComponent extends Component {
constructor(props) {
super(props);
}

render() {
return (
<div>ES6方式创建的组件</div>
);
}
}

MyComponent.propTypes = {
nameProp: React.PropTypes.string
};
MyComponent.defaultProps = {
nameProp: ''
};

export default MyComponent;

State

使用ES6 class语法创建组件,初始化state的工作要在constructor中完成。不需要再调用getInitialState方法。这种语法更加的符合JavaScript语言习惯。

React.createClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';

const MyComponent = React.createClass({
getInitialState: function() {
return { data: [] };
},

render: function() {
return (
<div>以前的方式创建的组件</div>
);

}
});

export default MyComponent;

React.Component(ES6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React,{ Component } from 'react';

class MyComponent extends Component {
constructor(props) {
super(props);
this.state = { data: [] };
}

render() {
return (
<div>ES6方式创建的组件</div>
);
}
}

export default MyComponent;

this

使用ES6 class语法创建组件, class中的方法不会自动将this绑定到实例中。必须使用 .bind(this)或者 箭头函数 =>来进行手动绑定。

React.createClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';

const MyComponent = React.createClass({
handleClick: function() {
console.log(this);
},
render: function() {
return (
<div onClick={this.handleClick}>以前的方式创建的组件</div>
);

}
});

export default MyComponent;

React.Component(ES6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React,{ Component } from 'react';

class MyComponent extends Component {
handleClick() {
console.log(this);
}

render() {
return (
<div onClick={this.handleClick.bind(this)}>ES6方式创建的组件</div>
);
}
}

export default MyComponent;

也可以将绑定方法写到constructor中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React,{ Component } from 'react';

class MyComponent extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
console.log(this);
}

render() {
return (
<div onClick={this.handleClick}>ES6方式创建的组件</div>
);
}
}

export default MyComponent;

或者使用箭头函数 => :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mport React,{ Component } from 'react';

class MyComponent extends Component {
handleClick = () => {
console.log(this);
}

render() {
return (
<div onClick={this.handleClick}>ES6方式创建的组件</div>
);
}
}

export default MyComponent;

Mixins

使用ES6语法来创建组件是不支持React mixins的,如果一定要使用React mixins就只能使用React.createClass方法来创建组件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React,{ Component } from 'react';

var SetIntervalMixin = {
doSomething: function() {
console.log('do somethis...');
},
};
const Contacts = React.createClass({
mixins: [SetIntervalMixin],

handleClick() {
this.doSomething(); //使用mixin
},
render() {
return (
<div onClick={this.handleClick}></div>
);

}
});

export default Contacts;

总结

总的来说使用ES6来创建组件的语法更加简洁,这种语法避免了过多的React样板代码,而更多的使用纯javascript语法,更符合javascript语法习惯。React官方并没有强制性要求使用哪种语法,根据需要合理的选择即可。

React Native配置和使用

发表于 2016-03-06   |  

React Native配置和使用(OS X)

个人比较喜欢react的函数式编程方式,native采用同样的方式写原生app,所以也开始学习react native,首先是环境配置以及跑起来第一个ios和android应用。

环境配置

  1. 操作系统使用mac os x进行开发。官方也是将os x作为最高优先级来支持,不过windows和linux可以进行Android开发。
  2. 推荐使用Homebrew来安装基本环境。
  3. 安装Nodejs
    • 直接官网下载安装包
    • 或者使用nvm命令行来安装
  4. 使用Homebrew安装watchman,命令行输入:brew install watchman
  5. 使用Homebrew安装flow,命令行输入:brew install flow
  6. 如果是使用homebrew进行的配置,命令行输入:brew update && brew upgrade 命令保持最新版环境。

ios开发环境

ios只需要从Appstore下载安装xcode7即可。

android开发环境

安装开发环境相比较要麻烦一些。

  1. 安装git,命令行输入brew install git。如果已经安装了xcode,则不需要安装。
  2. 安装最新的JDK
  3. 使用Homebrew安装android-sdk,命令行输入:brew install android-sdk
  4. 配置ANDROID_HOME环境变量:采用上述命令安装android-sdk,安装路径是/usr/local/opt/android-sdk,如果是别的方式安装的,则需要找到对应的安装目录。编辑~/.bashrc或者 ~/.bash_profile文件,添加以下代码:

    export ANDROID_HOME=/usr/local/opt/android-sdk
    
  5. 配置android sdk

    • 打开Android SDK Manager,命令行输入android 即可打开。根据官方网站的要求勾选需要的包:
      • Android SDK Build-tools version 23.0.1
      • Android 6.0 (API 23)
      • Android Support Repository
    • 选择需要安装的包,如下图:
      SDK Manager window
      SDK Manager window
      点击”Install Packages”即可。
    • 注意,可能会遇到一些坑
      • 要根据官网所要求的包进行安装,例如目前Android SDK Build-tools官网文档中要求版本是23.0.1,最新版本是23.0.2,要看清楚版本号进行安装。
      • Android Support Repository现在的名字变成Local Maven repository for Support Libraries
  6. 配置安卓模拟器
    官方建议使用Genymotion来安装配置模拟器。需要注册账号,下载个人免费版,然后启动程序需要安装VirtualBox等。
    个人觉得直接利用Android SDK Manager下载相应的包,启动谷歌自带模拟器更简单一点。配置过程如下:

    • 勾选安装包:
      • Intel x86 Atom System Image
      • Intel x86 Emulator Accelerator (HAXM installer)
        点击”Install Packages”
    • 配置HAXM
    • 创建一个模拟器( Android Virtual Device,AVD)
    • 命令行输入android avd,打开avd管理器,点击Create...新建一个模拟器,填写配置即可。
    • 选择已经建好的模拟器,点击Start...。
      SDK Manager window
      SDK Manager window

    注意:
    前面使用Android SDK Manager下载的HAXM其实并没有安装,只是下载到本地了,还需要手动安装一下,文件下载到了android-sdk目录下的extras文件夹中/usr/local/opt/android-sdk/extras/intel/Hardware_Accelerated_Execution_Manager

启动第一个应用

使用React Native命令行工具来创建应用

  • 安装React Native命令行工具,命令行输入:npm install -g react-native-cli。
  • 创建React Native项目,命令行输入:react-native init MyReactNativeApp。

运行ios app

  • 进入到初始化安装目录$ cd MyReactNativeApp。
  • 使用open .命令打开该目录,点击ios/MyReactNativeApp.xcodeproj项目文件,使用xcode打开该项目。
  • xcode中运行该项目,即可打开模拟器,看到初始化的ios app的效果。
  • 修改index.ios.js,然后在模拟器中使用command+r重新加载app。

运行android app

  • 运行之前设置好的安卓模拟器。
  • 进入到初始化安装目录$ cd MyReactNativeApp。
  • 命令行输入$ react-native run-android,运行该命令React Native会下载并使用gradle编译该项目。编译成功后,app即可在安卓模拟器中打开。(过程中会下载gradle,有时候命令行可能会卡住,多尝试几次即可)
  • 修改index.android.js,然后在模拟器中使用F2重新加载app。
  • 命令行中输入adb logcat *:S ReactNative:V ReactNativeJS:V,即可在命令行终端看到app运行的日志。
  • 注意:如果初始化的项目编译不通过,请检查Android SDK Manager中是否存在没有安装好或者版本不正确的package。
    SDK Manager window

我主要是参考官方文档进行的配置,在配置android环境过程中遇到了一些麻烦。

后续会继续更新。。。

How to use hexo

发表于 2016-03-05   |  

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

该教程是hexo自动生成的,之后会将hexo构建blog的过程详细的写出来。。。敬请期待

Yongjz

Yongjz

自由web开发者

4 日志
7 标签
github
© 2016 Yongjz
由 Hexo 强力驱动
主题 - NexT.Pisces