NPM 常用命令

npm是什么

资源包的管理器,安装NodeJS时会直接安装NPM

常用命令

$ 下载并安装node.js也就将npm直接安装

$ npm install npm@latest -g        // 更新npm

$ npm i [package_name]         // 安装包,将在当前目录创建一个node_modules目录,将下载的包保存到这个目录

$ npm update     // 获取最新的包并更新

$ npm uninstall [package_name]    // 卸载包,命令是将node_modules目录将包的文件删除

$ npm uninstall --save [package_name]    // 卸载包,将从package.json文件中删除依赖, 如果--save-dev安装就需要换成--save-dev

# npm i -g [package_name]    // 全局安装包,两种安装本地和全局安装,如果遇到acces错误权限问题,需要在前面加sudo,安装在/usr/local/lib/node_modules/中

$ npm update -g [package_name]    // 更新全局包

$ npm uninstall -g [package_name]    // 卸载全局包

$ npm init -y 快速创建package.json包

package.json参数介绍

name - 包名

version - 包的版本号

description - 包的描述

homepage - 包的官网 url 

author - 包的作者姓名

contributors - 包的其他贡献者姓名

dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下

repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上

main - main 字段是一个模块ID,它是一个指向你程序的主要项目。就是说,如果你包的名字叫 express,然后用户安装它,然后require("express")

keywords - 关键字

npm install 安装模块

npm i [package_name] --save-dev

1、-S, --save 安装包添加到dependencies(项目依赖)中

    npm i 包名 --save

    当发布时可以不会将node_module目录发布出去,可以将package.json发布出去,其它项目的人,可以通过npm install 下载dependenciess中的所有包内容.

    例:
        npm install gulp --save 或 npm install gulp -S

        "dependencies": {
            "gulp": "^1.0.0"
        }

2、-D, --save-dev 安装包信息将加入到devDependencies(开发阶段的依赖),所以开发阶段一般使用它

    npm install gulp --save-dev 或 npm install gulp -D

    "devDependencies": {
        "gulp": "^1.0.0"
    }


3、-O, --save-optional 安装包信息将加入到optionalDependencies(可选阶段的依赖)

    npm install gulp --save-optional 或 npm install gulp -O
    "optionalDependencies": {
        "gulp": "^1.0.0"
    }


4、-E, --save-exact 精确安装指定模块版本

    npm install gulp --save-exact 或 npm install gulp -E
    输入命令npm install gulp -ES,留意package.json 文件的 dependencies 字段,以看出版本号中的^消失了

    "dependencies": {
        "gulp": "3.9.1"
    }

npm i 选项–global,–save,–save-dev

-global: 简写 -g

    npm i express -g 为全局安装,这种就可以直接使用express命令, 否则会提示express不是内部或外部命令

-save: 简写 -S, 作用是在package.json的dependencies字段增加或修改安装包和版本号

-save-dev: 简写 -D, 是修改devDependencies, 这样就不用安装了某个包之后手动修改package.json

~ 与 ^ 版本

版本分为: 主版本号、次版本号、补丁版本号

"devDependencies": {
    "vue": "~2.2.2",            // 匹配最近小版本,如,会匹配所有的2.2.x版本,但最高不会匹配2.3.0
    "vue-router": "^2.2.0"        // 最近的一个大版本,所有 2.x.x但不不包括3.0.0,相当于 2.0.0 <= version < 3.0.0
}

npm ls 查看安装的模块

查看安装模块及依赖:npm ls -g

发布模块

使用以下命令在 npm 资源库中注册用户(使用邮箱注册):

$ npm adduser
Username: mcmohd
Password:
Email: (this IS public) xxxxxx@qq.com

来发布模块命令: $ npm publish

script属性

script属性定义的对应了一段shell脚本

1、npm start 启动模块

    该命令写在package.json文件scripts的start字段中,可以自定义命令来配置一个服务器环境和安装一系列的必要程序

    "scripts": {
        "start": "gulp -ws"
    }

2、npm stop 停止模块

3、npm restart 重新启动模块

常见错误

一、npm使用过程中遇到的 Cannot find module 'internal/fs',错误场景 gulp中执行任务报错,的确amd-optimize插件中的graceful-fs与gulp中graceful-fs的版本问题,

    解决:
        删除插件 npm uninstall graceful-fs
        安装  npm i graceful-fs --save

二、$ npm view react versions    查看包在npm所有版本

三、$ npm i vue@2.0.0 --save    安装指定版本

| 参考资料
| https://www.npmjs.com.cn/
| https://www.cnblogs.com/blackgan/p/7828047.html 常用命令
| http://www.alloyteam.com/2016/03/master-npm/
| https://github.com/ericdum/mujiang.info/issues/6/
| http://www.luckyonecn.com/blog/difference_between_dependencies_and_devdependencies_in_npm/

React Native之积累篇

IOS、Android的图标、app名称和启动图

一、App名称修改

    Xcode中打开项目下info.plist -> Bundle name 和 Bundle display name 都修改APP的名称

二、App图标

    方法1 Xcode中配置:

        Xcode工程目录下的Images.xcassets -> AppIcon中将来加载图标,分别为120x120、180x180

    方法2 代码中配置

        1、使用icon psd模板右键 Edit me and save图层 ——> 编辑内容 ——> 编辑完后保存 ——> 导出图片(窗口)——> 动作 ——> 右上角按钮选择载入动作将App Icon Template [3.3].atn文件载入 ——> 选择导出动作中有两种一个都是圆角图标一个是方形图标

        2、将导出的图标添加到项目中 找到ios/gzApp/Images.xcassets ——> 将所有导出的图标放到这个目录下

三、设置启动图

    方法1 Xcode中配置:

        Xcode工程下点击Images.xcassets -> 进入后 右键 App Icons & launch Image -> New IOS Launch Image -> 将启动图加载到各配置中(注意如果尺寸与当前不配置编译时会报错)

        在项目的General面板 Launch Images Source 选择刚才创建的launchImage, launch Screen File设置为空,App Icons Source就是图标的配置

    方法2 代码配置

        https://github.com/fbsamples/f8app/tree/master/ios/F8v2   // Base.lproj -> LaunchScreen.xib

        在ios文件夹 -> gzCar项目 -> Base.Iproj -> LaunchScreen.xib 使用文件替换,将Splash.png文件导入到项目中,启动


http://www.cnblogs.com/allenxieyusheng/p/5802179.html
http://www.jianshu.com/p/adpKye/
https://appicontemplate.com/ios9    图标尺寸导出  

转icon尺寸
http://www.zcool.com.cn/article/ZNjE1MTI=.html
http://makeappicon.com/
http://blog.csdn.net/leiyu231/article/details/52830151

fetch 请求提示netWork quest failed

在 Xcode 7.0 bata、ios9 中fetch默认https请求, 对http不起作用,需要修改info.plist配置

1、在Info.plist中添加 NSAppTransportSecurity 类型 Dictionary ; 
2、在 NSAppTransportSecurity 下添加 NSAllowsArbitraryLoads 类型Boolean ,值设为 YES;

http://blog.csdn.net/liyijun4114/article/details/51792179

网络图片无法加载问题

在iOS9之后,网络请求默认为Https请求,如需支持Http,修改info.plist文件添加键值对设置允许http访问。

在App Transport Security Settings中添加 Allow Arbitrary Loads设置为 YES

内测平台

https://www.pgyer.com/      蒲公英将测试包分发给多人的设备上进行测试

    在Xcode中倒出一个应用包,通过蒲公英上传进行分发多个设备

https://developer.apple.com/  苹果开发者账号申请

苹果开发者平台可以进行

苹果的安装包为 *.ipa 文件

1、先登录Account,注册苹果开发者账号个人或企业

2、Xcode 打包IOS文件 https://www.pgyer.com/doc/view/app_upload

代码类

一、render中调用方法将View返回

    class TabPage extends Component {

        _createNav(name, colorValue){
            return (
                <View>
                    <Text style={{color: colorValue}}>{name}</Text>
                </View>
            )
        }

        render(){
            return (
                <View>
                    {this._createNav('siguang', '#ddd')}
                </View>
            )
        }
    }

| 参考资料
| react.parts/native
|

Weex

Native调研

目前主流的应用大体分成三类:Native App, Web App, Hybrid App.

三类主流的优点

1、native App

    性能好
    用户体验也比较好
    开发成本高,无法跨平台
    升级困难(审核),维护成本高

2、Web App特点:

    开发成本低,更新快,版本升级容易,自动升级
    跨平台,Write Once , Run Anywhere
    无法调用系统级的API
    临时入口,用户留存度低
    性能差,体验差,设计受限制
    相比Native App,Web App体验中受限于以上5个因素:网络环境,渲染性能,平台特性,受限于浏览器,系统限制。

3、Hybrid App(混合app)特点:

    Native App 和 Web App 折中的方案,保留了 Native App 和 Web App 的优点。
    但是还是性能差。页面渲染效率低,在Webview中绘制界面,实现动画,资源消耗都比较大,受限于技术,网速等因素

weex

weex是阿里巴巴公司与2016年6月开源的一种用于构建移动跨平台的UI框架

特点:

    1、一次性编写代码多端运行,

    2、使用的vue的语法,RN使用的react语法

    3、轻量级,语法简单,易于使用

    4、可扩展,丰富内置组件,可扩展的API

    5、High Performance: 高性能


缺点: 只调研IOS

    1、weex开源比较晚资料也比较少,资料也不太容易看懂;

    2、没有对硬件调用的直接接口,需要swift进行modul的扩展();

    3、支持的组件也不完善;

    3、开发完成后对xcode不了解,所以在生成项目这块和导出包这块一直也没有解决(github上有人将创建了一个完整的项目);


weex流程:

    需要xcode、cocoapods、

    创建weex-cli  =>  




基于JS开发框架:

    weex基于vue.js


性能对比:


学习成本:

1.环境配置:

    ReactNative需要按照文档安装配置很多依赖的工具,相对比较麻烦。 weex安装cli之后就可以使用


2.vue vs react:

    react模板JSX学习使用有一定的成本 vue更接近常用的web开发方式,模板就是普通的html,数据绑定使用mustache风格,样式直接使用css

weex 使用

1、手机下载

    Weex Playground  可以通过二维码来看服务 


集成IOS

需要安装 iOS 开发环境 和 CocoaPods  


2、Cocoapods安装  http://blog.csdn.net/showhilllee/article/details/38398119/

    需要Ruby环境,Mac本身就自带Ruby

    $ sudo gem install cocoapods

思考:

1、与web app 相比 navtive是不是可以不用考虑页面兼容问题(不同设备和系统)

2、cocoapads,xcode的一些处理,打包是否不用处理,还是只考虑写vue这块的内容就可以

3、weex怎么调用硬件设备

4、如果是原生与weex结合怎么来进行通信

5、weex不适用于做什么,和优、缺点是什么

|
| https://weex.apache.org/cn/ weex官网
| https://market.dotwe.org/ext/list.htm#6
| https://github.com/joggerplus/awesome-weex
| https://github.com/weexteam/weex-hackernews/blob/master/README.zh.md
| https://github.com/weexteam
| http://i.youku.com/i/UNDEzMDY2NjI0MA==/videos
| http://www.jianshu.com/p/cb6de4b85ea3
| http://www.apicloud.com/
| https://yq.aliyun.com/articles/57996 react native与weex
| http://div.io/topic/1478
| https://github.com/Elemefe/weex-vue-starter-kit
|
|
| http://reactnative.cn/ react native
| https://github.com/jondot/awesome-react-native

React Native之组件篇

组件列表
ListView - 用于展示数据列表

ScrollView - 带滚动条的容器

Fetch - 请求网络接口数据

TarBarIOS - 切换菜单

TouchableHighlight - 封装视图,可以将一些内容放到这个容器中方便处理点击


View - 窗口,相当于div

Image - 加载图片

Text - 展示文本

TextInput - 文本输入框

Button - 按钮

根据ios、android不同系统

定义一个兼容组件:
    var Component = Platform.select({
        ios: () => require('ComponentIOS'),
        android: () => require('ComponentAndroid'),
    })();

调用组件
    <Component />


样式兼容:
    import { Platform, StyleSheet } from 'react-native';
    var styles = StyleSheet.create({
        height: (Platform.OS === 'ios') ? 200 : 100,
    });

    import { Platform, StyleSheet } from 'react-native';
    var styles = StyleSheet.create({
        container: {
            flex: 1,
            ...Platform.select({
            ios: {
                backgroundColor: 'red',
            },
            android: {
                backgroundColor: 'blue',
            },
            }),
        },
    });

react-native 内置组件

需要在react-native对象进行解构 

import {View, TextInput, Text} from 'react-native'

View 用来布局的组件

<View style={{ flex: 1; justifyContent: 'center', alignItems: 'center}}>
    <Text>xxxxx</Text>
</View>

Text 显示文本内容

<Text>Component A</Text>

TextInput 文本输入框

属性: 

    autoCapitalize - 枚举类型,可选值有none,sentences,words,characters.当用户输入时,用于提示。

    placeholder - 占位符,在输入前显示的文本内容。

    value - 文本输入框的默认值。

    placeholdertTextColor - 占位符文本颜色。

    password - 如果为ture , 则是密码输入框,文本显示为***。

    multiline - 如果为true , 则是多行输入。

    editable - 如果为false , 文本框不可输入。其默认值事true。

    autoFocus - 如果为true, 将自动聚焦。

    clearButtonMode - 枚举类型,可选值有never,while-enditing , unless-editing,always.用于显示清除按钮。

    maxLength - 能够输入的最长字符数。

    enablesReturnKeyAutomatically : 如果值为true,表示没有文本时键盘是不能有返回键的。其默认值为false。

    returnKeyType - 枚举类型,可选值有default,go,google,join,next,route,search,send,yahoo,done,emergency-call。表示软键盘返回键显示的字符串。

事件:

    onChangeText - 当文本输入框的内容发生变化时,调用该函数。onChangeText接收一个文本的参数对象。

    onChange - 当文本变化时,调用该函数。

    onEndEditing - 当结束编辑时,调用该函数。

    onBlur - 失去焦点出发事件。

    onFocus - 获得焦点出发事件。

    onSubmitEditing : 当结束编辑后,点击键盘的提交按钮出发该事件。


<TextInput
    style={styles.inp}
    placeholder="none"
    autoCapitalize="none" />

Keyboard 控制 键盘的

KeyboardAvoidingView 键盘

用于解决手机上弹出键盘挡住视图,本组件可以自动根据键盘的位置,调整自身的position

Button 按钮

color: 颜色       disabel: 是否可用       onPress: 点击事件       title: 按钮文本

<Button
    onPress={onPressLearnMore}
    title="Learn More"
    color="#841584"
    accessibilityLabel="Learn more about this purple button" />

Image 图片 加载的两种方式

onLayout: 元素挂载或布局中改变调用      onLoad: 加载成功完成调用        onLoadEnd: 加载结束后,无论成功失败都调用

resizeMode:  cover:等比拉伸      strech:保持原有大小       contain:图片拉伸  充满空间

source: 图片的资源地址


1、require引用  使用静态图片资源

    <Image source={require('./components/images/i1.png')} style={{width: 100, height: 100}} resizeMode={'cover'} />

    注: 使用require静态资源,不允许拼接url <Image source={require('./img/my_icon'+'.png')} />


2、uri  使用网络图片资源

    <Image source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}} style={{width: 400, height: 400}} />


