分类 编程 下的文章

我们知道做深拷贝的时候可以使用递归的方式也可以用 JSON.stringify + JSON.parse 这种看起来简单的方式。
那么 JSON.stringify + JSON.parse 这种方式真的好用吗?

我的经验告诉我:

JSON.stringify + JSON.parse 做深拷贝不安全,而且在大数据量的情况下存在性能问题,不推荐使用

下面我们主要围绕不安全的问题进行讨论,对于性能的问题简单提下。

为何不安全

不安全主要体现在两个方面:

  1. 拷贝过程中数据失真、丢失
  2. 处理特殊数据时候报错

数据失真、丢失

数据失真,丢失主要在这几种类型中有体现

Date对象拷贝后数据类型变成字符串

let obj = {
    d: new Date(),
};
console.log(JSON.parse(JSON.stringify(obj)));
// {d: "2020-08-12T04:47:40.958Z"}

正则对象、Error对象拷贝后变成空对象

let obj = {
    r: /\d+/gi,
    e: new Error('an error')
};
console.log(JSON.parse(JSON.stringify(obj)));
// {r: {}, e: {}}

对象里面的函数和undefined属性拷贝后属性丢失

let obj = {
    f: console.log,
    u: undefined
};
console.log(JSON.parse(JSON.stringify(obj)));
// {}

NaN、Infinity、-Infinity拷贝后变为null

let obj = {
    i: Infinity,
    l: -Infinity,
    n: NaN,

};
console.log(JSON.parse(JSON.stringify(obj)));
// {i: null, l: null, n: null}

改变对象的原型链

如果,对象的某个属性是由构造函数生成的,那么在拷贝后,他的 constructor 会指向 Object

var A = function () {
    this.a = 'a';
};
var a = new A();
var b = JSON.parse(JSON.stringify(a));
console.log(a.constructor, b.constructor);
// ƒ () {this.a = 'a'} ƒ Object() { [native code] }

特殊数据报错

这个简单的说就是如果对象中有环的话话就会报错,最简单的例子就是

console.log(JSON.parse(JSON.stringify(window)));

这个就会报错,所以在使用这种方式做深拷贝的时候也要注意环的问题。

为何性能差

关于性能的问题我这里不多说,推荐《如何提升JSON.stringify()的性能》这篇文章,这篇文章对JSON.stringify的性能问题说的很清晰,我也很认同。

参考文档

JSON.stringify深拷贝的缺点
如何提升JSON.stringify()的性能

应用举例

比如 AutoCAD、GoogleEarth、Unity、Unreal、PSPDKit、WebPack 等等。拿其中几个来简单说一下。

AutoCAD

这是一个用于画图的软件,在很长的一段时间是没有 Web 的版本的,原因有两个,其一,是 Web 的性能的确不能满足他们的需求。其二,在 WebAssembly 没有面世之前,AutoCAD 是用 C++ 实现的,要将其搬到 Web 上,就意味着要重写他们所有的代码,这代价十分的巨大。

而在 WebAssembly 面世之后,AutoCAD 得以利用编译器,将其沉淀了 30 多年的代码直接编译成 WebAssembly,同时性能基于之前的普通 Web 应用得到了很大的提升。正是这些原因,得以让 AutoCAD 将其应用从 Desktop 搬到 Web 中。

Google Earth

Google Earth 也就是谷歌地球,因为需要展示很多 3D 的图像,对性能要求十分高,所以采取了一些 Native 的技术。最初的时候就连 Google Chrome 浏览器都不支持 Web 的版本,需要单独下载 Google Earth 的 Destop 应用。而在 WebAssembly 之后呢,谷歌地球推出了 Web 的版本。而据说下一个可以运行谷歌地球的浏览器是 FireFox。

Unity 和 Unreal 游戏引擎

这里给两个油管的链接自己体验一下。

Unity WebGL 的戳这里:
https://youtu.be/rIyIlATjNcE
Unreal 引擎的戳这里:
https://www.youtube.com/watch?v=TwuIRcpeUWE

什么时候使用 WebAssembly?

说了这么多,我到底什么时候该使用它呢?总结下来,大部分情况分两个点。

对性能有很高要求的 App/Module/ 游戏。
在 Web 中使用 C/C++/Rust/Go 的库举个简单的例子。如果你要实现的 Web 版本的 Ins 或者 Facebook, 你想要提高效率。那么就可以把其中对图片进行压缩、解压缩、处理的工具,用 C++ 实现,然后再编译回 WebAssembly。

个人感觉,React 就像王者荣耀里的露娜,上手不容易,玩好更难,但上限很高,真的玩好了会非常秀。
Vue 就有点像亚瑟,上手容易,但上限不高,即使再熟练,也搞不出太秀的操作。

从团队角度:

  • 团队技术水平高,代码规范,用 React 更灵活。
  • 团队技术水平一般,用 Vue 更保险,出 bug 几率低点,也不容易写出很难维护的代码。

从个人角度:

  • 使用 React 的同时也是在加深自己对编程、函数式等等概念的理解,提升思考能力。慢慢的会有所积累和成长。
  • 而使用 Vue 一般能更快完成任务。但是个人感觉在技术方面的成长有限。

什么是注解 (Annotation)

注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理。

为什么要用注解

注解可以给类、方法上注入信息。

常见注解

java.lang包下存在着5个基本的Annotation,其中有2个Annotation我们是非常常见的了。

@Overried

@Overried是告诉编译器要检查该方法是实现父类的,可以帮我们避免一些低级的错误。

