xixijiang的主页


  • Home

  • Archives

  • Tags

shadowsocks与VPN区别

Posted on 2018-09-16

vpn

vpn(virtual private network, 虚拟私人网络), 是一种加密通讯技术。

vpn是一种统称,出现远早于GFW,所以它不是为翻墙而生的,它的目的是数据传输安全和网络匿名。

既然不是为翻墙而生的,那从翻墙的角度而言,vpn就存在诸多问题,最严重的一个就是流量特征过于明显,GFW目前已经能识别绝大部分的VPN,所以VPN得翻墙方式基本已经废了。

接入VPN就是接入了一个专有网络,你访问网络的所有流量都通过这个专有网络的出口出去,同时你的IP地址也变成了这个VPN分配的IP地址。

shadowsocks

是一种socks5的代理,而socks5的代理服务器则通过一个加密通道,将你的请求发给代理服务器,代理服务器将请求转发到目的地。

你没有加入任何新的网络,只是http/socks数据经过代理服务器的转发送出,并从代理服务器接收响应。

shadowsocks搭建

Posted on 2018-09-16

上一篇文章讲了一下shadowsocks翻墙原理(shadowsocks翻墙原理),这里要理论与实践结合了。开始~

这里我定义一些规则,避免混淆:

  • VPS = 你在搬瓦工购买的机器
  • ss服务端口号 = 执行shadowsocks.sh打印出的Your Server Port = shadowsocks.json里的port
  • ss服务IP = VPS的IP = 执行shadowsocks.sh打印出的Your Server IP
  • ss服务IP != shadowsocks.json里的server
  • 加密方式 = 执行shadowsocks.sh打印出的Your Encryption Method = shadowsocks.json里的method
  • ssserver = 你在VPS上启动的shadowsocks服务 = shadowsocks服务端
  • sslocal = 你在自己的电脑上安装的shadowsocks客户端 = shadowsocks客户端
  • ssserver密码 = 执行shadowsocks.sh打印出的password = shadowsocks.json里的password

文章里可能出现各种叫法,是为了适应不同的语境,不理解的话可以按照上述规则查看。

购买VPS

我是在搬瓦工购买的,当然这是师兄推荐的,否则以我的性格又得纠结死在哪儿买…哈哈哈

我买的是19.9$/年,512M,应该足够用了(嗯,其实就是看这个最便宜 嘿嘿)

搬瓦工套餐选择

配置

购买以后呢,就可以在我的账户里,点击Services-My service,就能看到了。

购买详情

然后点击右边的KiwiVM Control Panel进入管理界面。

管理界面

在Main control里可以看到相关配置信息

main control

然后你可以选择重装系统,也可以使用默认的。我是重新安装了,如果要重新安装,需要先在Main controls里点击stop进行关机,然后点右侧Install new OS,选择右边的操作系统(我选择的是centos-7-x86_64),然后点击同意,最后点击reload。大概等个1-2分钟,就好了。

reload

Reload后会给一个随机的root用户密码,以及随机端口号。然后点击Main controls,就可以看到你的VPS的IP地址(IP address)和端口号(SSH Port)了,这个是会用到的。(注意:这个端口号只有在登录的时候才会用到,后面配置shadowsocks的时候都不会用到。不要和shadowsocks服务的端口混淆。)

reload port

稍后,就可以进入虚拟机了,用自带的工具,或者其他ssh工具即可。

command

我觉得自带的不好用,用的是ssh,即在本地终端,用ssh连接:

1
ssh -p 分配的端口号(就是上面说的SSH Port)   VPS的IP地址

然后命令行会提示你输入密码,密码要是不知道,可以通过点击Root password modification,随机产生新的密码。

输入后,就可以进入登录到你的VPS操作了。

安装shadowsocks server端

这一步安装的就是上一篇文章中的ss server。

此处可以利用一个共享的python一键安装脚本。用root用户执行下面的语句:

