Yearly Archive 2019

By柏小白

apicloud实现短信验证功能

短信验证码SDK,为开发者提供全球通用的短信验证码工具,开发者可以用其在App植入短信验证码SDK、简单设置即可实现短信验证功能,集成快速便捷,且后期易于管理。

配置集成 开发者使用本模块之前需要先到Mob官网申请开发者账号,并在账号内填写相应信息创建自己的 APP,从而获取AppKeyAppSecret,然后添加SMSSDK功能,获取模板id。 详情参考:快速集成获取apppkey和appSecret

一. 准备工作

下载并安装开发工具:APICloud Studio 2

二. 创建应用

APICloud提供了两种创建应用的方式,方便开发者在云端或APICloud Studio中创建应用。 云端创建应用:

1) 注册并登录APICloud系统:https://www.apicloud.com/console 点击左上角“创建应用”, 如图:选择“Native”,填写“名称”及“说明”,应用创建完成。

1

APICloud Studio中创建应用:

1) 登录APICloud Studio,没有账号点击“注册账号”,已经注册,用之前注册的APICloud账号登录APICloud Studio

2

这2端可以相互同步信息:

同步本地应用到云端资源库,开发者在APICloud Studio创建的应用会和云端资源库建立连接。项目代码改动后,可以使用APICloud Studio的代码提交功能提交代码到云端资源库。

三. 添加模块

打开APICloud云端,同时添加SMSSDK模块、mobtools模块:

4

四. 模块使用攻略

Android: 使用此模块之前android需先配置config.xml文件,方法如下

<meta-data name="Mob-AppKey" value="moba6b6c6d6"/>
<meta-data name="Mob-AppSecret" value="b89d2427a3bc7ad1aea1e1e8c1d36bf3"/>

IOS: ios 需要将Info.plist 文件放入res目录下,文件内容内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MOBAppKey</key>
<string>moba6b6c6d6</string>
<key>MOBAppSecret</key>
<string>b89d2427a3bc7ad1aea1e1e8c1d36bf3</string>
</dict>
</plist>

字段描述:

  • Mob-AppKey:(必须配置)从Mob官网获取的 AppKey。
  • Mob-AppSecret:(必须配置)从Mob官网获取的 AppSecret。 申请方法参考快速集成获取apppkey和appSecret

编译app时iOS 请配置访问联系人的权限

5

Demo实例widget下载地址

五. 模块接口说明

需要引入模块: var moduleSMSSDK = api.require('smssdk'); (1)获取文本验证码 getTextCode({params}, callback(ret,err)) params:

phoneNumber:

类型:字符串

默认值:无

描述:手机号

zone:

类型:字符串

默认值:无

描述:区域号,不要加"+"号

tempCode:

类型:字符串

默认值:无

描述:模板id

