微信分享前端开发全程详解含iOS、安卓、H5、ReactNative以及微信开放标签的适配和使用
2024年9月,本人在做微信分享前端部分的iOS、安卓和H5的页面和功能时踩了不少坑,于是写了这篇文章,内容包括微信分享在上面三个端的技术点和坑点、解决办法,微信开放标签的相关适配,以及ReactNative的特别处理部分。
原文链接:http://www.kovli.com/2024/12/18/wx-share/
作者:Kovli
重要通知:红宝书第5版2024年12月1日出炉了,感兴趣的可以去看看,https://u.jd.com/saQw1vP
红宝书第五版中文版
红宝书第五版英文原版pdf下载(访问密码: 9696)
一、前期准备:微信开放平台、微信公众平台注册并审核通过
1、微信开放平台入口
2、微信公众平台入口
3、官网域名,分享用网页的域名(可以是二级域名)部署完成,ICP备案完成。
Tip1:微信开放平台认证一次性的三百。公众平台首次认证三百,认证到期后每年需要再年审三百。平台网页:https://developers.weixin.qq.com/community/develop/doc/000c040991c220d8dcea6324e56000
Tip2:官网域名用于iOS的Universal Links,分享用网页的域名用于放到js接口安全域名里面,凡是在js接口安全域名里的网页在微信里打开都可以使用开放标签和对应的微信js接口。后面会说怎么放。
Tip3:注册和申请这两个平台的同时,移动应用也添加和关联进去,后面要用到APPID,另外移动应用需要开启微信分享功能,详见文档。
二、客户端SDK集成:
1、友盟分享集成 开发文档
2、微信官网自行集成 开发文档
本人用的是友盟分享集成,也可以微信官网自行集成,如果友盟分享已经集成了那至少微信文档里SDK的集成这一步已经完成了。
不要遗漏文档里任何一处,以免后面遇到不该遇到的坑。
注意iOS需要配置Universal Links:参考文档
有个需要服务端或运维小伙伴配合的步骤,就是创建个文件名为apple-app-site-association的文件,没有后缀,里面的内容如下,
根据自己的参数来修改里面appID和paths的内容
{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID":"苹果teamID.com.aaa.bbb"
                "paths": ["/app/*"]
            }
        ]
    }
}
然后让后端或者运维小伙伴,把这个文件放在官网的服务器根目录下。
比如服务器地址是: https://www.baidu.com/ ,把文件放在这根目录下后,访问 https://www.baidu.com/apple-app-site-association ,这文件就会被下载下来。
然后根目录新建一个.well-known文件夹,里面也放一份这个文件,这样会先去根目录找,如果没找到还会去.well-known文件夹找
三、微信分享H5页面开发(本人用的是Vue2)
H5开发需要适配两个端:手机浏览器端和微信浏览器端
1、手机浏览器的适配比较简单,跳转APP采用Schema逻辑(iOS和安卓均可)。
H5
// 打开app 目前采用的方法 用于适配非微信端打开的网页跳转app并传参
openApp() {
    const sendInfo = {
        key: value
    }
    const schemaInfo = JSON.stringify(sendInfo)
    const appScheme = 'yourSchema://yourshare域名/pagePath?schemaInfo=' + schemaInfo // 替换为你的 app 的 URL scheme
    const fallbackUrl = '应用宝链接' // 替换为你的 app 在应用商店的链接
    const u = navigator.userAgent
    const isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
    if (isiOS) {
        window.location.href = appScheme // ios对应的app协议
        setTimeout(function () {
            let hidden = window.document.hidden || window.document.mozHidden || window.document.msHidden || window.document.webkitHidden
            if (typeof hidden === 'undefined' || hidden == false) {
                // App store下载地址
                window.location.href = fallbackUrl
            }
        }, 500)
    } else {
        window.location.href = appScheme // 安卓对应的app协议
        setTimeout(function () {
            let hidden = window.document.hidden || window.document.mozHidden || window.document.msHidden || window.document.webkitHidden
            if (typeof hidden === 'undefined' || hidden == false) {
                // 应用宝下载地址
                window.location.href = fallbackUrl
            }
        }, 1500)
    }
},
2、微信浏览器端的适配,需要使用微信的开放标签来处理H5和APP的跳转逻辑
微信开放标签:
1、绑定域名加入JS接口安全域名
登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
2、使用前需将「 JS 接口安全域名绑定的服务号」绑定在「移动应用的微信开放平台账号」下,并确保服务号与此开放平台账号同主体且均已认证。请前往 微信开放平台-管理中心-公众号详情-接口信息 设置域名与所需跳转的移动应用。
具体流程参考这篇说明文档:
https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_H5_Launch_APP.html
3、根据开发文档开发适配微信端网页开放标签,具体流程参考这篇文档:
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html
注意:
1、安全角度考虑,下面这段所需的参数从服务器接口获取,不要放前端
wx.config({
  debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印
  appId: '', // 必填,公众号的唯一标识
  timestamp: , // 必填,生成签名的时间戳
  nonceStr: '', // 必填,生成签名的随机串
  signature: '',// 必填,签名
  jsApiList: [], // 必填,需要使用的JS接口列表
  openTagList: [] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app']
});
2、网页的域名和审核通过的js安全域名Host完全一致 + 第一点里面的config初始化成功 + 要在微信里面打开此网页 = 能显示微信开放标签里的内容且点击能跳转到APP,缺一不可
3、开放标签里放不了本地的图,本地的图可以上传到oss上生成链接的形式展示
<wx-open-launch-app
            v-if="iswx"
            id="launch-btn"
            style="width:100%; position: relative; display: flex; flex-direction: row; align-items: center; justify-content: center;height: 44px; background-color: #F7F8FA"
            :extinfo="getSchemaInfo"
            appid="yourWxAppId"
            @error="handleError"
            @launch="handleLaunch"
        >
            <component is="script" type="text/wxtag-template">
                <div style="position: relative; display: flex; flex-direction: row; align-items: center; justify-content: center; height: 44px"
                     @click="onButtonClick">
                    <div style="font-size: 14px; font-weight: 400; color: #388BFF; line-height: 20px; text-align: center">固定在底部的条条</div>
                    <img style="width: 14px; height: 14px; margin-left: 4px;" src="https://yourResources.oss-cn-hangzhou.aliyuncs.com/yourPic.png" alt="">
                </div>
            </component>
        </wx-open-launch-app>