比如当我们写了@Overried之后,我们在实现equals()方法的时候,把euqals()打错了,那么编译器就会发现该方法并不是实现父类的,于是就会给予错误。

@Deprecated

该注解也非常常见,Java在设计的时候,可能觉得某些方法设计得不好,为了兼容以前的程序,是不能直接把它抛弃的,于是就设置它为过时。

自定义注解

我们自定义的注解是可以带成员变量的,定义带成员变量的注解叫做元数据Annotation

public @interface MyAnnotation {
    String username();
}

使用注解

// 注解拥有什么属性,在修饰的时候就要给出相对应的值
@MyAnnotation(username = "kangfenmao")
public void add() {}

读取注解

@MyAnnotation(username = "kangfenmao")
public void add() {
  MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
  String username = annotation.username();
}

总结

注入对象的步骤:

  1. 得到想要注入的对象属性
  2. 通过属性得到注解的信息
  3. 通过属性的写方法将注解的信息注入到对象上
  4. 最后将对象赋给类。

注解其实就是两个作用:

  1. 让编译器检查代码
  2. 将数据注入到方法、成员变量、类上

如果没有响应注解内容,注解可以说是没有实用价值。让注解真正发挥作用,主要就在于注解处理方法。

JDK 常用注解

  1. 基本 Annotation 在 lang 包下,用于常用于标记该方法,抑制编译器警告等
  2. 元 Annotaion 在 annotaion 包下,常用于修饰其他的 Annotation 定义

升级 macOS 10.15.6 后,发现 php 版本号已经升级到了 php7.3

运行 php -v

PHP 7.3.11 (cli) (built: Jun  5 2020 23:50:40) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.11, Copyright (c) 1998-2018 Zend Technologies

问题

但是比较坑的是默认没有安装 zip 扩展,而 Laravel 是需要 ext-zip 扩展包的:

composer global require laravel/installer

QQ20200901-170526@2x.png

brew 安装 [email protected]

brew update
brew install [email protected]
brew link [email protected] --force

必须要使用 force

编译安装 [email protected]

本文不再展开,参考这篇文章:https://segmentfault.com/a/1190000022699382

查考资料

discussions.apple.com
install-ext-zip-for-mac

我们在使用 function base component 的时候可以使用 useParams 来获取参数, 类似这样:

const { id } = useParams()

当我们使用 class base component 的时候该如何写好类型呢?

先说结论

import { RouteComponentProps } from 'react-router';

// example route
<Route path="/products/:name" component={ProductContainer} />

interface MatchParams {
    name: string;
}

interface Props extends RouteComponentProps<MatchParams> {
}

查看源代码

// from typings
export interface RouteComponentProps<P> {
  match: match<P>;
  location: H.Location;
  history: H.History;
  staticContext?: any;
}

export interface match<P> {
  params: P;
  isExact: boolean;
  path: string;
  url: string;
}

当我们升级 package.json 包后,容易出现下面的错误:

npm WARN [email protected] requires a peer of [email protected]>= 4.12.1 but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of @typescript-eslint/[email protected] but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of @typescript-eslint/[email protected] but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of [email protected] but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of [email protected] but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of [email protected]^5.0.0 || ^6.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of [email protected]>=6.1.0 but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of [email protected] - 6.x but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of [email protected]^3 || ^4 || ^5 || ^6 but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of [email protected]^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of [email protected]^1.8.18 but none is installed. You must install peer dependencies yourself.

audited 2211 packages in 7.724s

69 packages are looking for funding
  run `npm fund` for details

found 4992 low severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details

问题分析

package-lock.json 中锁定的包版本和新的包依赖的版本有冲突,所以 npm 不知道如何处理这些冲突。

解决办法

rm package-lock.json && rm -rf node_modules/ && npm install

注意⚠️

任何版本升级都可能产生新的问题,建议充分测试后才能上线运行

版本信息

react native: 0.62.2
react-native-apk-manager: ^1.1.0

错误摘要

java.lang.RuntimeException: Package manager has died at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:156)

错误信息

java.lang.RuntimeException: Package manager has died
    at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:139)
    at com.superhao.react_native_apk_manager.ApkManagerModule.isAppInstalled(ApkManagerModule.java:210)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
    at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
    at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
    at android.os.Looper.loop(Looper.java:148)
    at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
    at java.lang.Thread.run(Thread.java:818)
Caused by: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died
    at android.os.BinderProxy.transactNative(Native Method)
    at android.os.BinderProxy.transact(Binder.java:503)
    at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:2272)
    at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:134)
    ... 11 more
android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died
    at android.os.BinderProxy.transactNative(Native Method)
    at android.os.BinderProxy.transact(Binder.java:503)
    at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:2272)
    at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:134)
    at com.superhao.react_native_apk_manager.ApkManagerModule.isAppInstalled(ApkManagerModule.java:210)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
    at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
    at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
    at android.os.Looper.loop(Looper.java:148)
    at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
    at java.lang.Thread.run(Thread.java:818)

设备分布

image

问题解决

re: https://github.com/1556173267/react-native-apk-manager/issues/6

  1. 需要避免同时多个线程调用 getPackageInfo,这样容易导致内存溢出 (主要解决方案)
  2. 避免获取过多 package 信息,如下所示
pm.getPackageInfo("com.tencent.mm", PackageManager.GET_ACTIVITIES);
// 修改为 👇
pm.getApplicationInfo("com.tencent.mm", PackageManager.GET_META_DATA);

参考文档

Android 获取 PackageInfo 引发 Crash 填坑
Package Manager Died
Android Package manager has died with TransactionTooLargeException
分析 Package manager has died