3、使用原生图片资源(ios中的图片)

    打开xCode 在工程 打开images xcassets目录,将图片vue-banner.jpg拖入到appicon下进行导入

    加载 <Image source={{uri: 'vue-banner'}} style={styles.imgStyle2} resizeMode="cover" />  重启项目


4、使用本地文件系统中的资源(相册里的图片)

TarBarIOS 选项卡插件 只能在根元素不能被任何元素包裹

TabBarIOS 属性

    barTintColor 标签栏背景色 

    tintColor 当前选中的标签颜色 

    translucent Boolean  是否标签栏半透明

    <TabBarIOS barTintColor="#ccc" tintColor="#fff">
        <TabBarIOS.Item> ... </TabBarIOS.Item>
    </TabBarIOS>


TabBarIOS.Item 属性

    title: 图标下面的标题文字

    badge: 右上角显示一个红色气泡 提示气泡

    icon: 显示的图标  

        icon={require('../images/i4.png')} 加载本地

        icon={{uri: base64Icon, scale: 3}} 加载其它 并且带缩放

    selectedIcon: 选中的图标

    selected: 是否为选中状态 selected: {this.state.selectName = 'blue'}

    onPress(): 按下

Fetch 网络请求

获取接口数据, 处理get或post的请求

Example:

    1、Get

    onPressLearnMore(){
        let that = this;
        const REQUEST_URL = 'http://goucai.diyicai.com/lottery/hisnumber.action?lotteryId=001&issueLen=20&d=1496242956216';

        fetch(REQUEST_URL)
            .then((response) => {
                return response.json();  //把response转为json
            })
            .then((responseData) => {   // 接收上面的转好的json
                debugger;
                let data = responseData[0].ernieDate;
                that.setState({lotter: data})
                console.log(data);
            })
            .catch((error) => {
                this.setState({
                    error: true,
                    errorInfo: error
                })
            })
            .done();
    }

    2、post:

    fetch('https://mywebsite.com/endpoint/', {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            firstParam: 'yourValue',
            secondParam: 'yourOtherValue',
        })
    })

FlatList 加载网络数据

SectionList 分组的列表组件

不需要分组可以使用FlatList, 如果需要分组使用SectionList

TouchableHighlight 封装视图 把一部分内容放到一个容器中可以方便处理

render: function () {
    return (
        <TouchableHighlight onPress={this._onPressButton} style={styles.mt40}>
            <View>
                <Text>{this.state.userName}</Text>
                <Image
                    style={styles.mt40}
                    source={require('./src/images/1.jpg')}
                />
            </View>                    
        </TouchableHighlight>
    );
}

Dimensions 获取设备屏幕的宽高

let winWidth = Dimensions.get('window').width;
let winHeight = Dimensions.get('window').height;

DatePickerIOS 日期选择器

date: 当前被选中的日期      mode: 选择器模式       onDateChange: 选中事件

maximumdDate: 可选的最大日期       minimumDate: 可选的最小日期        

minuteInterval: 可选的最小分钟     timeZoneOffsetInMinutes: 时区差,单位分钟

TimePickerAndroid Android时间选择器的对话框

ActivityIndicator loading加载图标

animating: 是否显示 true false

color: 图片颜色

size: 大小  small large
不是弹窗,而是整弹个层

animationType: 展示层的动画效果  slide 滑动   fade 淡入淡出    none 无

onRequestClose: 

alert、AlertIOS 对话框