1
2
3
4
yum install -y wget
wget --no-check-certificate -O shadowsocks.sh https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocks.sh
chmod +x shadowsocks.sh
./shadowsocks.sh 2>&1 | tee shadowsocks.log

成功后,就会打出日志,最后几行就是ss server的信息。在后面配置ss local的时候都会用到。

打印的信息

其中,Your password(密码)、Your Encryption Method(加密方式)是那个一键安装脚本里写的默认值,你可以修改那个脚本。密码可以不用改,Your Encryption Method这个是必须要改的,因为脚本默认的是aes-256-gcm,但是后面我们配置ss local时是没有这个选项的,所以我改成了aes-256-cfb。更改的方式也很简单,就是打开/etc/shadowsocks.sh这个脚本,然后找到下图里的内容,应该就在脚本的前面,然后把aes-256-cfb放到第一个(注意:本来aes-256-cfb本来不是在第一个的,第一个是aes-256-gcm,图中是我修改后的)。

sh脚本

改完后,重新执行以下命令:

1
./shadowsocks.sh 2>&1 | tee shadowsocks.log

执行完毕后,打印出来的信息就是你刚才修改的。

这只是修改密码和加密方式的一种方式。你还可以通过/etc/shadowsocks.json来修改。这种方式更简单明了,推荐推荐推荐!!!

如果你已经通过上面那个脚本修改了,打开这个文件,就会看到port、password、method和刚才修改后打印出的日志是一样的,但server不一样,是0.0.0.0(表示可以接收任意IP的请求),因此注意这个server不需要修改。

1
2
3
4
5
6
7
8
9
10
{
"server":"0.0.0.0",
"port":"xxxx",
"local_address":"127.0.0.1",
"local_port":1080,
"password":"teddysun.com",
"timeout":300,
"method":"aes-256-cfb",
"fast_open":false
}

如果你想修改port、password、method,就可以改这个文件。然后执行命令:

1
service shadowsocks restart

以后你想修改port、password、method,都可以只修改这个shadowsocks.json文件,然后重启一下shadowsocks就可以了。

到此,你的ss server就配置成功了。

当然,如果不放心,你可以做一些验证。

  • 在你自己的电脑,打开终端:

    • 测试你的VPS是否能ping通:

      1
      ping VPS的IP
    • 测试shadowsocks服务端口是否已通

      1
      telnet ss服务IP ss服务端口号
  • 在你的VPS机器上

    • 查看进程里是否有ssserver,如果打印出信息就代表ssserver在运行

      1
      ps aux |grep ssserver
    • 查看服务端口是否在监听,如果打印出信息,则代表端口号在监听

      1
      lsof -i:shadowsocks.json里的端口号
    • 查看服务状态

      1
      service shadowsocks status

另外,提供一些常用命令:

1
2
3
4
启动ssserver: service shadowsocks start
停止ssserver: service shadowsocks stop
重启ssserver: service shadowsocks restart
查看ssserver状态: service shadowsocks status

配置多用户

如果你想配置多用户,可以把shadowsocks.json改成下图,即把port和password合并:

1
2
3
4
5
6
7
8
9
10
11
12
13

{
"server":"0.0.0.0",
"local_address":"127.0.0.1",
"local_port":1080,
"port_password":{
"port1":"password1",
"port2":"password2"
},
"timeout":300,
"method":"aes-256-cfb",
"fast_open":false
}

安装shadowsocks客户端

我的是mac,大家可以根据自己的需要在网上下载,应该很多。这里给出一个地址下载链接。

安装后,就会在浏览器看到一个小图标,点击图标,选择服务器-服务器设置,就会弹出下图设置框:

sslocal配置

  • 点击+号,新建一个服务器
  • 地址是ss服务IP, 冒号后面的是端口号,就是ss服务端口号
  • 加密方式就是加密方式
  • 密码就是ssserver密码
  • 备注随便填
  • 点击确定
  • 另外有三种模式可以选择:PAC自动模式、全局模式、手动模式,PAC自动模式只有在访问被墙的网页时才会走ssserver,全局模式是所有访问的网页都会走ssserver,一般情况下我们就用PAC自动模式就好了,如果访问的某些网站比较慢,比如MDN,可以采用全局模式