4、开放标签里不能使用postion为fixed和absolute的样式,无效,如果需要可以在开放标签里设置style,加fixed或absolute
参考5里面的代码
5、一个取巧的办法是开放标签可以是透明的盖住普通浏览器样式的页面组件,实现微信端点击跳转APP且无需适配开放标签里面的样式
<wx-open-launch-app
      v-if="iswx"
      id="launch-btn"
      style="max-width: 100%; height: 68px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #F1F2F4; position: fixed; top: 0; left: 0; right: 0; width: 100%; margin: auto; z-index: 99;"
      @error="handleError"
      @launch="handleLaunch"
      :extinfo="getSchemaInfo"
      appid="yourWxAppId"
  >
    <script type="text/wxtag-template">
      <div style="opacity: 0; width: 100%; height: 68px;"></div>
    </script>
  </wx-open-launch-app>
6 也可以根据网页是否在微信里打开的来进一步适配浏览器和微信的页面
如上的代码里有个iswx
let ua = window.navigator.userAgent.toLowerCase()
// 通过正则表达式匹配ua中是否含有MicroMessenger字符串
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
    this.iswx = true
} else {
    this.iswx = false
}
四、客户端处理跳转逻辑
参考开发文档
1、安卓部分 - 浏览器通过schema跳转
安卓 MainActivity.java文件里
  @Override
  public void onNewIntent(Intent intent){
    super.onNewIntent(intent);
    setIntent(intent);
  }
  @Override
  protected void onResume() {
    super.onResume();
    Intent intent = getIntent();
    if (!intentHex.equals("") && intentHex.equals(Integer.toHexString(System.identityHashCode(intent)))) {
      // 获取 scheme 名称 重复的intent 不予处理
    } else {
      intentHex = Integer.toHexString(System.identityHashCode(intent));
      String scheme = intent.getScheme();
      Uri uri = intent.getData();
      if(scheme!= null && uri != null) {
        if (uri.getScheme().equals("yourSchema")) {
          String schemaInfo = uri.getQueryParameter("schemaInfo");
          try {
            // 原生的可以处理跳转逻辑,ReactNative的这里可以发送到RN端
            MainApplication application = (MainApplication) this.getApplicationContext();
            WritableMap map = Arguments.createMap();
            map.putString("schemaInfo", schemaInfo);
            ReactInstanceManager reactInstanceManager = application.getReactNativeHost().getReactInstanceManager();
            ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
            if(reactContext != null) {
              reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("Demo_EmitterKey", map);
            } else {
              reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
                @Override
                public void onReactContextInitialized(ReactContext context) {
                  Handler handler = new Handler();
                  handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                      /**
                       *要执行的操作
                       */
                      context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("Demo_EmitterKey", map);
                    }
                  }, 4000);//4秒后执行Runnable中的run方法
                  reactInstanceManager.removeReactInstanceEventListener(this);
                }
              });
            }
          } catch (SecurityException e) {
            // TODO: Display a yellow box about this
          }
        }
      }
    }
  }