Alert.alert(标题,内容,[{确定按钮和}]

<Text onPress={() => Alert.alert(
    '这里是弹层的标题',
    '这里是内容',
    [
        {text: '取消', onPress: () => console.log('Cancel Pressed!')},    // 显示的按钮和回调函数
        {text: '确定', onPress: () => console.log('OK Pressed!')},
    ]
)}>点击显示弹窗</Text>

StatusBar 状态栏组件

控制顶部运营商、电量位置的

属性: 

    animated bool   进行设置当状态栏的状态发生变化的时候是否需要加入动画。当前该动画支持backgroundColor,barStyle和hidden属性

    hidden  bool  进行设置状态栏是否隐藏(最顶部的那条栏)

    backgroundColor   color类型,仅支持Android设备,设置状态栏的背景颜色

    translucent bool类型,仅支持Android设备, 进行设置状态栏是否为透明。当状态栏的值为true的时候,应用将会在状态栏下面进行绘制显示。这样在Android平台上面就是沉浸式的效果,可以达到Android和iOS应用一致性效果。该值常常配置半透明效果的状态栏颜色一起使用

    barStyle  enum('default','light-content')  枚举类型,仅支持iOS设备。进行设置状态栏文字的颜色

    networkActivityIndicatorVisible   bool类型,仅支持iOS设备。设置状态栏上面的网络进度加载器是否进行显示

    showHideTransition   enum('fade','slide') 枚举类型,仅支持iOS设备。进行设置当隐藏或者显示状态栏的时候的动画效果。默认值为'fade'

Picker PickerIOS 象select的选择菜单

1、selectedValue: 选中后的值

2、onValueChange: 触发事件

3、PickerIOS.Item: 定义菜单的先项

<PickerIOS
    style={{width:200}}
    selectedValue={this.state.language}
    onValueChange={(lang) => this.setState({language: lang})}>
    <PickerIOS.Item label="Java" value="java" />
    <PickerIOS.Item label="JavaScript" value="js" />
</PickerIOS>
<Text>当前选择的是:{this.state.language}</Text>

Slider 选择一个范围的组件, 滑块

属性:

    disabled    bool    如果为true,用户无法移动这个滑动条。默认值为false

    maximumValue    number    滑动条最大值。默认为1

    minimumValue    number    滑动条最小值,默认为0

    onSlidingComplete    function    当用户完成值的改变被回调的方法

    onValueChange    function    当用户正在滑动滑动条持续回调的方法

    step    number    滑动条的最小单位。这个值应该在0到最大值-最小值之间。默认为0

    testID    string    

    value    number    滑动条选择的值。这个值应该在最小值和最大值之间,它们的默认值分别为0和1。默认值为0

Switch 开关滑块组件

disabled 是否禁用

onValueChange 当值改变时回调

value 开关是否打开,默认false

onTintColor: 开启状态背景颜色

tintColor: 关闭状态时边框颜色或背景色

thumbTintColor: 开关上按钮的颜色     

testID

ScrollView 可滚动的容器,可以放多个组件和视图

ListView 用于垂直滚动列表,用于元素之间结构近似而仅数据不同, listView不立即渲染所有元素, 优先渲染屏幕可见元素

ListView继承了ScrollView所以有ScrollView下所有的属性

ListView属性:

    dataSource - 列表数据源

    renderRow - 解析每一条的数据的展示结构样式

    rowHasChanged - 函数也是ListView的必需属性。这里我们只是简单的比较两行数据是否是同一个数据(===符号只比较基本类型数据的值,和引用类型的地址)来判断某行数据是否变化了。

    scrollView - automaticallyAdjustContentInsets={false} 如果不设置这个属性顶部会一个块留白

ListView方法:

    ListView.DataSource - 用于比对两条数据是否相同,如果不相同进行渲染


Example:

    class DemoComponent extends Component {
        constructor(props){
            super(props);
            const ds = new ListView.DataSource({
                rowHasChanged: (r1, r2) => r1 !== r2
            });

            this.state = {
                dataSource: ds.cloneWithRows([
                    {
                        img: require('../../images/car-img.png'),
                        title: '福特福克斯2011款 两厢 1.8L 自动时尚型'
                    },
                    {
                        img: require('../../images/car-img.png'),
                        title: '福特福克斯2011款 两厢 1.8L 自动时尚型'
                    },
                    {
                        img: require('../../images/car-img.png'),
                        title: '福特福克斯2011款 两厢 1.8L 自动时尚型'
                    }               
                ])
            }
        }

        // 返回每一条数据的结构
        carRenderRow(rowData){
            return (
                <View style={styles.carList}>
                    <Image source={rowData.img} style={styles.carPicture} />
                    <Text style={styles.carFont1}>{rowData.title}</Text>                  
                </View>
            )
        }

        render(){
            return (
                <View style={{flex: 1, marginBottom: 60}}>
                    <ListView
                        dataSource={this.state.dataSource}
                        renderRow={this.carRenderRow} />             
                </View>
            )
        }
    }

RefreshControl 下拉刷新

PullToRefreshAndroidView 也是实现下拉刷新的组件

属性方法

    onRefresh  function方法 当视图开始刷新的时候调用

    refreshing  bool  决定加载进去指示器是否为活跃状态,也表名当前是否在刷新中

    colors [ColorPropType]   android平台适用  进行设置加载进去指示器的颜色,至少设置一种,最好可以设置4种

    enabled  bool   android平台适用   用来设置下拉刷新功能是否可用

    progressBackgroundColor ColorPropType  设置加载进度指示器的背景颜色

    size RefreshLayoutConsts.SIZE.DEFAULT  android平台适用  加载进度指示器的尺寸大小 ,具体可以查看RefreshControl.SIZE(详细点击进入)

    tintColor ColorPropType   iOS平台适用  设置加载进度指示器的颜色

    title string iOS平台适用  设置加载进度指示器下面的标题文本信息

AppState、AppStateIOS 获取应用当前是在前台还是在后台,并且能在状态变化的时候通知你

AppState.currentState 返回的值:

    active - 表示当前App在前台运行

    background - 应用正在后台运行。用户既可能在别的应用中,也可能在桌面。

    inactive - 该状态我们可以暂时不考虑,因为该状态不会出现在现在React Native App中。

AppRegistry 运行RN的入口

AppRegistry.registerComponent('exampleApp', () => exampleApp);

AsyncStorage 简单的、未加密的、异步的、持久化的Key-Value存储系统

一、方法: anyncStroage每个方法返回的都是一个Promise

    getItem(key, callback) - 获取数据

    setItem(key, value, callback) - 存储数据

    removeItem(key, callback) - 删除数据

    mergeItem(key, value, callback) - 合并两值

    clear() - 删除全部

    getAllKeys(callback) - 获取所有可访问到的数据

    flushGetRequest() - 清除所有进行中的查询操作

    multiGet(keys <Array>, callback) - 获取keys所包含所有的字段值

        AsyncStroage.multiGet(['user', 'enter'])
            .then((data) => {
                // 这里可以取到本地存储中的user和enter的值
                /*
                * data返回的是一个二维数组
                * [
                *   ["key1", "aaa"],
                *   ["key2", "bbb"]
                * ]
                */

                let user = JSON.parse(data);
            })

ProgressBarAndroid ProgressViewIOS 加载进度

属性:

    View 相关属性样式全部继承(例如:宽和高,背景颜色,边距等相关属性样式)

    progress number  设置进度值 (范围在0-1之间)

    progressTintColor  string  设置进度条本身的颜色

    progressViewStyle   enum('default','bar')  加载进度的风格   枚举类型 默认风格和Bar条类型

    trackImage   Image.propTypes.source    设置一个可以拉伸的图片,设置进度条剩下部分进度的图片

    trackTintColor  string   进度条的颜色(已经有进度的情况)

DrawerLayoutAndroid 抽屉 安卓

SegmentedControlIOS
ToolbarAndroid
TouchableNativeFeedback
TouchableOpacity
TouchableWithoutFeedback
ViewPagerAndroid
VirtualizedList
WebView

API
ActionSheetIOS
AdSupportIOS
Animated
BackAndroid
BackHandler 后退
CameraRoll 访问本地相册
Clipboard 读写剪贴板的内容
DatePickerAndroid Android日期选择器的对话框
Easing
Geolocation 地理位置
ImageEditor 根据URI剪裁对应的图片
ImagePickerIOS
ImageStore
InteractionManager
LayoutAnimation 当布局变化时,自动将视图运动到它们新的位置上
Linking 传入和传出的App链接进行交互
NativeMethodsMixin
NetInfo 获取设备联网或离线的状态信息
PanResponder 多点触摸操作协调成一个手势
PermissionsAndroid
PixelRatio 获取设备的像素密度的方法
PushNotificationIOS 处理应用的推送通知
Share 打开一个对话框来共享文本内容
StyleSheet 定义css样式
Systrace
ToastAndroid
Vibration 控制设备震动
布局样式属性
阴影样式属性


第三方组件

rnpm介绍 下载完第三方组件不能直接用,需要通过rnpm 直接组件的内容挂载到工程中去,需要执行$ rnpm link 模块名 ,与react-native link 相同

    安装rnpm  $ npm i rnpm -g

    link的原因:以下组件都是原生开发,wrapper了一层js,因此需要link .a到project中

$ react-native-elements   基础组件    https://github.com/react-native-community/react-native-elements#buttons


$ lodash 

$ query-string 

$ sha1      加密模块

$ react-native-button

$ react-native-audio      音频

$ react-native-video      视频  https://github.com/react-native-community/react-native-video

$ react-native-image-picker  摄像头和本地相册  https://github.com/marcshilling/react-native-image-picker

$ react-native-progress   进度条

$ react-native-storage    存储  https://github.com/sunnylqm/react-native-storage/blob/master/README-CHN.md

$ react-native-sk-countdown  倒计时

$ react-native-swiper     轮播 https://github.com/leecade/react-native-swiper

$ react-native-barcodescanner 扫二维码 Android   https://github.com/ideacreation/react-native-barcodescanner
                                      http://blog.csdn.net/liu__520/article/details/52889950

$ react-native-animatable  动画   https://github.com/oblador/react-native-animatable

$ react-native-device-info 设备信息  https://github.com/rebeccahughes/react-native-device-info
                                    http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/react-native-acquisition-device-information-component-react-native-device-info/


Geolocation  内置的地理位置 


http://www.jianshu.com/p/53ff78168acc       整理的插件
https://yq.aliyun.com/articles/78154?utm_campaign=wenzhang&utm_medium=article&utm_source=QQ-qun&201759&utm_content=m_20152
http://www.jianshu.com/p/e30feb098ef2

react-native-vector-icons 图标类

1、安装 npm i react-native-vector-icons --save && react-native link react-native-vector-icons

2、引用

    import FontAwesome from 'react-native-vector-icons/FontAwesome';        // 引用的FontAwesome文件
    /* 图标文件名
        Entypo
        EvilIcons 
        FontAwesome 
        Foundation 
        Ionicons 
        MaterialIcons
        MaterialCommunityIcons 
        Octicons 
        Zocial 
        SimpleLineIcons 
    */

    class myfirst extends Component {
        render() {
            return (
                <View style={styles.container}>
                    <FontAwesome name="address-book" size={20} color="#4F8EF7" />
                </View>
            )
        }
    }

3、注意: 这里icon对一些组件封装

    import Icon from 'react-native-vector-icons/Ionicons';
    return (
        <TabBarIOS>
            <Icon.TabBarItem        // 这里对TabBarItem进行了封装  或者ToolbarAndroid
                title="Home"
                iconName="ios-home-outline"     // 这里属性名为 iconName 而不是 icon
                selectedIconName="ios-home">    // 选中的
                <View style={styles.tabContent}><Text>Home Tab</Text></View>
            </Icon.TabBarItem>
        </TabBarIOS>
    );

https://github.com/oblador/react-native-vector-icons
http://ionicons.com/  图标官网
http://blog.csdn.net/uuq/article/details/53150841  使用方法  
https://github.com/oblador/react-native-vector-icons#installation   图标对应的name值 

mockjs

npm i mockjs --save

import Mock from 'mockjs'

| 参考资料
| react.parts/native // 第三方组件列表
|
| http://www.lcode.org/react-native/
| http://blog.csdn.net/hsbirenjie/article/category/6307126/3
| http://blog.csdn.net/jiangqq781931404/article/category/6055594/2
| http://www.hangge.com/blog/cache/category_76_1.html

| http://blog.csdn.net/mengks1987/article/category/6850683
| http://blog.csdn.net/yuanguozhengjust/article/category/6058018
|

Linux命令

命令格式

[root@localhost~]  root 管理员    localhost 主机名    ~ 用户的目录 $ 为普通用户 # root用户

1、命令 [选项] [参数]    选项可简化  -a 等于 --all

2、Linux中 .xxx文件 是隐藏文件

3、命令补全: 输入一个开头字母,按Tab会将当前目录下所有的相关文件显示

4、sudo 使用超级使用者权限

    nginx 目录: /usr/local/openresty/nginx 9092

    启动 sudo /usr/local/openresty/nginx/sbin/nginx

    重启 /usr/local/openresty/nginx/sbin/nginx -s reload

常用命令

使用 SecureCRT 工具

1、exit 或 logout 退出登录

2、文件上传和下载 到服务器

    $ sz 要下载的文件或目录   在secureCRT中设置 options -> session options -> x/z/zmodem 中设置的下载目录 

    $ rz 上传的文件            上传相同名文件,不会被替换,不能上传目录,可以直接发压缩gzip包在传

3、查看日志  使用tail 或 cat来查看文件内容

    $ tail -fn  显示行数  服务器的日志文件

    tail -fn 100  /opt/server/apache-tomcat-6.0.41_8084/logs/catalina.2016-01-21.log    56服务器8084端口

4、解决secureCRT 中文编码问题

    options ->    session options  -> terminal 下的 appearance  将 character encodeing 改成 utf-8

5、清理服务器缓存

    进入到apache-tomcat服务中的works中清楚loaclhost目录 

    $ cd /opt/server/apache-tomcat-6.0.41_8084/works/Catalina

    $ rm -rf  localhost

    8084:网站  apache-tomcat-6.0.41_8084

    8086:H5   apache-tomcat-7.0.47_8086

6、open 打开目录

7、clear 清除当前屏    cmd+k 清除所有

8、tree 打印出目录树

    mac下没有tree命令,需要安装  brew install tree

    $ tree -a   显示所有的文件和目录

    $ tree -L 1 -a  只显示一个层级的目录和文件(包括隐藏文件)

    $ tree -L 1 -d

文件权限

-rw-r--r--@   1 apple  staff   8196  5 15 19:26 .gitignore.js

一、文件类型    -rw-r--r--

    rw- u所有者    r-- g所属组    r-- o其它人

    r 读        w 写        x 执行

二、目录类型    drwxr-xr-x

    开头第一个字符

    1、- 文件    如果第一个符号为'-'就是一个普通文件

    2、d 目录 

    3、l 软链接文件

三、查目录权限 

    $ cd /

    $ ls -l         显示根目录所的权限

四、chown设置目录权限

    1、命令格式:

        chown [选项]... [所有者][:[组]] 文件...

        $ sudo chown apple:admin /data/db

    2.命令功能:

        通过chown改变文件的拥有者和群组。在更改文件的所有者或所属群组时,可以使用用户名称和用户识别码设置。普通用户不能将自己的文件改变成其他的拥有者。其操作权限一般为管理员。

    3.命令参数:

        -c 显示更改的部分的信息

        -f 忽略错误信息

        -h 修复符号链接

        -R 处理指定目录以及其子目录下的所有文件

        -v 显示详细的处理信息

        -deference 作用于符号链接的指向,而不是链接文件本身

        选择参数:

        --reference=<目录或文件> 把指定的目录/文件作为参考,把操作的文件/目录设置成参考文件/目录相同拥有者和群组

        --from=<当前用户:当前群组> 只有当前用户和群组跟指定的用户和群组相同时才进行改变

        --help 显示帮助信息

        --version 显示版本信息

目录处理

1、cd 切换到指定目录

    cd 与 cd ~  回到apple目录, 也就是 /Users/apple

    cd -   回到上次操作所在的目录

    cd ..  进入到上一级目录

2、mkdir [-p]  dirname 创建目录 

    -p 递归创建  mkdir -p parent/child    可以直接创建多个目录,如果不加就只能创建一个目录

3、pwd 显示当前路径

4、rm 删除文件或目录   rm -rf [文件或目录]

    -r  递归删除文件夹        -f  强制

5、cp 复制目录或文件   cp [选项][原文件或目录][目标目录]

    -r 递归复制文件夹

    -p 连带文件属性复制 一些类似文件创建日期等

    -d 如果源文件是链接文件,则复制链接属性

    -a 相当于 -pdr

6、mv 移动目录或文件    mv [源文件或目录] [目标目录]

    mv dir1 dir2 将文件或目录移动到指定目录中  

    mv a.js b.js 修改文件或目录名  

文件处理

1、touch .gitignore  创建文件

2、ls 显示文件

    # 显示详细列表
    # ls -l  显示详细信息   ll 是ls -l的简写
    # 
    $ ls -a 显示所有文件,包含隐藏文件(以. 起头的文件名)
    $ 
    $ ls -f 显示文件(后跟*)和目录(后跟/)

    SG:Linux siguang.liu$ ls -al
    total 128
    文件属性          文件数     拥有者                所属者group 文件大小 建档日期 文件名
    drwxr-xr-x  5         siguang.liu  staff        160          11:01  ..
    drwxr-xr-x  1         siguang.liu  staff        192          2016   Linux基本操作命令.doc

3、cat 查看文件内容

    cat package.json

4、echo 输出内容

5、ln 文件名   将文件的内容输出带行号

    $ ln package.json

6、wc 文件名  统计指定文件中的字节数、字数、行数,并将统计结果显示输出

    -c 统计字节数。

    -l 统计行数。

    -m 统计字符数。这个标志不能与 -c 标志一起使用。

    -w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串。

    -L 打印最长行的长度。

文件搜索

一、find [搜索范围] [搜索条件]  避免大范围搜索,耗费系统资源

    通配符: * 匹配任意内容      ? 匹配任意一个字符      [] 匹配任意一个中括号内的字符

    $ find / -name person.js     从根目录开始搜索person.js文件

    1、-name   搜索文件名   

    2、-iname  搜索的文件名不区分大小写

    3、-user   按所有者文件

    4、-nouser  没有所有者文件


    按时间搜索   $ find /var/log/ -mtime +10    搜索10天前修改的文件   +10 十天前     10 十天当天修改    -10  十天内修改文件

    5、-mtime  修改文件内容

    6、-atime  文件访问时间

    7、-ctime  改变文件属性


    按文件大小搜索   $ find . -size 25k  查找文件大小是25K的文件  -25k 小于25k   +25k 大于25k   25k 等于25k

        注意: M 是大写   k 用小写

    8、-size  查找文件大小

    9、-inum  通过 i 节点个数来查看   ls -i 查看当前目录下的文件带id   29560848 Bluetooth    31587257 opendirectoryd.log.0  31745680 system.log.4.gz

        find /var/log/ -inum  29560848   // 返回 Bluetooth

        逻辑运算符  -a 逻辑与    -o 逻辑或

        example   find /var/log -size +20K  -a  -size -50K    查找大于20K并小于50K

    10、-exec ... {} \;  将搜索的内容交给第二条命令来处理

         find /var/log -size +20k -a -size -500M  -exec ls -l -a {} \;


二、grep 在文件中匹配符合条件的字符串   grep [选项]  字符串   文件名

    -i 忽略大小写

    -v 排除指定字符串    

    $ grep -i 丝绸之路 package.json 


三、whereis 和 which  搜索系统命令

    whereis vim        // 输出 /usr/bin/vim 可以

    which 指令会在环境变量$PATH设置的目录里查找符合条件的文件。

        which mongod        // 返回当前mongod命令所在的目录 /usr/local/bin/mongod


四、locate 文件名   在后台数据库中按文件名搜索,搜索速度快

    locate不是在文件中搜索,而是在 /var/lib/mlocat 只在后台数据库搜索

    updatedb  更新数据库

ln 链接命令 ln -s [原文件或目录] [目录文件]

硬链接: 硬链接看做是文件或目录的副本,两个i节点和存储,可以看成是同一个文件

软链接: 软连接看做是文件或目录的快捷方式

选项:

-s 创建软链接    

    注意软链接要写绝对路径 ln -s /Users/apple/siguang.liu/linux/catalogB   /Users/apple/siguang.liu/linux/lnDir

    appledeMacBook-Pro-2:lnDir apple$ ls -il
    31874703 lrwxr-xr-x  1 apple  staff  39  5 16 18:54 catalogB -> /Users/apple/siguang.liu/linux/catalogB    这种 -> 就是指的软链接 或者 lrwxr第一个类型为『l』

删除软连接就去目标目录直接把软链接的目录直接删掉

压缩文件、目录

一、zip压缩文件、目录

    1、压缩文件     zip 压缩文件名  源文件        // 注意这里压缩zip,原文件不会被删除,而gzip会删除源文件

    2、压缩目录  zip -r 压缩文件名.zip   要压缩的目录       // 最好先进入要指定压缩的目录,如果写成a/b/c,这样就会将所有目录一层一层压缩,如果要压缩的目录在aa下面,而且你当前就在aa目录下面不用从指定根目录,否则解压后会一层一层的 

    3、解压缩    unzip 解压的文件

二、gzip压缩

    压缩后的扩展为 .gz  

    1、gzip 源文件      压缩为.gz文件,源文件会消失

    2、gzip -c 源文件 > 压缩 文件     压缩为.gz文件,保留源文件

    3、gzip -r 目录     压缩目录下所有子文件,但不压缩目录

    2、gunzip             解压文件名


三、tar压缩目录

    通过tar来对目录进行打包,然后通过gzip进行压缩

    $ 格式: tar -cvf  打包文件名  源文件

    1、打包成 .tar文件

        1) tar -cvf  压缩后的目录  要压缩的目录 

        2) tar -xcvf  打包的文件名

    2、打包成 .tar.gz格式

        1) tar -zcvf  压缩后的目录  要压缩的目录 

        2) tar -zxcvf  打包的文件名

        -c: 产生.tar打包文件

        -v: 显示详细信息

        -f: 指定压缩后缀名

        -z: 打包同时压缩

    3、当包解压到当前目录

        tar zxf  nginx-1.12.2.tar.gz

进程

所有程序只要运行就会产生一个进程,客户端网页访问服务器就会产生一个进程

一、查看进程

    1、ps aux  系统所有进程

        ps -ef|grep nginx 查看nginx服务的进程

            a 显示所有进程,包括会话引线

            u 显示进程的归属用户及内存使用情况

            x 显示没有控制终端进程

            -l 长格式显示 显示更详细信息

            -e 显示所有进程,和-A作用一致

    2、pstree [选项]

        -p 显示进程PID

        -u 显示进程用户

    3、top  每3秒查看一下系统的健康,有cpu和内存的数据和所有进程


二、杀死进程

    kill -1 进程ID    重启进程

    kill -9 进程ID    强制杀死进程

网络

一、curl

https://www.cnblogs.com/duhuo/p/5695256.html

二、ping 

环境变量和host的修改

一、host文件

    $ vim etc/hosts