至此,全部配置完毕。可以愉快的上网学习了!

可能出现的问题

配置后,发现还是无法翻墙。可能是ss服务端口号被防火墙阻挡了,因此,可以登录你的VPS,把防火墙关闭,或者把你的ss服务端口号加入到防火墙允许的列表中。

shadowsocks原理

Posted on 2018-09-16

之前一直用jj师兄搭建的shadowsocks服务器,前几天师兄说他的快到期了,不打算继续用了,并且他说刚好最近搬瓦工上最便宜的那款(19.9$/年)有货,于是乎我赶紧抓住机会买了。在搭建的过程中,虽然参考了很多大佬的教程,但也不是那么顺畅。于是决定自己梳理原理,从源头搞起,才能知道自己搭建的哪儿有问题。搞起~

翻墙的原理

防火长城(Great Firewall),简称GFW,就是常说的防火墙。由于不同的请求具有不同的数据特征,不同的协议具有不同的特征,比如HTTP/HTTPS这类请求,会很明确的告诉GFW它们要请求哪些域名,再比如TCP请求,它只会告诉GFW它们要请求哪个IP。防火墙采用黑名单方式,有域名黑名单和IP黑名单,像常见的国外网站谷歌,Facebook等都是在黑名单里的(封锁网站列表)。

也就是说,当你访问一个在黑名单列表里的网站时,就会被GFW拦截。但如果你有一台国外的服务器,并且这个服务器不在GFW的黑名单列表里,你就可以访问这台服务器了。因此,翻墙的一个方案就出来了:你想看什么网页,就告诉国外的机器,让国外的机器代理抓取,然后送回来。我们要做的就是保证你于国外机器通讯时不被GFW怀疑和窃听。

ssh tunnel是比较具有代表性的防窃听通讯隧道,通过ssh与境外服务器建立一条加密通道,此时GFW会将通讯当做普通的流量,但是随着用的人越来越多,GFW不干了,它开始通过一些流量特征分析,识别哪些连接是ssh隧道,并对它进行干扰,于是众多隧道都走不通了。

shadowsocks是如何翻墙的呢?

shadowsocks翻墙原理

这里要理解一个概念,就是socks5是什么?

这是一个比较成熟的代理协议,被大多数操作系统、浏览器、客户端软件支持,以前翻墙大多数是这个协议。

在GFW外面部署一个socks5协议的server,在GFW内使用一个支持socks5协议的客户端连接server,通过一定的认证和加密将数据通过TCP socket按照socks5协议发送数据包给server,在server端根据socks5协议解密后将明文包转发给某个GFW外的目标机器,从而实现翻墙。

因为数据都是经过协商加密的,所以一开始GFW是没法发现的,后来socks5协议大量被用于翻墙,GFW就开发相应的流量识别策略,毕竟连接协商阶段的流量是明文的,因此总有办法截掉你的连接,所以socks5开始变得不那么稳定了。

那么shadowsocks是如何做到的呢?

再看上面那张图片,shadowsocks在客户端本地部署了一个socks5 server端(也就是shadowsocks软件),用户使用一个支持socks5协议的客户端软件(如浏览器)连接这个socks5 server发送数据。

这个socks5 server是shadowsocks按照socks5协议开发的,它解密数据为明文后,按照shadowsocks自定义的一种新的通讯协议(GFW没时间来分析这个协议)加密后发送给GFW外部署的shadowsocks server,这个server按照shadowsocks定义的协议解密数据为明文,转发给墙外的目标机。

shadowsocks之所以在客户端本地启动一个socks5代理server,是因为socks5协议已被大多数操作系统、浏览器、客户端软件支持,因此我们只需要配置一下浏览器通过shadowsocks的socks5 server代理,那么shadowsocks就可以拦截到浏览器的流量,通过重新包装发送给远程的shadowsocks server了,这对于一个新的翻墙工具的普及无疑是很有利的。