安卓部分 - 微信页面通过开放标签跳转
在WXEntryActivity文件里
/**
     * 从微信启动App
     *
     * @param req
     */
    @Override
    public void onReq(BaseReq req) {
        super.onReq(req);
        //获取开放标签传递的extinfo数据逻辑
        try {
            if (req.getType() == ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX && req instanceof ShowMessageFromWX.Req) {
                ShowMessageFromWX.Req showReq = (ShowMessageFromWX.Req) req;
                WXMediaMessage mediaMsg = showReq.message;
                String extInfo = mediaMsg.messageExt;
                // 原生这里已经可以处理跳转逻辑了
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
2、iOS部分 - 浏览器通过schema跳转
参考这篇帖子
iOS AppDelete.mm文件里
// 支持所有iOS系统
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    BOOL result = [[UMSocialManager defaultManager] handleOpenURL:url sourceApplication:sourceApplication annotation:annotation];
    if (!result) {
         // 其他如支付等SDK的回调
    }
      //开启SDK Log
      [WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString *log) {
          NSLog(@"WeChatSDK: %@", log);
      }];
      // 在调用WXApi的handle方法前,须先调用registerApp注册。ret为注册结果,若注册失败,请根据sdk的log排查原因
      BOOL ret = [WXApi registerApp:@"微信的AppID" universalLink:@"https://your域名/app/"];
      if ([WXApi handleOpenURL:url delegate:self]) {
          /// handled by OpenSDK
      }
      // 上面做了微信开放标签的适配
  if ([[url scheme] isEqualToString:@"yourSchema"]) {
    NSString *inputString = [url query];
    // 找到等号的位置
    NSUInteger equalSignIndex = [inputString rangeOfString:@"="].location;
    if (equalSignIndex != NSNotFound) {
        // 提取等号后面的字符串
        NSString *schemaInfo = [inputString substringFromIndex:equalSignIndex + 1];
        NSLog(@"schemaInfo: %@", schemaInfo);
      if (![schemaInfo isEqualToString:@""]) {
        NSDictionary *schemaInfoDic = @{@"schemaInfo":schemaInfo};
        //这里原生的就可以跳转了,ReactNative的则把消息全部转给JS
        NSMutableDictionary *mutableDic = [NSMutableDictionary dictionaryWithDictionary:schemaInfoDic];
        //把消息全部转给JS
        [[NSNotificationCenter defaultCenter] postNotificationName:@"SendSchemaInfoToRN" object:mutableDic?mutableDic:@{}];
      } else {
          NSLog(@"empty schemaInfo");
      }
    } else {
        NSLog(@"No '=' found.");
    }
  }
    return result;
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
    BOOL result = [[UMSocialManager defaultManager]  handleOpenURL:url options:options];
    if (!result) {
        // 其他如支付等SDK的回调
    }
  //开启SDK Log
      [WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString *log) {
          NSLog(@"WeChatSDK: %@", log);
      }];
      // 在调用WXApi的handle方法前,须先调用registerApp注册。ret为注册结果,若注册失败,请根据sdk的log排查原因
      BOOL ret = [WXApi registerApp:@"微信的Appid" universalLink:@"https://your域名/app/"];
      if ([WXApi handleOpenURL:url delegate:self]) {
          /// handled by OpenSDK
      }
      // 上面做了微信开放标签的适配
  if ([[url scheme] isEqualToString:@"yourSchema"]) {
    NSString *inputString = [url query];
    // 找到等号的位置
    NSUInteger equalSignIndex = [inputString rangeOfString:@"="].location;
    if (equalSignIndex != NSNotFound) {
        // 提取等号后面的字符串
        NSString *schemaInfo = [inputString substringFromIndex:equalSignIndex + 1];
        NSLog(@"schemaInfo: %@", schemaInfo);
      if (![schemaInfo isEqualToString:@""]) {
        NSDictionary *schemaInfoDic = @{@"schemaInfo":schemaInfo};
        //这里原生的就可以跳转了,ReactNative的则把消息全部转给JS
        NSMutableDictionary *mutableDic = [NSMutableDictionary dictionaryWithDictionary:schemaInfoDic];
        //把消息全部转给JS
        [[NSNotificationCenter defaultCenter] postNotificationName:@"SendSchemaInfoToRN" object:mutableDic?mutableDic:@{}];
      } else {
          NSLog(@"empty schemaInfo.");
      }
    } else {
        NSLog(@"No '=' found.");
    }
  }
    return result;
}
iOS部分 - 微信页面通过开放标签跳转
在通过Schema跳转的代码基础上,加上下面这段
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler
{
    if (![[UMSocialManager defaultManager] handleUniversalLink:userActivity options:nil]) {
        // 其他SDK的回调
      NSLog ( @"其他SDK的回调11");
    }
    NSLog ( @"其他SDK的回调22");
  //开启SDK Log
      [WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString *log) {
          NSLog(@"WeChatSDK: %@", log);
      }];
  // 在调用WXApi的handle方法前,须先调用registerApp注册。ret为注册结果,若注册失败,请根据sdk的log排查原因
  BOOL ret = [WXApi registerApp:@"微信的Appid" universalLink:@"https://your域名/app/"];
      if ([WXApi handleOpenUniversalLink:userActivity delegate:self]) {
          /// handled by OpenSDK
      }
  // 触发回调方法
    [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
    NSURL *url = userActivity.webpageURL;
    if (url && [TencentOAuth CanHandleUniversalLink:url]) {
      return [TencentOAuth HandleUniversalLink:url];
    }
    return YES;
}
-(void) onReq:(BaseReq*)req
{
    if([req isKindOfClass:[GetMessageFromWXReq class]])
    {
        // 微信请求App提供内容, 需要app提供内容后使用sendRsp返回
        NSString *strTitle = [NSString stringWithFormat:@"微信请求App提供内容"];
        NSString *strMsg = @"微信请求App提供内容,App要调用sendResp:GetMessageFromWXResp返回给微信";
    }
    else if([req isKindOfClass:[ShowMessageFromWXReq class]])
    {
        ShowMessageFromWXReq* temp = (ShowMessageFromWXReq*)req;
        WXMediaMessage *msg = temp.message;
        //显示微信传过来的内容
        WXAppExtendObject *obj = msg.mediaObject;
        NSString *strTitle = [NSString stringWithFormat:@"微信请求App显示内容"];
        NSString *strMsg = [NSString stringWithFormat:@"标题:%@ \n内容:%@ \n附带信息:%@ \n缩略图:%u bytes\n\n", msg.title, msg.description, obj.extInfo, msg.thumbData.length];
    }
    else if([req isKindOfClass:[LaunchFromWXReq class]])
    {
        //从微信启动App
      LaunchFromWXReq* temp = (LaunchFromWXReq*)req;
      WXMediaMessage *msg = temp.message;
      NSLog ( @"req:%@", msg.messageExt);
      NSDictionary *schemaInfoDic = @{@"schemaInfo":msg.messageExt};
      //把消息全部转给JS
      NSMutableDictionary *mutableDic = [NSMutableDictionary dictionaryWithDictionary:schemaInfoDic];
      NSLog(@"Schema 收到微信回调参数 #####%@",mutableDic);
      //把消息全部转给JS
      [[NSNotificationCenter defaultCenter] postNotificationName:@"SendSchemaInfoToRN" object:mutableDic?mutableDic:@{}];
    }
}
3、ReactNative的特殊处理
ReactNative在获取到浏览器或者微信传来的ScehmaInfo后,发送消息到JS的操作在前面的代码里都写进去了,可以看对应的注释。
安卓由于ReactNative就一个MainActivity,拿到微信传过来的数据后还需要特殊处理。
在WXEntryActivity文件里
/**
     * 从微信启动App
     *
     * @param req
     */
    @Override
    public void onReq(BaseReq req) {
        super.onReq(req);
        //获取开放标签传递的extinfo数据逻辑
        try {
            if (req.getType() == ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX && req instanceof ShowMessageFromWX.Req) {
                ShowMessageFromWX.Req showReq = (ShowMessageFromWX.Req) req;
                WXMediaMessage mediaMsg = showReq.message;
                String extInfo = mediaMsg.messageExt;
                // 原生这里已经可以处理跳转逻辑了,ReactNative需要特殊处理下,判断MainActivity是否存在,也就是说APP是否处于杀死状态
                if (ActivityUtils.isActivityExistsInStack(MainActivity.class)) {
                    // 发送到RN端
                    MainApplication application = (MainApplication) this.getApplicationContext();
                    WritableMap map = Arguments.createMap();
                    map.putString("schemaInfo", extInfo);
                    ReactInstanceManager reactInstanceManager = application.getReactNativeHost().getReactInstanceManager();
                    ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
                    if (reactContext != null) {
                        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("Demo_EmitterKey", map);
                    } else {
                        reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
                            @Override
                            public void onReactContextInitialized(ReactContext context) {
                                Handler handler = new Handler();
                                handler.postDelayed(new Runnable() {
                                    @Override
                                    public void run() {
                                        /**
                                         *要执行的操作
                                         */
                                       context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("Demo_EmitterKey", map);
                                    }
                                }, 4000);//4秒后执行Runnable中的run方法
                                 reactInstanceManager.removeReactInstanceEventListener(this);
                            }
                        });
                    }
                } else {
                // 如果APP处于启动状态,则到MainActivity里处理逻辑
                    Intent intent = new Intent(this, MainActivity.class);
                    intent.putExtra("keySendToMainActivity", extInfo);
                    startActivity(intent);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
在MainActivity文件里
@Override
  protected void onCreate(Bundle savedInstanceState) {
    MainApplication.getInstance().setMainActivity(this);
    super.onCreate(null);
    ...
    // 这里加上这些内容
    if (getIntent().getStringExtra("keySendToMainActivity") != null && !"".equals(getIntent().getStringExtra("keySendToMainActivity"))) {
      // 发送到RN端
      MainApplication application = (MainApplication) this.getApplicationContext();
      WritableMap map = Arguments.createMap();
      map.putString("schemaInfo", getIntent().getStringExtra("keySendToMainActivity"));
      ReactInstanceManager reactInstanceManager = application.getReactNativeHost().getReactInstanceManager();
      ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
      if(reactContext != null) {
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("Demo_EmitterKey", map);
      } else {
        reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
          @Override
          public void onReactContextInitialized(ReactContext context) {
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
              @Override
              public void run() {
                /**
                 *要执行的操作
                 */
                context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("Demo_EmitterKey", map);
              }
            }, 4000);//4秒后执行Runnable中的run方法
            reactInstanceManager.removeReactInstanceEventListener(this);
          }
        });
      }
    }
  }