二、环境变量、export 设置或显示环境变量  export [-fnp][变量名称]=[变量设置值]

    -f  代表[变量名称]中为函数名称。 

    -n  删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。 

    -p  列出所有的shell赋予程序的环境变量。

    $ export   查看环境变量

三、设置环境变量有两种: 

    1、使用export命令 这种方法只是临时设置,重启终端就

        $ export PATH=$PATH:/opt/au1200_rm/build_tools/bin

    2、修改profile文件 或者设置到 source ~/.bash_profile 

        $ vi ~/.profile

            export PATH=$PATH:/usr/local/zend/mysql/bin/:/usr/local/zend/bin     // 设置$PATH 扩展一下加一下

        $ source ~/.profile    立即执行

    注意两个环境变量使用 “:”来分割


三、通过环境变量来设置命令快捷键

    $ vi ~/.profile

        alias ll="ls -a"
        alias ..="cd .."
        alias ...="cd ../../"
        alias ....="cd ../../../"
        alias .....="cd ../../../../"
        alias ......="cd ../../../../../"

man –help 帮助

一、man ls  查看帮助信息

二、ls --help   

系统

一、日期

    date 日期时间

    cal  显示当前日历

二、关机重启系统

    1、shutdown [选项] 时间   来进行重启和关机

        shutdown -r now    立即重启  -h 立即关机

        shutdown -r 18:00  定时在18点的时候重启

        -c: 取消前一个关机命令

        -h: 关机

        -r: 重启

    2、halt  回车  直接关机

    3、poweroff  回车  直接关机

    4、init 0 回车  直接关机

    5、reboot 回车  直接重启

三、查询与自动挂载

    1、mount

linux根目录

Linux的目录

    根目录下的 bin目录不允许操作, usr下的 sbin目录是可以操作的

    /bin 传统unix命令的存放目录,如ls,rm,mv等。
    /sbin 传统unix管理类命令存放目录,如fdisk,ifconfig等等。
    /tmp 临时文件存放目录,其权限为所有人任意读写。此目录实际为指向/private/tmp的链接。
    /usr 第三方程序安装目录。
        /usr/bin, /usr/sbin, /usr/lib,其中/usr/lib目录中存放了共享库(动态链接库).
    /etc. 标准unix系统配置文件存放目录,如用户密码文件/etc/passwd。此目录实际为指向/private/etc的链接。        
    /var 存放经常变化的文件,如日志文件。此目录实际为指向/private/var的链接。


OS X系统中,除了标准的unix目录外,还增加了特有的目录。

    /Applications 应用程序目录,默认所有的GUI应用程序都安装在这里;
    /Library 系统的数据文件、帮助文件、文档等等;
    /Network 网络节点存放目录;
    /System 他只包含一个名为Library的目录,这个子目录中存放了系统的绝大部分组件,如各种framework,以及内核模块,字体文件等等。
    /Users 存放用户的个人资料和配置。每个用户有自己的单独目录。
    /Volumes 文件系统挂载点存放目录。
    /cores 内核转储文件存放目录。当一个进程崩溃时,如果系统允许则会产生转储文件。
    /private 里面的子目录存放了/tmp, /var, /etc等链接目录的目标目录。


/bin是系统的一些指令。bin为binary的简写主要放置一些系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar等。

/sbin一般是指超级用户指令。主要放置一些系统管理的必备程式例如:cfdisk、dhcpcd、dump、e2fsck、fdisk、halt、ifconfig、ifup、 ifdown、init、insmod、lilo、lsmod、mke2fs、modprobe、quotacheck、reboot、rmmod、 runlevel、shutdown等。

/usr/bin 是你在后期安装的一些软件的运行脚本。主要放置一些应用软体工具的必备执行档例如c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome*、 gzip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb*、wget等。

/usr/sbin   放置一些用户安装的系统管理的必备程式例如:dhcpd、httpd、imap、in.*d、inetd、lpd、named、netconfig、nmbd、samba、sendmail、squid、swap、tcpd、tcpdump等。

/usr/bin用于分发包管理器(如Ubuntu apt等)存放它所管理的应用的路径, /usr/sbin与/usr/bin的关系类似与/bin和/sbin的关系

/usr/local/bin用于存放用户自己的程序(如自己编译出来的包等),不受分发包管理器的控制。如果用户把自己的程序放在/usr/bin下,则有可能在未来被包管理器给修改或删除了。

vim 编辑器

vi  文件名    或vim 文件名

命令状态:

H 左  L 右   K 上  J 下

0(零) 行首     $(shift+5) 行尾       G(shift+G) 文件尾

i 插入命令

dd  删除当前行    pp 复制并粘贴当前行    x: 删除光标处字符

control+f 后翻页        control+b前翻页

u 取消上次操作)


a,A:追加命令,a 在当前光标后追加,A 在行末追加

o,O:打开命令,o 在当前行下打开一行,O在当前行上插入一行

r,R :替换命令,r 替换当前光标处字符,R从光标处开始替换

数字s: 替换指定数量字符

d0: 删除光标前半行

d$: 删除光标后半行

/string 查找字符串

n 继续查找

N 反向继续查找

% 查找对应括号

ex命令状态

    :0 文件首

    :1,5 copy 7 块拷贝

    :1,5 del 块删除

    :1,5 move 7 块移动

    :1,$s/string1/string2/g 全文件查找string1并替换为string2

    :wq! 存盘退出

保存命令

    按ESC键 跳到命令模式,然后:

    :w   保存文件但不退出vi

    :w file 将修改另外保存到file中,不退出vi

    :w!  强制保存,不推出vi

    :wq  保存文件并退出vi

    :wq! 强制保存文件,并退出vi

    q:   不保存文件,退出vi

    :q!  不保存文件,强制退出vi

    :e!  放弃所有修改,从上次保存文件开始再编辑

Mac终端下的快捷键

control + A 移到行首
control + E 移到行尾
control + U 清除当前行命令

cmd + L 清除上一次操作的内容
cmd + K 清除所有内容

cmd + option + H 将所有窗口隐藏到应用程序图标
cmd + H  将当前窗口隐藏到图标中
cmd + M  将当前窗口最小化到

cmd + shift + .  显示/隐藏当前文件的隐藏文件

traceroute -n 125.35.51.162        // 查看当前网络访问到这个ip都通过哪些途径

mac 打开iphone界面  iphone与mac连接,打开QuickTime,文件 -> 新建录制影片

修改mac地址

$ ifconfig        en0:  ether ac:bc:32:83:a2:4b     原始的mac地址

$ openssl rand -hex 6 | sed 's/\(..\)/\1:/g; s/.$//'        生成一个mac地址

$ sudo /System/Library/PrivateFrameworks/Apple80211.framework/Resources/airport -z      断开airport无线网卡连接

$ sudo ifconfig en0 ether 7e:df:f7:a5:07:56        修改成生成的mac地址

$ networksetup -detectnewhardware     重新连接网卡

$ ifconfig    查看是否是改后的mac地址

44:9f:24:59:ca:13

SSL 权限

open ~/.ssh/     打开ssh目录

GitLab:中配置ssh

1、登录后找到 Profile Settings  ->  SSH Keys  添加Key来做映射

id_rsa.pub 文件

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCcmEJCTlr57na4TJ8xU8J/EG0KcrH6Dh+MwBPbs0NO6lA09Pdjro4knzSRYeSHc9X63Km/OR18JU7ygcE9foUYAN8BatnDTh6R+ynyQxyIqIG7Gn8YGF0g8Zx8oU9LsYc8SWP6zdryr7NaQZ41583jt9JxheG0la6D10cqZRQiix7pZMiXmHZXPKCSkTZSGHLaR0Ws24eyz1Yt9HmLXZem8FPkUrwmn2IYk05cwMyK+NE/g9pPJbpVEtEssU4FZ4I++LNltMbAxgtCp2QXZ7Z7ViA3CvK9u8g6u4yLLBPydpXWJ1pu+9bYFV3CvoTEawN3+mIIMsDaDhPlI6PJMk/j 632505215@qq.com

| http://www.cnblogs.com/webzhangnan/p/3221410.html

金融类

居民的金融四大决策

一、消费决策和储蓄决策 - 多少财富用于消费,多少为储蓄

二、投资决策 - 怎么样投资所节省的资金

三、融资决策 - 自己的消费和投资计划

四、风险管理决策 - 降低金融不确定性

金融体系

金融体系包括金融市场、金融中介、金融服务企业以其它用来执行居民户、企业和政府的金融决策的机构

1、金融中介 - 主要业务向客户提供金融产品和服务的企业,主要包括(银行、存款储蓄机构、保险公司、养老金与退休基金、投资银行、风险投资企业、资产管理企业)

M0、M1、M2

什么是金融、金融体系

赤字

金融危机和次贷危机

低利率照成所有人去银行进行贷款,然后使用杠杆

杠杆是用借贷来扩大交易收益的手段

杠杆的操作 - 例 A用1万美元买一个箱子,在以1.1万美元卖给B,在中间赚了1千美元,这属于正常操作,

        如果用杠杆就是 A有1万美元,又去借了99万美元,这样手里有100万美元,去买了100个箱子,赚取110万,还了99万借款和1万的利息,赚了9万美元

次贷危机 - 通过这种杠杆会使大面积的贷款短贷导金融系统破崩溃

刚性兑付

就是投资、理财的项目,无论赔了赚了都有平台或银行兜底,打破刚性兑付就是将打掉这种模式,平台和银行不在兜底,赔与赚要看借款方的企业

投资类型

股票

基金

p2p借贷

股权投资

总结

只要政府低利率加上超发货币,资产一定会上涨

https://www.youtube.com/watch?v=mu9W9SrsXu8&t=340s
https://www.youtube.com/watch?v=mu9W9SrsXu8&t=342s

https://www.youtube.com/watch?v=eY_Y_w_95JY&list=PLuoaTfw_1lYwyxS-RQ7_Hwsd_gcAdVJtV 金融学

函数式编程

什么是函数式编程

什么是函数式编程,就是把函数本身上升到一等公民的地位,进行编程构建

为什么要学习函数式编程

为了更好的模块化(模块化使得开发更快、维护更容易,模块可以重用,模块化便于单元测试和debug)

掌握函数式编程有助于更好的理解和使用Rxjs、Redux等一些前端类库和框架

函数是一等公民

所谓一等公民,是指函数和其他数据类型拥有平等的地位

函数可以赋值给变量: 

    let sub = (x, y) => x + y;
    sub(10, 20);

函数可以被作为实参传递:

    let funA = (x, y) => x * y;
    let funB = (n, m) => n * m
    funB(funA(1, 2), 3)

函数可以被另一个函数返回

    let funC = (a, cb) => {
        return (c) => {
            cb();
            return c + 2
        }
    }
    function cb();


函数可以返回另一个函数

函数可以作为形

纯函数

纯函数:一个没有任何副什么用,并且返回值由输入决定的函数

一、非纯函数

    const add = n => n + 1;
    add(1);

二、纯函数

    function addPure( x ){
        return function ( y ) {
            return y + x
        }
    }
    addPure(10)(20);            // 30

函数柯里化

只传递函数的一部分参数来调用它,让它返回一个函数去处理剩下的参数

// 柯里化
function addPure(x){
    return function(y) {
        return y + x
    }
}

var addTemp = addPure(5)
var res = addTemp(1) // 6
var res1 = addTemp(10) // 15

// 非柯里化
function addPure(x, y){
    return y + x
}

// 柯里化结合ES6
const add = x => y => x + y

// redux middlreware
const loggerMiddleware = store => next => action => {
    // do some thing.
}

高阶函数

把函数当做其它函数的参数来传递

let sum = (x, y) => x+y;

let calculate = (fn, x, y) => fn(x,y);

calculate(sum, 1, 2)

compose 函数组合

将多个函数组合成一个函数

var compose = function(fn1, fn2) {
    return function(arg){
        return fn1(fn2(arg));
    }
}

var a = arg => arg + 'a';
var b = arg => arg + 'b';

var c = compose(a, b); // 将a,b函数进行组合
console.log(c('c'));  // => cba

判断数据类型

function isDataType(type){
    var typeName = '';

    switch(toString.call(type)){
        case '[object String]':
            return 'string';
            break;

        case '[object Number]':
            if(type.toString().indexOf('.') != -1){
                return 'float';
            }
            else{
                return 'number';
            }
            break;

        case '[object Array]':
            return 'array';
            break;

        case '[object Object]':
            return 'object';
            break;

        case '[object Undefined]':
            return 'undefined';
            break;

        case '[object Null]':
            return 'null';
            break;    

        default: break;
    }

    return typeName;
}

| https://github.com/GuoYongfeng/js-functional-program
| http://www.alloyteam.com/2016/09/talk-about-functional-programming/
| https://llh911001.gitbooks.io/mostly-adequate-guide-chinese/content/ch1.html

Git

git

Git安装

1、安装brew   https://brew.sh/ 后在安装

2、brew install git

3、git --version   查看git版本

4、which git       显示当前git所在的目录

版本控制

版本控制分为 集成式版本控制 和 分布式开发

    1、集成式开发(svn):需要一台中央服务器,客户端需要连接到中央服务器进行更新、提交代码文件等

    2、分布式开发(Git):每台机器都有自己版本库不需要中央服务器

分布式的好处:

    1、不需要连接中央服务器来提交和更新文件

    2、通过不同的版本来做不同阶段开发,不同的版本文件可以随时切换回之前的版本

    3、分支管理上要比svn方便很多


[master +0 ~0 -0 !1]: + 增加  ~修改  -删除  !有冲突

Git的配置文件:vi ~/.gitconfig

常用命令

1、将远端的库克隆到本地:  git clone 远程代码库

2、切换到一个分支: git checkout 分支名

3、如果分支存在只切换分支,如果不存在则创建并切换到分支:  git checkout -b   新建本地分支名  origin/远端分支名

4、查看当前所在的分支:  git branch

    创建新分支: git branch 新分支名

5、将文件添加到暂存区:  git add 文件名         git add .  添加所有工作区文件到暂存区

6、提交文件:  git commit -m '提交版本内容说明'

7、将本地代码提交到远端库:  git push origin/develop_web  本地分支名

8、查看当前文件状态:  git status 

