react-native Android集成穿山甲
1. 在**android/app/build/gradle** 添加Maven的引用 ```js repositories { flatDir { dirs 'libs' } maven{ url'https://artifact.bytedance.com/repository/AwemeOpenSDK' } } dependencies { ... implementation(name: 'open_ad_sdk', ext: 'aar') } ``` 2. 在 android/app/libs下 添加 open_ad_sdk.aar (文件可在官网下载) 3. 在**android/app/src/main/AndroidManifest.xml** 添加权限控制 ```xml <<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 穿山甲广告开始 --> <!--必要权限,解决安全风险漏洞,发送和注册广播事件需要调用带有传递权限的接口--> <permission android:name="${applicationId}.openadsdk.permission.TT_PANGOLIN" android:protectionLevel="signature" /> <uses-permission android:name="${applicationId}.openadsdk.permission.TT_PANGOLIN" /> <!--可选权限--> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> <uses-permission android:name="android.permission.GET_TASKS"/> <!--可选,穿山甲提供“获取地理位置权限”和“不给予地理位置权限,开发者传入地理位置参数”两种方式上报用户位置,两种方式均可不选,添加位置权限或参数将帮助投放定位广告--> <!--请注意:无论通过何种方式提供给穿山甲用户地理位置,均需向用户声明地理位置权限将应用于穿山甲广告投放,穿山甲不强制获取地理位置信息--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 如果视频广告使用textureView播放,请务必添加,否则黑屏 --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!--demo场景用到的权限,不是必须的--> <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" /> <!-- 穿山甲3400版本新增:建议添加“query_all_package”权限,穿山甲将通过此权限在Android R系统上判定广告对应的应用是否在用户的app上安装,避免投放错误的广告,以此提高用户的广告体验。若添加此权限,需要在您的用户隐私文档中声明! --> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> ``` 4. 在**android/app/src/main/AndroidManifest.xml** 添加provider控制 ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android"> ... <application> <provider android:name="com.bytedance.sdk.openadsdk.TTFileProvider" android:authorities="${applicationId}.TTFileProvider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> <provider android:name="com.bytedance.sdk.openadsdk.multipro.TTMultiProvider" android:authorities="${applicationId}.TTMultiProvider" android:exported="false" /> </application> </manifest> ``` 5. 在android/app/src/main/res/xml目录下,新建一个xml文件file_paths,在该文件中添加如下代码 ```xml <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!--为了适配所有路径可以设置 path = "." --> <external-path name="tt_external_root" path="." /> <external-path name="tt_external_download" path="Download" /> <external-files-path name="tt_external_files_download" path="Download" /> <files-path name="tt_internal_file_download" path="Download" /> <cache-path name="tt_internal_cache_download" path="Download" /> </paths> ``` 6.在android/app/src/main/java/com/project_name目录下 新建TTAd目录,并添加以下3个文件 ```java // android/app/src/main/java/com/project_name/TTAd/TTAdModule.java package your_package_name; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.Arrays; import java.util.List; import javax.annotation.Nonnull; public class TTAdModule implements ReactPackage { @Nonnull @Override public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) { return Arrays.<NativeModule>asList( new TTAdFullScreenVideoModule(reactContext) ); } @Nonnull @Override public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) { return Arrays.<ViewManager>asList( ); } } ``` ```java // android/app/src/main/java/com/project_name/TTAd/TTAdFullScreenVideoModule.java package your_package_name; import android.util.Log; import androidx.annotation.Nullable; import com.bytedance.sdk.openadsdk.AdSlot; import com.bytedance.sdk.openadsdk.TTAdConstant; import com.bytedance.sdk.openadsdk.TTAdManager; import com.bytedance.sdk.openadsdk.TTAdNative; import com.bytedance.sdk.openadsdk.TTFullScreenVideoAd; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; import javax.annotation.Nonnull; public class TTAdFullScreenVideoModule extends ReactContextBaseJavaModule { public static final String REACT_CLASS = "TTAdFullScreenVideo"; public static final String EVENT_AD_SHOWED = "fullScreenVideoAdShowed"; public static final String EVENT_AD_VIDEO_BAR_CLICK = "fullScreenVideoAdClick"; public static final String EVENT_AD_VIDEO_CLOSE = "fullScreenVideoAdClose"; public static final String EVENT_AD_VIDEO_COMPLETE = "fullScreenVideoComplete"; public static final String EVENT_AD_SKIPPED_VIDEO = "fullScreenVideoSkipped"; public static final String EVENT_AD_FAILED_TO_LOAD = "fullScreenVideoAdFailedToLoad"; private static final String TAG = "FullScreenVideoActivity"; private TTFullScreenVideoAd mttFullVideoAd; public TTAdFullScreenVideoModule(@Nonnull ReactApplicationContext reactContext) { super(reactContext); } @Nonnull @Override public String getName() { return REACT_CLASS; } @ReactMethod public void showFullScreenVideoAd(String codeId) { try { initFullScreenVideoAd(codeId); Log.e("...ScreenVideoModule", codeId); } catch (Exception e) { Log.e("...ScreenVideoModule", e.getMessage()); } } private void showAd() { getCurrentActivity().runOnUiThread(new Runnable() { @Override public void run() { if (mttFullVideoAd != null) { mttFullVideoAd.showFullScreenVideoAd(getCurrentActivity(), TTAdConstant.RitScenes.GAME_GIFT_BONUS, null); } } }); } private void sendEvent(String eventName, @Nullable WritableMap params) { getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params); } @ReactMethod public void initFullScreenVideoAd(String codeId) { //step1:初始化sdk TTAdManager ttAdManager = TTAdManagerHolder.get(); //step2:(可选,强烈建议在合适的时机调用):申请部分权限,如read_phone_state,防止获取不了imei时候,下载类广告没有填充的问题。 TTAdManagerHolder.get().requestPermissionIfNecessary((MainActivity) getCurrentActivity()); //step3:创建TTAdNative对象,用于调用广告请求接口 TTAdNative mTTAdNative = ttAdManager.createAdNative((MainActivity) getCurrentActivity()); //step4:创建广告请求参数AdSlot,具体参数含义参考文档 AdSlot adSlot = new AdSlot.Builder() .setCodeId(codeId) .setSupportDeepLink(true) .setImageAcceptedSize(1080, 1920) .setOrientation(TTAdConstant.VERTICAL)//必填参数,期望视频的播放方向:TTAdConstant.HORIZONTAL 或 TTAdConstant.VERTICAL .build(); //step5:请求广告 mTTAdNative.loadFullScreenVideoAd(adSlot, new TTAdNative.FullScreenVideoAdListener() { @Override public void onError(int code, String message) { //TToast.show(getCurrentActivity(), message + "message"); WritableMap error = Arguments.createMap(); error.putInt("code", code); error.putString("message", message); sendEvent(EVENT_AD_FAILED_TO_LOAD, error); } @Override public void onFullScreenVideoAdLoad(TTFullScreenVideoAd ad) { mttFullVideoAd = ad; mttFullVideoAd.setFullScreenVideoAdInteractionListener(new TTFullScreenVideoAd.FullScreenVideoAdInteractionListener() { @Override public void onAdShow() { //TToast.show(getCurrentActivity(), "FullVideoAd show1"); sendEvent(EVENT_AD_SHOWED, null); } @Override public void onAdVideoBarClick() { //TToast.show(getCurrentActivity(), "FullVideoAd bar click"); sendEvent(EVENT_AD_VIDEO_BAR_CLICK, null); } @Override public void onAdClose() { //TToast.show(getCurrentActivity(), "FullVideoAd close"); sendEvent(EVENT_AD_VIDEO_CLOSE, null); } @Override public void onVideoComplete() { //TToast.show(getCurrentActivity(), "FullVideoAd complete"); sendEvent(EVENT_AD_VIDEO_COMPLETE, null); } @Override public void onSkippedVideo() { // TToast.show(getCurrentActivity(), "FullVideoAd skipped"); sendEvent(EVENT_AD_SKIPPED_VIDEO, null); } }); showAd(); } @Override public void onFullScreenVideoCached() { // 已废弃 请使用 onRewardVideoCached(TTRewardVideoAd ad) 方法 } @Override public void onFullScreenVideoCached(TTFullScreenVideoAd ad) { Log.e(TAG, "Callback --> onFullScreenVideoCached"); } // @Override // public void onRewardVideoCached() { // } }); } } ``` ```java // android/app/src/main/java/com/xiyuangeneroapp/TTAd/TTAdManagerHolder.java package your_package_name; import android.content.Context; import android.util.Log; import com.bytedance.sdk.openadsdk.TTAdConfig; import com.bytedance.sdk.openadsdk.TTAdConstant; import com.bytedance.sdk.openadsdk.TTAdManager; import com.bytedance.sdk.openadsdk.TTAdSdk; /** * 可以用一个单例来保存TTAdManager实例,在需要初始化sdk的时候调用 */ public class TTAdManagerHolder { private static boolean sInit; public static TTAdManager get() { if (!sInit) { throw new RuntimeException("TTAdSdk is not init, please check."); } return TTAdSdk.getAdManager(); } public static void init(Context context) { doInit(context); } //step1:接入网盟广告sdk的初始化操作,详情见接入文档和穿山甲平台说明 private static void doInit(Context context) { if (!sInit) { // TTAdSdk.init(context, buildConfig(context)); TTAdSdk.init(context, buildConfig(context), new TTAdSdk.InitCallback() { @Override public void success() { // Log.i(TAG, "success: " + TTAdSdk.isInitSuccess()); } @Override public void fail(int code, String msg) { // Log.i(TAG, "fail: code = " + code + " msg = " + msg); } }); sInit = true; } } private static TTAdConfig buildConfig(Context context) { return new TTAdConfig.Builder() .appId("5001121") .useTextureView(true) //使用TextureView控件播放视频,默认为SurfaceView,当有SurfaceView冲突的场景,可以使用TextureView .appName("APP测试媒体") .titleBarTheme(TTAdConstant.TITLE_BAR_THEME_DARK) .allowShowNotify(true) //是否允许sdk展示通知栏提示 .debug(true) //测试阶段打开,可以通过日志排查问题,上线时去除该调用 .directDownloadNetworkType(TTAdConstant.NETWORK_STATE_WIFI, TTAdConstant.NETWORK_STATE_3G) //允许直接下载的网络状态集合 .supportMultiProcess(false)//是否支持多进程 //.httpStack(new MyOkStack3())//自定义网络库,demo中给出了okhttp3版本的样例,其余请自行开发或者咨询工作人员。 .build(); } } ``` 7. 在 android/app/src/main/java/com/project_name/MainApplication.java 初始化 ```java public class MainApplication extends Application implements ReactApplication { @Override protected List<ReactPackage> getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List<ReactPackage> packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for example: // packages.add(new MyReactNativePackage()); packages.add(new TTAdModule()); // 加这里 return packages; } @Override public void onCreate() { super.onCreate(); TTAdManagerHolder.init(this); // 加这里 SoLoader.init(this, /* native exopackage */ false); if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for this app. DefaultNewArchitectureEntryPoint.load(); } ReactNativeFlipper.initializeFlipper(this,getReactNativeHost().getReactInstanceManager()); } } ```
0
2023-09-04 18:49
kernel_task占用大量CPU
# kernel_task占用大量CPU **设备:** MBP2018 **显示器:** 通过雷电口转DP连接 LG显示器 **解决方案:** 1、重置SMC 2、去天才吧看看 3、清理灰尘试试(不建议) 4、删除相关配置(不建议) 5、拔掉外接显示器(问题解决) **总结:** 拔掉外接设备或更换接口
Mac
0
2023-07-16 21:31
一次iPad降级的历程
### 寻找方案: 1、使用leetdown 2、使用vieux 这里使用了leetdown,带有GUI,简单方便。 下载地址https://github.com/rA9stuff/LeetDown/releases ### 进入iPad的DFU模式 1、同时按住关机键和Home键10s 2、松开关机键、继续按住Home键5s 踩坑记录:进入了恢复模式,误以为恢复模式和DFU模式是同一个,折腾好久才发现不是同一个模式,又以为是不是需要越狱才可以开启DFU,往越狱方向又折腾。 最终查阅资料发现和使用的数据线有关系,**开启DFU时不能使用type-c ~ light** ### 下载ipsw文件 下载ipsw文件,直接从爱思组手上下载 加载ipsw文件,确认运行 ### 开始降级 1、选择ipsw文件 2、验证ipsw文件 3、解压 4、点击downgrade 5、当出现绿屏后再开机重启就降级成功了 ### 异常问题: 1、iPadmini2 dfu无法进入 A:检查数据线问题,按键时间或顺序不对(可按leetdown的helper的引导图来操作) 2、Failed to exploit device, please re-enter DFU mode and try again A:需要重新打开下leetdown程序 3、failed to restore A:再试一次,或更换leetdown版本 4、5 second cooldown, re-plug now A:取下拓展坞,使用typeC~lightning线,等待重试 ### 总结: 1、可提前下载好ipsw文件,大约2G 2、下载leetdown程序 3、数据线连接iPad,连接线不可用type-c ~ light(会进不了DFU模式),可使用USB ~ light + 扩展坞,如果mac中只有雷电接口,需要备一根type-c ~ light线,后面要用 4、进入DFU模式,时间和顺序要对 5、开始执行降级时提示 5 second cooldown, re-plug now, 提示这个就是一开始用的带扩展坞的数据线不行,这个时候把type-c ~lightning线的lightning头插进iPad 总耗时22:00~02:59 5小时 https://zhuanlan.zhihu.com/p/546144891
Mac
51
2023-04-29 02:46
Mac配置
* Homebrew macOS(或 Linux)缺失的软件包的管理器 官网:https://brew.sh/index_zh-cn 安装方式: ```sh /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` *
67
2023-02-09 09:10
导航页设计
搜索类 npm、github、掘金、微信搜一搜、百度、百度开发者、google、语雀、
68
2022-11-19 16:19
卖烧饼的故事
小郎开着一家烧饼铺, 店铺位置开在经度33.222、纬度101.222,人们如果导航去买烧饼,记录位置的经纬度通常是不好记,但是记住《小郎烧饼铺》这几个字相比经纬度还是容易记忆得 正向代理 反向代理 DNS解析 负载均衡 消息队列 数据缓存 QPS TPS PV:(page view)访问量, UV:用户量 DAU day active user MAU month active user 举例:VPN、明星的经纪人、商品代购、黄牛、二房东、
87
2022-11-19 14:01
正则表达式
`[ABC]` 匹配括号中字符, ```js const str = 'hello, 2019' str.match(/[hello]/g) // ['h', 'e', 'l', 'l', 'o'] str.match(/[helo]/g) // ['h', 'e', 'l', 'l', 'o'] str.match(/[abcde123]/g) // ['e', '2', '1'] ``` `[A-Z]` 区间匹配,匹配从A到Z 26个大写 ```js const str = 'Hello, 2019' str.match(/[A-Z]/g) // ['H'] ``` `[a-z]` 匹配从a到z 26个小写 ```js const str = 'Hello, 2019' str.match(/[a-z]/g) // ['e','l', 'l', 'o' ] ``` `[A-z]` 匹配a-z和A-Z ,共52个大小写 ```js const str = 'Hello, 2019' str.match(/[A-z]/g) // ['H', 'e','l', 'l', 'o' ] ``` `[a-Z]` 错误的区间 Range out of order in character class `[A-Za-z0-9]` 多个区间匹配, ```js const str = 'Hello, 2019' str.match(/[A-Za-z0-9]/g) // ['H', 'e','l', 'l', 'o', '2', '0', '1', '9' ] ``` `[^ABC]` 匹配除括号以外的字符, ```js const str = 'hello, 2019' str.match(/[^hello]/g) // [',', ' ', '2', '0', '1', '9'] str.match(/[^abcde123]/g) // ['h', 'l', 'l', 'o', ',', ' ', '0', '9'] ``` 修饰符 `+` 修饰符前面的字符出现次数 >=1,等价于 {1,} `*` 修饰符前面的字符出现次数 >=0,等价于 {0,} `?` 修饰符前面的字符出现次数 <=1,等价于 {0,1}(非贪婪限定符) `.` 匹配任意字符 `^` `$` `{n}` 字符出现的次数 `{n,m}` 字符出现的次数区间 非打印字符 `\d`匹配数字 `\D`匹配非数字 `\s`匹配空格和换行 `\S`匹配非空格和非换行 `\w`匹配单字字符(数字、字母、下划线) `\W`匹配非单字字符 `\` 转义字符 `\f`匹配换页 `\r`匹配回车 `\n`匹配换行 `\t`匹配水平制表符 `\v`匹配垂直制表符 `\` `\`
99
2022-06-06 10:39
状态管理redux和mobx
redux是通过reducer 纯函数来管理状态,返回一个新的state mobx 是通过类来组织state,通过`get`和`set` 来精准的更新state,
117
2022-04-08 11:15
敏捷开发
> 敏捷宣言 个体和互动 高于流程和工具 工作的软件 高于详尽的文档 客户合作 高于合同谈判 响应变化 高于遵循计划 ### 敏捷的价值观 承诺、专注、开放、尊重和勇气 ### 敏捷的原则 1. 我们最优先要做的是通过`尽早的`、`持续的`交付`有价值的`软件来使得客户满意 2. 即使到了开发的后期,也欢迎改变需求,敏捷过程利用变化来为客户创造竞争优势。 3. 经常性地交付可以工作的软件,交付的间隔可以从几个星期到几个月,交付的时间间隔越短越好。 4. 在整个项目开发期间,业务人员和开发人员必须天天都在一起工作 5. 围绕被激励起来的个体来构建项目。给他们提供所需的环境和支持,并且信任他们能够完成工作 6. 在团队内部,最具有效果并且富有效率的传递信息的方法,就是面对面的交谈 7. 工作的软件是首要的进度度量标准。 8. 敏捷过程提倡可持续的开发速度。责任人、开发者和用户应该能够保持一个长期的、恒定的开发速度。 9. 不断地关注优秀的技能和好的设计会增强敏捷能力。 10. 简单——使未完成的工作最大化的艺术——是根本的。 11. 最好的构架、需求和设计出自于自组织的团队。 12. 每隔一定时间,团队会在如何才能更有效地工作方面进行反省,然后相应地对自己的行为进行调整。 ### 敏捷的方法 常用`scrum`框架 3个角色:产品(Product Owner)、敏捷教练(Scrum Master)、团队(team) 3个工件:产品待办(Product Backlog)、迭代待办(Sprint Backlog)、产品增量(Product Increment) 5个事件:迭代计划会议、每日站会、迭代评审会议、迭代回顾会议、产品待办梳理会议 5个价值:承诺、专注、开放、尊重、勇气
128
2022-03-16 12:14
猴子管理法则
项目管理中猴子管理
项目管理
133
2022-03-02 18:20
项目管理五大过程组
- 启动过程组 设定项目的目标和授权,让项目团队“有事可做” - 计划过程组 指定工作路线,让项目团队“有法可依” - 执行过程组 让项目团队“有法必依” - 监控过程组 测量项目绩效(质量、进度、成本)偏差,让项目团队“违法必究” 并做到防患于未然 - 收尾过程组 了结项目,总结经验教训,项目文件归档,开会庆祝。
项目管理
112
2022-03-02 18:20
小程序开发
- 客服能力规则 客户发送一条,客服最多可回复5条,且在48小时内 客服不能主动搭话(之前可以设置一条欢迎语) - 小程序的生命周期 App级 onLaunch onShow onHide Page级 onLoad onShow onReady onHide onUnload Component级 created attached detached - 不得在小程序初始页面设置强制授权弹窗中断用户后续流程(部分类目可以) - 小程序引导关注公众号,使用微信提供的组件`official-account`,但是这个限制较多,还有一种方式是跳转至webview中,内容是引导长按识别的推文。 - 一些原生能力返回会触发App、Page 的onShow 和 的onHide (例如 地图,电话,分享, 客服会话, 选择图片) - 原生组件的事件监听不能使用 bind:eventname 的写法,只支持 bindeventname。原生组件也不支持 catch 和 capture 的事件绑定方式 - 获取手机号等一些用户信息时需要用户点击按钮来手动触发 - WXSS无法获取本地资源图片,可以使用网络图片,或者 base64,或者使用<image/>标签 - 小程序根目录下的 sitemap.json文件可以配置哪些页面可以被爬取,类似于rebot.txt - 注意iOS中 的日期格式问题 - WXML中{{}} 变量不能换行 - Textarea 组件 在 fixed布局下光标显示问题 - text 组件 可以识别 \r 等转义字符,view 组件不可以 - 小程序中 request请求需要白名单中声明才可以调用 - 小程序中 webview的地址 需要通过域名校验(绑定的公众号的文章例外),最多支持100个 - 服务域名和业主域名都需要走https协议 - 保存图片需要提前在服务域名的download中声明 - 小程序端绘制海报需要头像时记得把微信头像地址在download声明 - createOffScreenCanvas设置宽高是最大不能超出4000px,否则真机下会返回null(IDE正常返回 `OffscreenCanvas` 对象) - 小程序中不能长按识别普通二维码 - image组件添加`show-menu-by-longpress`可以长按弹出菜单,有腾讯相关二维码,可以识别 - tabbar 最多5项 最少2项,并且不能是分包页面 - 小程序运营指标和性能指标都达到优秀,可以享受2小时内审核特权 - 一个开发者的微信账号可以加50个小程序开发者 - 插件个数不能超出10个 - 小程序分享可通过参数来知道是通过右上角菜单转发还是点击按钮转发 - 小程序可以让用户主动订阅消息通知来达到 主动给用户推消息的目的 - 小程序组件中样式隔离策略: 默认组件中的样式是完全隔离的,不会受page和app影响,也不会影响父容器 配置styleIsolation属性: `isolated` 启用隔离(默认),`apply-shared` page中样式会影响component, component不会影响page,`shared`page和component互相影响 注:app和page中配置标签选择器会影响子组件 ```css view { color: red; } ``` 想使用个别app或page中样式又不想开启`styleIsolation`配置,可以使用 `~`,例如使用某个字体图标 `<text class="~iconfont ~icon-plus" />` 引用该组件的父组件的样式可以使用 `^`,例如 `<text class="^parent-component-class">我显示的是父组件的样式</text>` - 小程序中使用iconfont ,可以在iconfont配置勾选 WOFF2和 Base64,生成的css文件保存为wxss到项目,再使用 `@import './iconfont.wxss';`引入到app.wxss中 - 在component中,使用 `this.setUpdatePerformanceListener` 函数可以查看性能开销 - 小程序分包限制: 小程序包(主包/分包)不能超出2M,所有分包之和不能大于20M - 在微信开发者工具中调试面板中 Audits 可以进行小程序体检,会根据性能、体验、最佳实践来给出报告 - 小程序改名:发布前 个人和企业都有2次机会,发布后个人 2次/年,企业可通过认证改名(300元) - 小程序的某些功能需要认证对应的资质才能使用,否则审核不通过,比如视频播放,论坛,评论等功能 - 小程序码生成方式有临时和永久之分,临时没有数量限制,有效期30天,永久数量10万个 - 在app.wxss中声明image的宽高熟悉,可解决image标签mode为widthFix时,图片加载大小闪动的问题 ```css // app.wxss image { width:auto; height:auto; } ``` - 模块导入时, 路径不能省略文件夹中的index - wxs可以做一些数据处理, 速度相比JavaScript在iOS端快2~10倍,Android无差异 - 小程序中2018年1月开放了打开App的能力,于2021年5月回收,因为该能力被滥用导致(部分小程序阅读全文也要跳转至App才可以看) - 从其他App中跳转至微信小程序中,微信小程序没有再次冷启动的情况下是可以返回到App中的 - 小程序中分享好友API可以拿到用户点击了分享,但是拿不到分享成功的事件(也是被滥用导致能力回收) - 分享朋友圈目前只有Android支持,并且分享出去的页面为“单页模式”,并且部分接口和组件受限,可以想象为小程序之外的入口页面,纯展示。比如`wx.login`接口被限 - 跳转其他小程序只需要对方的 appid 和 path - 小程序中可以使用微信开发工具提供的代码依赖分析来优化包体积 - 大部分情况下图片资源会占包很大体积,优化体积可以从这里开始 - 小程序开发中未发布的小程序路径,生成小程序码会失败 - 小程序中 开发版小程序和体验版小程序 识别小程序码都会跳转至正式小程序 - 针对以上问题可以先保存小程序至本地,再使用微信开发工具 中 添加编译模式-选择添加方式改为“解析二维码”,这样就可以拿到小程序路径和参数了,注意scene。 - 在小程序开发版和体验版,右上角可以有打开调试功能 - 当在开发版或调试版打开小程序调试,退出再进入正式版,这样正式版中调试也不会关闭 - 小程序中显示富文本的方案: 1、使用第三方开源库 wxParse 优点:可以处理事件 可以显示视频 缺点:使用繁琐 作者停止维护了 2、使用微信组件 rich-text 优点:使用简单 可处理string 和 nodes 无需引用第三方包 缺点:不支持事件 不可以显示视频 不支持本地图片及其他资源 3、使用第三方库 mp-html(推荐) 优点:配置简洁 可以处理事件 可以显示视频 缺点:会占包大小 大致27k左右
199
2022-02-23 14:10
webpack的loader和plugin
待开始
125
2022-02-21 22:21
React fiber
待开始
React
102
2022-02-21 22:20
异常处理
查看日志 复现问题 定位问题 确定问题范围及影响范围 排除依赖 解决问题 测试问题 复盘记录归档
83
2022-02-21 22:19
hooks的出现
> Hook是 React16.8 的新增特性, 它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 以此来提高代码的复用性, 关于代码的复用的解决方案 从最开始的 `Mixin` 到 `HOC` 到 `Render Prop`,再到至今的 `Custom Hook`。 Mixin在使用上存在一些弊端, 复用的代码的相关依赖都是隐式的,不好追踪的,结果也是不好预测的,并且存在命名冲突覆盖的问题,后面被React不推荐使用 ```js var sentryMixin = { this.startTime = null componentDidMount() { this.startTime = new Date() }, componentWillUnmount() { const endTime = new Date() console.log(endTime - this.startTime) } } ``` ```js var UserPage = React.createClass({ mixins: [sentryMixin], render() { return <div>UserPage Component</div> } }) ``` HOC 本质上是一个函数,接收Component 为参数, 返回一个新的组件 组件可分为容器组件(container)和展示组件(component) 容器组件保存状态和事件,展示组件根据父级传入的状态和事件用来渲染 ```js const withSentry = (Component) => { class HOCComponent extends React.Component { componentDidMount() { this.startTime = new Date() } componentWillUnmount() { const endTime = new Date() console.log(endTime - startTime) } render() { return <Component /> } } return <HOCComponent /> } class UserPage extends React.Component { render() { return <div>UserPage</div> } } withSentry(UserPage) ``` 至今的就是 React Hook了 函数组件可以实现代码复用, 但是函数没有状态,也无法复用。 hook的出现就是为了解决复用函数组件时不能复用状态的问题,hook的核心是闭包, hook只能在函数的顶层使用,不能在判断、循环语句,jsx中使用 - useState 最基础的hook,状态生成器,函数接收一个参数为默认状态值。 返回一个数组 `[state, setState]`, 既状态值和修改状态的方法 例如: ```js const [foo, setFoo] = useState('hello') console.log(foo) // hello setFoo('world') console.log(foo) // world ``` - userEffect 接收两个参数 effectCallback 和 dept? effectCallback为 当 dept 发生变化时的回调函数,当组件卸载时会执行 `effectCallback()()`,可利用此特性移除一些副作用函数,比如监听事件,定时器 dept 为需要监听的值,这个值的类型为数组或`undefined`,当为undefined时,则每次函数执行都会调用,当为`[]`则表示不监听任何值,只会执行一次,利用此特性 注意dept的值不能为复杂类型的普通值,可以是简单类型的值或者经过缓存的状态值,因为,复杂类型的值和闭包中保存的值来比较,由于内存地址不同是不等的, 此hook没有返回值 示例 ```js const Counter = () => { useEffect(() => { const timer = setInterval(() => { console.log('hi!') },1000) return () => clearInterval(timer) },[]) } ``` 加上状态值 ```js const Counter = () => { const [count, setCount] = useState(0) useEffect(() => { const timer = setInterval(() => { console.log(count) setCount(count + 1) },1000) return () => clearInterval(timer) },[]) } ```
React
129
2022-02-21 22:14
函数组件和类组件的区别(待开始)
待开始
React
132
2022-02-21 22:14
SKU 和 算法实现(待开始)
待开始
25
2022-02-21 16:11
组件设计原则
最小职责 最小参数 默认参数 参数扁平化 最小依赖 最小副作用 业务处理交给hook 易拓展
25
2022-02-21 15:42
Top K问题(待开始)
(待开始)
86
2022-02-21 15:40
TCP协议(待开始)
(待开始)
16
2022-02-21 15:39
Mac终端设置代理
编辑 .zshrc 或 .bash_profile 文件 `vim ~/.zshrc` 或 `vim ~/.bash_profile` 假设你的代理地址在 127.0.0.1:4780 和 127.0.0.1:4781 也可以使用局域网内其他用户开的代理 比如 192.168.1.33:4780 和 192.168.1.33:4781 ![海报.png](https://7pou.com/upload/2022/01/01/00/9debcee5-2929-438d-8cea-81d7f23ff30d.png "海报.png") 添加配置 ```js setproxy() { export http_proxy="http://127.0.0.1:4780" export https_proxy="http://127.0.0.1:4780" export socks5_proxy="socks5://127.0.0.1:4781" export all_proxy="socks5://127.0.0.1:4781" echo "HTTP Proxy on" } ``` ```js unsetproxy () { unset http_proxy unset https_proxy unset socks5_proxy unset all_proxy echo "HTTP Proxy off" } ``` 重载配置 `source ~/.zshrc` 或 `source ~/.bash_profile` 使用代理 `setproxy` 测试是否配置成功 `curl ip.sb` 关闭代理 `unsetproxy`
Mac
116
2021-12-31 16:13
二叉树
> 概念 1. 根结点:没有父结点的结点 2. 叶结点:没有子结点的结点 3. 内部结点:一个结点既不是根结点也不是叶结点 4. 度 > 遍历方式 - 前序遍历 :根 --> 左 --> 右 - 中序遍历 :左 --> 根 --> 右 - 后序遍历 :左 --> 右 --> 根
42
2021-12-29 17:43
浅拷贝与深拷贝
JavaScript中数据类型分为基本类型和复杂类型(引用类型) 基本类型:String,Number,Boolean,undefined,null,Symbol, BigInt 复杂类型:object(包含 Array、Object、Date、RegExp等) 最简单的方法 ```js const clone = JSON.parse(JSON.stringify(json)) ``` 转为JSON字符串再解析为JSON对象,缺点是会丢失原型链上的属性 最先想到的递归处理 ```js function deepCopy(data) { if (!data) return data if (typeof(data) === 'object' ) { let cloneTarget = Array.isArray(data) ? [] : {} for (const key in data) { cloneTarget[key] = deepCopy(data[key]) } return cloneTarget } return data } ```
67
2021-12-29 15:50
前端性能优化
# 优化方式 ## 网络层面 - CDN - Gzip - http2 - 缓存 ## 资源层面 - 代码压缩 - 图片压缩 - 雪碧图 - 抽象复用 - 文件名加版本号(通过webpack hash) ## 逻辑层面 - 虚拟滚动 - 虚拟dom - 图片懒加载、合适的大小及格式 - vue:keep-live - vue:计算属性 - react:hook - memo - react:shouldcomponentUpdate - 代码分割 - React.lazy - React.Profiler 查看渲染耗时 - 节流防抖 - 减少回流重绘 - 大量计算使用 Web Workers - react:减少bind - 使用事件委托 - v-if 和 v-shew - 虚拟DOM绑定key - 关闭productionSourceMap,关闭css-sourceMap - webpack-bundle-analyzer查看包大小 ## 性能指标 - FCP【First Contentful Paint】显示第一段dom所花费的时间 - LCP【Largest Contentful Paint】最大内容绘制标记绘制最大文本或图像的时间 - TTI【Time to Interactive】交互时间是指页面完全交互所需的时间 - Speed Index 衡量页面加载期间内容以视觉方式显示的速度 - TBT【Total Blocking Time】当任务长度超过50ms时,FCP和交互时间之间的所有时间段的总和,以毫秒为单位。测量页面被阻止响应用户输入(例如鼠标点击、屏幕点击或键盘按下)的总时间
50
2021-12-21 16:21
Array.prototype.sort
```js Array.prototype.sort = () => { for(let i = 0; i < this.length; i++) { for(let j = i+1; j< this.length; j++) { if (this[i] > this[j]) { let temp = this[i] this[i] = this[j] this[j] = temp } } } return this } ```
41
2021-12-16 16:26
异步终极方案-Promise
## 什么是Promise 青铜段位 ```js class MyPromise { constructor(fn) { this.status = 'penging' this.fulfilled = null this.rejected = null this.finallyFn = null const onResolvedCallback = (value) => { this.status = 'fulfilled' this.fulfilled(value) this.finallyFn(value) } const onRejectedCallback = (value) => { this.status = 'rejected' this.rejected(value) this.finallyFn(value) } fn(onResolvedCallback, onRejectedCallback) } then(fn) { this.fulfilled = fn return this } catch (fn) { this.rejected = fn return this } finally(fn) { this.finallyFn = fn return this } } ``` ```js function post() { return new MyPromise((resolve, reject) => { setTimeout(() => { Math.random() > 0.5 ? resolve('成功') : reject('失败') }, 1000); }) } post().then(res => { console.log(res) }).catch(err => { console.log(err) }).finally(res => { console.log(res) }) ``` > 静态方法 Promise.all Promise.any Promise.race Promise.allSettled
javaScript
76
2021-12-08 17:19
Array.prototype.flat()
## Array.prototype.flat() ### 定义: > flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。 —— MDN flat(depth:number)函数接收一个 depth 展开深度,默认值为 1 ```js [1,2,[3,[4,5]]].flat() // [1,2,3,[4,5]] [1,2,[3,[4,5]]].flat(1) // [1,2,3,[4,5]] [1,2,[3,[4,5]]].flat(2) // [1,2,3,4,5] ``` 传0会怎么样呢 ```js [1,2,[3,[4,5]]].flat(0) // [1,2,[3,[4,5]]] ``` 传负数呢 ```js [1,2,[3,[4,5]]].flat(-1) // [1,2,[3,[4,5]]] [1,2,[3,[4,5]]].flat(-100) // [1,2,[3,[4,5]]] ``` 由此可见, 当展开深度小于1时,则返回的新数组和原数组相同(非相等) 当需要把未知深度的数组拍成一维时可以用 Infinity ```js [1,2,[3,[4,5]]].flat(Infinity) // [1,2,3,4,5] [1,2,[3,[4,5,[6]]]].flat(Infinity) // [1,2,3,4,5,6] ``` 可以过滤empty值 ```js [1,2,,4].flot() // [1,2,4] [1,2,null,4].flot() // [1,2,null,4] ``` 下面来手动实现下 ###### 递归实现 ```js Array.prototype.myFlat = function(depth) { const result = [] const loop = (_list, loopDepth = 1) => { _list.forEach(e => { if (Array.isArray(e) && depth >= loopDepth) { result.concat(loop(e, loopDepth+1)) } else { result.push(e) } }) } loop(this) return result } ``` ###### reduce实现 ```js Array.prototype.myFlat = function (depth = 1) { const loop = (list, deep) => { if (deep < 1) return list return list.reduce((prev, current) => { const val = Array.isArray(current) ? loop(current, deep - 1) : current return prev.concat(val) },[]) } return loop(this, depth) } ``` ###### Array.prototype.join 实现 ```js [1,2,[3,[4,5]]].join() // '1,2,3,4,5' ``` ```js Array.prototype.myFlat = function() { return this.join().split(',') } ``` join的缺点: 1. 只能处理 `Array<string>` 2. 不支持层级 ###### Array.prototype.toString 实现 ```js [1,2,[3,[4,5]]].toString() // '1,2,3,4,5' ``` ```js Array.prototype.myFlat = function() { return this.toString().split(',') } ``` 原理同join > MDN 地址 [https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flat](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flat)
javaScript
308
2021-04-22 14:31
测试markdown
--- # frontmatter: https://jekyllrb.com/docs/front-matter/ layout: post title: Blogging Like a Hacker --- ## Markdown Basic Syntax I just love **bold text**. Italicized text is the _cat's meow_. At the command prompt, type `nano`. My favorite markdown editor is [ByteMD](https://github.com/bytedance/bytemd). 1. First item 2. Second item 3. Third item > Dorothy followed her through many of the beautiful rooms in her castle. ```js import { Editor, Viewer } from 'bytemd' import gfm from '@bytemd/plugin-gfm' const plugins = [ gfm(), // Add more plugins here ] const editor = new Editor({ target: document.body, // DOM to render props: { value: '', plugins, }, }) editor.on('change', (e) => { editor.$set({ value: e.detail.value }) }) ``` ## GFM Extended Syntax Automatic URL Linking: https://github.com/bytedance/bytemd ~~The world is flat.~~ We now know that the world is round. - [x] Write the press release - [ ] Update the website - [ ] Contact the media | Syntax | Description | | --------- | ----------- | | Header | Title | | Paragraph | Text | ## Footnotes Here's a simple footnote,[^1] and here's a longer one.[^bignote] [^1]: This is the first footnote. [^bignote]: Here's one with multiple paragraphs and code. Indent paragraphs to include them in the footnote. `{ my code }` Add as many paragraphs as you like. ## Gemoji Thumbs up: :+1:, thumbs down: :-1:. Families: :family_man_man_boy_boy: Long flags: :wales:, :scotland:, :england:. ## Math Equation Inline math equation: $a+b$ $$ \displaystyle \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) $$ ## Mermaid Diagrams ```mermaid graph TD; A-->B; A-->C; B-->D; C-->D; ```
javaScript
109
2021-04-02 23:49
常见排序策略(待开始)
待开始
算法
137
2021-03-31 17:32
前端安全总结
# 总结前端常见的安全问题 ## 1、XSS攻击 全称为跨站脚步攻击 xss的原理就是让浏览器执行攻击者写的代码。 怎么让浏览器中加载攻击者的代码呢? 物理方案:趁你不注意跑到你电脑前F12,手动输入,回车。 现实方案: - 存储性XSS攻击 让网页中自动带有攻击脚步,比如你在贴吧回复时插入 ```javascript <script>alert('hi xss')</script> 或者 <img src="null" onerror="alert('hi xss')" /> ``` 点击保存,这段代码就会存服务器,当任何人点开你的这篇贴子时就会执行的这段代码,目的就达成了。当然这是在没有做任何防护的情况下。这种模式就叫存储型XSS攻击, 也叫持久型XSS。 - 反射性XSS攻击 ## CSRF攻击 ## 越权操作 前端的权限控制分为:操作按钮控制和路由控制 在未加路由控制的情况下,用户在浏览器直接输入URL访问 ## 连续编号类参数篡改 当前的数据的主键ID是自增的情况下,用户访问到某个URL为 `/api/user/get?userId=1` 那么用户就可以联想到 userId =2 、3、4 等情况 从而看到不属于自己的数据 前端防范措施: 协同后端对请求接口参数添加签名 ## 文件上传 ## DNS劫持 ## SQL注入 ## 爬虫爬取数据 ## 暴力尝试 ## 接口滥用 ## 截取包
105
2021-03-06 17:57
CommonJs模块和 ES6模块的差异
CommonJS模块 输出值的拷贝,ES6模块 输出 值的引用 ```js // a.js let user = 'xxx' const setUser = (val) => { user = val } module.exports = { user, setUser} ``` ```js // b.js const { user, setUser } = require('./a.js') console.log(user) // xxx setUser('yyy') console.log(user) // xxx ``` 当前的 user 并没有改变 > CommonJs的拷贝为浅拷贝 ```js // a.js let user = { name: '?', abilitys: { say() { console.log('hi') }, play: 'js' } } const changeAbility = (val) => { user.abilitys = {...user.abilitys, ...val} } module.exports = { user, changeAbility} ``` ```js // b.js const { user, changeAbility } = require('./a.js') console.log(user.abilitys.play) // js changeAbility({ play: 'css' }) console.log(user.abilitys.play) // css ``` user.abilitys.play 更改为了 css - CommonJS模块 运行时加载,ES6模块 编译时输出接口 - CommonJS模块 同步加载模块,ES6模块 异步加载
68
2021-03-31 17:57