版权声明:
转载时请注明作者Kovli以及本文地址:
http://www.kovli.com/2024/12/18/wx-share/
微信分享前端开发全程详解含iOS、安卓、H5、ReactNative以及微信开放标签的适配和使用的更多相关文章
- IOS开发之----详解在IOS后台执行
		
文一 我从苹果文档中得知,一般的应用在进入后台的时候可以获取一定时间来运行相关任务,也就是说可以在后台运行一小段时间. 还有三种类型的可以运行在后以,1.音乐2.location 3.voip 文二 ...
 - 微信小程序开发之详解生命周期方法
		
生命周期是指一个小程序从创建到销毁的一系列过程 在小程序中 ,通过App()来注册一个小程序 ,通过Page()来注册一个页面 先来看一张小程序项目结构 从上图可以看出,根目录下面有包含了app.js ...
 - Extjs MVC开发模式详解
		
Extjs MVC开发模式详解 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开发模式, ...
 - 免费的HTML5连载来了《HTML5网页开发实例详解》连载(二)
		
最近新浪.百度.腾讯.京东.大众点评.淘宝等流行的网站都加大了招聘HTML5的力度,HTML5开发人员成了抢手货,本次连载的是由大众点评前端工程师和一淘网前端工程师基情奉献的<HTML5网页开发 ...
 - 当里个当,免费的HTML5连载来了《HTML5网页开发实例详解》连载(一)
		