9、从远端拉取拉取代码:  git pull 远端分支名

10、临时保存:  git stash   git stash pop

11、从主分支上merge代码:  git merge origin/dev_crm2.6_base

12、将本地分支提交到主分支:  pull resquest

13、删除本地分支:  git branch -d dev_crm2.8_base

14、删除远端分支:  git push origin :PublicAdmin_V1.0.0_liusiguang        或  git branch -r -d origin/PublicAdmin_V1.0.0_liusiguang

15、从远端单独拉取一个文件:  git checkout origin/master -- path/to/file

16、撤消最后一次commit提交:git reset --hard HEAD^

17、chenkout一个分支后,pull的时候想在app_master下切换,使用下面命令进行转换

    git branch --set-upstream 当前分支名 远端分支名

18、git branch --track branch 名称 远端branch 建立一个 tracking 远端 branch 的 branch,这样以后 push/pull都会直接对应到该远端的branch。
    git branch --set-upstream branch 远端branch 将一个已存在的 branch 设定成 tracking 远端的branch。

19、git branch -m oldName newName   修改分支名

版本库介绍

工作区(文件)      暂存区(过度)     版本库      |       远端(服务器版本)    

1、工作区:  开发的区域

2、暂存区: 将工作区的文件执行 git add '文件名' 命令会使文件添加到暂存区,工作区的文件如果删除了就没有了

    git add '文件名'


3、版本库: 通过commit来提交到版本库, 暂存区需要先提交到版本库才能最后提交远端

    git commit -m '提交说明内容'

    add 和 commit 连着的写法
    git commit -a -m '提交注释'        // 这样可以省略add的步骤


4、提交到远端: 通过push来提交到远端

    git push origin develop_web_2.5.1_siguang            // 提交到远端

初始化配置

1、设置贡献者

    设置用户名:     git config  --global user.name “username”

    设置Email-id:  git config  --global user.email “email”


2、设置避免PULLING提交合并:   git config --global branch.autosetuprebase always

3、颜色高亮

    git config --global color.ui true

    git config --global color.status auto

    git config --global color.branch auto


4、设置默认编辑器:  git config --global core.editor vim        Git的使用系统默认取自VISUAL或EDITOR环境变量的编辑器

5、设置默认的合并工具:  git config --global merge.tool vimdiff

6、查看设置命令:  git config --list

创建版本库

1、git init 库名: 创建一个git仓库 

2、git remote add origin git@xbc.me:wordpress.git:    添加远程版本库origin

3、git remote:   显示所有远端主机

    git remote  -v:        查看远程仓库地址

    git remote show <主机名>:     查看远端主机的详细信息

    git remote add <主机名> <网址>:    添加远程主机

    git remote rm <主机名>:    删除远端主机

    git remote rename <原主机名> <新主机名>:    对无端主机进行改名

     在clone完成之后,Git 会自动为你将此远程仓库命名为origin,origin只相当于一个别名,运行git remote –v可以看到下载的版本路径。


克隆项目地址:    git clone 远程版本库地址

文件操作(查看、添加、提交、删除、恢复,撤消)

查看:

    1、git help:  显示help的命令功能

    2、git show 提交ID:  显示某次提交的内容,先通过git log来查看提交的ID

        commit 1bf1682120ac9fa622b04a77781f5999e0aabd9e         // 提交的ID

    3、git log:  显示所有的版本记录


添加: 添加到缓存区

    1、git add 文件名:  将工作文件修改提交到本地暂存区            add只是刷新了git的跟踪信息,并未发到版本库中

    2、git add --all:  将所有修改过的文件提交到暂存区


提交: 提交到版本库

    1、git commit -m '注释提交理由':  代码提交

        git commit --amend:  使用最后一次提交的注释在提交

    2、git push origin master:  将刚commit的版本推送到远程服务器


删除:

    1、git rm 'main.js':     删除暂存区的main.js文件

    2、git rm -f 'main.js':  删除工作区和暂存区的main.js文件

    3、git rm --cached 'main.js':  如果工作区和暂存区都有main.js,只删除暂存区文件


恢复:

    如果把工作区的文件删除了,想从版本库中恢复回来的方法:

    1、恢复文件

        先 git log 来查看commit 提交的历史版本,获取到commitId

        commit d5d9357319a4ef536b30f2d906bb71700beaa899
        Author: liusiugang <liusiguang@renrendai.com>
        Date:   Thu Nov 3 15:19:00 2016 +0800

            修改邻趣配送时间接口加merchantId参数+

        git checkout  [commitId] [要恢复的文件名]

        // 这样就将版本库中的common.coffee.js恢复到了工作区
        git checkout d5d9357319a4ef536b30f2d906bb71700beaa899  common.coffee.js   


    2、恢复版本

        1)git reset --hard  [commitId]    恢复到指定提交的版本的内容,通过commitId来指定版本

        2)git reset --hard HEAD^    恢复向后一个版本

        3)git reflog    显示所有的操作记录


撤消:

    1、git reset <file>:    从暂存区恢复到工作文件

    2、git reset --:        从暂存区恢复到工作文件

    3、git reset --hard:    恢复最近一次提交过的状态,即放弃上次提交后的所有本次修改

    4、git reset HEAD b:    现在你只想提交a文件,不想提交b文件,应该这样

    5、git revert <$id>:    恢复某次提交的状态,恢复动作本身也创建了一次提交对象

    6、git revert HEAD:    恢复最后一次提交的状态

        git checkout -- filname  撤回远端版本或指定文件

        git commit --amend    重新合并一个提交方案


文件重命名:

    1、git mv reademe.txt 新文件名


查看当前库状态: git status

    显示
    On branch master        // master分支
    Your branch is up-to-date with 'origin/master'.        // 你的分支是最新的 master
    nothing to commit, working directory clean            // 无提交,工作目录干净

分支

1、查看分支

    查看本地分支:  git branch

    查看远端分支:  git branch -r

    查看本地和远端分支:  git branch -a

    查看各分支最后提交的信息:  git branch -v

2、创建分支 

    git branch 分支名称

3、把分支提交到远端分支

    # 如果在本地创建一个分支,与团队其它人一起开发,需要将这个分支push到远端仓库,不然这个分支只是存在你本地,其它人看不到,每次commit完代码就需要提交到远端

    git push origin 本地分支名

    Example:

        appledeMacBook-Pro:what apple$ git push origin dev_1.1_lulu           // 将dev_1.1_lulu分支提交到远端
        Username for 'https://github.com': siguang1983            // 输入远程的用户名
        Password for 'https://siguang1983@github.com':             // 输入远端的密码
        Counting objects: 4, done.                                // 成功

4、删除分支: 

    git branch -d <branch>        删除某个分支

    git branch -D <branch>        强制删除某个分支 (未被合并的分支被删除的时候需要强制)

5、切换分支:

    git checkout [要切换的分支]        本地切换分支

    git checkout -b [新创建分支名]  [远端地址分支]            创建并且直接切换到这个新分支

6、合并分支: git morge  分支名

    git merge master develop        # 将develop分支合并到master上

7、分支的更新与合并

    # 将本地仓库更新到最新版本,直接更新自动合并冲突,如果执行fetch提示哪些冲突需要手动更新
    git pull origin 分支名

    # 执行一下合并,并且手动去去解决冲突
    git merge [branch]

    # 将这个冲突文件重新提交 
    git add <filename>

    # 查看一下冲突的文件
    git diff 确定分支  目标分支

    # 冲突处理完后就可以commit
    git commit -m 'fix conflict'

8、删除远程版本:  git push origin :br-1.0.0

9、查看分支提交的记录:  git whatchanged

    commit 1bf1682120ac9fa622b04a77781f5999e0aabd9e            // 提交的状态码
    Author: apple <apple@appledeMacBook-Pro.local>            // 提交人
    Date:   Mon May 2 17:24:16 2016 +0800                    // 日期

        siguang add index.js                // 提交文件

    :100644 100644 e69de29... 50b777e... M  js/index.js

拉取远端代码 和 提交远端代码

1、pull、fetch 两种都是拉取代码功能,有一些小的差别

    git pull : 拉取远端代码,直接合并        // Already up-to-date. 已更新

    git fetch: 拉取远端代码,并不合并,查看哪些冲突,来进行手动merge合并


2、版本库提交到远端

    git push origin/dev_crm2.9_base


3、pull request:将代码提交到远端版本中的一个请求,在merge就可以直接合到远端仓库中

解决冲突

拉取完代码git pull 或 git fetch 拉取远程某个分支的代码,再与本地的指定分支合并(包括fetch和merge)

git pull origin/dev_crm2.5_base 

git merge origin/dev_crm2.5_base

    有时候pull的时候会提示:git branch --set-upstream master origin/next  

    Git会自动在本地分支与远程分支之间,建立一种追踪关系(tracking)

    如果有冲突 常见提示:
        Auto-merging addCacke.html            // 自动合并了这个文件
        CONFLICT (content): Merge conflict in addCacke.html            // 有冲突在 addCacke.html文件中合并冲突
        Automatic merge failed; fix conflicts and then commit the result.        // 自动合并失败解决冲突

查看文件之间区别 diff

1、git diff <file>:        是工作区与暂存区的

2、git diff <$id1> <$id2>:    比较两次提交之间的差异

3、git diff <branch1>..<branch2>:    在两个分支之间比较

4、git diff --staged:    比较暂存区和版本库差异

5、git diff --cached:    暂存区和版本区差异

6、git diff --stat:         仅仅比较统计信息

查看提交记录

1、git log:  显示提交的所有记录

    commit 1bf1682120ac9fa622b04a77781f5999e0aabd9e         // 提交的ID
    Author: apple <apple@appledeMacBook-Pro.local>         // 提交用户
    Date:   Mon May 2 17:24:16 2016 +0800                 // 提交日期

        siguang add index.js            // 提交文件

2、git log <file>:        查看该文件每次提交记录

3、git log -p <file> :    查看每次详细修改内容的diff

4、git log -p -2:        查看最近两次详细修改内容的diff

5、git log --status:     查看提交统计信息

sourceTree 使用

一、设置拉取、推送默认密码

    仓库 -> 远程仓库 -> 将路径配置成 http://用户名:密码@172.16.x.xx:7990/scm/ibg/ibg.git

gitLab生成公钥私钥

$ ls -l .ssh            // 查看.ssh目录下有哪些配置文件

$ cd .ssh                    // 进入到.ssh目录

$ ssh-keygen -t rsa -C "liusiguang@youxin.com"                 // 创建公钥和私钥

    创建时会提示输入的密码直接回车为空就行

    会创建这两个文件: id_rsa.youxin、id_rsa.youxin.pub

$ vi config            // 创建一个config文件,将下面内容加入进去,config文件就是配置git起哪个公钥

    Host *.youxin.com
IdentityFile ~/.ssh/id_rsa.youxin
User git

$ vim id_rsa.youxin.pub         // 将.pub文件内的内容copy出来

$ 进入gitlab -> setting -> 点左侧栏的ssh keys

    key的就把刚才id_rsa.youxin.pub的内容粘进去

    title: 随便起名

$ SourceTree配置

    1、偏好设置 电子邮件填写 "liusiguang@youxin.com"

    2、仓库/仓库设置 -> 高级 -> 电子邮件填写 "liusiguang@youxin.com"

    3、仓库/仓库设置 -> 远程仓库 -> 编辑配置文件(路径中修改为gitlab中拉代码的SSH地址,而不是HTTP的)

遇到问题

一、工作test分支冲突

    1、测试不试不合test

    2、如果test, A、B两个分支冲突解决方法

        1、如果一起上,将A、B合并处理冲突

        2、将A上线,合并到master,在将B拉master解决冲突,在合test

    3、如果同一文件不改同一行不会冲突

| 参考资料:
|
| 可视化工具:SourceTree
| 远端:stash
|
| http://iissnan.com/progit/
| http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/
| http://www.yiibai.com/git/git_update_operation.html

JS作用域和整理经验

作用域

一、示例

    console.log(str);        // 输出undefined  因为读取的是解析器预先存储的str,还没有被赋值
    var str = 1;         


二、示例2

    alert(a);        // 弹出的是function a(){ alert("2")}
    var a = 1;
    function a(){
        alert("2")
    }

    预解析会保存a = undefinded,和 a = function a(){alert("2")},
    在变量与函数名相同时,输出的结果函数优先级高于变量名


三、示例3

    var a = 1;
    function a(){
        alert("2");
    }

    // 报错,因为a被定义成变量,typeof a 返回的是number,如果 var a;不能定义值那么a()是成功的
    a();


四、示例4

    var a = 1;
    function fn1(){
        alert(a);
        // 注意这里因为是使用var声明所以执行数预解析器才会把它单独存储起来,
        // 如果将var去掉,a=2改成了赋值这样会改变外部的a的值,下面输出的就是1,2
        var a = 2;
    }

    fn1();                        // undefined
    console.log(a);        // 1


五、示例5

    function foo(){
        return fn1;

        function fn1(){};
        var fn1 = 10;
    }

    var f = foo();

    // 输出 function fn1(){};    因为调用foo函数时,return  fn1因为变量还未定义所以 直接调用的函数
    alert(f);                


六、示例6

    var  myname = "global"; // 全局变量
    function fun() {
        alert(myname);             // "undefined",如果函数内不在定义myname就会找全局的变量,但函数本身已经定义了,预编译会先将变量定义成undefined        
        var myname = "local";
        alert(myname);             // "local"
    }
    fun();


七、示例7

    <script>
        window.name = 'globalName';

        var getName = function(){
            return this.name;
        }

        getName();         // 'globalName'
    </script>


八、示例8

    <script>
        window.id = 'window';

        document.getElementById('divA').onclick = function(){
            alert(this.id);            // output "divA"

            var callback = function(){
                return this.id;        // output "window"
            }
            callback();            // 指针指向window
        }
    </script>


 九、示例9

    1、var getName;
        console.log(getName)    // undefined

        getName()                // Uncaught TypeError: getName is not a function

        var getName = function() {
            console.log('wscat')
        }

    2、var getName;
        console.log(getName)    // function getName() {console.log('oaoafly')}

        getName()                // oaoafly

        function getName() {
            console.log('oaoafly')
        }