callback(ret,err) ret:

  • 类型:JSON 对象
  • 内部字段: { smart:0 //是否为智能验证 bool类型, ios 忽略此字段 }

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码

var param = {zone:'86', phoneNumber:'18500000000',tempCode:'1319972'};
moduleSMSSDK.getTextCode(param, function(ret, err){
    if (err !== null && err !== undefined && err !== '') {
     // 错误消息示例:{"msg":"Template not exist.","code":484}
    alert("Error:\n" + JSON.stringify(err));
    } else {
        // 正常消息示例:{"smart":false}
       alert("Success:\n" + JSON.stringify(ret));
    }
    });

(2)获取语音验证码 getVoiceCode({params}, callback(ret, err)) params:

phoneNumber:

类型:字符串

默认值:无

描述:手机号

zone:

类型:字符串

默认值:无

描述:区域号,不要加"+"号

callback(ret, err) ret:

  • 类型:JSON 对象 内部字段: { }

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码:

// param中的key命名不能改变
var param = {zone:'86', phoneNumber:'18500000000'};
moduleSMSSDK.getVoiceCode(param, function(ret, err){
  if (err !== null && err !== undefined && err !== '') {
   // 错误消息示例:{"msg":"Template not exist.","code":484}
   alert("Error:\n" + JSON.stringify(err));
  } else {
     // 正常消息示例:{}
    alert("Success:\n" + JSON.stringify(ret));
    }
   });

(3)提交验证码 commitCode({params}, callback(ret, err)) params:

phoneNumber:

类型:字符串

默认值:无

描述:手机号

zone:

类型:字符串

默认值:无

描述:区域号,不要加"+"号

code:

类型:字符串

默认值:无

描述:验证码

callback(ret, err) ret:

  • 类型:JSON 对象 内部字段: { }

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码:

// param中的key命名不能改变
var param = {zone:'86', phoneNumber:'18500000000', code:'4847'};
moduleSMSSDK.commitCode(param, function(ret, err){
if (err !== null && err !== undefined && err !== '') {
  // 错误消息示例:{"msg":"Template not exist.","code":484}
  alert("Error:\n" + JSON.stringify(err));
  } else {

  }
  });

(4)获取区号 getSupportedCountries(callback(ret, err)) callback(ret, err) ret:

  • 类型:JSON 对象
  • 内部字段:
{
    countries =
    (
        {
        rule = "^\\d+";
        zone = 1868;
        }
    )
}

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码:

// param中的key命名不能改变
moduleSMSSDK.getSupportedCountries(function(ret, err){
    if (err !== null && err !== undefined && err !== '') {
     // 错误消息示例:{"msg":"Template not exist.","code":484}
      alert("Error:\n" + JSON.stringify(err));
     } else {
      // 正常消息示例:{"countries":[{zone=590, rule=^\d+},{zone=680, rule=^\d+}]}
        alert("Success:\n" + JSON.stringify(ret));
    }
  });

(5)获取通讯里好友信息 getFriends(callback(ret, err)) callback(ret, err) ret:

  • 类型:JSON 对象
  • 内部字段:
{
    friends =
    (
     {
        lastname : "你好";
        others =     (
                {
                desc = "董浩";
                phone = 10101155;
                type = 1;
            }
        );
        phones =     (
                    {
                desc = "董浩";
                phone = 10101155;
                type = 1;
            }
        );
        recordid = 411;
        specialdate =     (
                    {
                desc = "生日";
                date = "1988-07-08";
                type = 1;
            }
        );
    }
    )
}

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码:

// param中的key命名不能改变
moduleSMSSDK.getFriends(function(ret, err){
   if (err !== null && err !== undefined && err !== '') {
    // 错误消息示例:{"msg":"Template not exist.","code":484}
    alert("Error:\n" + JSON.stringify(err));
   } else {
    // 正常消息示例:{"countries":[{zone=590, rule=^\d+}, {zone=680, rule=^\d+}]}
     alert("Success:\n" + JSON.stringify(ret));
    }
 });

(6)提交用户资料 submitUserInfo({params}, callback(ret, err)) params:

uid:

类型:字符串

默认值:无

描述:用户id

nickname:

类型:字符串

默认值:无

描述:用户昵称

avatar:

类型:字符串

默认值:无

描述:头像地址

phoneNumber:

类型:字符串

默认值:无

描述:手机号

zone:

类型:字符串

默认值:无

描述:区域号,不要加"+"号

callback(ret, err) ret:

  • 类型:JSON 对象
  • 内部字段: { }

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};`

示例代码:

// param中的key命名不能改变
var uid = "3241241";
var nickname = "SmsSDK_Api_Cloud_User_" + uid;
var avatar = "http://download.sdk.mob.com/510/deb/0c0731ac543eb71311c482a2e2.png";
// param中的key命名不能改变
var param = {uid:uid, nickname:nickname, avatar:avatar, phoneNumber:'18500000000', zone:'86'};

moduleSMSSDK.submitUserInfo(param, function(ret, err){
if (err !== null && err !== undefined && err !== '') {
// 错误消息示例:{"msg":"Template not exist.","code":484}
alert("Error:\n" + JSON.stringify(err));
} else {
}
});

(7)获取版本号 getVersion(callback(ret, err)) callback(ret,err) ret

  • 类型:JSON 对象
  • 内部字段: { version: "1.0.0" }

err

  • 类型:JSON 对象
  • 内部字段: { }

示例代码:

// param中的key命名不能改变
moduleSMSSDK.getVersion(function(ret, err){
if (err !== null && err !== undefined && err !== '') {
// 错误消息示例:{"msg":"Template not exist.","code":484}
alert("Error:\n" + JSON.stringify(err));
} else {
// 正常消息示例:{"version":'3.2.2'}
}
});

(8)是否允许访问通讯录好友 enableWarn({params}) params:

isWarn:

类型:布尔

默认值:true

描述:YES 代表启用 NO 代表不启用 默认为启用

示例代码:

// param中的key命名不能改变
var isWarn = true;
// param中的key命名不能改变
var param = {isWarn:isWarn};
moduleSMSSDK.enableWarn(param);
By柏小白

Nginx配置一键生成

关于Nginx部署、配置的文章网上已经发布过很多,包括我自己也私藏了不少还发布过两篇:

整理出来为的就是需要的时候,复制、粘贴就能使用。

然而千奇百怪的实际开发中,你肯定需要增删Nginx配置。你就得上网搜一下,复制粘贴出bug了又得调一下…

搞定还得保存下来以备后患。多了不好找还得整理…就搞得很麻烦

后果

今天我给大家推荐一款”Nginx配置利器”,配配变量就能一键生成常用配置。和繁琐低效配置说再见👋


网站链接:

nginxconfig 目前支持:

  • Angular、React、Vue、Node.js
  • PHP、Python
  • wordpress、Magento、Drupal
  • 缓存、Https、日志等各种配置…

使用

实现用户访问*.myweb.com域名自动跳转到myweb.com配置,并且开启http强制跳转到https的配置。

图片描述
图片描述

配置完之后,下方还有安装步骤指导你配置生效。交互体验相当好

图片描述

生成配置 /etc/nginx/sites-available/myweb.com.conf 如下:

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;

	server_name myweb.com;
	root /var/www/myweb.com/public;

	# SSL
	ssl_certificate /etc/letsencrypt/live/myweb.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/myweb.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/myweb.com/chain.pem;

	# security
	include nginxconfig.io/security.conf;

	# index.html fallback
	location / {
		try_files $uri $uri/ /index.html;
	}

	# additional config
	include nginxconfig.io/general.conf;
}

# subdomains redirect
server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;

	server_name *.myweb.com;

	# SSL
	ssl_certificate /etc/letsencrypt/live/myweb.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/myweb.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/myweb.com/chain.pem;

	return 301 https://myweb.com$request_uri;
}

# HTTP redirect
server {
	listen 80;
	listen [::]:80;

	server_name .myweb.com;

	include nginxconfig.io/letsencrypt.conf;

	location / {
		return 301 https://myweb.com$request_uri;
	}
}
复制代码

网站下方还罗列了推荐的nginx配置、安全配置…以作参考/etc/nginx/nginx.conf

# Generated by nginxconfig.io
# https://nginxconfig.io/?0.domain=myweb.com&0.php=false&0.index=index.html&0.fallback_html

user www-data;
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;

events {
	multi_accept on;
	worker_connections 65535;
}

http {
	charset utf-8;
	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	server_tokens off;
	log_not_found off;
	types_hash_max_size 2048;
	client_max_body_size 16M;

	# MIME
	include mime.types;
	default_type application/octet-stream;

	# logging
	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log warn;

	# SSL
	ssl_session_timeout 1d;
	ssl_session_cache shared:SSL:10m;
	ssl_session_tickets off;

	# Diffie-Hellman parameter for DHE ciphersuites
	ssl_dhparam /etc/nginx/dhparam.pem;

	# Mozilla Intermediate configuration
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

	# OCSP Stapling
	ssl_stapling on;
	ssl_stapling_verify on;
	resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
	resolver_timeout 2s;

	# load configs
	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}
复制代码

/etc/nginx/nginxconfig.io/security.conf

# security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# . files
location ~ /\.(?!well-known) {
	deny all;
}
复制代码

拓展

以上就满足日常开发需求啦。如果你压抑不住,想要展示你的高端操作。
你可以加入到项目本身开发中;nginxconfig项目本身是MIT开源协议,你也可以在此基础上迭代出自己的版本

作者:锐玩道
链接:https://juejin.im/post/5dbb88e56fb9a0208055c5fa
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

By柏小白

前端Nginx学习笔记

 前端Nginx学习笔记

nginx简介

1.nginx是一个高性能的Web服务器和反向代理服务器,也可作为电子邮件代理服务器。
2.在连接高并发的情况下,nginx是Apache服务不错的替代品,能够支持高达 50,000 个并发连接数的响应。

正向代理与反向代理

1.反向代理是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回到Internet上请求连接的客户端,此时代理服务器对外表现为一个反向代理服务器。
2.正向代理是指用户无法访问某网站,但是可以访问某代理服务器,而代理服务器可以访问该网站。于是用户通过访问代理服务器去访问该网站,代理服务器把响应信息返回给用户。
3.反向代理中用户访问的IP和端口是nginx服务器的,不知道提供服务的底层服务器,底层服务器在内网中,对外公开的是nginx服务器,nginx充当中间层。而正向代理中用户是知道目标服务器的域名信息的,知只是迫于网络的限制无法访问,如最常见的访问外网。

nginx安装与启动

nginx有windows版本和linux版本,一般针对项目需求是在Linux部署nginx服务器。

下载地址

nginx.org/en/download…

windows版本下载一个压缩包,解压即可
1.文件路径必须为英文,否则启动不成功。
2.查看端口占用,nginx服务器默认启动80端口。
cmd中

  • netstat -an  显示出电脑中所有被打开的端口列表
  • netstat -ano  显示出所有占用端口的列表
  • netstat -ano | findstr “80”  显示出80端口占用的详细情况
  • tasklist | findstr “80”    查询端口具体哪个应用占用

Netstat是在内核中访问网络连接状态及其相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告。

启动成功

  • 启动方式1:双击nginx.exe
  • 启动方式2:start nginx
  • 关闭方式1: 结束进程(两个进程)
  • 关闭方式2:nginx -s stop

nginx配置文件

nginx的核心配置文件nginx.conf主要由3个部分组成

  • 基本配置

#user  nobody; #配置worker进程运行用户
worker_processes  1; #配置工作进程数目,根据硬件调整,通常等于CPU数量或者2倍于CPU数量

#error_log  logs/error.log; #配置全局错误日志及类型 [debug|info|notice|warn|error|crit(致命错误)]默认error
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid; #配置进程pid文件

复制代码
  • events配置
#配置工作模式和连接数
events {
    # use epoll; #事件处理模型优化
    worker_connections  1024; #配置每个worker进程连接数上限,nginx支持的总连接数等于worker_connections*worker_processes
    # 我的本双核四线程,多任务运行弱,CPU主频2.4GHz,运行缓慢
}
复制代码
  • http配置,基本配置和多个server配置
http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
    
    #配置access_log日志及存放路径,并使用上面定义的main日志格式
    #access_log  logs/access.log  main;

    sendfile        on; #开启高效文件传输模式
    #tcp_nopush     on; #防止网络阻塞

    #keepalive_timeout  0;
    keepalive_timeout  65; #长连接超时时间,单位是秒
    #gzip  on; #开启gzip压缩输出

    #可以配置多个server
    server {
        listen       80; #配置监听端口
        server_name  localhost; #配置服务名

        #charset koi8-r; #配置字符集(俄罗斯字符集)      
        #access_log  logs/host.access.log  main;

        #默认的匹配斜杠/的请求,当访问路径中有/,会被该localtion匹配到并进行处理
        #nginx根目录 /html/index.html
        location / {
            root   html; #root根目录,nginx安装主目录下的html目录
            index  index.html index.htm; #配置首页文件的名称
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html; #配置50x错误页面 html/50x.html
        #精确匹配
        location = /50x.html {
            root   html;
        }

        #PHP脚本请求全部转发道到Apache处理
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        #PHP脚本请求全部转发道到FastCGI处理
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}
        
        # 禁止访问.htaccess文件-通常是禁止外网访问的文件
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }

    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

    #配置https服务,安全的网络传输协议,加密传输,端口443
    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem; #证书
    #    ssl_certificate_key  cert.key; #秘钥

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}
复制代码

nginx的主要应用

1.静态网站

nginx是一个http的web服务器,可以将服务器上的静态文件(html、图片等)通过http协议返回给浏览器客户端;若不配置location,nginx默认查找根目录html中的index.html文件。

    server {
        listen       8080;
        server_name  localhost;
        location / {
	    root   wangyi/static; #root= ip+端口 
            index  index.html index.htm;
        }
        #访问:localhost:8080,或如下配置
         location /static {  
	    root   wangyi;  #注意分号
            index  index.html index.htm;
        }
        #访问:localhost:8080或localhost:8080/static
        .......
    }
复制代码

2.负载均衡

网站服务器由多台服务器组成一个集群对外提供服务,当用户输入域名进行访问的时候,负载均衡负责将用户请求分发到集群中的不同服务器,从而提高并发处理能力。nginx作为访问的统一入口,分发请求,实现负载均衡。
负载均衡实现方式有硬件负载均衡和软件负载均衡。

nginx负载均衡

nginx通过在nginx.conf配置文件中配置实现负载均衡
1.首先在http模块配置

upstream network1{
    server 127.0.0.1:3000 weight=3; #权重
    server 127.0.0.1:5000 weight=1; 
}
复制代码

weight表示权重,用于后端服务器性能不均的情况,访问比率约等于权重之比,权重越大,被访问的几率越大。
upstream是配置nginx与后端服务器负载均衡非常重要的一个模块,他还能对后端服务器的健康状态进行检查,若后端服务器中有一台发生故障,则前端请求不会转发到该故障机器。
2.然后在server模块里配置

localtion /webname{
    proxy_pass http://network1; 
}
复制代码

参数‘http://’是固定的,network1字符串要和upstream后面的字符串相等。代理转发到network1下,然后去匹配upstream,再去匹配upstream下的server。

node.js创建http服务器实现负载均衡

node http服务创建好以后,nginx 配置好以后,node作为代理服务器就可以访问node服务了。

upstream network1 {
        server 127.0.0.1:3000 weight=3;
	server 127.0.0.1:5000 weight=1;
        server 127.0.0.1:6000 weight=1;
    }

    server {
        listen       8080;
        server_name  localhost;

        location / {
            # 访问不到服务器,做详细配置。
	    #proxy_redirect off; 
            #proxy_set_header X-Real-IP $remote_addr; 
            #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
            #proxy_set_header X-Forwarded-Proto $scheme; 
            #proxy_set_header Host $http_host; 
            #proxy_set_header X-NginX-Proxy true; 
            #proxy_set_header Connection ""; 
            #proxy_http_version 1.1; 
           proxy_pass http://network1;
        }
复制代码
const http = require('http')
const fs = require('fs')
const url = require('url')
http
    .createServer((req, res) => {
        if(req.url == '/favicon.ico'){
            res.writeHead(200);
            res.end()   
            return;
        }
        res.writeHead(200);
        fs.createReadStream(__dirname + '/index.html') #不同的页面内容查看效果
        .pipe(res);
    })
    .listen(5000)  #3000、6000。。。
------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>node</title>
   <link rel="stylesheet" href="">
</head>
<body>
    <div>第 1 个node服务器</div>
    <!-- <div>第 2 个node服务器</div> -->	
    <!-- <div>第 3 个node服务器</div> -->				
</body>
</html>
复制代码

nginx可以成功访问服务器,访问比大约等于权重之比。



nginx常用负载均衡策略
  • 1.轮询(默认)

每个请求轮流分配到不同的后端服务器,如果后端服务器宕机,则自动剔除。具体轮询策略为第一次访问第一个,第二次访问第二个。。。。。

upstream network1 {
        server 127.0.0.1:3000;
	    server 127.0.0.1:5000;
        server 127.0.0.1:6000;
    }
复制代码
  • 2.权重

每个请求按照一定比例分发到不同的后端服务器,用于后端服务器性能不均的情况,访问比率约等于权重之比,权重越大,被访问的几率越大。

upstream network1 {
        server 127.0.0.1:3000 weight=3;
	    server 127.0.0.1:5000 weight=1;
        server 127.0.0.1:6000 weight=1;
    }
复制代码
  • 3.ip_hash

ip_hash也叫ip绑定,每个请求按照访问ip的哈希值分配,这样每个访问客户端会固定访问一个后端服务器,可以解决会话session丢失的问题,常见账号密码登录。
哈希函数hash(“192.168.0.164”)%2 = 0 / 1,ip不变的话访问的服务器不变。

upstream network1 {
       ip_hash;
       server 127.0.0.1:3000;
       server 127.0.0.1:5000;
       server 127.0.0.1:6000;
   }
复制代码

当使用ip_hash以后,会一直访问3000端口的第一台服务器。

  • 最少连接
    web请求会被转发到连接数最少的服务器上。
 upstream network1 {
        least_conn;
        server 127.0.0.1:3000;
	    server 127.0.0.1:5000;
        server 127.0.0.1:6000;
    }
复制代码
负载均衡其他配置
upstream network1 {
        least_conn;
        server 127.0.0.1:3000;
	server 127.0.0.1:5000 backup; #备份 当所有非backup机器宕机的时候才能请求backup机器。
    }
upstream network1 {
        least_conn;
        server 127.0.0.1:3000;
	server 127.0.0.1:5000 down;#当前机器是down状态,不参与负载均衡。
    } 
复制代码

3.静态代理

把所有静态资源的访问改为访问nginx,而不是后台服务器,nginx更擅长于静态资源的处理,性能更好,效率更高。在实际应用中将静态资源html,css,js,图片交给nginx处理。
nginx实现静态代理,在nginx.conf配置文件中添加静态资源的location。
1.基于请求资源的后缀

location ~ .*\.(js|css|htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)${
    &emsp;root/html; #访问静态资源的路径,静态资源部署在nginx安装目录下,跟访问html/index.html一个道理。
}
复制代码

2.基于请求资源的存放目录

location ~ .*/(js|css|img|imgags)${
    &emsp;root/opt/static
}
复制代码

linux安装目录:/user/local/nginx,在linux作为nginx部署服务器时,请求linux中存放静态资源的某个目录,如/opt/static

4.动静分离

nginx的负载均衡和静态代理结合在一起,实现动静分离,服务器专注于动态资源,nginx专注于静态资源。


5.虚拟主机

虚拟主机就是把一台物理服务器划分成多个“虚拟”的服务器,这样我们的一台物理服务器就可以当做多个服务器来使用,从而可以配置多个网站。
Nginx下,一个server标签就是一个虚拟主机,设置多个虚拟主机,配置多个server即可。

基于端口的虚拟主机

根据nginx的不同端口即可访问到基于不同端口的服务器。

 server {
        listen       8080; #9090。。。
        server_name  localhost;
        location / {
		  proxy_pass http://network1;
        }
 }
 upstream network1 {
        server 127.0.0.1:3000;
    }
 upstream network2 {
        server 127.0.0.1:5000;
    }
复制代码
基于域名的虚拟主机

基于域名的虚拟主机访问时以域名的形式访问,这就涉及到DNS域名解析的知识,我们可以在windows的hosts文件给域名直接指定端口,windows C:\Windows\System32\drivers\etc有个hosts文件,如果在这里指定了一个域名对应的ip地址,那浏览器会首先使用这个ip地址。

 server {
        listen       8080;
        server_name  www.maanshan.com
        # server_name  www.laioyang.com
        location / {
		  proxy_pass http://network1;
          #proxy_pass http://network2;
        }
 }
复制代码
upstream network1 {
        server 127.0.0.1:3000;
    }
 upstream network2 {
        server 127.0.0.1:5000;
    }
复制代码
虚拟主机实例

www.meituan.com

  • www.liaoyang.meituan.com
  • www.maanshan.meituan.com



转载:https://juejin.im/post/5ddce45a6fb9a071594b3c76?utm_source=gold_browser_extension

By柏小白

Web网站打包成APP

appcan是国内比较老牌的平台
apicloud是新起之秀
phonegap是国外的大牌。
长期来看,phonegap是开源的,理所当然是技术最好。但是中文资料较少,国内应用的也不多。
appcan近期被apicloud蹂躏得不行不行的,基本全方位被碾压了。渐渐处于apicloud下风,后力不继。
apicloud还提供了简单的后端api,用户系统,自定义字段什么的基本不用写后端代码了。
但是最后我要劝你一句,不要用这种混合打包平台,最终都是然并卵的。君不见淘宝技术有多牛,写出来的app(html5)还是卡到死。

这里以APICLOUD为例演示:

1、首先访问http://www.apicloud.com/进行用户注册

1

2、注册成功登陆页面,点击左上角–创建应用

2

3、选择中间的–Web–输入项目名字–在网址一栏要输入在线网址(一定要是在线项目),点击创建即可3

4、直接点击云编译

4

5、填写应用名称–选择平台–类型–调试模式–全局加密–渠道打包–版本信息–点击云编译

5

6、如果点击云编译发现没有Android证书,则重新选择类型为–测试版,也可以先去申请个证书。

7、出现编译完成和二维码,就可以扫码或者下载你的个人APP了

6
————————————————
版权声明:本文为CSDN博主「小小猪~」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyj_15067066062/article/details/78499410

By柏小白

webstorm 怎么打断点

  1. 打开Chrome软件的右上角 Jet插件,在下拉菜单中选择选项,如下图所示。

    1
  2. 在打开的Jet选项设置窗体,设置端口(要调试程序的访问端口)后并点击Apply。如下图所示。

    2
  3. 打开WebStorm项目工程,在软件首页的右上角位置打开Edit Configurations选项,添加一个JavaScript debug调试设置。

    3
  4. 在新增的JavaScript Debug调试设置,将URL设置为调试的路径,点击Apply,完成debug设置。

    4
  5. 在Terminal运行npm run dev命令,启动vue工程并设置断点,如下图所示。

    5
  6. 启动debug,自动打开chrome,可以看到成功命中断点。

    6

    7

 

By柏小白

手把手教你WebStorm+chrome实现时时调试刷新

  1. WebStorm 安装

    WebStorm是一款收费的IDE,即使你不购买也可以免费使用30天,个人认为非常靠谱。

    进入官网,点击Download即可下载最新版本,这里默认window版本;

    安装过程没有一般默认即可,安装之后打开页面如图

    1

    2

    3

  2. WebStorm设置

    WebStorm 是通过live edit功能与浏览器实现时时刷新,这里在页面中点击Configure然后选择Settings打开WebStore的设置对话框;

    在搜索框输入live edit回车找到live edit选项,选中Auto in(ms)然后设置时间。然后勾选Restart if hotswap fails.然后点击apply应用即可

    4
  3. Chrome插件安装

    打开Chrome在地址栏右侧最后面点击三杠,选择“更多工具”->”扩展程序”;

    打开已安装的扩展在页面底部点击“获取更多扩展程序”进入Chrome网上商店,搜索JetBrains IDE Suport扩展程序。点击“添加至Chrome“按钮,安装该扩展程序到Chrome。安装完成后会在地址栏后面看到对应的图标。

    5

    6

    7

    8ad4b31c8701a18bad38c40d992f07082938fea5

     

  4. 简单示例

    软件设置完毕,扩展也安装。这时候可以尝试一把了。

    创建一个项目,文件test.html。如图所示。然后右击文件选择“Debug

    ‘test.html’”设置默认打开浏览器为Chrome。然后就可以愉快的编写代码了。

    8
    9
    10
    11
    转载:https://jingyan.baidu.com/article/454316ab68ac03f7a7c03ae3.html

 

By柏小白

Nginx:TCP / 正向 / 反向代理 / 负载均衡

写在前面的话

 

在我们日常的工作中,不可能所有的服务都是简单的 HTML 静态网页,nginx 作为轻量级的 WEB 服务器,其实我们将它用于更多的地方还是作为我们网站的入口。不管你是后端接口,还是前端页面,我们让用户的请求都到这个服务。原因大致有以下几个:

1. 集中管理更便于管理。

2. 对外服务都是需要公网 IP 的,需要带宽,如果每台机器都专门配置公网 IP 和带宽,实在是太浪费,可以看看最近我整理的几个云服务商带宽收费情况:

带宽越高,费用越高,这种高还不是成几何式增长。

3. 我们的服务一般不会是单点服务,那么前后端如果需要通信,怎么配置地址,每个都配置?会不会太麻烦了点。

4. 在我们生成服务器中,类似 redis / 数据库这类的服务器一般是不允许联网的为了安全,但是我们是不是每次都必须要登录上服务器去查数据呢?

这还是一些简单的理由,接下来我们来聊聊几种代理,顺便说说他们的应用场景,当然这种设计对于中小型公司是足够了,毕竟没去过大厂,不知道他们的具体架构和实现方式。

 

 

TCP 代理

 

在说 TCP 代理之前我们肯定还是要先了解下代理是什么东西?

代理(proxy)其实就是一个网络信息的中转站,我们使用一个图来表示:

比如这样一个场景,用户在外边,能够通过公网能够访问到云服务商集群中的一台具有公网 IP 的服务器,但是集群内部其它服务器都是不具备公网 IP 的,所以通过公网,我们无法访问到,但是有公网 IP 的服务器恰好在集群中,它的内网网卡和这些没有公网 IP 的机器是可以通信的,这意味着这台机器既可以和用户通信,又可以和后端的集群通信。那我们就得想办法,用户要访问后面的集群,让这个有公网 IP 的服务器帮忙传达一下,那么这个,当个中间人,实现用户和后端集群之间互相传话,于是这个有公网 IP 机器就成为了代理服务器。

 

那啥又是 TCP 代理?

在我们日常访问 WEB 应用,我们都是使用 http://,https://。但是有些服务不是 http 的,比如连接 MySQL 这种,这种明显不是 WEB 服务,所以我们不能像代理 WEB 服务一样代理它。而这类服务,就是 TCP 服务,我们得专门使用 TCP 代理。

我们同时举例 TCP 代理 MySQL 来具体说说如何使用配置。

在我们编译得时候加入了 –with-stream 参数,该参数是我们使用代理不可或缺的。

同样,我们在主配置文件 nginx.conf 中通过 include 来配置单独的目录,用于放置 TCP 配置的配置文件:

值得注意是,TCP 代理不是 HTTP 服务,所有我们的 include 和之前的位置不一样,我们得放在 http 的外层:

复制代码
user  root;
...
http {
    ...
}
stream {
    include tcp/*.conf;
}
复制代码

我们新建 tcp 目录:我这里为了方便使用直接 nginx 使用 root 用户

mkdir /data/services/nginx/conf/tcp

在目录下增加 MySQL 代理配置文件:mysql-proxy-demo.conf

复制代码
upstream MYSQL-PROXY-DEMO {
    hash $remote_addr consistent;
    server 192.168.10.204:3306;
}

server {
    listen  5000;
    proxy_connect_timeout   10s;
    proxy_timeout   300s;
    proxy_pass  MYSQL-PROXY-DEMO;
}
复制代码

简单说明:

1. 在 nginx 中,如果需要对后端多个机器做代理,就需要使用到 upstream,MYSQL-PROXY-DEMO 是给这个 upstream 取的名称,要求唯一。

2. hash xxx 是一种调度模式,当然这里只写了一条 server 记录,所有不存在调度到其他节点问题。

3. server xxx 是一条需要代理的记录,每一条一个 server。

4. server 段和 http WEB 服务类似,但是不需要 server_name。

5. proxy_connect_timeout 为连接超时时间,proxy_timeout 代理超时时间。

6. proxy_pass,代理中最为关键的一句,指名了我们把这个端口代理到哪个服务或者哪个 upstream。后面还会用到。

 

重载 nginx 此时我们测试数据库连接:

可以看到,我们成功的使用代理服务器的 IP + 端口通过 Navict 连接到了其它主机的 MySQL 数据库。

 

 

正向代理

 

正向代理不是我们使用的重点,因为在日常的使用中用的并不多,但是在某些特殊的场景下很有用。

比如有个局域网用户,无法访问互联网,但是局域网中另外一台机器却能够访问到互联网,所有我们可以通过那台机器作为代理去访问互联网。

正向代理的好处在于能够对需要访问的网站隐藏用户的真实信息。

 

这是系统为我提供的解决方案,但是并不是很好用,如果你想直接使用,请直接跳到后面第三方模块搭建正向代理

我们在 vhosts 目录下新建配置:forward-proxy-demo.conf

复制代码
server {
    resolver 8.8.8.8;
    access_log off;
    listen 6080;
    location / {
        proxy_pass $scheme://$http_host$request_uri;
        proxy_set_header HOST $http_host;

        # 配置缓存大小,关闭磁盘缓存读写减少I/O,以及代理连接超时时间
        proxy_buffers 256 4k;
        proxy_max_temp_file_size 0;
        proxy_connect_timeout 30;

        # 配置代理服务器 Http 状态缓存时间
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 301 1h;
        proxy_cache_valid any 1m;
        proxy_next_upstream error timeout invalid_header http_502;
    }
}
复制代码

简单说明:

红色部分为核心配置,这里我们直到了,在 nginx 中我们是可以通过 proxy_set_header 来处理请求头的。

 

我们在另外一台不能上网的机器上增加配置:

重载 nginx 后我们在不能上网的机器上执行 curl 百度:

curl -I --proxy 192.168.100.111:6080 http://www.baidu.com

结果如下:

当然我们也可以将代理配置定义成环境变量:

export http_proxy=http://192.168.100.111:6080

这样就能直接执行:

 

 

第三方模块搭建正向代理

 

当然,上面的配置都是针对 HTTP 的,对于 HTTPS 代理或者说整个正向代理,我们推荐使用第三方模块:ngx_http_proxy_connect_module

GITHUB 地址:

https://github.com/chobits/ngx_http_proxy_connect_module

将下载的 zip 包上传到服务器,重新编译 nginx,具体方法参考前面的动态添加模块:

https://www.cnblogs.com/Dy1an/p/11227796.html

1. 安装依赖:

yum install -y patch

 

2. 解压打补丁,编译:

从 GITHUB 上面,我们可以看到各个版本的 nginx 对应的补丁版本:

cd /data/packages/nginx
unzip ngx_http_proxy_connect_module-master.zip
cd nginx-1.16.0/
patch -p1 < /data/packages/nginx/ngx_http_proxy_connect_module-master/patch/proxy_connect_rewrite_101504.patch

编译不安装:

复制代码
./configure --prefix=/data/services/nginx \
--user=nginx \
--group=nginx \--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-http_secure_link_module \
--with-http_flv_module \
--with-http_ssl_module \
--with-http_mp4_module \
--with-stream \
--with-http_realip_module \
--with-http_v2_module \
--with-http_sub_module \
--with-http_image_filter_module \
--with-pcre=/data/packages/nginx/pcre-8.43 \
--with-openssl=/data/packages/nginx/openssl-1.1.1c \
--with-zlib=/data/packages/nginx/zlib-1.2.11 \
--add-module=/data/packages/nginx/nginx-upload-module-master \
--add-module=/data/packages/nginx/nginx-upstream-fair-master \
--add-module=/data/packages/nginx/ngx_cache_purge-master \
--add-module=/data/packages/nginx/ngx-fancyindex-master \
--add-module=/data/packages/nginx/echo-nginx-module-master \
--add-module=/data/packages/nginx/ngx_http_proxy_connect_module-master

# 编译
make
复制代码

 

3. 备份替换旧版:

复制代码
# 备份
mv /data/services/nginx/sbin/nginx  /data/backup/nginx/nginx_$(date +%F)

# 更新
cp /data/packages/nginx/nginx-1.16.0/objs/nginx /data/services/nginx/sbin/

# 查看
/data/services/nginx/sbin/nginx -V
复制代码

如图:

 

4. 添加 nginx 正向代理配置:

复制代码
server {
    listen       6080;
    resolver 202.96.128.166;
    resolver_timeout 30s;

    # 代理配置
    proxy_connect;
    proxy_connect_allow            443 563;
    proxy_connect_connect_timeout  10s;
    proxy_connect_read_timeout     10s;
    proxy_connect_send_timeout     10s;

    location / {
        proxy_pass http://$host;
        proxy_set_header Host $host;
    }
}
复制代码

重载配置访问测试:

curl -I --proxy 192.168.100.111:6080  http://www.baidu.com
curl -I --proxy 192.168.100.111:6080  https://www.alipay.com

HTTP 访问结果:

HTTPS 访问结果:

当然,我们也可以设置环境变量:

export http_proxy=http://192.168.100.111:6080
export https_proxy=http://192.168.100.111:6080
no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com"

最后,小结一下:正向代理配置虽然能够满足我们的一定需求,但是有些时候不是很稳定,包括在配置过程中,有时候并不能一次就能访问成功,需要多测试几次。

 

 

反向代理 / 负载均衡

 

反向代理一直是我们 nginx 服务配置的重中之重,我们工作的项目中大部分其实都是围绕着反向代理展开的。如果你用 nginx,你说你没有配置过静态资源 WEB 我相信,但是你没有用过反向代理,那你一定不是做运维的。

那什么是反向代理?

这需要我们和正向代理结合起来理解,我们之前正向代理的时候是我们代理别人的服务让我们能够访问到。

那么反向代理就是代理我们的服务让别人能够访问到,是不是一下子就清晰了。

我们本次测试环境用到了三台机器,一台是我们的 nginx,另外两台是安装了 tomcat 服务的服务器。我们要实现以下图示:

用户访问 nginx 的 8090 端口调度到后端的 TOMCAT 8080 上面去。

至于 TOMCAT 怎么安装部署这里就不做过多说明,这里做了个小处理,在 TOMCAT webapps 下面默认 ROOT 项目的 index.jsp 文件增加了本机 IP 用于区分:

此时我们启动两个 TOMCAT 访问测试:

节点1结果:

 

节点2结果:

 

在 nginx 的 vhosts 目录下增加如下配置:reverse-proxy-demo.conf

复制代码
upstream REVERSE-PROXY-DEMO {
    ip_hash;
    server 192.168.100.112:8080 weight=1 max_fails=3 fail_timeout=10s;
    server 192.168.100.113:8080 weight=1 max_fails=3 fail_timeout=10s;
} 

server {
    listen      8090;
    server_name localhost;
    
    location / {
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30;
        proxy_send_timeout 30;
        proxy_read_timeout 30;
        proxy_pass http://REVERSE-PROXY-DEMO;
    }
}
复制代码

如 TCP 代理一般,TCP 代理其实也是反向代理的一种,我们定义的 upstream 的名称要求唯一。

重载 nginx 访问测试:

可以看到请求被分配到了 100.113 这台机器上面去了!

 

我们之前就在使用 upstream,但是 upstream 到底是啥我们一直没说,其实 upstream 就是负载均衡。

从字面上的意思就可以理解,负载均衡就是均衡的,按照特定的调度算法,将请求调度到指定的节点(upstream server)。

upstream 配置说明:

1. nginx 负载均衡调度算法加上我们安装的 fair 模块,大致有以下 4 种:

调度算法 说明
权重轮询(默认) 按照顺序逐一分配到不同的后端。自动剔除失败的机器,使访问不受影响。
ip_hash 每个请求按照 IP 的 Hash 结果分配,使来自同一 IP 的固定访问到同一后端。能解决部分程序 session 没共享的问题
fair 更智能的算法,可以根据页面大小和加载速度进行智能负载,响应快的优先分配。
url_hash 需要按照 nginx hash 模块,按照访问的 URL 定向到某个机器上,能提升后端缓存服务器的效率。

日常用到比较多的就是前三个。

 

2. server 后面的参数:

参数 说明
weight 分配到请求权重,权重比例多高分配到请求的机会越大。
max_fails 最大的失败连接次数。
fail_timeout 等待请求的目标服务器响应的时长。
backup 当所有机器都 down 掉才会调度到这台机器。
down 手动停用某台机器。

这其实就是一些健康检查参数,但是这些参数存在不足,在实际应用中,可以结合 keepalived 来完成,后面会单独说明。

 

server 段关于反向代理的一些配置:

参数 说明
proxy_redirect 重写应答头部的报文
proxy_connect_timeout nginx 将一个请求发送至 upstream server 之前等待的最大时长
proxy_set_header 将发送至 upsream server 的报文的某首部进行重写
proxy_cookie_domain 将 upstream server 通过 Set-Cookie 首部设定的 domain 修改为指定的值,可以为字符串、正则或变量
proxy_cookie_path 将 upstream server 通过 Set-Cookie 首部设定的 path 修改为指定的值,可以为字符串、正则或变量
proxy_hide_header 设定发送给客户端的报文中需要隐藏的首部
proxy_send_timeout 发送至 upstream server 的写操作的超时时长
proxy_read_timeout 发送至 upstream server 的读操作的超时时长
proxy_pass 指定将请求代理至 upstream server 的 URL 路径

其实上面的配置我们可以简单的做个调整就能变成我们反向代理的配置模板。

By柏小白

webstorm功能、操作技巧及快捷键索引

欲先善其事,必先利其器,如题。看到网上一篇介绍webstrom的文章,觉得功能确实强大,也知道为什么阿里巴巴的前端传到github上的文件为啥都有一个 .idea 文件,(传说淘宝内部推荐写js用webstrom)

我们可以理解 IDE 就是集成了很多你想要的功能,或者你不想要的功能。换句话说就是装了很多插件的 editor ,所以到目前为止,我还觉得没必要给它装什么插件。

那么接下来开始介绍webstrom的特色功能:

WebStorm 是 JetBrains 推出的一款商业的 JavaScript 开发工具

WebStorm 是 JetBrains 推出的一款商业的 JavaScript 开发工具
  1. 任何一个编辑器都需要保存(ctrl + s),这是所有win平台上编辑类软件的特点,但是webstorm编辑文件右上角是没有那个熟悉的 * 的。
    好处:省去了ctrl + s之后,在结合Firefox的vim,基本不动鼠标就可以看到结果页面了。
    坏处:没有以前的 * 标识,万一键盘误操作也会被立即存储。
  2. 任何一个编辑器只要文件关闭了就没有历史记录了,但是webstorm有。 vcs->Local History -> Show History(快捷键:ALT+~ -〉7)
    好处:只要webstorm不关闭,你的文件随时可以返回到之前的操作(这也是为啥在 webstorm 里ctrl+y是删除一行的原因了)。
    坏处:webstorm关闭重启后这些历史记录就没有了;还有一个坏处就是由此带来的内存消耗也必然比较大。
  3. 任何一个编辑器,除了服务器svn之外,没有本地版本,但是webstorm提供一个本地文件修改历史记录(快捷键:ALT+SHIFT+c)。也可以 Ctrl + E 弹出最近打开的文件。
    好处:相当于本地svn。
    坏处:内存消耗也必然比较大。
  4. 集成了zencoding,html5,ftp,即时编辑(chrome),自动完成,基于Mozilla的JavaScript调试器,JSLint、Less支持、CoffeeScript支持、Node.JS、单元测试、集成git和svn版本控制等特性。
  5. 在编写CSS中,会智能的提示各种文件以及图片的路径,就不用再去确认这个文件是否存在了。

其他的特性就介绍了,想了解更多可以去webstrom官网继续寻找。看完了这么介绍以及那么多给力的特性,是不是有种冲动马上试一试这款传说中的前端神器啊?嗯。

首先在官方下载页下载webstrom后,点击安装,然后一直下一步,最后会出现需要注册码界面,因为webstrom是商业的IDE,而天朝的屌丝前端当然没那么钱去买那么昂贵的神器了,不用担心,我们可以用WebStorm的密钥来学习使用这款前端神器。嘿嘿~~~~

安装成功后,肯定会有些不适,就像我一样觉得这配色真的太戳了。还有一些设置也不是我也要的。那么以下我就列出我习惯的设置。

webstorm安装后的一些设置技巧:

  • 如何更改主题(字体&配色):
    File -> settings -> Editor -> colors&fonts -> scheme name.主题下载地址
  • 如何让webstorm启动的时候不打开工程文件:
    File -> Settings->General去掉Reopen last project on startup.
  • 如何完美显示中文:
    File -> Settings->Appearance中勾选Override default fonts by (not recommended),设置Name:NSimSun,Size:12
  • 如何显示行号:
    File -> Settings->Editor,”Show line numbers”打上勾,就显示行号了
  • 如何代码自动换行:
    File -> settings -> Editor “Use Soft Wraps in editor” 打上钩,代码就自动换行了
  • 如何点击光标,显示在本行末尾:
    File -> Settings->Editor “Allow placement of caret after end of line”去掉勾就行了。
  • 如何修改快键键:
    File -> Settings->Keymap,然后双击要修改快捷的功能会有提示框出来,按提示操作
  • 换成自己熟悉编辑器的快键键:
    File ->Settings->Keymap,支持像Visual Studio、Eclipse、NetBeans这样的主流IDE。
  • javascript类库提示。
    File -> settings -> Javascript -> Libraries -> 然后在列表里选择自己经常用到的javascript类库,最后Download and Install就ok了.
  • 在开发js时发现,需要ctrl + return 才能选候选项:
    File -> Setting -> Editor -> Code Completion -> Preselect the first suggestion: “Smart” 改为 “Always”
  • js提示比较迟缓
    File -> Code Completion -> Autopopup in 下 1000改为0
  • git配置:
    File -> settings -> Editor -> github,进去改github的账户,如果没有git则不需要.
  • 插件安装:
    File ->plugins,然后就选择给力的插件们再安装.(“css-X-fire”插件,用于当使用firebug修改css属性时,编辑器内的css代码也会发生变化。)
  • 以后更新

webstorm使用心得

  • 收藏夹功能:
    当工程目录很庞大时,有些子目录很经常打开,但层级又很深,这时候可以把目录添加到收藏夹里面,添加成功后,左侧有个“Favorites”菜单
  • 面包屑导航:
    除了左侧的工程页面,可以选择目录之外,在顶部菜单下有一个类似网站面包屑导航一样的目录也可以实现相同功。点击每个目录就会有下拉菜单显示其下的子目录,很实用.
  • 构造器界面:
    注释符合格式的话就会出现。如果是js文件则是js类的函数和对象;css文件的话则是这个css文件的概括;html文件的话则是节点的结构图。话说这几个就是为了方便查看代码的结构性.
  • todo界面:
    给代码加todo注释就会出现这个界面
  • 双栏代码界面:
    右击代码选项卡上的文件,然后右键 -> spilt vertically(左右两屏)或者spilt horizontally(上下两屏)
  • 本地历史功能:
    找回代码的好办法

git使用

webstorm中只集成了git的常用操作,并不能完全替代命令行工具。在界面的右下角可以查看处于哪个git分支。也可以在上面点击切换或者新建分支。

  • 查看当前代码与版本库代码的差异:
    右击代码界面任意区域,选择git -> compare with然后选择要比较的版本库。

webstorm的快捷键说明:

Editing编辑相关快捷键

  • Ctrl + Space:
    Basic code completion (the name of any class, method or variable) 基本代码完成(任何类、函数或者变量名称),改为Alt+S
  • Ctrl + Shift + Enter:
    Complete statement 补全当前语句
  • Ctrl + P:
    Parameter info (within method call arguments) 参数信息 包括方法调用参数
  • Ctrl + mouse over code
    Brief Info 简单信息
  • Ctrl + F1
    Show description of error or warning at caret 显示光标所在位置的错误信息或者警告信息
  • Alt + Insert
    Generate code…(Getters, Setters, Constructors)新建一个文件或者生成代码,…构造函数,可以创建类里面任何字段的getter与setter方法
  • Ctrl + O
    Override methods 重载方法
  • Ctrl + I
    Implement methods 实现方法
  • Ctrl + Alt + T
    Surround with…(if, else, try, catch, for, etc)用 * 来围绕选中的代码行,( * 包括 if 、 while 、 try catch 等)
  • Ctrl + /
    Comment/uncomment with line comment 行注释/取消行注释
  • Ctrl + Shift + /
    Comment/uncomment with block comment 块注释/取消块注释
  • Ctrl + W
    Select successively increasing code blocks 选择代码块,一般是增量选择
  • Ctrl + Shift + W
    Decrease current selection to previous state 上个快捷键的回退,减量选择代码
  • Alt + Q
    Context info 上下文信息
  • Alt + Enter
    Show intention actions and quick-fixes 意图行动,快速见效
  • Ctrl + Alt + L
    Reformat code 根据模板格式对代码格式化
  • Tab/ Shift + Tab
    Indent/unindent selected lines 对所选行进行缩排处理/撤销缩排处理
  • Ctrl + X or Shift + Delete
    Cut current line or selected block to clipboard 剪切当前行或所选代码块到剪切板
  • Ctrl + C or Ctrl + Insert
    Copy current line or selected block to chipboard 拷贝当前行或者所选代码块到剪切板
  • Ctrl + V or Shift + Insert
    Paste from clipboard 粘贴剪切板上的内容
  • Ctrl + Shift + V
    Paste from recent buffers 粘贴缓冲器中最新的内容
  • Ctrl + D
    Duplicate current line or selected block 复制当前行或者所选代码块
  • Ctrl + Y
    Delete line at caret 删除光标所在位置行
  • Ctrl + Shift + J
    Smart line join(HTML and JavaScript only)加入智能行 (HTML 和JavaScript)
  • Ctrl + Enter
    Smart line split(HTML and JavaScript only)分离智能行 (HTML 和JavaScript)
  • Shift + Enter
    Start new line 另起一行
  • Ctrl + Shift + U
    Toggle case for word at caret or selected block 光标所在位置大小写转换
  • Ctrl + Shift + ]/[
    Select till code block end/start 选择直到代码块结束/开始
  • Ctrl + Delete
    Delete to word end 删除文字结束
  • Ctrl + Backspace
    Delete to word start 删除文字开始
  • Ctrl + NumPad+/-
    Expand/collapse code block 扩展/缩减代码块
  • Ctrl + Shift+ NumPad+
    Expand all 扩张所有
  • Ctrl + Shift+ NumPad-
    Collapse 缩减所有
  • Ctrl + F4
    Close active editor tab 关闭活跃编辑标签

Search/replace搜索/替代相关快捷键

  • Ctrl + F
    Find 当前文件内快速查找代码
  • Ctrl + Shift + F
    Find in path 指定文件内寻找路径
  • F3
    Find next 查找下一个
  • Shift + F3
    Find previous 查找上一个
  • Ctrl + R
    Replace 当前文件内代码替代
  • Ctrl + Shift + R
    Replace in path 指定文件内代码批量替代

Usage Search搜索相关快捷键

  • Alt + F7/Ctrl + F7
    Find usages/Find usages in file 找到使用/在文件找到使用
  • Ctrl + Shift + F7
    Highlight usages in file文件中精彩使用
  • Ctrl + Alt + F7
    Show usages 显示使用

Running运行

  • Alt + Shift + F10
    Select configuration and run 选择构架,运行
  • Alt + Shift + F9
    Select configuration and debug 选择构架,修补漏洞
  • Shift + F10
    Run 运行
  • Shift + F9
    Debug 修补漏洞
  • Ctrl + Shift + F10
    Run context configuration from editor 从编辑运行内容构架
  • Ctrl + Shift + X
    Run command line 运行命令行

Debugging Debugging相关快捷键

  • F8
    Step over 不进入函数
  • F7
    Step into 单步执行
  • Shift + F7
    Smart step into 智能单步执行
  • Shift + F8
    Step out 跳出
  • Alt + F9
    Run to cursor 运行到光标处
  • Alt+ F8
    Evaluate expression 评估表达
  • F9
    Resume program 重新开始程序
  • Ctrl + F8
    Toggle breakpoint 切换断点
  • Ctrl + Shift + F8
    View breakpoints 查看断点

Navigation 定位相关快捷键

  • Ctrl + N
    Go to class跳转到指定类
  • Ctrl + Shift + N
    Go to file 通过文件名快速查找工程内的文件
  • Ctrl + Alt +Shift + N
    Go to symbol 通过一个字符查找函数位置
  • Alt + Right/ left
    Go to next/ previous editor tab 进入下一个/ 上一个编辑器选项
  • F12
    Go back to previous tool window 进入上一个工具窗口
  • Esc
    Go to editor(from tool window) 从工具窗口进入编辑器
  • Shift + Esc
    Hide active or last active window 隐藏活动窗口
  • Ctrl + Shift + F4
    Close active run/message/find/…tab 关闭活动….标签
  • Ctrl + G
    Go to line 跳转到第几行
  • Ctrl + E
    Recent files popup 弹出最近打开的文件
  • Ctrl + Alt + Left/Right
    Navigate back/forward 导航前进/后退
  • Ctrl + Shift + Backspace
    Navigate to last edit location 向最近编辑定位导航
  • Alt + F1
    Select current file or symbol in any view 查找当前选中的代码或文件在其他界面模块的位置
  • Ctrl + B or Ctrl + Click
    Go to declaration跳转到定义处
  • Ctrl + Alt + B
    Go to implementation(s) 跳转方法实现处
  • Ctrl + Shift + B
    Go to type declaration 跳转方法定义处
  • Ctrl + Shift + I
    Open quick definition lookup 打开定义快速查找
  • Ctrl + U
    Go to super-method/super-class 跳转方法/超阶级
  • Alt + Up/Down
    Go to previous/next method 在方法间快速移动定位
  • Ctrl + ]/[
    Move to code block end/start 跳转到编码块结束/开始
  • Ctrl + F12
    File structure popup 文件结构弹出
  • Ctrl + H
    Type hierarchy 类型层次
  • Ctrl + Alt + H
    Call hierarchy 调用层次结构
  • F2/ Shift + F2
    Next/previous highlighted error 跳转到后一个/前一个错误,高亮错误或警告快速定位,使用这个快捷键可以快捷在出错的语句之间进行跳转。
  • F4/Ctrl + Enter
    Edit source/ View source 编辑源代码/查看源代码
  • Alt + Home
    Show navigation bar 显示导航栏
  • F11
    Toggle bookmark 切换标记
  • Ctrl + F11
    Toggle bookmark with mnemonic 采用记忆切换标记
  • Ctrl + #[0-9]
    Go to numbered bookmark 跳转到带编号的标记
  • Shift + F11
    Show bookmark 显示标记

Refactoring 重构相关快捷键

  • F5
    Copy 拷贝
  • F6
    Move 移动
  • Alt + Delete
    Safe Delete 安全删除
  • Shift + F6
    Rename 重新命名
  • Ctrl + Alt + N
    Inline Variable 嵌入变量
  • Ctrl + Alt + M
    Extract Method( Javascript only) 提取函数
  • Ctrl + Alt + V
    Introduce Variable 引入变量
  • Ctrl + Alt + F
    Introduce Field 引入域
  • Ctrl + Alt + C
    Introduce Constant 引入常量

VCS/Local History 版本控制系统/ 本地历史相关快捷键

  • Alt + BackQuote( )
    ‘VCS’quick popup 快速弹出 VCS
  • Ctrl + K
    Commit project to VCS 提交项目至VCS
  • Ctrl + T
    Update project from VCS 从VCS 更新项目
  • Alt + Shift + C
    View recent changes 查看最新改变

General 常用的相关快捷键

  • Ctrl + Shift +A
    Find action 查找并调用编辑器的功能
  • Alt + #[0-9]
    Open corresponding tool window 快速切换打开界面模块
  • Ctrl + Alt + F11
    Toggle full screen mode 切换全屏模式
  • Ctrl + Shift + F12
    Toggle maximizing editor 切换最大化编辑器
  • Alt + Shift + F
    Add to Favorites 将当前文件添至收藏夹
  • Alt + Shift + I
    Inspect current file with current profile 使用当前属性检查当前文件
  • Ctrl + BackQuote( )
    Quick switch current scheme 快速转换现有组合
  • Ctrl + Alt + S
    Open setting dialog 打开设置对话框
  • Ctrl + Tab
    Switch between tabs and tool window 标签和工具窗的转换(与windows快捷键冲突)

我把以上快捷做成pdf了,大家可以点击WebStorm.快捷键pdf分享

文章内容转载于http://www.html5jscss.com/ide-webstrom.html

By柏小白

九种跨域方式实现原理(完整版)

前言

前后端数据交互经常会碰到请求跨域,什么是跨域,以及有哪几种跨域方式,这是本文要探讨的内容。

本文完整的源代码请猛戳github博客,纸上得来终觉浅,建议大家动手敲敲代码。

一、什么是跨域?

1.什么是同源策略及其限制内容?

同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

url的组成

同源策略限制内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 节点
  • AJAX 请求发送后,结果被浏览器拦截了

但是有三个标签是允许跨域加载资源:

  • <img src=XXX>
  • <link href=XXX>
  • <script src=XXX>

2.常见跨域场景

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。常见跨域场景如下图所示:

特别说明两点:

第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。

第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”

这里你或许有个疑问:请求跨域了,那么请求到底发出去没有?

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。

二、跨域解决方案

1.jsonp

1) JSONP原理

利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。

2) JSONP和AJAX对比

JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)

3) JSONP优缺点

JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。

4) JSONP的实现流程

  • 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。
  • 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
  • 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('我不爱你')
  • 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。

在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP函数。

// index.html
function jsonp({ url, params, callback }) {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script')
    window[callback] = function(data) {
      resolve(data)
      document.body.removeChild(script)
    }
    params = { ...params, callback } // wd=b&callback=show
    let arrs = []
    for (let key in params) {
      arrs.push(`${key}=${params[key]}`)
    }
    script.src = `${url}?${arrs.join('&')}`
    document.body.appendChild(script)
  })
}
jsonp({
  url: 'http://localhost:3000/say',
  params: { wd: 'Iloveyou' },
  callback: 'show'
}).then(data => {
  console.log(data)
})
复制代码

上面这段代码相当于向http://localhost:3000/say?wd=Iloveyou&callback=show这个地址请求数据,然后后台返回show('我不爱你'),最后会运行show()这个函数,打印出’我不爱你’

// server.js
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
  let { wd, callback } = req.query
  console.log(wd) // Iloveyou
  console.log(callback) // show
  res.end(`${callback}('我不爱你')`)
})
app.listen(3000)
复制代码

5) jQuery的jsonp形式

JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存。

$.ajax({
url:"http://crossdomain.com/jsonServerResponse",
dataType:"jsonp",
type:"get",//可以省略
jsonpCallback:"show",//->自定义传递给服务器的函数名,而不是使用jQuery自动生成的,可省略
jsonp:"callback",//->把传递函数名的那个形参callback,可省略
success:function (data){
console.log(data);}
});
复制代码

2.cors

CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求复杂请求

1) 简单请求

只要同时满足以下两大条件,就属于简单请求

条件1:使用下列方法之一:

  • GET
  • HEAD
  • POST

条件2:Content-Type 的值仅限于下列三者之一:

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

2) 复杂请求

不符合以上条件的请求就肯定是复杂请求了。 复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

我们用PUT向后台请求时,属于复杂请求,后台需做如下配置:

// 允许哪个方法访问我
res.setHeader('Access-Control-Allow-Methods', 'PUT')
// 预检的存活时间
res.setHeader('Access-Control-Max-Age', 6)
// OPTIONS请求不做任何处理
if (req.method === 'OPTIONS') {
  res.end() 
}
// 定义后台返回的内容
app.put('/getData', function(req, res) {
  console.log(req.headers)
  res.end('我不爱你')
})
复制代码

接下来我们看下一个完整复杂请求的例子,并且介绍下CORS请求相关的字段

// index.html
let xhr = new XMLHttpRequest()
document.cookie = 'name=xiamen' // cookie不能跨域
xhr.withCredentials = true // 前端设置是否带cookie
xhr.open('PUT', 'http://localhost:4000/getData', true)
xhr.setRequestHeader('name', 'xiamen')
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      console.log(xhr.response)
      //得到响应头,后台需设置Access-Control-Expose-Headers
      console.log(xhr.getResponseHeader('name'))
    }
  }
}
xhr.send()
复制代码
//server1.js
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
复制代码
//server2.js
let express = require('express')
let app = express()
let whitList = ['http://localhost:3000'] //设置白名单
app.use(function(req, res, next) {
  let origin = req.headers.origin
  if (whitList.includes(origin)) {
    // 设置哪个源可以访问我
    res.setHeader('Access-Control-Allow-Origin', origin)
    // 允许携带哪个头访问我
    res.setHeader('Access-Control-Allow-Headers', 'name')
    // 允许哪个方法访问我
    res.setHeader('Access-Control-Allow-Methods', 'PUT')
    // 允许携带cookie
    res.setHeader('Access-Control-Allow-Credentials', true)
    // 预检的存活时间
    res.setHeader('Access-Control-Max-Age', 6)
    // 允许返回的头
    res.setHeader('Access-Control-Expose-Headers', 'name')
    if (req.method === 'OPTIONS') {
      res.end() // OPTIONS请求不做任何处理
    }
  }
  next()
})
app.put('/getData', function(req, res) {
  console.log(req.headers)
  res.setHeader('name', 'jw') //返回一个响应头,后台需设置
  res.end('我不爱你')
})
app.get('/getData', function(req, res) {
  console.log(req.headers)
  res.end('我不爱你')
})
app.use(express.static(__dirname))
app.listen(4000)
复制代码

上述代码由http://localhost:3000/index.htmlhttp://localhost:4000/跨域请求,正如我们上面所说的,后端是实现 CORS 通信的关键。

3.postMessage

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的iframe消息传递
  • 上面三个场景的跨域数据传递

postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递

otherWindow.postMessage(message, targetOrigin, [transfer]);

  • message: 将要发送到其他 window的数据。
  • targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串”*”(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。
  • transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

接下来我们看个例子: http://localhost:3000/a.html页面向http://localhost:4000/b.html传递“我爱你”,然后后者传回”我不爱你”。

// a.html
  <iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> //等它加载完触发一个事件
  //内嵌在http://localhost:3000/a.html
    <script>
      function load() {
        let frame = document.getElementById('frame')
        frame.contentWindow.postMessage('我爱你', 'http://localhost:4000') //发送数据
        window.onmessage = function(e) { //接受返回数据
          console.log(e.data) //我不爱你
        }
      }
    </script>
复制代码
// b.html
  window.onmessage = function(e) {
    console.log(e.data) //我爱你
    e.source.postMessage('我不爱你', e.origin)
 }
复制代码

4.websocket

Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

我们先来看个例子:本地文件socket.html向localhost:3000发生数据和接受数据

// socket.html
<script>
    let socket = new WebSocket('ws://localhost:3000');
    socket.onopen = function () {
      socket.send('我爱你');//向服务器发送数据
    }
    socket.onmessage = function (e) {
      console.log(e.data);//接收服务器返回的数据
    }
</script>
复制代码
// server.js
let express = require('express');
let app = express();
let WebSocket = require('ws');//记得安装ws
let wss = new WebSocket.Server({port:3000});
wss.on('connection',function(ws) {
  ws.on('message', function (data) {
    console.log(data);
    ws.send('我不爱你')
  });
})
复制代码

5. Node中间件代理(两次跨域)

实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。 代理服务器,需要做以下几个步骤:

  • 接受客户端请求 。
  • 将请求 转发给服务器。
  • 拿到服务器 响应 数据。
  • 将 响应 转发给客户端。
    ” alt=”” data-src=”https://user-gold-cdn.xitu.io/2019/1/17/1685c5bed77e7788?imageView2/0/w/1280/h/960/format/webp/ignore-error/1″ data-width=”600″ data-height=”237″ />

我们先来看个例子:本地文件index.html文件,通过代理服务器http://localhost:3000向目标服务器http://localhost:4000请求数据。

// index.html(http://127.0.0.1:5500)
 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
      $.ajax({
        url: 'http://localhost:3000',
        type: 'post',
        data: { name: 'xiamen', password: '123456' },
        contentType: 'application/json;charset=utf-8',
        success: function(result) {
          console.log(result) // {"title":"fontend","password":"123456"}
        },
        error: function(msg) {
          console.log(msg)
        }
      })
     </script>
复制代码
// server1.js 代理服务器(http://localhost:3000)
const http = require('http')
// 第一步:接受客户端请求
const server = http.createServer((request, response) => {
  // 代理服务器,直接和浏览器直接交互,需要设置CORS 的首部字段
  response.writeHead(200, {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': '*',
    'Access-Control-Allow-Headers': 'Content-Type'
  })
  // 第二步:将请求转发给服务器
  const proxyRequest = http
    .request(
      {
        host: '127.0.0.1',
        port: 4000,
        url: '/',
        method: request.method,
        headers: request.headers
      },
      serverResponse => {
        // 第三步:收到服务器的响应
        var body = ''
        serverResponse.on('data', chunk => {
          body += chunk
        })
        serverResponse.on('end', () => {
          console.log('The data is ' + body)
          // 第四步:将响应结果转发给浏览器
          response.end(body)
        })
      }
    )
    .end()
})
server.listen(3000, () => {
  console.log('The proxyServer is running at http://localhost:3000')
})
复制代码
// server2.js(http://localhost:4000)
const http = require('http')
const data = { title: 'fontend', password: '123456' }
const server = http.createServer((request, response) => {
  if (request.url === '/') {
    response.end(JSON.stringify(data))
  }
})
server.listen(4000, () => {
  console.log('The server is running at http://localhost:4000')
})
复制代码

上述代码经过两次跨域,值得注意的是浏览器向代理服务器发送请求,也遵循同源策略,最后在index.html文件打印出{"title":"fontend","password":"123456"}

6.nginx反向代理

实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。

使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。

实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

先下载nginx,然后将nginx目录下的nginx.conf修改如下:

// proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;
    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}
复制代码

最后通过命令行nginx -s reload启动nginx

// index.html
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
复制代码
// server.js
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
    var params = qs.parse(req.url.substring(2));
    // 向前台写cookie
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:脚本无法读取
    });
    res.write(JSON.stringify(params));
    res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
复制代码

7.window.name + iframe

window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

其中a.html和b.html是同域的,都是http://localhost:3000;而c.html是http://localhost:4000

 // a.html(http://localhost:3000/b.html)
  <iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
  <script>
    let first = true
    // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
    function load() {
      if(first){
      // 第1次onload(跨域页)成功后,切换到同域代理页面
        let iframe = document.getElementById('iframe');
        iframe.src = 'http://localhost:3000/b.html';
        first = false;
      }else{
      // 第2次onload(同域b.html页)成功后,读取同域window.name中数据
        console.log(iframe.contentWindow.name);
      }
    }
  </script>
复制代码

b.html为中间代理页,与a.html同域,内容为空。

 // c.html(http://localhost:4000/c.html)
  <script>
    window.name = '我不爱你'  
  </script>
复制代码

总结:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

8.location.hash + iframe

实现原理: a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

具体实现步骤:一开始a.html给c.html传一个hash值,然后c.html收到hash值后,再把hash值传递给b.html,最后b.html将结果放到a.html的hash值中。 同样的,a.html和b.html是同域的,都是http://localhost:3000;而c.html是http://localhost:4000

 // a.html
  <iframe src="http://localhost:4000/c.html#iloveyou"></iframe>
  <script>
    window.onhashchange = function () { //检测hash的变化
      console.log(location.hash);
    }
  </script>
复制代码
 // b.html
  <script>
    window.parent.parent.location.hash = location.hash 
    //b.html将结果放到a.html的hash值中,b.html可通过parent.parent访问a.html页面
  </script>
复制代码
 // c.html
 console.log(location.hash);
  let iframe = document.createElement('iframe');
  iframe.src = 'http://localhost:3000/b.html#idontloveyou';
  document.body.appendChild(iframe);
复制代码

9.document.domain + iframe

该方式只能用于二级域名相同的情况下,比如 a.test.comb.test.com 适用于该方式。 只需要给页面添加 document.domain ='test.com' 表示二级域名都相同就可以实现跨域。

实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

我们看个例子:页面a.zf1.cn:3000/a.html获取页面b.zf1.cn:3000/b.html中a的值

// a.html
<body>
 helloa
  <iframe src="http://b.zf1.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe>
  <script>
    document.domain = 'zf1.cn'
    function load() {
      console.log(frame.contentWindow.a);
    }
  </script>
</body>
复制代码
// b.html
<body>
   hellob
   <script>
     document.domain = 'zf1.cn'
     var a = 100;
   </script>
</body>
复制代码

三、总结

  • CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案
  • JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
  • 不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。
  • 日常工作中,用得比较多的跨域方案是cors和nginx反向代理

给大家推荐一个好用的BUG监控工具Fundebug,欢迎免费试用!

作者:浪里行舟
链接:https://juejin.im/post/5c23993de51d457b8c1f4ee1
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

By柏小白

vue 常用方法

vue 获取元素高度
<div ref="elememt" >
</div>

//获取高度值
var height= this.$refs.text.offsetHeight; //100
//获取元素样式值,element 为元素ref="element"
var heightCss = window.getComputedStyle(this.$refs.element).height; // 100px
// var heightCss =this.$refs.element.offsetHeight;  //100 不带px
//获取元素内联样式值
var heightStyle =this.$refs.element.style.height; // 100px
By柏小白

教你怎么实现缩短网址功能

本文将简单介绍,如何去完成一个缩短网址的功能。

Node.js + MySQL + Redis版本的源码地址:github

Demo地址:www.ecool.fun/shortLink

文章阅读大概需要8分钟。

什么是短链接

短链接,通俗来说,就是将长的URL网址,通过程序计算等方式,转换为简短的网址字符串。

大家经常可以从微博或者各类营销短信中,看到短链接,形式一般类似于 t.cn/xxxxxx,点击后,就能跳转到对应的页面。

早期短链接广泛应用于图片上传网站,通过缩短网址URL链接字数,达到减少代码字符串的目的。更便于使用者引用网址,写入代码中,节省字符数空间。常见于网店图片分类的使用,因有字符限制,运用短链接,达到外链图片的目的,自微博盛行以来,在微博字数有限的特色下,短链接也盛行于微博网站,以节省字数,给博主发布更多文字的空间。

将长链接转成短链接,一般是为了方便记忆或者传播。

需要完成的功能

从上面的介绍中,我们得出,缩短网址需要完成以下两个功能点:

  • 将长网址缩短成短链接
  • 点击生成短链接,能正常跳转到原来的长网址页面

全流程设计

其实上述功能点的原理很简单,简单描述一下:

  • 用户输入长网址,服务端接收后进行处理,并根据长网址的内容,生成一个短码,并将映射关系进行存储。然后根据短码拼接出短链接,返回给用户;
  • 用户点击短链接,服务器端根据短链接中的短码,查找到对应的长网址,并302跳转到对应的页面。

知识点:为什么要使用302跳转,而不是301跳转呢?

301是永久重定向,302是临时重定向。短地址一经生成就不会变化,所以用301是符合http语义的。但是如果用了301, Google,百度等搜索引擎,搜索的时候会直接展示真实地址,那我们就无法统计到短地址被点击的次数了,也无法收集用户的Cookie, User Agent 等信息,这些信息可以用来做很多有意思的大数据分析,也是短网址服务商的主要盈利来源。

引自知乎-武林的回答,原文链接

整个流程的设计如下图所示:

短网址全流程

可以看到,我用到了MySQLRedis来存储长网址和短码之间的映射关系。

MySQL想必大家都能理解,但是为什么要用 Redis 呢,直接用数据库不就好了吗?

这个主要是考虑到生成短链接,在投放之后的访问量会比较大,使用 Redis 缓存后,能有效降低数据库的压力。

生成短码的方法

通过上面的全流程设计,会发现主要的问题就是如何通过长网址,去生成对应的短码。

短码一般是由 [a - z, A - Z, 0 - 9] 这62 个字母或数字组成,短码的长度也可以自定义,但一般不超过8位。比较常用的都是6位,6位的短码已经能有568亿种的组合:(26+26+10)^6 = 56800235584,已满足绝大多数的使用场景。

目前比较流行的生成短码方法有:自增id摘要算法普通随机数

  • 自增id

该方法是一种无碰撞的方法,原理是,每新增一个短码,就在上次添加的短码id基础上加1,然后将这个10进制的id值,转化成一个62进制的字符串。

一般利用数据表中的自增id来完成:每次先查询数据表中的自增id最大值max,那么需要插入的长网址对应自增id值就是 max+1,将max+1转成62进制即可得到短码。

但是短码 id 是从一位长度开始递增,短码的长度不固定,不过可以用 id 从指定的数字开始递增的方式来处理,确保所有的短码长度都一致。同时,生成的短码是有序的,可能会有安全的问题,可以将生成的短码id,结合长网址等其他关键字,进行md5运算生成最后的短码。

10进制转成62进制的具体实现:

function string10to62(number) {
    const chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ';
    const charsArr = chars.split('');
    const radix = chars.length;
    let qutient = +number;
    let arr = [];
    do{
        let mod = qutient % radix;
        qutient = (qutient - mod) / radix;
        arr.unshift(charsArr[mod]);
    }while(qutient);
    return arr.join('');
}
复制代码
  • 摘要算法

摘要算法又称哈希算法,它表示输入任意长度的数据,输出固定长度的数据。相同的输入数据始终得到相同的输出,不同的输入数据尽量得到不同的输出。

算法思路:

1、将长网址通过 md5 运算,生成 32 字符的 hex string,分为 4 段,每段 8 个字符;

2、对这四段循环处理,取 8 个字节,将其看成 16 进制串,并与 0x3fffffff(30位1) 与操作,即超过 30 位的忽略处理;

3、这 30 位分成 6 段,每 5 位的数字作为字母表的索引取得特定字符,依次进行获得 6 位字符串。

4、总的 md5 串可以获得 4 个 6 位串,取里面的任意一个就可作为这个长网址的短链接 url 地址。

虽然几率很小,但是该方法依然存在碰撞的可能性,解决冲突会比较麻烦。不过该方法生成的短码位数,是固定的,也不存在连续生成的短码有序的情况。

  • 普通随机数

该方法是从62个字符串中随机取出一个6位短码的组合,然后去数据库中查询该短码是否已存在。如果已存在,就继续循环该方法重新获取短码,否则就直接返回。

该方法是最简单的一种实现,不过由于Math.round()方法生成的随机数属于伪随机数,碰撞的可能性也不小。在数据比较多的情况下,可能会循环很多次,才能生成一个不冲突的短码。

具体实现:

// 获取唯一的Link
async getShortLink() {
    const shortLink = this.generateShortLink();

    // 查询数据库中是否存在该链接,如果存在,就直接返回
    const searchResult = await this.searchByLinkInMySQL(shortLink);

    if (searchResult && searchResult.length > 0) {
    // 如果shortLink已经存在,就遍历重新生成
        return this.getShortLink();
    }
    return shortLink;

}

// 生成随机的Link
generateShortLink() {
    let str = '';
    const arr = [
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    ];

    for (let i = 0; i < 6; i++) {
        const pos = Math.round(Math.random() * (arr.length - 1));
        str += arr[pos];
    }
    return str;
}
复制代码

综上,比较推荐使用第一种方法来实现短码的生成。

广告时间

最后,欢迎大家star我们的人人贷大前端团队博客,所有的文章还会同步更新到知乎专栏掘金账号,我们每周都会分享几篇高质量的大前端技术文章。

作者:人人贷大前端技术中心
链接:https://juejin.im/post/5d2d33885188253a2e1b8626
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

By柏小白

JS正则大全

2019年JS正则大全(常用)

来源: any86.github.io/any-rule/

24小时制时间(HH:mm:ss)

/^((?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$)/

12小时制时间(hh:mm:ss)

/^(1[0-2]|0?[1-9]):[0-5]\d:[0-5]\d$/

base64格式

/^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i

数字/货币金额(支持负数、千分位分隔符)

/(^[-]?[1-9]\d{0,2}($|(,\d{3})*($|(\.\d{1,2}$))))|((^[0](\.\d{1,2})?)|(^[-][0]\.\d{1,2}))$/

数字/货币金额 (只支持正数、不支持校验千分位分隔符)

/(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/

银行卡号(16或19位)

/^([1-9]{1})(\d{15}|\d{18})$/

中文姓名

/^([\u4e00-\u9fa5·]{2,10})$/

新能源车牌号

/[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(([0-9]{5}[DF])|([DF][A-HJ-NP-Z0-9][0-9]{4}))$/

非新能源车牌号

/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/

车牌号(新能源+非新能源)

/^([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9 挂学警港澳]{1})$/

URL链接(网址)

/^((https?|ftp|file):\/\/)?([\da-z.-]+)\.([a-z.]{2,6})(\/\w\.-]*)*\/?/

手机号(严谨), 根据工信部2019年最新公布的手机号段

/^1((3[\d])|(4[5,6,7,9])|(5[0-3,5-9])|(6[5-7])|(7[0-8])|(8[\d])|(9[1,8,9]))\d{8}$/

手机号(宽松), 只要是13,14,15,16,17,18,19开头即可

/^1[3-9]\d{9}$/

手机号(最宽松), 只要是1开头即可, 如果你的手机号是用来接收短信, 优先建议选择这一条

/^1\d{10}$/

日期, 如: 2000-01-01或2000-1-1

/^\d{4}(-)([0-1][0-2]|\d)\1([0-2]\d|\d|30|31)$/

email地址

/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/

国内座机电话,如: 0341-86091234

/\d{3}-\d{8}|\d{4}-\d{7}/

一代身份证号(15位数字)

/^\d{8}(0\d|11|12)([0-2]\d|30|31)\d{3}$/

二代身份证号(18位数字),最后一位是校验位,可能为数字或字符X

/^\d{6}(18|19|20)\d{2}(0\d|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$/

身份证号, 支持1/2代(15位/18位数字)

/(^\d{8}(0\d|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/

帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线组合

/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/

纯中文/汉字

/^[\u4E00-\u9FA5]+$/

是否小数

/^\d+\.\d+$/

电话(座机)

/^0\d{2,3}-\d{7,8}$/

纯数字

/^\d{1,}$/

是否html标签

/<(.*)>.*<\/\1>|<(.*) \/>/

是否qq号格式正确

/^[1-9][0-9]{4,10}$/

是否由数字和字母组成

/^[A-Za-z0-9]+$/

纯英文字母

/^[a-zA-Z]+$/

纯小写英文字母组成

/^[a-z]+$/

纯大写英文字母

/^[A-Z]+$/

密码强度正则,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符

/^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$/

用户名正则,4到16位(字母,数字,下划线,减号)

/^[a-zA-Z0-9_-]{4,16}$/

ipv4地址正则

/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/

16进制颜色

/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/

微信号,6至20位,以字母开头,字母,数字,减号,下划线

/^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/

中国邮政编码

/^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/

只包含中文和数字

/^(([\u4E00-\u9FA5])|(\d))+$/

非字母

/[^A-Za-z]$/
By柏小白

vue中8种组件通信方式

vue中8种组件通信方式, 值得收藏!

之前写了一篇关于vue面试总结的文章, 有不少网友提出组件之间通信方式还有很多, 这篇文章便是专门总结组件之间通信的

vue是数据驱动视图更新的框架, 所以对于vue来说组件间的数据通信非常重要,那么组件之间如何进行数据通信的呢? 首先我们需要知道在vue中组件之间存在什么样的关系, 才更容易理解他们的通信方式, 就好像过年回家,坐着一屋子的陌生人,相互之间怎么称呼,这时就需要先知道自己和他们之间是什么样的关系。 vue组件中关系说明:

1563158686(1)

如上图所示, A与B、A与C、B与D、C与E组件之间是父子关系; B与C之间是兄弟关系;A与D、A与E之间是隔代关系; D与E是堂兄关系(非直系亲属) 针对以上关系我们归类为:

  • 父子组件之间通信
  • 非父子组件之间通信(兄弟组件、隔代关系组件等)

本文会介绍组件间通信的8种方式如下图目录所示:并介绍在不同的场景下如何选择有效方式实现的组件间通信方式,希望可以帮助小伙伴们更好理解组件间的通信。

1563158701(1)

一、props / $emit

父组件通过props的方式向子组件传递数据,而通过$emit 子组件可以向父组件通信。

1. 父组件向子组件传值

下面通过一个例子说明父组件如何向子组件传递数据:在子组件article.vue中如何获取父组件section.vue中的数据articles:['红楼梦', '西游记','三国演义']

// section父组件
<template>
  <div class="section">
    <com-article :articles="articleList"></com-article>
  </div>
</template>

<script>
import comArticle from './test/article.vue'
export default {
  name: 'HelloWorld',
  components: { comArticle },
  data() {
    return {
      articleList: ['红楼梦', '西游记', '三国演义']
    }
  }
}
</script>

复制代码
// 子组件 article.vue
<template>
  <div>
    <span v-for="(item, index) in articles" :key="index">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: ['articles']
}
</script>
复制代码

总结: prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。

2. 子组件向父组件传值

对于$emit 我自己的理解是这样的: $emit绑定一个自定义事件, 当这个语句被执行时, 就会将参数arg传递给父组件,父组件通过v-on监听并接收参数。 通过一个例子,说明子组件如何向父组件传递数据。 在上个例子的基础上, 点击页面渲染出来的ariticleitem, 父组件中显示在数组中的下标

// 父组件中
<template>
  <div class="section">
    <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
    <p>{{currentIndex}}</p>
  </div>
</template>

<script>
import comArticle from './test/article.vue'
export default {
  name: 'HelloWorld',
  components: { comArticle },
  data() {
    return {
      currentIndex: -1,
      articleList: ['红楼梦', '西游记', '三国演义']
    }
  },
  methods: {
    onEmitIndex(idx) {
      this.currentIndex = idx
    }
  }
}
</script>
复制代码
<template>
  <div>
    <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
  </div>
</template>

<script>
export default {
  props: ['articles'],
  methods: {
    emitIndex(index) {
      this.$emit('onEmitIndex', index)
    }
  }
}
</script>
复制代码

二、 $children / $parent

1563158473(1)

上面这张图片是vue官方的解释,通过$parent$children就可以访问组件的实例,拿到实例代表什么?代表可以访问此组件的所有方法和data。接下来就是怎么实现拿到指定组件的实例。

使用方法

// 父组件中
<template>
  <div class="hello_world">
    <div>{{msg}}</div>
    <com-a></com-a>
    <button @click="changeA">点击改变子组件值</button>
  </div>
</template>

<script>
import ComA from './test/comA.vue'
export default {
  name: 'HelloWorld',
  components: { ComA },
  data() {
    return {
      msg: 'Welcome'
    }
  },

  methods: {
    changeA() {
      // 获取到子组件A
      this.$children[0].messageA = 'this is new value'
    }
  }
}
</script>
复制代码
// 子组件中
<template>
  <div class="com_a">
    <span>{{messageA}}</span>
    <p>获取父组件的值为:  {{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messageA: 'this is old'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>
复制代码

要注意边界情况,如在#app上拿$parent得到的是new Vue()的实例,在这实例上再拿$parent得到的是undefined,而在最底层的子组件拿$children是个空数组。也要注意得到$parent$children的值不一样,$children 的值是数组,而$parent是个对象

总结

上面两种方式用于父子组件之间的通信, 而使用props进行父子组件通信更加普遍; 二者皆不能用于非父子组件之间的通信。

三、provideinject

概念:

provideinject 是vue2.2.0新增的api, 简单来说就是父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。

注意: 这里不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中回去数据

举例验证

接下来就用一个例子来验证上面的描述: 假设有三个组件: A.vue、B.vue、C.vue 其中 C是B的子组件,B是A的子组件

// A.vue

<template>
  <div>
	<comB></comB>
  </div>
</template>

<script>
  import comB from '../components/test/comB.vue'
  export default {
    name: "A",
    provide: {
      for: "demo"
    },
    components:{
      comB
    }
  }
</script>
复制代码
// B.vue

<template>
  <div>
    {{demo}}
    <comC></comC>
  </div>
</template>

<script>
  import comC from '../components/test/comC.vue'
  export default {
    name: "B",
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    },
    components: {
      comC
    }
  }
</script>
复制代码
// C.vue
<template>
  <div>
    {{demo}}
  </div>
</template>

<script>
  export default {
    name: "C",
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    }
  }
</script>
复制代码

四、ref / refs

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据, 我们看一个ref 来访问组件的例子:

// 子组件 A.vue

export default {
  data () {
    return {
      name: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      console.log('hello')
    }
  }
}
复制代码
// 父组件 app.vue

<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.name);  // Vue.js
      comA.sayHello();  // hello
    }
  }
</script>
复制代码

五、eventBus

eventBus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。

eventBus也有不方便之处, 当项目较大,就容易造成难以维护的灾难

在Vue的项目中怎么使用eventBus来实现组件之间的数据通信呢?具体通过下面几个步骤

1. 初始化

首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它.

// event-bus.js

import Vue from 'vue'
export const EventBus = new Vue()
复制代码

2. 发送事件

假设你有两个组件: additionNum 和 showNum, 这两个组件可以是兄弟组件也可以是父子组件;这里我们以兄弟组件为例:

<template>
  <div>
    <show-num-com></show-num-com>
    <addition-num-com></addition-num-com>
  </div>
</template>

<script>
import showNumCom from './showNum.vue'
import additionNumCom from './additionNum.vue'
export default {
  components: { showNumCom, additionNumCom }
}
</script>

复制代码
// addtionNum.vue 中发送事件

<template>
  <div>
    <button @click="additionHandle">+加法器</button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js'
console.log(EventBus)
export default {
  data(){
    return{
      num:1
    }
  },

  methods:{
    additionHandle(){
      EventBus.$emit('addition', {
        num:this.num++
      })
    }
  }
}
</script>
复制代码

3. 接收事件

// showNum.vue 中接收事件

<template>
  <div>计算和: {{count}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      count: 0
    }
  },

  mounted() {
    EventBus.$on('addition', param => {
      this.count = this.count + param.num;
    })
  }
}
</script>
复制代码

这样就实现了在组件addtionNum.vue中点击相加按钮, 在showNum.vue中利用传递来的 num 展示求和的结果.

4. 移除事件监听者

如果想移除事件的监听, 可以像下面这样操作:

import { eventBus } from 'event-bus.js'
EventBus.$off('addition', {})
复制代码

六、Vuex

1. Vuex介绍

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. Vuex 解决了多个视图依赖于同一状态来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上

2. Vuex各个模块

  1. state:用于数据的存储,是store中的唯一数据源
  2. getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
  3. mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
  4. actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
  5. modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护

3. Vuex实例应用

// 父组件

<template>
  <div id="app">
    <ChildA/>
    <ChildB/>
  </div>
</template>

<script>
  import ChildA from './components/ChildA' // 导入A组件
  import ChildB from './components/ChildB' // 导入B组件

  export default {
    name: 'App',
    components: {ChildA, ChildB} // 注册A、B组件
  }
</script>
复制代码
// 子组件childA

<template>
  <div id="childA">
    <h1>我是A组件</h1>
    <button @click="transform">点我让B组件接收到数据</button>
    <p>因为你点了B,所以我的信息发生了变化:{{BMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        AMessage: 'Hello,B组件,我是A组件'
      }
    },
    computed: {
      BMessage() {
        // 这里存储从store里获取的B组件的数据
        return this.$store.state.BMsg
      }
    },
    methods: {
      transform() {
        // 触发receiveAMsg,将A组件的数据存放到store里去
        this.$store.commit('receiveAMsg', {
          AMsg: this.AMessage
        })
      }
    }
  }
</script>
复制代码
// 子组件 childB

<template>
  <div id="childB">
    <h1>我是B组件</h1>
    <button @click="transform">点我让A组件接收到数据</button>
    <p>因为你点了A,所以我的信息发生了变化:{{AMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        BMessage: 'Hello,A组件,我是B组件'
      }
    },
    computed: {
      AMessage() {
        // 这里存储从store里获取的A组件的数据
        return this.$store.state.AMsg
      }
    },
    methods: {
      transform() {
        // 触发receiveBMsg,将B组件的数据存放到store里去
        this.$store.commit('receiveBMsg', {
          BMsg: this.BMessage
        })
      }
    }
  }
</script>
复制代码

vuex的store,js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
  // 初始化A和B组件的数据,等待获取
  AMsg: '',
  BMsg: ''
}

const mutations = {
  receiveAMsg(state, payload) {
    // 将A组件的数据存放于state
    state.AMsg = payload.AMsg
  },
  receiveBMsg(state, payload) {
    // 将B组件的数据存放于state
    state.BMsg = payload.BMsg
  }
}

export default new Vuex.Store({
  state,
  mutations
})
复制代码

七、localStorage / sessionStorage

这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。 通过window.localStorage.getItem(key)获取数据 通过window.localStorage.setItem(key,value)存储数据

注意用JSON.parse() / JSON.stringify() 做数据格式转换 localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题.

八 $attrs与 $listeners

现在我们来讨论一种情况, 我们一开始给出的组件关系图中A组件与D组件是隔代关系, 那它们之前进行通信有哪些方式呢?

  1. 使用props绑定来进行一级一级的信息传递, 如果D组件中状态改变需要传递数据给A, 使用事件系统一级级往上传递
  2. 使用eventBus,这种情况下还是比较适合使用, 但是碰到多人合作开发时, 代码维护性较低, 可读性也低
  3. 使用Vuex来进行数据管理, 但是如果仅仅是传递数据, 而不做中间处理,使用Vuex处理感觉有点大材小用了.

vue2.4中,为了解决该需求,引入了$attrs 和$listeners , 新增了inheritAttrs 选项。 在版本2.4以前,默认情况下父作用域的不被认作props的属性百年孤独,将会“回退”且作为普通的HTML特性应用在子组件的根元素上。接下来看一个跨级通信的例子:

// app.vue
// index.vue

<template>
  <div>
    <child-com1
      :name="name"
      :age="18"
      :gender="女"
      :height="158"
      title="程序员成长指北"
    ></child-com1>
  </div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
  components: { childCom1 },
  data() {
    return {
      name: "zhang",
      age: "18",
      gender: "女",
      height: "158"
    };
  }
};
</script>
复制代码
// childCom1.vue

<template class="border">
  <div>
    <p>name: {{ name}}</p>
    <p>childCom1的$attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
  props: {
    name: String // name作为props属性绑定
  },
  created() {
    console.log(this.$attrs);
     // { "age": "18", "gender": "女", "height": "158", "title": "程序员成长指北" }
  }
};
</script>
复制代码
// childCom2.vue

<template>
  <div class="border">
    <p>age: {{ age}}</p>
    <p>childCom2: {{ $attrs }}</p>
  </div>
</template>
<script>

export default {
  inheritAttrs: false,
  props: {
    age: String
  },
  created() {
    console.log(this.$attrs); 
    // { "name": "zhang", "gender": "女", "height": "158", "title": "程序员成长指北" }
  }
};
</script>
复制代码

总结

常见使用场景可以分为三类:

  • 父子组件通信: props$parent / $childrenprovide / inject ; ref ; $attrs / $listeners
  • 兄弟组件通信: eventBus ; vuex
  • 跨级通信: eventBus;Vuex;provide / inject 、$attrs / $listeners

今天就分享这么多,如果对分享的内容感兴趣,可以关注公众号「程序员成长指北」,或者加入技术交流群,大家一起讨论。

加入我们一起学习吧!

转:https://juejin.im/post/5d267dcdf265da1b957081a3?utm_source=gold_browser_extension

By柏小白

webstorm插件

File -> settings -> Plugins 即可调出设置中的插件选项。

或者直接快捷键 ctrl + alt + s也可调出设置菜单。

具体的插件安装方法不再赘述,不会的同学建议先看一下明河的:《webstorm入门指南》

接下来主要收录了一些常用的插件,方便查阅:

ideaVim

此插件可以让 webstorm 编辑器支持 vim。

.ignore

可以支持 .gitignore 的语法规则,并提供了一些自动将文件加入 .gitignore 中的功能。

eslint

语法检查的插件

AceJump

用于光标的快速定位与跳转,默认的快捷键是ctrl + ;,按快捷键之后,再按下自己想要将光标跳转到的目标的字母,这时会出现很多A,B,C,D,E…… 的序号,按下相应的字母即可跳转到相应的地方。(区分大小写)

1560500769-6514-ace-jump

点多次的话,好像对应不同的用法,待我再研究研究。。。。

Key Promoter

鼠标完成某项操作时,会提示相应的快捷键。

1560500772-5360-key-promoter

Markdown

支持 markdown 语法

AngularJS

支持 angularjs 的语法提示

activate-power-mode

atom 上的神器啊,抱着试一试的心态一搜,webstorm 上居然也有了,安装之后可以在 window -> activate-power-mode 中关闭震动以及开启彩色模式。

Material Theme UI

主题类的插件,可以改改界面颜色呀,文件图标啥的,至少比默认的稍微强那么点。。。。

1560500773-4107-material

CodeGlance

这也是强烈推荐的,用过 sublime 的同学会知道右侧有一个当前文件中代码的缩略图,这个插件可以让 webstorm 也具有此功能。

1560500771-1182-code-glance

 

Translation

  • 多翻译引擎
    • Google翻译
    • 有道翻译
    • 百度翻译
  • 多语言互译
  • 语音朗读
  • 自动选词
  • 自动单词拆分
  • 53e79ae43a865a4b83b62372089a3585

live-server 快速搭建服务

浏览器自动刷新,这里可以使用 live-server 很简单就能启动一个看起来很专业的本地服务。

你只需要全局安装live-server:

npm install -g live-server

并在项目根目录执行这条命令:

live-server

注意!文件路径需cd到文件夹

By柏小白

nginx配置ssl证书实现https访问

一.已签发的证书,下载 签发SSL证书自行google

二.

下载的文件有两个:

1,214292799730473.pem  或者 214292799730473.crt

2,214292799730473.key

三.

配置ca证书

1,nginx的安装目录为:/etc/nginx/。进入目录,增加cert/文件夹,把刚刚下载的两个文件上传到cert/文件夹中。

2,在/etc/nginx/vhost/下,增加web.xn--w0sz4as21fs7k.com.com文件。内容如下:

说明:下面的配置是对443端口和80端口进行监听,443端口要启用ssl。监听443端口的server配置可以仿照上面ca认证页面的nginx配置示例进行配置。

root节点笔者创建了一个web.xn--w0sz4as21fs7k.com/的文件夹,专门存放来自这个域名的请求以示区分。

server {
listen 443;
server_name web.xn--w0sz4as21fs7k.com; // 你的域名
ssl on;
root /var/www/web.xn--w0sz4as21fs7k.com; // 前台文件存放文件夹,可改成别的
index index.html index.htm;// 上面配置的文件夹里面的index.html
ssl_certificate cert/98989898989.pem;// 改成你的证书的名字
ssl_certificate_key cert/98989898989.key;// 你的证书的名字
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
index index.html index.htm;
}
}
server {
listen 80;

# 你的域名
server_name web.xn--w0sz4as21fs7k.com;

# 把http的域名请求转成https
rewrite ^(.*)$ https://$host$1 permanent;
}

By柏小白

webstorm关闭eslint检测

vue项目已经设置关闭eslint,但是代码还是很多标红线的地方,原因是webstorm这个ide默认启用了eslint,可以在设置中关闭

1558082214(1)

By柏小白

Invalid Host header 服务器域名访问出现的问题

项目是通过局域网访问来演示的,这个项目是采用Vue.js前后端分离的方式来做的,然而发现只能在本地访问,不能通过域名nginx反向代理访问。

通过服务器域名访问时是显示Invalid Host header,这是由于新版的webpack-dev-server出于安全考虑,默认检查hostname,如果hostname不是配置内的,将中断访问。可以在build目录中的webpack.base.config.js中添加如下webpack-dev-server配置:devServer: { disableHostCheck: true, }

By柏小白

frps在win10下通过winsw启动自动运行

使用方法:
1、下载最新版的 Windows Service Wrapper 程序[winsw],比如我下载的名称是 “winsw-1.9-bin.exe”, 百度相关下载地址:https://www.cr173.com/soft/101797.html

然后,把它命名成你想要的名字(比如: “winsw.exe”,当然,你也可以不改名)

2、将重命名后的 winsw.exe 复制到 frp_0.13.0_windows_386 的安装目录(我这里是 “C:\Frps\frp_0.13.0_windows_386″)

3、在同一个目录下创建一个Windows Service Wrapper的XML配置文件,名称必须与第一步重命名时使用的名称一致(比如我这里是 “winsw.xml”, 如果,你没有重命名,则应该是 “winsw-1.9-bin.xml”)

文件内容如下:

<service>
<id>frp</id>
<name>frp</name>
<description>用frps外网穿透</description>
<executable>frpc</executable>
<arguments>-c frpc.ini</arguments>
<logmode>reset</logmode>
</service>

4、命令行下执行以下命令,以便将其安装成Windows服务。

C:\Frps\frp_0.13.0_windows_386> winsw.exe install

OK,至此,完工,确认一下:我的电脑 右键 -> 管理 -> 服务 -> 是否有了个 frp 服务呢?

8

补充:

Windows Servcie Wrapper的命令格式如下:

# 安装服务

CMD:\> winsw.exe install

# 卸载服务

CMD:\> winsw.exe uninstall

By柏小白

webstorm通过sftp修改本地文件 直接上传到服务器上

在开发中需要修改本地环境文件 保持ctrl+s保存 直接上传到服务器上 下面是具体步骤

Tools–Deployment–Configuration 这时候Upload to等都是灰色的

1

打开之后设置

添加个服务 左侧绿色加号 添加 这时候会选择类型 SFTP 用的centos,windows就是ftp。填写服务名称 我写的home,然后确定下面的 ,填写具体的地址

  • SFTP host : 服务器IP地址域名
  • Port : 端口号
  • Root Path : 找到服务器上的需要的文件夹
  • User name:用户名 一般都是root
  • Password :密码 这里后面打勾 保持密码 Save password
  • 填写正确后测试下Test SFTP connection 成功后就可以使用
  • Web server root URL : 访问的地址 这里根据服务器的IP来访问 Open 下看是否正确打开

2

测试连接是否成功:Test Connection

3

配置ctl+s保存上传文件

Tools–Deployment–Options

5

如下选项需要更改

  • Upload changed files automatically to the default server 这里选择 On explicit save action (Ctrl + S)
  • Upload external changes 这里勾选
  • Override default permissions on files 权限777 或者775
  • Override default permissions on folders 权限775

4

如下图的配置 确定后就可以看到,打开相应的服务器文件

6

直接修改文件ctrl+s保存到服务器上

7

By柏小白

webstorm 通过ssh连接服务器

必要条件
服务器一台 ( 虚拟机也可以 ); 一般 linux 安装完成,配置好网络等相关参数之后,可以通过 ssh 工具来连接,连接之后就可以在本地电脑对远程服务器进行操作了. 今天无意间发现, webstorm 也自带了 ssh 连接工具;具体使用如下 :

1 在 Tools下拉菜单中,有个start ssh session.,如下图所示:

2

2 点击 Start SSH session , 跳出如下对话框 :

3

3 点击 Edit credentials … ,跳出如下对话框 :

1

4 配置相关参数, Host 是目标服务器的IP ,这里本人使用的是Centos 7 系统的虚拟机 ; Linux的 Port 默认是 22 ,用户名和密码就是你要连接的服务器的用户名和密码,配置好好之后点击 OK 按钮,即可连接 , 连接成功后,会自动打开Terminal 工具,显示如下

4

5 远程ssh连接的terminal乱码

5

By柏小白

有哪些鲜为人知,但是很有意思的网站?

扩展阅读

工具类

图片/视频工具

IT/AI/工具类

音乐/影视类

二次元/动漫类

文艺类

网盘/搜索类

文学/百科类

实用/行政类

趣味/无聊类

声音/太空类

怀旧类

游戏/测试类

网站之最

特别推荐

原文地址:

 

By柏小白

Linux中设置服务自启动的三种方式

有时候我们需要Linux系统在开机的时候自动加载某些脚本或系统服务

主要用三种方式进行这一操作:

ln -s                       在/etc/rc.d/rc*.d目录中建立/etc/init.d/服务的软链接(*代表0~6七个运行级别之一)

chkonfig                命令行运行级别设置

ntsysv                   伪图形运行级别设置

 

注意:1.这三种方式主要用于以redhat为基础的发行版

2.如果还不知道运行级别是什么,那么最好先看看相关资料再实验

 

第一种方式:ln -s 建立启动软连接

在Linux中有7种运行级别(可在/etc/inittab文件设置),每种运行级别分别对应着/etc/rc.d/rc[0~6].d这7个目录

Tips:/etc/rc[0~6].d其实是/etc/rc.d/rc[0~6].d的软连接,主要是为了保持和Unix的兼容性才做此策

 

这7个目录中,每个目录分别存放着对应运行级别加载时需要关闭或启动的服务

由详细信息可以知道,其实每个脚本文件都对应着/etc/init.d/目录下具体的服务

K开头的脚本文件代表运行级别加载时需要关闭的,S开头的代表需要执行

因此,当我们需要开机启动自己的脚本时,只需要将可执行脚本丢在/etc/init.d目录下,然后在/etc/rc.d/rc*.d中建立软链接即可

[root@localhost ~]# ln -s /etc/init.d/sshd /etc/rc.d/rc3.d/S100ssh

此处sshd是具体服务的脚本文件,S100ssh是其软链接,S开头代表加载时自启动

如果需要在多个运行级别下设置自启动,则需建立多个软链接

这种方式比较繁琐,适用于自定义的服务脚本

如果系统中已经存在某些服务(比如安装apache时就会有httpd服务项),可以使用下面的两种方式

 

第二种方式:chkconfig

如果需要自启动某些服务,只需使用chkconfig 服务名 on即可,若想关闭,将on改为off

在默认情况下,chkconfig会自启动2345这四个级别,如果想自定义可以加上–level选项

上面我们先将sshd服务的所有启动级别关闭,然后使用–level选项启动自定义级别

Tips:–list选项可查看指定服务的启动状态,chkconfig不带任何选项则查看所有服务状态

 

第三种方式:ntsysv 伪图形

ntsysvchkconfig其实是一样的,只不过加上了图形而已

启动ntsysv有两种方式,一是直接在命令行中输入ntsysv,二是使用setup命令,然后选择系统服务

默认情况下,当前运行级别为多少,在ntsysv中设置的启动服务的级别便是多少

比如,我当前的运行级别是3,那么我在伪图形界面中选择启动服务后,它的运行级别也会是3

如果想自定义运行级别可使用ntsysv –level方式

 

以上三种操作需要保证服务脚本文件可执行,并且要有root权限

其中,第一种方式多用于自定义脚本,第二、三种多用于系统已存在的服务

比如ftp、samba、ssh、httpd等等

并且,要做相关设置需要弄清楚运行级别的问题

 

Tips:如果想手动启动某服务,传统的方式是 /etc/init.d 服务名 start

实际上还可以这样,service 服务名 start

转自:https://www.cnblogs.com/nerxious/archive/2013/01/18/2866548.html

By柏小白

jenkins配置记录(1)–添加用户权限

 

这样我直接用QQ截图直接粘贴就可以把简书的外链图片全部替换成本地图片链接了。

前一阵子在线上部署了一套jenkins环境,作为线上代码发布平台使用。
部署记录:http://web.xn--w0sz4as21fs7k.com/?p=1895

下面重点记录下jenkins安装后的一些配置:

(1)添加用户权限

jenkins初次登陆后,要先注册一个用户作为管理员:

依次点击“系统管理”->“Configure Global Security”

1548228117-5908-6-20161101155031049-98639631

 

如下:
选择“启用安全”模式,
“安全域”->“Jenkins专用用户数据库”->不选择“允许用户注册”(如果此处选择了”允许用户注册“,那么任何人都可以注册,只是注册后没有任何的操作权限,登陆后会提示“Access Denied,没有Overall/Read权限”,还是需要在管理员账号下授权后才能操作)。先注册一个管理员账号,然后在管理员下创建普通账号,再授予这些账号相应的操作权限。
“授权策略”->“项目矩阵授权策略”,添加账号。

1548228114-9862-20161101155821643-1395315569

 

 

首先添加一个admin账号作为管理员,先”应用“,注意这个时候千万别点击“保存”。由于admin用户还没有注册,所以这里添加到权限策略内会显示红色,等后面将这个admin用户注册后就会变成正常的绿色。

1548228116-5046-20161101160747565-2035208245

如上,点击“应用”后,再点击回到“jenkins”首页,会提示进行注册,则使用上面添加的管理员账号admin进行注册

1548228113-2373-20161101161617236-510924690

 

注册成功后,就可以用管理员admin账号登陆了

1548228118-2462-20161101161240080-369601404

 

在管理员账号下就可以创建普通用户

依次点击“系统管理”->“管理用户”

1548228119-9716-20161101161442315-1791228697

 

1548228121-9704-20161101161518721-415777939

1548228122-1515-20161101161729580-1298369009

 

然后在“系统管理”->“Configure Global Security”的->“授权策略”->“项目矩阵授权策略”里授予wangshibo用户相应的权限。
由于wangshibo用户已经注册好了,所以这里添加进去后是绿色的

1548228124-1199-20161101162206315-1425033646

其他用户的添加步骤跟上面一样操作即可。

一般而言,除了运维人员(管理员)具有所有项目操作的权限外,一般只给开发人员(通常是项目组的负责人)Beta环境下的项目操作权限。如下,只给caogaokui和yuxiaogang这两个用户设置构建项目的权限。

 

“系统管理”->“管理用户”->“创建用户”

1548228125-4966-20161103011609580-190636434

“系统管理”->“Configure Global Security”->“授权策略”->“项目矩阵授权策略”

1548228126-7193-20161103012153799-1436603609

 

 

然后再相应的项目构建配置里,“启用项目安全”,将相应的用户添加进去,赋予操作权限,说明他们对该项目由构建的权限。如下:

1548228127-6465-20161103011954471-850219423

1548226500-9090-20161103011954471-850219423

 

By柏小白

Jenkins安装及入门配置

一、安装Jenkins

Jenkins是开源的,使用Java编写的持续集成的工具,在Centos上可以通过yum命令行直接安装。记录下安装的过程,方便以后查找。需要先安装Java,如果已经Java可以跳过该步骤。

安装Java

看到当前系统Java版本的命令:

java -version

如果显示Java版本号,说明已经正确安装,如果显示没有该命令,需要安装Java:
sudo yum install java

该命令如果检测到Java不存在可以直接安装Java,如果已存在则可以升级Java。

最新版本Jenkins安装**

首先要先添加Jenkins源:

sudo wget -O /etc/yum.repos.d/jenkins.repo http://jenkins-ci.org/redhat/jenkins.repo

sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key

添加完成之后直接使用yum命令安装Jenkins:

yum install jenkins

历史版本安装

通过yum直接安装的是最新版本的Jenkins,必须使用JDK1.8,,由于我们使用的是jdk1.7,所以需要安装历史版本的jenkins。以下是安装步骤。

一、下载历史版本

https://jenkins.io/网站,点击下载进入下载页面

1548227927-8497-2223516-d8a8a896f5499066
image

选择对应操作系统

1548227928-8663-2223516-e4e498d842434397
image

选择版本,由于2.54以上的版本需要JDK1.8,所以我们选择选择低版本,这里选择使用2.46.3的版本

1548227931-5418-2223516-5c68677cb31029ef
image

将下载下来的rpm文件上传到服务器

1548227934-9764-2223516-e055089c2e917312
image

首先先添加Jenkins源:

sudo wget -O /etc/yum.repos.d/jenkins.repo http://jenkins-ci.org/redhat/jenkins.repo

sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key

使用rpm命令安装jenkins

1548227932-4578-2223516-16b272eead3aae31
image

启动Jenkins

使用命令启动Jenkins:

sudo service jenkins start

Starting Jenkins [ OK ]

在浏览器中输入:http://<服务器ip>:8080/ 就可以进入Jenkins界面直接使用了 。

停止Jenkins服务的命令为:

sudo service jenkins stop

相关配置

Jenkins安装目录:

/var/lib/jenkins/

Jenkins配置文件地址:

/etc/sysconfig/jenkins

这就是Jenkins的配置文件,可以在这里查看Jenkins默认的配置。

cat jenkins

这里介绍下三个比较重要的配置:

  • JENKINS_HOME
  • JENKINS_USER
  • JENKINS_PORT

JENKINS_HOME是Jenkins的主目录,Jenkins工作的目录都放在这里,Jenkins储存文件的地址,Jenkins的插件,生成的文件都在这个目录下。

<pre class="md-fences md-end-block" lang="shell" contenteditable="false" cid="n105" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace; font-size: 0.9em; white-space: pre; display: block; break-inside: avoid; text-align: left; background: var(--code-block-bg-color); background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(221, 221, 221); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">## Path:    Development/Jenkins
## Description: Jenkins Continuous Integration Server
## Type:    string
## Default:   "/var/lib/jenkins"
## ServiceRestart: jenkins
#
# Directory where Jenkins store its configuration and working
# files (checkouts, build reports, artifacts, ...).
#
JENKINS_HOME="/var/lib/jenkins"</pre>

JENKINS_USER是Jenkins的用户,拥有$JENKINS_HOME和/var/log/jenkins的权限。

<pre class="md-fences md-end-block" lang="shell" contenteditable="false" cid="n108" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace; font-size: 0.9em; white-space: pre; display: block; break-inside: avoid; text-align: left; background: var(--code-block-bg-color); background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(221, 221, 221); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">## Type:    string
## Default:   "jenkins"
## ServiceRestart: jenkins
#
# Unix user account that runs the Jenkins daemon
# Be careful when you change this, as you need to update
# permissions of $JENKINS_HOME and /var/log/jenkins.
#
JENKINS_USER="jenkins"</pre>

JENKINS_PORT是Jenkins的端口,默认端口是8080,我们这里修改为8000。

<pre class="md-fences md-end-block" lang="shell" contenteditable="false" cid="n111" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Consolas, &quot;Liberation Mono&quot;, Courier, monospace; font-size: 0.9em; white-space: pre; display: block; break-inside: avoid; text-align: left; background: var(--code-block-bg-color); background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(221, 221, 221); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">## Type:    integer(0:65535)
## Default:   8080
## ServiceRestart: jenkins
#
# Port Jenkins is listening on.
# Set to -1 to disable
#
JENKINS_PORT="8000"</pre>

主要问题

一、没有那个文件或目录

1548227935-5894-2223516-a5955ae08a2a75fb
image

这是由于找不到jdk目录报的错,只要修改/etc/init.d/jenkins配置文件,配置下jdk目录就好了。

1548227937-8885-2223516-b03bf8102d7619b6
image

保存,重新启动,启动成功

1548227938-4914-2223516-6dc3cab073eb5792
image

二、Jenkins的访问

验证jenkins是否运行正常,通过访问http://localhost:8000

这里有一个临时密码需要输入,在/home/tomcat/.jenkins/secrets/initialAdminPassword里面

1548227939-5880-2223516-6411c1e94adceae7
image

输入密码进入:

1548227941-1161-2223516-33c7ded732879540
image

这里选择把建议的插件全装上去

1548227942-3286-2223516-a03b47b94c74d5a6
image

–设置用户名和密码

1548227943-8985-2223516-84300bb4d31c1088
image

保存,进入主界面

1548227945-5214-2223516-10d4e95114675bb5
image

三、主要配置

系统配置

点击系统管理-系统设置配置邮件通知,通过Test测试,能收到邮件表明配置成功

1548227946-3405-2223516-c62e65781d022202
image

点击系统管理-管理插件,安装SSH插件

1548227948-8138-2223516-527b98d617bc4c88
image

这样,在配置邮件的下面就会有Publish over ssh选项

1548227949-3719-2223516-713f24e65b1910ca
image

配置远程ssh主机地址,通过Test返回success表示配置成功

1548227952-5047-2223516-51bfb993fe4c709a
image

点击保存

全局工具配置

系统管理-全局工具配置,配置JDK,Git,Maven等信息。

1548227954-7097-2223516-b0136eba51d2beec
image

四、新建项目

安装Git plugin插件,注意,由于我们使用的是旧版的jerkin,所以直接安装Git plugin插件是失败的,所以我们需要自己手动下载安装。

插件手动安装步骤

1、下载插件

首先,在可选插件上面过滤输入git,就可以搜索到Git plugin插件,点击此插件

1548227956-4589-2223516-cd7d49d118b052fa
image

进入后查看ID,知道ID为git。

1548227958-3467-2223516-cd21acfaa3bad49f
image

浏览插件网站http://updates.jenkins-ci.org/download/plugins/,找到对应插件git,点击进去。

1548227960-3429-2223516-aa13567c5a636dab
image

注意不要选择最新版本,我们这里选择历史版本,即3.5.0下载

1548227962-7282-2223516-4f882cce085be92a
image

下载到本地。

1548227964-8430-2223516-bee71e0a169d7edc
image

2、安装

还是系统管理-插件管理,选择高级,有上传插件选项,点击文件并上传。

1548227965-7604-2223516-57a523b11f50c926
image

上传之后,会开始安装,这里安装失败,点击查看原因,是缺少另外一个插件,这里需要先安装对应插件,按照上面的步骤。

1548227968-1497-2223516-7321b851ab95b5eb
image

插件安装过程中,如果需要重启jenkins的,可以直接在页面上出入restart重启

1548227970-7246-2223516-ea553e81048f2cc3
image

安装完成之后,在已安装插件中就可以找到对应插件

1548227971-3194-2223516-58050c7a3ac82d9b
image

项目创建

点击新建

1548227973-8848-2223516-58d43228387fb646
image

书写项目名称,选择风格

1548227974-6657-2223516-5a5b59732617a686
image

店家ok之后进入配置界面,在源码管理中选择git,输入项目url,设置分支。如果有账号密码,点击add添加

1548227976-5611-2223516-e8ca5e26b5f5340a
image

添加git的账号密码

1548227978-7010-2223516-f0ad49fd2fbf2ac1
image

构建,选择之前配置的maven,并配置要执行的操作,保存。

1548227980-5316-2223516-f9f0df87f5cec44f
image

点开立即构建,即开始构建项目

1548227982-6524-2223516-8187fa2df9edf5b8
image

构建成功后显示为蓝色,如果有问题显示为红色或黄色。点击任何一次可以进入。

1548227984-8122-2223516-a8bf894695bfa3bf
image

点击Console Output可以查看对应的控制台输出

By柏小白

frp-linux下配置运行

群晖NAS+frp发挥更大作用
Posted by SunnyRx on October 21, 2016
原文地址:http://www.sunnyrx.com/2016/10/21/simple-to-use-frp/

该文章于2018年7月7日将frp版本从0.13.0更新到0.20.0,下文针对frp 0.20.0配置。

NAS没有公网IP是一件很不方便的事情,尤其是在国内的网络环境,学校和小区内的用户通常都没有公网IP。为了解决这个问题,则需要内网穿透,而内网穿透的方法有很多种,例如使用花生壳ngrok等,该文章要介绍的是使用frp让群晖实现内网穿透。

实际上frp有官方的中文文档,上面的内容已经非常详尽,对相关操作比较熟悉的人可以直接阅读官方的中文文档。

什么是frp

frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内网穿透,对外网提供服务,支持 tcp, http, https 等协议类型,并且 web 服务支持根据域名进行路由转发。

准备

在使用frp之前,需要一台有公网IP的服务器(下文称外网主机),一台需要实现内网穿透的机器(下文称内网主机),SSH工具,以及一个域名(如果只是建立SSH反向代理则不需要域名)。

该文章中笔者所使用的服务器是朋友推荐的Vultr服务器,虽然服务器是在国外,但胜在带宽够,有需要的朋友可以注册一个。而需要实现内网穿透的机器则是笔者用上网本搭建的黑群晖。SSH工具使用的是Xshell 5。而域名笔者则是使用自己个人网站的域名。

开始使用

根据机器的操作系统,在Release页面中找到对应的frp程序,然后分别在外网主机和内网主机中下载它。

下面的所示范用的frp程序版本是以笔者的服务器为主的。

外网主机

SSH连接上外网主机后,使用wget指令下载frp。

wget https://github.com/fatedier/frp/releases/download/v0.20.0/frp_0.20.0_linux_amd64.tar.gz

使用tar指令解压tar.gz文件

tar -zxvf frp_0.20.0_linux_amd64.tar.gz

使用cd指令进入解压出来的文件夹

cd frp_0.20.0_linux_amd64/

外网主机作为服务端,可以删掉不必要的客户端文件,使用rm指令删除文件。

  1. rm -f frpc
  2. rm -f frpc.ini

接下来要修改服务器配置文件,即frps.ini文件。使用vi指令对目标文件进行编辑。

vi frps.ini

打开frps.ini后可以看到默认已经有很多详细的配置和示范样例,该文章仅以达到内网穿透为目的,所以这里选择删掉或注释掉里面的所有内容,然后根据群晖的情况,按照官方的中文文档添加以下配置。(这里的操作都使用vi命令,关于vi命令的使用方式这里不作详细介绍,可以自行搜索相关使用方法。)

  1. [common]
  2. bind_port = 7000
  3. vhost_http_port = 8080

[common]部分是必须有的配置,其中bind_port是自己设定的frp服务端端口,vhost_http_port是自己设定的http访问端口。

保存上面的配置后,使用以下指令启动frp服务端。(如果需要在后台运行,请往下翻阅关于后台运行的部分。)

./frps -c ./frps.ini

服务端的工作就到此结束了。

客户端

客户端前面的操作和服务端是一模一样的,这里不一一解释。

  1. wget https://github.com/fatedier/frp/releases/download/v0.20.0/frp_0.20.0_linux_amd64.tar.gz
  2. tar -zxvf frp_0.20.0_linux_amd64.tar.gz
  3. cd frp_0.20.0_linux_amd64
  4. rm -f frps
  5. rm -f frps.ini
  6. vi frpc.ini

客户端的配置如下

  1. [common]
  2. server_addr = x.x.x.x
  3. server_port = 7000
  4. [ssh]
  5. type = tcp
  6. local_ip = 127.0.0.1
  7. local_port = 22
  8. remote_port = 6000
  9. [nas]
  10. type = http
  11. local_port = 5000
  12. custom_domains = no1.sunnyrx.com
  13. [web]
  14. type = http
  15. local_port = 80
  16. custom_domains = no2.sunnyrx.com

上面的配置和服务端是对应的。

[common]中的server_addr填frp服务端的ip(也就是外网主机的IP),server_port填frp服务端的bind_prot

[ssh]中的local_port填群晖的ssh端口。

[nas]中的type对应服务端配置。local_port填群晖的DSM端口。custom_domains为要映射的域名,记得域名的A记录要解析到外网主机的IP。

[web]同上,local_port填群晖的web端口。这里创建了两个http反向代理是为了分别映射群晖两个重要的端口,500080,前者用于登录群晖管理,后者用于群晖的Web StationDS Photo

保存配置,输入以下指令运行frp客户端。(同样如果需要在后台运行,请往下翻阅关于后台运行的部分。)

./frpc -c ./frpc.ini

此时在服务端会看到”start proxy sucess”字样,即连接成功。

现在可以用SSH通过外网主机IP:6000和群晖建立SSH连接。通过浏览器访问no1.sunnyrx.com:8080打开群晖nas的管理页面,访问no2.sunnyrx.com:8080打开群晖Web Station的网站,DS Photo app可以连接no2.sunnyrx.com:8080进入DS Photo管理。

让frp在后台运行

虽然现在frp运作起来了,内网穿透也实现了,但这还是不够的。此时如果断开与服务端或者客户端的SSH连接(比如关掉了Xshell)也就中止了frp的运行。

保持frp运行是关键是让服务端的frp和客户端的frp在后台运行,这里提两个方法供参考,一个是使用screen指令,另一个是使用nohup指令。由于群晖的系统默认是没有screen指令的,这里也不提供安装screen的方法,所以推荐群晖直接使用nohup

其实服务端也直接用’nohup’就好了。

使用screen让frp在后台运行

下面的示范是运行服务端的frp,客户端就不示范了,前面提过群晖的系统没有screen指令。

首先使用screen指令创建一个会话。

screen -dmS frp

然后进入这个会话。

screen -r frp

最后使用运行frp的指令,在后面加上” &”。(如果之前断开了SSH连接,记得用cd指令进入frp的目录先。)

./frps -c ./frps.ini &

这样就让frp在后台运行了。

使用nohup指令

nohup指令的使用方法相对简单,只需要在nohup后面加上frp的运行指令即可。下面示范的指令是运行frp客户端。(同样,如果之前断开了SSH连接,记得用cd指令进入frp的目录先。)

nohup ./frpc -c ./frpc.ini &

这样就成功让frp在后台运行了。