读懂<HTML5网页开发实例详解>这本书 你还在用Flash嘛?帮主早不用了 乔布斯生前在公开信“Flash之我见”中预言:像HTML 5这样在移动时代中创立的新标准,将会在移动设备上获得 ...
 - ubuntu apache2配置详解(含虚拟主机配置方法)
		
ubuntu apache2配置详解(含虚拟主机配置方法) 在Windows下,Apache的配置文件通常只有一个,就是httpd.conf.但我在Ubuntu Linux上用apt-get inst ...
 - ext.js的mvc开发模式详解
		
ext.js的mvc开发模式详解和环境配置 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开 ...
 - 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING
		
<Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th 2014 Email:skyseraph00@163.com 更多精彩请直接 ...
 - C++的性能C#的产能?! - .Net Native 系列《二》:.NET Native开发流程详解
		
之前一文<c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATIVE初窥> 获得很多朋友支持和鼓励,也更让我坚定做这项技术的推广者,希望能让更多的朋友了解这项技术,于是先从官方 ...
 - Cocos2d-x 3.X手游开发实例详解
		
Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...
 
随机推荐
- Element-UI 中关于 Table 的几个功能点简介(行列的合并和样式、合计行配置等)
			
〇.前言 本文记录了关于 Element 框架中 Table 的几个功能点,后续将持续更新. el-table 官网地址:https://element.eleme.cn/#/zh-CN/compon ...
 - ZCMU-1111
			