十、bind()

    <script>
        window.id = 'window';

        document.getElementById('divA').onclick = function(){
            alert(this.id);

            var obj = {
                id: 'objId',
                callback: function(){
                    return this.id;         // 'divA'
                }.bind(window)                // 通过bind来将callback内的this指向window
            }

            console.log(obj.callback());    // 'window'
        }
    </script>


十一、严格模式下的this

    <script>
        function fun(){
            'use strict'
            alert(this);        // 返回 'undefined', strict模式下this不会指向全局
        }

        fun();
    </script>


十二、箭头函数下的this指向

    let Template = {
        test(){
            console.log(this);        // this指向Template

            document.querySelector("#showThis").onclick = () =>{
                // 如果非箭头函数this应该指向 #showThis
                // 箭头函数不是指向调用者,所以指向了Template
                console.log(this);    
            }
        }
    }
    Template.test();

构造函数

1、函数本身是对象也是构造函数

2、直接挂载到函数下,是对象的属性和方法

    function Foo(){};
    Foo.username = '111';     // 注意这里Foo的属性不能为name因为被name被函数的名所占用
    Foo.getName = function(){ console.log(this.username) };

    // 调用
    Foo.getName();        // 111

3、在构造函数的内部属性和方法

    function Foo() {
        this.username = '111'
    }
    Foo.username = '222';

    // 调用
    console.log(new Foo().username);    // 111

<script>
    function Foo() {
        getName = function() {
            console.log(this);
            console.log(1);
        }

        console.log(this);
        return this;
    }
    Foo.getName = function() { // 这里只是Foo对象的方法,而不是构造函数的方法,
        console.log(2);
    }

    // Foo.prototype.getName = function() {
    //     console.log(3);
    // }

    var getName = function() {
        console.log(4)
    }

    function getName() {
        console.log(5)
    }

    Foo.getName(); // 2    Foo.getName=function(){}  是存到Foo下的方法,Foo.getName()可以取到
    getName(); // 4     如果一个作用域下有var的变量名与函数名相当,var的作用域提升高于function
    Foo().getName();     // 1  因为Foo()被调用,定义中加了return this,Foo指向的是window全局,getName没有var升明,所以为全局,所以相当于window.getName(),Foo中如果不加return this那么就会报错,因为Foo没有链式调用,所以this断了
    getName(); // 1   因为上面已经将Foo内的getName()提升成全局,所以会赋盖掉var getName、function getName定义的全局方法

    // var ofo = new Foo;
    // ofo.getName();   // 3

    // Foo既是一个对象也是一个构造函数
    new Foo.getName();             // 2  先执行Foo.getName(),因为"."运算符优先于new运算符,所以这时候Foo只是一个对象并不是构造函数的实例,所以会调用对象下的方法
    new Foo().getName();         // 1  先执行new Foo()构造函数,然后在构造函数下去找getName方法,Foo构造函数方法没有getName()所以到prototype下去找
    new new Foo().getName();     // 1  与上面相同
</script>

对象拷贝并不引用指针

有两种方法,一是使用for...in将一个对象拷贝到另一个对象

另一种方法是使用Object.assign()

let objA = {
        name: 'siguang',
        age: 30
}
let objB = Object.assign({}, objA);
objB.name = 'lulu';         // lulu
console.log(objA.name);     // siguang

对象的深拷贝和浅拷贝

浅拷贝就是对象之间还存引用关系,深拷贝就是两个对象之间没有引用关系

一、浅拷贝

    const a = {t: 1, p: 'gg'};
    const b = a;
    b.t = 3;
    console.log(a);     // {t: 3, p: 'gg'}
    console.log(b);     // {t: 3, p: 'gg'}

二 、深拷贝

    1、Object.assign() 方法是将两个对象合并,并返回一个新对象

        **** 注意assig()只能拷贝一层,多层结构还是浅拷贝 ****

        const c = {t: 1, p: 'gg'};
        const d = Object.assign({}, c);
        d.t = 3;
        console.log(c);     // {t: 1, p: 'gg'}
        console.log(d);     // {t: 3, p: 'gg'}

    2、Object.create() 将拷贝的内容入到prototype下,对象和数组都适用

        对象:

            var oA = {
                name: 'sg',
                age: 33
            }
            var oB = {};
            oB = Object.create(oA);

            oB.eat = 'bread';

        数组:

            var oA = [1,2]
            var oB = [];
            oB = Object.create(oA);

            oB.push(3);            // oB.length;   3        

    3、jQuery.extend()

        let oa = {a: 1, b: 2}; let ob = {};  
        $.extend(ob, oa); 
        ob.b = 3; 
        console.log(oa, ob);        // oa: {a: 1, b: 2}、 ob: {a: 1, b: 3}

数组的深拷贝

一、concat()方法

    var arrA = [1,2,3];
    var arrB = arrA.concat();
    arrB.push(5,6,7);
    console.log(arrA);  // [1,2,3]
    console.log(arrB);  // [1,2,3,5,6,7]

二、slice()方法

    var arrA = [1,2,3];
    var arrB = arrA.slice();
    arrB.push(5,6,7);
    console.log(arrA);  // [1,2,3]
    console.log(arrB);  // [1,2,3,5,6,7]

两种方法都是回返一个不带指向的新数组

三、使用扩展运算符...

    let a1 = [1, 2, 3];
    let a2 = [...a1];
    a2.push(4);

    console.log(a1);        // [1, 2, 3]
    console.log(a2);        // [1, 2, 3, 4]

对象处理

一、查看是否是一个空对象

    // 使用keys来查看对象中所有的kyes并返回一个数组,如果数组为度为0则对象为空
    var obj = {}; 
    var Len = Object.keys(obj);         
    console.log(Len.length);

二、查看对象中属性是否存在

    var obj = {
        a: 1,
        b: 2
    }
    console.log(obj.c);        // undefined

判断数据类型

function isType(data, name){ 
    return Object.prototype.toString.call(data) === `[object ${name}]` 
}
isType({}, 'Object');            // true;

数组处理

一、数据去重

    1、普通方法

        function clearRepeat(arr){
            var newArr = [], obj = {};
            for(var i=0; i<arr.length; i++){
                if(!obj[arr[i]]){
                    obj[arr[i]] = '';        // 对象属性相同会直接覆盖
                    newArr.push(key);        
                }
            }
            return newArr;
        }

        let retArray = clearRepeat([3,2,3,4,5,5,2,2,13,3]);


    2、使用ES6的set()方法,set方法就是不允许数组内有重复的值

        let setValue = new Set([1,2,33,1,22,2,2,4,2,1,2])
        console.log(setValue, Object.prototype.toString.call(setValue));        // {size: 5, [1, 2, 33, 22, 4]}, '[Object Set]'
        let arr = [...setValue];        // [1, 2, 33, 22, 4] 需要进行解构

    http://www.jb51.net/article/46154.htm


二、数组排序

    1、使用sort方法

        var arr = [6,7,5,8,4,2342342,23,234,2,34];
        arr.sort(function(v1, v2){
            return v1 - v2        // 正序
        })
        console.log(arr);


三、返回数组中最大最小值

    let arr = [2,1,423,343,5,67,8];
    let min = Math.min.apply(null, arr);
    let max = Math.max.apply(null, arr);

编写组件并兼容AMD、ES6写法

;(function(root, factory){

    // 判断使用的支持定义模块的方式
    if(typeof define === 'function' && define.amd){
        define(factory)
    }
    else if(typeof exports === 'object'){
        module.exports = factory();
    }
    else{
        root.NProgress = factory();
    }

})(this, function(){
    console.log(`输出内容`);
})

千分位函数

Vue.filter('thousand', function (num, fixed) {
    let n = Number(num)
    if (isNaN(n)) return 0;
    if (fixed && String(n).split('.').length === 2 && String(n).split('.')[1].length > fixed) {
        n = num.toFixed(fixed)
    }
    const str = (n || 0).toString()
    return str.indexOf('.') > -1 ? str.replace(/(\d)(?=(?:\d{3})+\.)/g, '$1,') : str.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
});

小技巧

1、获取arguments参数中的不同

    function execute(name){
        return [].slice.call(arguments, 1);
    }
    execute(1,2,3,4);        // 返回 [2, 3, 4];

2、case语句

    function showTest(type) { 
        switch(type) { 
            case "a":                    // type为a/b/c都执行console.log('ok')
            case "b":
            case "c":
                console.log('ok');
                break;

            default:
                console.log('error');
                break;
        }
    }

兼容不支持的方法

1、 Object.create不支持的处理

    Object.create = Object.create || function(obj){
        var F = function(){};
        F.prototype = obj;
        return new F();
    }

2、bind()

    Function.prototype.bind = Function.prototype.bind || function(){
        var self = this,
                context = [].shift.call(arguments),
                args = [].slice.call(arguments);

        return function(){
            return self.apply(context, [].concat.call(args, [].slice.call(arguments)))
        }
    }

| https://juejin.im/entry/58db95eaac502e0058f8472e
| http://www.iamaddy.net/2015/04/front-end-engineering/ // 前端的发展

EggJS基础

| this.ctx - 当前请求的上下文 Content对象的实例ctx,Request、Response
| this.app - 当前应用对象Application 对象的实例
| this.service - 定义的Service,等价于 this.ctx.service
| this.config - 应用配置
| this.logger - 日志对象 方法debug, info, warn, error
| app.cache - 缓存

Egg介绍、安装

Egg.js属于MVC类型的NodeJS框架

一、下载脚手架:

    $ npm i egg-init -g
    $ egg-init egg-example --type=simple
    $ cd egg-example
    $ npm i

二、启动项目:

    $ npm run dev
    $ open localhost:7001

配置