参考资料

shadowsocks原理

shadowsocks原理简介及安装指南

感谢以上作者!

call与apply

Posted on 2017-07-29

this指向

直接看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function fn() {
console.log(this);
}
fn();//window

fn.call(1);//this=>数字1的包装类对象Number,相当于new Number(1)
fn.call('str'); // this=>字符串'str'的包装类对象String,相当于new String('str')

fn.call([1,2,3]); // this=>数组[1,2,3]
fn.call({}); // this=>对象

fn.call();//window
fn.call(null);//window
fn.call(undefined);//window

function fn1(name, age){
console.log(this, name, age);
}

fn1.call(null, '张三', 20); //window, '张三',20

fn1.apply(null, ['张三',20]); //windwo, '张三',20

数据类型的判断方法大汇总

Posted on 2017-07-29

总的来说,判断数据类型有很多种方法:

  • typeof 基本类型可以,但是对于复杂类型不准确
  • instanceof
  • constructor 比较准确(只是有时constructor会被改变)
  • isPrototypeOf
  • Object.prototype.toString.call() 最好的

typeof 返回值:

undefined:未定义 (只声明,但是未定义的也是undefined)

boolean: 布尔值

number: 数值

string: 字符串

object: 对象或null

function: 函数

constructor返回值:

就是其对应的构造函数(但是constructor有可能被修改)

instanceof返回值:

判断对象是否属于这个构造函数(查找对象与构造函数在原型链上是否有关系)

Object.prototype.toString.call()返回值

最准确的

[object Number]

[object String]

[object Boolean]

[object Array]

[object Object]

[object Function]

[object RegExp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 基本数据类型
var num = 3;
var str = "hello";
var b =false;

//复杂数据类型
var arr = ['1','2','3'];
var obj={};
var fn = function(){};
var reg = new RegExp(/a/g);

console.log(typeof num, num.constructor, Object.prototype.toString.call(num));
console.log(typeof str, str.constructor, Object.prototype.toString.call(str));
console.log(typeof b, b.constructor, Object.prototype.toString.call(b));

console.log(typeof arr, arr.constructor, Object.prototype.toString.call(arr));
console.log(typeof obj, obj.constructor, Object.prototype.toString.call(obj));
console.log(typeof fn, fn.constructor, Object.prototype.toString.call(fn));
console.log(typeof reg, reg.constructor, Object.prototype.toString.call(reg));

输出结果:

1
2
3
4
5
6
7
number function Number() { [native code] } [object Number] 
string function String() { [native code] } [object String]
boolean function Boolean() { [native code] } [object Boolean]
object function Array() { [native code] } [object Array]
object function Object() { [native code] } [object Object]
function function Function() { [native code] } [object Function]
object function RegExp() { [native code] } [object RegExp]

setTimeout中的this

Posted on 2017-07-28

setTimeout函数执行的代码是从执行上下文中调用的,与setTimeout调用的函数不同。

如果在非严格模式下,this将指向全局或者window对象,在严格模式下,是undefined。

看下面的例子:

myArray = ['zero', 'one', 'two'];
myArray.myMethod = function (sProperty) {
    alert(arguments.length > 0 ? this[sProperty] : this);
};

myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"

而如果用setTimeout呢?

setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, '1'); // prints "undefined" after 1.5 seconds

myArray.myMethod函数被传递给setTimeout,当它被调用时,它的this没有被设置,所以默认指向window对象,并且也没有选项能够传thisArg给setTimeout,使用call也是不行的。

setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error

解决方案

1、将调用的函数使用一个包裹函数包裹