与背包和动态规划有关(我认为) 采用dp数组存放吃掉i千克食物要用掉的钱 dp最开始要尽量的大方便过程中判断和最后的输出判断 实时更新dp,保留最小的钱 以前不知道的 printf函数可以这样用 fi ...
 - 移动端NES网页模拟器(3)
			
前言 前面2个章节已经封装好了摇杆和NES虚拟按键,现在配合jsnes这个包来完成一个移动端版的NES模拟器. 这是插件的github地址:bfirsh/jsnes 这个包可以直接拿来用,但是没有适配 ...
 - C# 获取系统盘符
			
1.使用.net管理对象(引入System.Management) public static List<string> getDisk() { WqlObjectQuery wmique ...
 - 【NAS】绿联NAS UGOS PRO 使用natfrp(Sakura Frp)内网穿透访问Docker应用
			
配置加速 https://registry.cn-hongkong.aliyuncs.com 下载镜像 创建容器 [容器]-[创建]-[手动创建]- 选择你下载的镜像即可 在日志中查看密码 访问容器 ...
 - shell 将文件内容读取到 数组中
			
#!/bin/bash prod_file=/home/vmuser/linbo/kettleDemo/job/test/CA-20201224.csv test_file=/home/vmuser/ ...
 - 即时通讯技术文集(第9期):Java NIO和Netty入门系列 [共19篇]
			
为了更好地分类阅读52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第9 期. [-1-] 少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别 [链接] http ...
 - IM通讯协议专题学习(二):快速理解Protobuf的背景、原理、使用、优缺点
			
本文由vivo技术团队Li Guanyun分享,为了提升阅读体验,进行了较多修订和重新排版. 1.引言 Protobuf 作为一种跨平台.语言无关.可扩展的序列化结构数据通讯协议,已广泛应用于网络数据 ...
 - 一套十万级TPS的IM综合消息系统的架构实践与思考
			
本文由作者jhon_11分享,有大量修订和改动. 1.引言 如何设计一款高性能.高并发.高可用的im综合消息平台是很多公司发展过程中会碰到且必须要解决的问题.比如一家公司内部的通讯系统.各个互联网平台 ...
 - Windows安全加固(一)
			
目录: 1.在win ser2016中如何管理重命名administrator,禁用GUEST 2.禁用GUEST账户 3.系统不显示上次登录的账户名. 4.清理系统无效账户. 5.按用户类型分配账号 ...