config
    |- config.default.js        // 默认配置文件, 所有环境都会加载这个配置文件, 一般用于开发环境的默认配置文件
    |- config.test.js           // 开发环境配置
    |- config.prod.js           // 生产环境配置
    |- config.unittest.js
    |- plugin.js                // 控制插件
    `- config.local.js

一、引入插件

    module.exports = {
        mysql: {
            enable: true,           // 开启或关闭插件
            package: 'egg-mysql'    // package为一个npm模块,package.json中的dependencies中,框架会在node_modules目录中找到这个插件入口
        },
    };

    也可以指定path 来替代 package
    const path = require('path');
    module.exports = {
        mysql: {
            enable: true,
            path: path.join(__dirname, '../app/plugin/egg-mysql'),
        },
    };

Router 路由

用来接收请求URL,在交给controller处理

app.get(路由名,路由URL路由,middleware(可以配置多个),controller)

一、controller的两个写法

    # router
    module.exports = app => {
        // 1、controller目录的home.js进行处理
        app.get('/', 'home');             

        // 2、payTool目录、billquery.js文件、getLoanUser方法
        app.post('/getLoanUser', 'payTool.billquery.getLoanUser');
    };

    # controller/home.js
    module.exports = function* () {
        this.body = 'Hello World';  // 响应给客户端
    };

    # controller/payTool/billquery.js
    module.exports = app => {
        class BillqueryController extends app.Controller {
            async getLoanUser(){
                const { ctx, app, service } = this;
                const response = await service.payTool.billquery.getRepayPlan(ctx.request.body);
                ctx.body = response;
            }
        }
    }


二、路由中间件 - 在访问路由之前通过中间件来对路由进行一些处理

    app.get('router-name', 'middleware1', 'middleware2', 'controlll');

    1、创建中间件 checkAuth.js  middleware

        module.exports = app => {
            return async function checkAuth(ctx, next){
                const user = ctx.session.user;
                console.log(`UserSession ------ ${JSON.stringify(user)}`)

                if(user){
                    console.log(`visited true`);
                    await next();
                }
                else{
                    console.log(`visited false`);
                    ctx.body = {
                        data:{},
                        status: 10001,
                        message: 'Session失效'
                    }
                }
            } 
        }

    2、路由中调用

        注意如果路由中使用中间件分为全局和和单个路由第一次两种,如果配置到config.default.js中的就为全局

        // 引用中间件
        const checkAuth = app.middleware.checkAuth();

        // 不需要验证
        app.get('/', 'home.index');
        app.get('/query', 'query.query.queryPage');

        // 需要验证是否登录的
        app.post('/web/getUserList', checkAuth, 'user.user.getUserList');            // 获取用户列表


三、参数获取

    1、获取url参数 ctx.query

        http://127.0.0.1:7001/search?name=egg  取出name的值

        // app/router.js
        module.exports = app => {
            app.get('/search', app.controller.search);
        };

        // app/controller/search.js
        module.exports = function* (ctx) {
            tx.body = `search: ${this.query.name}`;
        };


    2、路由参数 ctx.params

        http://127.0.0.1:7001/user/123/xiaoming   取出 123、xiaoming

        // app/router.js
        module.exports = app => {
            app.get('/user/:id/:name', app.controller.user.info);
        };

        // app/controller/user.js
        exports.info = function* (ctx) {
            ctx.body = `user: ${ctx.params.id}, ${ctx.params.name}`;
        };


四、重定向 - 当路由不存在时会走一个默认的路由

    1、内部重定向

        // app/router.js
        module.exports = app => {
            app.get('index', '/home/index', 'home.index');
            app.redirect('/', '/home/index', 302);
        };

    2、外部重定义 controller中来执行路由的跳转 redirect

        // app/router.js
        module.exports = app => {
            app.get('/search', 'search');
        };

        // app/controller/search.js
        module.exports = function* () {
            const type = this.query.type;
            const q = this.query.q || 'nodejs';

            // 这里跳转
            ctx.redirect('/admin/home');
        };

五、多路由映射 - 将路由分类并用一个路由主文件加载其它的路由文件

    // router.js 通过一个主文件将两个路由加载进来
    module.exports = app => {
        require('./router/news')(app);
        require('./router/admin')(app);
    };

    // router/news.js
    module.exports = app => {
        app.get('/news/list', app.controller.news.list);
        app.get('/news/detail', app.controller.news.detail);
    };

    // router/admin.js
    module.exports = app => {
        app.get('/admin/user', app.controller.admin.user);
        app.get('/admin/log', app.controller.admin.log);
    };

六、路由规则

    // app/router.js
    module.exports = app => {
        app.get(/^\/package\/([\w-.]+\/[\w-.]+)$/, app.controller.package.detail);
    };

Controller

controller继承于app.controller, 负责解析用户输入,处理后返回相应的结果, 框架推荐Controller层主要对用户请求参数进行处理(校验、转换),然后用service处理业务结果并返回

用户HTTP请求(路由处理) <=> 校验组装(controller处理)<=> 业务处理(service)

一、Controller的两种写法

    1、函数写法

        // app/controller/posts.js
        exports.index = function* () {};
        exports.new = function* () {};
        exports.create = function* () {};

        // router 调用
        app.get('/index', 'posts/index');
        app.get('/new', 'posts/new');
        app.get('/create', 'posts/create');


    2、类写法

        // app/controller/post.js
        module.exports = app => {
            class PostController extends app.Controller {       // 定义一个PostController的类,继承了app.Controller
                * create() {
                    const { ctx, service } = this;
                    const createRule = {
                        title: { type: 'string' },
                        content: { type: 'string' },
                    };
                    // 校验参数
                    ctx.validate(createRule);
                    // 组装参数
                    const author = ctx.session.userId;
                    const req = Object.assign(ctx.request.body, { author });
                    // 调用 Service 进行业务处理
                    const res = yield service.post.create(req);
                    // 设置响应内容和响应状态码
                    ctx.body = { id: res.id };
                    ctx.status = 201;
                }
            }
            return PostController;
        }

        // router 调用   app/router.js
        module.exports = app => {
            app.post('createPost', '/api/posts', 'post.create');
        }


二、app.Controller下this上挂载的几个属性

    1、this.ctx: 当前请求的上下文Context对象的实例

    2、this.app: Application对象的实例,可以取到框架的全局对象和方法

    3、this.service: 取到service下的方法

    4、this.config: 获取或添加修改config的配置

    5、this.logger: 日志打印


三、获取请求

    ctx.method: 请求方法

    ctx.path: 请求路径

    ctx.host: 请求IP

    1、query 获取GET请求中传递参数

        url: /posts?category=egg&language=node  通过context.query拿到url的参数  

        class QueryController extends app.Controller{
            async getQuery(app){
                const { ctx, app, service } = this;

                const queryName = ctx.query;    // {category: 'egg', language: 'node'}
                ctx.body = queryName;
            }
        }

    2、queries 能取到重复的参数key

        // posts?category=egg&id=1&id=2&id=3 参数解析成对象,id有多个

        class QueryController extends app.Controller{
            async getQuery(app){
                const { ctx, app, service } = this;

                const queryName = ctx.queries;    // {category: 'egg', id: [1,2,3]}
                ctx.body = queryName;
            }
        }

    3、params 参数   

        路由: app.get('/projects/:projectId/app/:appId', 'app.listApp');

        // 请求: GET /projects/1/app/2
        class QueryController extends app.Controller{
            async getParams(app){
                const { ctx, app, service } = this;

                const params = {
                    id: ctx.params.projectId,
                    appid: ctx.params.appId
                };
                ctx.body = queryName;
            }
        }

    4、body 获取post传的数据

        框架内置bodyParser中间件,挂载到 context.request.body上,来取post的内容

        ctx.request.body  来获取post传过来的参数 {"username":"","password":"1111"}

        exports.listPosts = function* (ctx) {
            assert.equal(ctx.request.body.title, 'controller');
            assert.equal(ctx.request.body.content, 'what is controller');
        };

        <input type="text" name="title" value="" />
        <input type="text" name="content" value="" />

        config.default.js设置, 默认body最大长度为100kb
        bodyParser: {
            jsonLimit: '1mb',
            formLimit: '1mb',
        }

    5、获取上传文件

        浏览器上都是通过 Multipart/form-data 格式发送文件的,框架通过内置 Multipart 插件来支持获取用户上传的文件

        <form method="POST" action="/upload?_csrf={{ ctx.csrf | safe }}" enctype="multipart/form-data">
            title: <input name="title" />
            file: <input name="file" type="file" />
            <button type="submit">上传</button>
        </form>

        const path = require('path');
        const sendToWormhole = require('stream-wormhole');

        module.exports = function* (ctx) {
            const stream = yield ctx.getFileStream();
            const name = 'egg-multipart-test/' + path.basename(stream.filename);
            // 文件处理,上传到云存储等等
            let result;
            try {
                result = yield ctx.oss.put(name, stream);
            } catch (err) {
                // 必须将上传的文件流消费掉,要不然浏览器响应会卡死
                yield sendToWormhole(stream);
                throw err;
            }
            ctx.body = {
                url: result.url,
                // 所有表单字段都能通过 `stream.fields` 获取到
                fields: stream.fields,
            };
        };


        在config/config.default.js 中配置来新增支持的文件扩展名,或者重写整个白名单

        新增支持的文件扩展名
        module.exports = {
            multipart: {
                fileExtensions: [ '.apk' ], // 增加对 .apk 扩展名的支持
            },
        };

        覆盖整个白名单
        module.exports = {
            multipart: {
                whitelist: [ '.png' ], // 覆盖整个白名单,只允许上传 '.png' 格式
            },
        };

    6、发送HTTP响应

        1)设置status

            exports.create = function* (ctx) {
                // 设置状态码为 201
                ctx.status = 201;
            };

        2)设置body 响应给请求方

            exports.show = function* (ctx) {
                ctx.body = {
                    name: 'egg',
                    category: 'framework',
                    language: 'Node.js',
                };
            };
            exports.page = function* (ctx) {
                ctx.body = '<html><h1>Hello</h1></html>';
            };

ctx.header 头

获取整个 header 对象的方法: context.headers、context.header、context.request.headers、context.request.header

get()方法获取请求 header 中的一个字段的值、字段不存在返回null:  context.get(name)、context.request.get(name)

一、获取header

    1、ctx.header 取出头信息

        async from(ctx){
            console.log(ctx.header);
        }

        返回结果
        { 
            host: '127.0.0.1:7001',
            connection: 'keep-alive',
            'content-length': '21',
            'postman-token': 'a7e094b5-21ce-eb94-e403-3d517c832f30',
            'cache-control': 'no-cache',
            origin: 'chrome-extension://aicmkgpgakddgnaphhhpliifpcfhicfo',
            'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
            'content-type': 'application/json',
            accept: '*/*',
            'accept-encoding': 'gzip, deflate, br',
            'accept-language': 'zh-CN,zh;q=0.8,en;q=0.6',
            cookie: 'csrfToken=RNHUjeOf0Zsl9jNl2f5BhreT' 
        }

    2、ctx.protocol 协议

    3、ctx.ips 返回ip 一个数组

    4、ctx.ip 返回请求方的ip

    5)、ctx.host


二、设置header

    context.set(key, value) 方法可以设置一个响应头,context.set(headers) 设置多个 Header。

    exports.show = function* (ctx) {
        const start = Date.now();
        ctx.body = yield ctx.service.post.get();
        const used = Date.now() - start;
        // 设置一个响应头
        ctx.set('show-response-time', userd.toString());
    };
可以通过context.cookies来获取和设置cookie值

一、cookie操作

    获取 ctx.cookies.get('count');

    设置 ctx.cookies.set('count', ++count);

    删除 ctx.cookies.set('count', null);


二、设置options

    context.cookies.set(key, value, options)

    1、maxAge: nunber - 在浏览器保存最长时间

    2、expires: date - 设置这个键值的失效时间

    3、path: string - 设置生效的URL路径

    4、domain: string - 设置生效的域名

    5、httpOnly: boolean - 设置是否可以被js访问

    6、secure: boolean - 设置键值只在HTTPS连接上传输

    7、overwrite: boolean - 设置key相当键值如何处理,true为后设置的覆盖前面设置的,false发送两个

    8、sign: boolean - 对cookie是否进行签名,true防止前端对这个值进行篡改

    9、encrypt: boolean - 对cookie进行加密

    ctx.cookies.set(key, value, {
        httpOnly: false,
        sign: false,
    });


三、Cookie 密钥

    config/config.default.js

    module.exports = {
        keys: 'key1,key2',
    };

Session

context.session 来访问或者修改当前用户 Session 

1、读取和设置 Session

    exports.fetchPosts = function* (ctx) {

        // 获取 Session 上的内容
        const user = ctx.session.user;
        const posts = yield ctx.service.post.fetch(userId);

        // 修改 Session 的值
        ctx.session.user = ctx.session.user ? ctx.session.user++ : 1;

        ctx.body = {
            success: true,
            posts,
        };
    };


2、删除session

    exports.deleteSession = function* (ctx) {
        ctx.session = null;
    };


3、设置 session配置

    // config.default.js

    exports.session = {
        key: 'EGG_SESS',
        maxAge: 24 * 3600 * 1000, // 1 天
        httpOnly: true,
        encrypt: true,
    };

service 处理业务逻辑

service 需要继承于 app.Service,service在复杂业务场景下做业务逻辑封装的抽象层

一、service的ctx

    1、this.ctx.curl - 发起网络调用

    2、this.ctx.service.otherService - 调用service

    3、this.ctx.db - 发起数据库调用


二、Example

    // 调用服务 app/controller/user.js
    module.exports = app => {
        class User extends app.controller {
            async addUser(){
                let userData = {
                    name: 'siguang',
                    password: 'xxxxxx'
                }
                let isAddUser = yield ctx.service.user.createUser(userData)
                if(setUser){
                    ctx.body = '添加成功'
                }
                ctx.body = isAddUser ? '添加成功' : '添加失败';
            }
        }
    }

    // 定义一个Service app/service/user.js
    module.exports = app => {                   // 中间件支持两个参数 options中间件配置,app当前应用Application的实例
        class User extends app.Service {

            async createUser(params){
                const url = '/account/blance/createUser';
                let result = await this.ctx.curl(url, {
                    method: 'POST',
                    dataType: 'json',   // 自动解析 JSON response
                    data: params,
                    timeout: 15000,     // 15 秒超时
                }

                this.ctx.logger.info('响应结果:', result.data);
                return result.data;
            }
        }
        return User;
    };

中间件 Middleware

一、config.default.js 配置全局

    module.exports = {
        // 配置需要的中间件,数组顺序即为中间件的加载顺序
        middleware: [ 'gzip' ],
        // 配置 gzip 中间件的配置
        gzip: {
            threshold: 1024, // 小于 1k 的响应体不压缩
        },
    };

    // 框架中默认的中间件
    module.exports = {
        bodyParser: {
            jsonLimit: '10mb',
        },
    };


二、通用配置

    enable:控制中间件是否开启。

    match:设置只有符合某些规则的请求才会经过这个中间件。

    ignore:设置符合某些规则的请求不经过这个中间件。

    bodyParser: {
        enable: false,
        jsonLimit: '10mb',
    },


三、Example: 

    1、创建中间件 checkAuth.js  middleware

        module.exports = app => {
            return async function checkAuth(ctx, next){
                const user = ctx.session.user;
                console.log(`UserSession ------ ${JSON.stringify(user)}`)

                if(user){
                    console.log(`visited true`);
                    await next();
                }
                else{
                    console.log(`visited false`);
                    ctx.body = {
                        data:{},
                        status: 10001,
                        message: 'Session失效'
                    }
                }
            } 
        }

    2、路由中调用

        注意如果路由中使用中间件分为全局和和单个路由第一次两种,如果配置到config.default.js中的就为全局

        // 引用中间件
        const checkAuth = app.middleware.checkAuth();

        // 不需要验证
        app.get('/', 'home.index');
        app.get('/query', 'query.query.queryPage');

        // 需要验证是否登录的
        app.post('/web/getUserList', checkAuth, 'user.user.getUserList');            // 获取用户列表

Validate 参数校验插件

validate只用于post请求的参数校验,get请求取出的都是字符串

一、在config/plugin.js配置中添加

    exports.validate = {
        enable: true,
        package: 'egg-validate',
    };


二、Exmaple

    async refundApply() {
        const { ctx, app, service } = this;
        const paramRule = {
            partnerUserId: { type: 'string' },
            applyUserName: { type: 'string' },
            amount: { type: 'string' }, 
            sourceAccount: { type: 'string' }, 
        };

        const paramErrors = app.validator.validate(paramRule, ctx.request.body);
        if (paramErrors) {
            ctx.body = app.renderBody({
                statusType: app.statusType.paramsError,
                error: paramErrors,
            });
            return;
        }

        const response = await service.payTool.billquery.refundApply(ctx.request.body);
        ctx.body = response;
    }


三、验证规则  

    1、required - 是否当前字段必须有,required: false 可以为空

    2、allowEmpty - 允许为空

    2、int - 只能为整数

    3、number - 可以是整数和浮点数

    4、date - 日期 'YYYY-MM-DD'     birthoday: 'date'

    5、dateTime - 日期 YYYY-MM-DD HH:mm:ss

    6、boolean - 是否是布尔值        working: 'boolean'

    7、string - 是否是字符串, 字符串的四个规则:            

        allowEmpty - 允许为空字符串

        format - 使用正则来验证字符串的格式

        max - 字符串最大长度

        min - 字符串最小长度

        const rule = {
            username: { allowEmpty: true, min: 10, max: 100, format: /^\d+$/ }
        }

    8、email - 是否是email格式

    9、password - 密码验证规则 max最大、min最小、compare比较  pass: {type: password, max: 32, min: 6}

    10、url - 是否是url

    11、enum - 如果是枚举需要加一个规则

        // operateType的值必须是values数组中的一项,values必须为数组
        const paramRule = {
            operateType: { type: 'enum', values: ['REPAYMENT', 'CHARGE'] },        
        }
        const paramErrors = app.validator.validate(paramRule, ctx.request.body);

    12、object - 如果是对象,需要加一个规则 

    13、array - 如果是数组,需要加一个规则

        itemType 数组中每一个元素的规则 
        rule - An object that validate the items of the array. Only work with itemType.
        max - 数组最大长度
        min - 数组最小长度

    https://github.com/node-modules/parameter#rule
    https://github.com/node-modules/parameter/blob/master/benchmark.js
    https://github.com/node-modules/parameter/blob/master/example.js

jsonp

jsonp只能get请求

// config/config.default.js  jsonp的配置
exports.jsonp = {
    callback: 'callback', // 识别 query 中的 `callback` 参数
    limit: 100, // 函数名最长为 100 个字符
};


// app/router.js  路由加jsonp中间件
module.exports = app => {
    const jsonp = app.jsonp();
    app.get('/api/posts/:id', jsonp, 'posts.show');         // 通过jsonp中间件来让路由支付一个中间件
    app.get('/api/posts', jsonp, 'posts.list');
};

// app/controller/posts.js
exports.show = function* (ctx) {
    ctx.body = {
        name: 'egg',
        category: 'framework',
        language: 'Node.js',
    };
};

用户请求 /api/posts/1?callback=fn,响应为 JSONP 格式,如果用户请求 /api/posts/1

HttpClient

Node模拟客户端请求,curl请求默认 content-type: application/x-www-form-urlencoded,

app.curl(url, options) 和 app.httpclient.request(url, options) 两种方法相同

一、Example:

    module.exports = app => {
        class HttpClientController extends app.Controller{
            async getHttpClient(){
                const {ctx, app, service} = this;

                let result = await this.ctx.curl('http://goucai.diyicai.com/lottery/getissue.action?lotteryId=001&issueLen=100&d=1502966960306', {
                    methods: 'get'
                });

                let phones = [];
                let resultObj = JSON.parse(result.data.toString());
                resultObj.forEach((data, idx, arr)=>{
                    console.log('data------', data);
                    phones.push(data.endTime);
                })

                ctx.body = phones.join(',')
            }
        }
        return HttpClientController;
    }


二、config配置 // config/config.default.js

    exports.httpclient = {
        // 默认开启 http/https KeepAlive 功能
        keepAlive: true,

        // 空闲的 KeepAlive socket 最长可以存活 4 秒
        freeSocketKeepAliveTimeout: 4000,

        // 当 socket 超过 30 秒都没有任何活动,就会被当作超时处理掉
        timeout: 30000,

        // 允许创建的最大 socket 数
        maxSockets: Infinity,

        // 最大空闲 socket 数
        maxFreeSockets: 256,

        // 是否开启本地 DNS 缓存,默认关闭
        // 一旦设置开启,则每个域名的 DNS 查询结果将在进程内缓存 10 秒
        enableDNSCache: false,
    };


三、options对象

    let options = {};
    const result = yield ctx.curl('https://httpbin.org/get?foo=bar', options);

    1、mothod: 请求方法

    2、data: 需要发送的数据 { foo: 'bar' }

    3、dataAsQueryString: Boolean 如果为 true 即使在post情况下,也会强制将options.data以 querystringstringify处理后拼接到url的query参数上

    4、content(String|Buffer): 发送请求的正文,如果设置了此参数会忽略data参数

        ctx.curl(url, {
            method: 'POST',
            // 直接发送原始 xml 数据,不需要 HttpClient 做特殊处理
            content: '<xml><hello>world</hello></xml>',
            headers: {
                'content-type': 'text/html',
            },
        });

    5、stream(ReadStream): 发送请求正文的可读数据流

        ctx.curl(url, {
            method: 'POST',
            stream: fs.createReadStream('/path/to/read'),
        });

    6、writeStream: 接受响应数据的可写数据流

        ctx.curl(url, {
            writeStream: fs.createWriteStream('/path/to/store'),
        });

    7、consumeWriteStream: Boolean 是否等待 writeStream 完全写完才算响应全部接收完毕,默认是 true

    8、contentType: 请求数据的格式,默认undefined

    9、dataType: 响应数据格式

    10、headers: 自定义请求头

    11、timeout: 请求超时时间 默认是 [ 5000, 5000 ],即创建连接超时是 5 秒,接收响应超时是 5 秒。

    12、agent: 

    13、httpsAgent

    14、auth、digestAuth

    15、followRedirect:Boolean 是否自动跟进3xx的跳转响应

    16、maxRedirects: number 最大自动跳转次数

    17、formatRedirectUrl: 自定义实现302、301

    18、beforeRequest: 请求发送前会调用beforeRequest钩子

        ctx.curl(url, {
            beforeRequest: options => {
                // 例如我们可以设置全局请求 id,方便日志跟踪
                options.headers['x-request-id'] = uuid.v1();
            }
        });

    19、streaming:是否直接返回响应流

    20、gzip: Boolean是否开始gzip

    21、timing: Boolean 是否开启请求各阶段的时间没是


四、Get请求

    1、options.method = 'post'  设置请求参数,默认是get请求可以不用加

         const result = yield ctx.curl('https://httpbin.org/get?foo=bar', {method: post});

    2、status: 响应状态码

    3、headers: 响应头信息 有{'conent-type': 'text/html'}

    4、data: 响应body, 返回的是Buffer类型, 如果设置了options.dataType会根据参数来处理


五、Post请求

    module.exports = function* post(ctx) {
        const result = yield ctx.curl('https://httpbin.org/post', {

            // 必须指定 method
            method: 'POST',

            // 通过 contentType 告诉 HttpClient 以 JSON 格式发送
            contentType: 'json',

            // 要传的数据 
            data: {
                hello: 'world',
                now: Date.now(),
            },

            // 明确告诉 HttpClient 以 JSON 格式处理返回的响应 body
            dataType: 'json',
        });
        ctx.body = result.data;
    };


六、From表单提交

    以 ontent-type: application/x-www-form-urlencoded 的格式提交请求数据

    // app/controller/form.js
    module.exports = function* form(ctx) {
        const result = yield ctx.curl('https://httpbin.org/post', {
            // 必须指定 method,支持 POST,PUT 和 DELETE
            method: 'POST',
            // 不需要设置 contentType,HttpClient 会默认以 application/x-www-form-urlencoded 格式发送请求
            data: {
                now: Date.now(),
                foo: 'bar',
            },
            // 明确告诉 HttpClient 以 JSON 格式处理响应 body
            dataType: 'json',
        });

        ctx.body = result.data.form;
        // 响应最终会是类似以下的结果:
        // {
        //   "foo": "bar",
        //   "now": "1483864184348"
        // }
    };


七、Multipart 方式上传

    From 表单提交时包含文件的时候,需要用到 multipart/form-data 进行提交了

    引用fromstream 第三方模块

    // app/controller/multipart.js
    const FormStream = require('formstream');
    module.exports = function* multipart(ctx) {
        const form = new FormStream();

        // 设置普通的 key value
        form.field('foo', 'bar');

        // 上传当前文件本身用于测试
        form.file('file', __filename);
        const result = yield ctx.curl('https://httpbin.org/post', {
            // 必须指定 method,支持 POST,PUT
            method: 'POST',
            // 生成符合 multipart/form-data 要求的请求 headers
            headers: form.headers(),
            // 以 stream 模式提交
            stream: form,
            // 明确告诉 HttpClient 以 JSON 格式处理响应 body
            dataType: 'json',
        });

        ctx.body = result.data.files;
        // 响应最终会是类似以下的结果:
        // {
        //   "file": "'use strict';\n\nconst For...."
        // }
    };

    // 添加更多文件
    form.file('file1', file1);
    form.file('file2', file2);


八、以Stream 方式上传文件

    Stream 实际会以 Transfer-Encoding: chunked 传输编码格式发送

    // app/controller/stream.js
    const fs = require('fs');
    module.exports = function* stream(ctx) {
        // 上传当前文件本身用于测试
        const fileStream = fs.createReadStream(__filename);

        // httpbin.org 不支持 stream 模式,使用本地 stream 接口代替
        const url = `${ctx.protocol}://${ctx.host}/stream`;
        const result = yield ctx.curl(url, {
            // 必须指定 method,支持 POST,PUT
            method: 'POST',
            // 以 stream 模式提交
            stream: fileStream,
        });

        ctx.status = result.status;
        ctx.set(result.headers);
        ctx.body = result.data;
        // 响应最终会是类似以下的结果:
        // {"streamSize":574}
    };