setTimeout(function(){myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(function(){myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds

2、箭头函数

setTimeout(() => {myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(() => {myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds

3、bind

myArray = ['zero', 'one', 'two'];
myBoundMethod = (function (sProperty) {
    console.log(arguments.length > 0 ? this[sProperty] : this);
}).bind(myArray);

myBoundMethod(); // prints "zero,one,two" because 'this' is bound to myArray in the function
myBoundMethod(1); // prints "one"
setTimeout(myBoundMethod, 1000); // still prints "zero,one,two" after 1 second because of the binding
setTimeout(myBoundMethod, 1500, "1"); // prints "one" after 1.5 seconds

4、重写setTimeout和setInterval函数

/ Enable setting 'this' in JavaScript timers

var __nativeST__ = window.setTimeout, 
    __nativeSI__ = window.setInterval;

window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  var oThis = this, 
      aArgs = Array.prototype.slice.call(arguments, 2);
  return __nativeST__(vCallback instanceof Function ? function () {
    vCallback.apply(oThis, aArgs);
  } : vCallback, nDelay);
};

window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  var oThis = this,
      aArgs = Array.prototype.slice.call(arguments, 2);
  return __nativeSI__(vCallback instanceof Function ? function () {
    vCallback.apply(oThis, aArgs);
  } : vCallback, nDelay);
};

测试一下:

myArray = ['zero', 'one', 'two'];
myArray.myMethod = function (sProperty) {
    alert(arguments.length > 0 ? this[sProperty] : this);
};

setTimeout(alert, 1500, 'Hello world!'); // the standard use of setTimeout and setInterval is preserved, but...
setTimeout.call(myArray, myArray.myMethod, 2000); // prints "zero,one,two" after 2 seconds
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // prints "two" after 2.5 seconds

被誉为神器的requestAnimationFrame

Posted on 2017-07-28

setTimeout和setInterval的问题

我们知道js动画的核心技术是计时器。编写动画循环的关键是要知道延迟多长时间执行。一方面,循环时间必须足够短,才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能保证浏览器有能力渲染产生的变化。

大多数电脑显示器的刷新频率是60Hz,大概相当于每秒重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有所提升,因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms。

而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览区UI线程队列中,以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后才执行,

requestAnimationFrame的好处

requestAnimationFrame采用系统时间间隔,保持最佳绘制效率。不会因为间隔时间过短,造成过度重绘,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。

requestAnimationFrame的特点

  • requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
  • 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流。这当然意味着更少的CPU、GPU和内存使用量
  • requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的使用,并且如果页面不是激活状态的话,动画会自动暂停,有效节省了CPU开销。

requestAnimationFrame的使用

equestAnimationFrame的用法与settimeout很相似,只是不需要设置时间间隔而已。requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行。

requestID = requestAnimationFrame(callback); 
//控制台输出1和0
var timer = requestAnimationFrame(function(){
    console.log(0);
}); 
console.log(timer);//1

cancelAnimationFrame方法用于取消定时器

//控制台什么都不输出
var timer = requestAnimationFrame(function(){
    console.log(0);
}); 
cancelAnimationFrame(timer);

cancelAnimationFrame的兼容性

IE9-浏览器不支持该方法,可以使用setTimeout来兼容。

if(!window.requestAnimationFrame){
        var lastTime = 0;
        window.requestAnimationFrame = function(callback){
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0,16.7-(currTime - lastTime));
            var id  = window.setTimeout(function(){
                callback(currTime + timeToCall);
            },timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        }
    }

取消动画

if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
    }

webpack相关

Posted on 2017-07-24

vue踩坑之路

Posted on 2017-07-24

1、检测数组

Vue不能检测到对象属性的添加或者删除。由于Vue在初始实例化时对属性执行getter/setter转化过程,所以属性必须在data对象上存在才能让vue转换它,这样才能让它是响应的。

对于数组,

  • 直接利用索引修改某个项目,即:vm.items[indexOfItem] = newValue;
  • 直接修改数组的长度,即:vm.items.length = newLength;

这两种旧方式虽然可以修改数组,但是不会同步显示在view视图中,需要改成如下方式:

  • 修改数组中某个项目
1
2
3
4
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
  • 修改数组的长度
1
vm.items.splice(newLength)

2、检测对象

Vue不能检测到对象属性的添加或者删除。由于Vue在初始实例化时对属性执行getter/setter转化过程,所以属性必须在data对象上存在才能让vue转换它,这样才能让它是响应的。例如:

var vm = new Vue({
    data: {
        a:1
    }
})
// vm.a是响应的
vm.b = 2;
// vm.b 是非响应的

那有什么办法是在实例创建之后添加属性并且让它是响应的?

对于Vue实例,可以使用$set(key, value)实例方法。Vue不允许在已经创建的实例上动态添加新的根级响应式属性(root-level reactive property),然而它可以使用Vue.set(object,key, value)方法将响应属性添加到嵌套的对象上。

1
Vue.set(vm.someObject, 'b',2)

或者

1
2
3
4
	this.$set(this.someObject, 'b', 2)
```

有时候想向已有对象上添加一些属性,例如使用 Object.assign() 或 _.extend() 方法来添加属性。但是,添加到对象上的新属性不会触发更新(**但是呢,亲测,如果放在methods里,是可以触发更新的呀!!!只是在console中不会更新,有点奇怪!!!**)。在这种情况下可以创建一个新的对象,让它包含原对象的属性和新的属性:

// 代替 Object.assign(this.someObject, { a: 1, b: 2 })
this.someObject = Object.assign({}, this.someObject, { a: 1, b:2})
`

综上,我们来举个例子:

如下代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>vue</title>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <style>
    li:hover {
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="wrap">
    <ul>
      <li v-for="item,index in items" v-on:click="handle(index)">
        <span>{{item.name}}</span>
        <span>{{numbers[index]}}</span>
      </li>
    </ul>
  </div>
  <script>
    var vm = new Vue({
      el: ".wrap",
      data: {
        numbers: [],
        items: [
          {name: 'jjj'},
          {name: 'kkk'},
          {name: 'lll'},
        ]
      },
      methods: {
        handle: function (index) {
          // WHY: 更新数据,view层未渲染,但通过console这个数组可以发现数据确实更新了
           if (typeof(this.numbers[index]) === "undefined" ) {
             this.numbers[index] = 1;
           } else {
             this.numbers[index]++;
           }
        }
      }
    });
  </script>
</body>
</html>
1、我们的本意是点击列表,对应的number就加1,可是,我们发现,浏览器不管怎么点都没有变化:

这里写图片描述

此时是不是应该换成我们的正确方法啦~~

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>vue</title>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <style>
    li:hover {
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="wrap">
    <ul>
      <li v-for="item,index in items" v-on:click="handle(index)">
        <span>{{item.name}}</span>
        <span>{{numbers[index]}}</span>
      </li>
    </ul>
  </div>
  <script>
    var vm = new Vue({
      el: ".wrap",
      data: {
        numbers: [],
        items: [
          {name: 'jjj'},
          {name: 'kkk'},
          {name: 'lll'},
        ]
      },
      methods: {
        handle: function (index) {
          // WHY: 更新数据,view层未渲染,但通过console这个数组可以发现数据确实更新了
           if (typeof(this.numbers[index]) === "undefined" ) {
             // this.numbers[index] = 1;
             Vue.set(this.numbers, index, 1);
           } else {
             // this.numbers[index]++;
             Vue.set(this.numbers, index, ++this.numbers[index]);
           }
        }
      }
    });
  </script>
</body>
</html>

这次发现了,哇塞,变了!

这里写图片描述

另外,大家试试第二种正确的方式,把Vue.set(this.numbers, index, 1);换成this.$set(this.numbers, index, 1);,把Vue.set(this.numbers, index, ++this.numbers[index]);换成this.$set(this.numbers, index, ++this.numbers[index]),会发现也是会变化哦~

当然我们这里说的变化时视图的变化,console里的数据老方法旧方法都是可以变的。

2、点击时给items中元素添加一个属性,{“id”: index}

这里有点奇怪~~~

  • 如果是用老方法,也是可以渲染的,这跟网上的一些说法不同,只是在console中直接这么用确实不能渲染。

    methods: {
        handle: function (index) {
          // WHY: 更新数据,view层未渲染,但通过console这个数组可以发现数据确实更新了
           if (typeof(this.numbers[index]) === "undefined" ) {
             // this.numbers[index] = 1;
             // Vue.set(this.numbers, index, 1);
             // this.$set(this.numbers, index, 1); 
    
             Object.assign(this.items[index], {"id": index});
           } else {
             // this.numbers[index]++;
             // Vue.set(this.numbers, index, ++this.numbers[index]);
             this.$set(this.numbers, index, ++this.numbers[index])
           }
        }
      }
    

这样也是可以的!

如果把Object.assign(this.items[index], {"id": index});改成新方法Vue.set(this.items, index, (Object.assign({},this.items[index], {"id": index})));,当然也没问题,而且在console中使用也没问题。

所以为了确保无论如何都会渲染,那就采用新方法吧!!!

vue还是react

Posted on 2017-07-24

前两天有人给建议,让学学react,周末时间学习了一下,并做了一些demo。初学者可以参考我的github。在学习以及做demo的过程,发现其实react和vue有很多相像的地方,于是想对比一下这两个框架。

1、设计思想

react和vue设计思想是类似的,都是组件化开发(component),双向数据绑定,props参数传递,组件通信,state状态管理器,lifecircle生命周期等。

2、代码结构

react和vue代码结构有很大不同,vue采用模板,需要学习整套模板指令(当然也不难);react使用的JSX,不需要学习模板指令,只需要会HTML和js就行了。

从这个角度说,vue的模板结构

优点:

  • 更容易理解
  • 功能和布局分开

缺点:

  • 需要学习模板语法(不过这也很简单啦)

react的JSX:

优点:

  • 不需要学习指令,只需要会HTML和js
  • 渲染函数更易于调试和测试(vue也提供了使用模板或者渲染函数的选项)
  • 安全(这个待找资料)

缺点:

  • 对新手而言不如vue容易上手

3、状态(state)

  • react里的数据是不可变的(immutable)

必须使用setState方法

why?

react是通过比较当前状态和前一个状态的区别来决定何时以及如何重新渲染DOM的内容,因此需要使用不可变得状态。

  • vue中数据是可变的(mutated)

vue是如何对状态进行管理的?

当向状态中添加一个新对象时,vue将遍历其中的所有属性,并且将他们装换为getter,setter方法。然后vue的响应系统开始对该状态的跟踪弄,当该状态的内容发生变化时,就会自动重新渲染DOM。

不过vue的响应系统还是有些坑的,比如它无法检测属性的添加和删除,以及某些数组的更改,这时候就要用Vue API中的类似于React的set方法的措施来解决。

4、项目规模

  • 如果你的应用需要尽可能的小和快,请使用vue

vue的渲染系统比react的更快。react压缩版本体积是vue的两倍大,但不能带来双倍的功能。

  • 如果你计划构建一个大型应用程序,请使用react(大型项目需要透明度和可测试性)

vue的模板容易出现难以注意到的运行时错误,同时也不容易测试,重构和分解。

相比之下,js模板可以组织成经过良好分解,且使用DRY(don’t repeat yourself-避免重复代码)原则的代码的组件,因而具有更强的可重用性和可测试性。

vue也有组件系统和渲染函数,但是react渲染系统可配置型更强,并包含如shallow rendering这样的特性,可结合react的测试工具一起使用,从而大大提高代码的可测试性以及可维护性。

5、适用于web端和原生App的框架

React Native适用于移动端开发,所以react更适合同时兼顾web端和原生App的项目。

1…345…8
xixijiang

xixijiang

切莫停下前进的脚步

74 posts
1 categories
12 tags
© 2019 xixijiang
Powered by Hexo
Theme - NexT.Muse