模板渲染

读取数据渲染模板,呈现给用户

一、使用

    1、$ npm i egg-view-ejs --save         // 安装

    2、配置

        // config/plugin.js
        exports.nunjucks = {
            enable: true,
            package: 'egg-view-nunjucks'        // 开启插件使用numjucks模板
        };

        // config/config.default.js
        exports.view = {
            defaultViewEngine: 'nunjucks',
            mapping: {
                '.tpl': 'nunjucks',
            },
        };


    3、render、renderString两个方法

        render(fileName, locals, viewOptions): filenName: 文件路径、locals: 渲染的数据、viewOptions: 用户传入的配置

    render(name, locals) 渲染模板文件, 并赋值给 ctx.body

    renderView(name, locals) 渲染模板文件, 仅返回不赋值

    renderString(tpl, locals) 渲染模板字符串, 仅返回不赋值


- numjucks模板语法: http://mozilla.github.io/nunjucks/cn/templating.html  见模板md -

静态资源

Egg内置static插件,static默认映射到 public目录

app/public
├── css
│   └── news.css
└── js
    ├── lib.js
    └── news.js

内置对象

一、Application - 全局的方法和对象挂载到Application中

    // extend/application.js 创建全局对象
    module.exports = {
        // 全局返回状态类型
        statusType: 'STATUS_TYPE',

        // 生成返回报文
        renderBody(params) {
            if (typeof params.statusType === 'undefined') {
                throw new Error('statusType error');
            }
            const response = {
                data: params.data || {},
                message: params.message || this.statusMessage[params.statusType],
                status: params.statusType,
            };

            if (params.error) response.error = params.error;
            return response;
        }
    }

    // 调用 controller、service都可以
    module.exports = app => {
        return class UserController extends app.Controller {
            async fetch() {
                this.ctx.body = app.cache.get(this.ctx.query.id);
            }
        };
    };

定时任务

有些任务需要定时来完成,如定时上报,定时远程更新本地缓存,定时任务都统一存放在 app/schedule 目录下

创建一个定时任务 app/schedule/update_cache.js

module.exports = {
    // 通过 schedule 属性来设置定时任务的执行间隔等配置
    schedule: {
        interval: '1m',     // 1 分钟间隔
        type: 'all',        // 指定所有的 worker 都需要执行
    },

    // task 是真正定时任务执行时被运行的函数,第一个参数是一个匿名的 Context 实例
    * task(ctx) {
        const res = yield ctx.curl('http://www.api.com/cache', {
            dataType: 'json',
        });
        ctx.app.cache = res.data;
    }
};

框架扩展

框架扩展自身功能: Application、Context、Request、Response、Helper,在这几个对象上扩展就在extend目录里创建相应的js文件,application.js

    Helper用来提供一些实用的utility函数

一、Application 全局应用对象扩展

    访问 ctx.app,  Controller,Middleware,Helper,Service 中都可以通过 this.app 访问到 Application 对象

    // app.js
    module.exports = app => {
        app.config
    };

    // 扩展 app.foo() 方法 app/extend/application.js
    module.exports = {
        foo(param) {
            // this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性
        },
    };


二 、Context 扩展

    extend/content.js

    module.export = {
        async foo(param){
            ...
        }
    }

    引用 await this.ctx.foo({name: 'siguang'});


三、Helper 用来编写一些实用函数

    // app/extend/helper.js
    const moment = require('moment');
    exports.relativeTime = time => moment(new Date(time * 1000)).fromNow();

    // 也可以在模板里面使用:
    <!-- app/views/news/list.tpl -->
    {{ helper.relativeTime(item.time) }}

启动自定义

用来进行应用启动时进行初始化工作

// 通过入口文件app.js
module.exports = app => {
    app.beforeStart(function* () {

        // 应用会等待这个函数执行完成才启动
        app.cities = yield app.curl('http://example.com/city.json', {
            method: 'GET',
            dataType: 'json',
        });
    });
};

logger 日志

this.ctx.logger.info('xxxxxx');

logger.debug()、logger.info()、logger.warn()、logger.error()

config.deubgger 文件配置

    logger: {
        level: 'DEBUG',
        dir: '../app/logger',
    }

本地开发

egg-bin模块(用于本地开发和单元测试)

package.json:

   {
       "scripts": {
           "dev": "egg-bin dev --port 7001"
       }
   }

插件的开发

自定义插件存储目录:  lib/plugin/...

egg-ua插件

1、创建package.json

    {
        "eggPlugin": {
            "name": "ua"
        }
    }

2、egg-ua/us.js插件文件

    module.exports = {
        get isIOS() {
            const iosReg = /iphone|ipad|ipod/i;
            return iosReg.test(this.get('user-agent'));
        },
    };

3、config/plugin.js   通过path来挂载插件

    const path = require('path');
    exports.ua = {
        enable: true,
        path: path.join(__dirname, '../lib/plugin/egg-ua'),
    };

项目目录

├── package.json
├── app.js (可选)            // 启动初始化
├── agent.js (可选)
├── app
|   ├── router.js           // 路由规则
│   ├── controller          // 处理用户的输入
│   |   └── home.js
│   ├── service (可选)       // 服务,编写业务逻辑
│   |   └── user.js
│   ├── middleware (可选)    // 中间件
│   |   └── response_time.js
│   ├── schedule (可选)      // 定时任务
│   |   └── my_task.js
│   ├── public (可选)        // 静态目录
│   |   └── reset.css
│   ├── view (可选)          // 模板
│   |   └── home.tpl
│   └── extend (可选)        // 框架扩展
│       ├── helper.js (可选)
│       ├── request.js (可选)
│       ├── response.js (可选)
│       ├── context.js (可选)     // ctx扩展
│       ├── application.js (可选)     // 全局方法
│       └── agent.js (可选)
├── config                  // 配置
|   ├── plugin.js
|   ├── config.default.js
│   ├── config.prod.js
|   ├── config.test.js (可选)
|   ├── config.local.js (可选)
|   └── config.unittest.js (可选)
└── test                    // 测试
    ├── middleware
    |   └── response_time.test.js
    └── controller
        └── home.test.js

参考资料
http://koa.bootcss.com/