HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

facebook登录插件(android版)

facebook

贴出源码回报社区!

下面贴出关键代码,稍后完善更多细节步骤。个人android开发经验不多,如果有更好的集成方式,请大家分享!

前提:

  1. 能在android stuido成功运行官方给出的android SDK集成和插件示例程序和离线打包
  2. 能在android stuido成功运行官方给出的集成示例facebook-android-sdk(https://github.com/facebook/facebook-android-sdk) 中的 FBLoginSample 示例程序
  3. 注册了facebook开发者平台,并完成了官方android集成步骤(https://developers.facebook.com/docs/facebook-login/android?locale=zh_CN)
  4. 下面的部分.facebook.plugin.的代码采用了官方示例代码,参考:https://github.com/facebook/facebook-android-sdk/tree/master/samples/FBLoginSample

源代码目录结构

1.js层代码调用插件login.js

var App ={  

    loginWithFacebook: function()  
    {  
        Common.showLoading();  

        setTimeout(function(){  
            if(App.timeout)  
            {  
                Common.hideLoading();  
                mui.toast(Common.messages.LOGIN_TIMEOUT,{ duration:'short', type:'div' });  
            }  
        }, 5000);  

        if(Common.debug)  
        {  
            setTimeout(function(){  
                App.facebookAuthSuccessCallBack('{"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1791872767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1791872767544170","email":"your@email.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"","gender":"male","birthday":"01\/01\/1991","location":{"id":"106324046073002","name":"Shanghai, China"}}');  
                //App.facebookAuthCancelCallBack();  
                //App.facebookAuthErrorCallBack("auth fail!");  
            }, 2000);  

        }else  
        {  
            //plus.facebookplug.logOut();  
            plus.facebookplug.logIn();  
        }  

    },  

    // 这个回调函数会在 cn.your.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthSuccessCallBack: function(data)  
    {  
        App.timeout = false;  

        if(data != null)  
        {  
         // data example:  '{"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1791872767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1791872767544170","email":"your@email.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"","gender":"male","birthday":"01\/01\/1991","location":{"id":"106324046073002","name":"Shanghai, China"}}'  
            try{  
                var facebookUser = JSON.parse(data);  
                facebookUser.unionid = facebookUser.id;  
                mui.toast(Common.messages.LOGIN_SUCCESS,{ duration:'short', type:'div' });  
                App.checkUserIsExist(facebookUser, "facebook");  
            }catch(err)  
            {  
                Common.hideLoading();  
                mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' });  
                Common.log("====login.js, facebookAuthSuccessCallBack(), decode facebook user JSON string fail!");  
            }  
        }else  
        {  
            Common.hideLoading();  
            App.facebookAuthErrorCallBack();  
        }  
    },  

    // 这个回调函数会在 cn.your.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthCancelCallBack: function()  
    {  
        App.timeout = false;  
        Common.hideLoading();  

        mui.toast(Common.messages.LOGIN_CANCEL,{ duration:'long', type:'div' });  
    },  

    // 这个回调函数会在 cn.your.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthErrorCallBack: function(error)  
    {  
        App.timeout = false;  
        Common.hideLoading();  

        mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' });  
    }  

};  

if(window.plus){  
    App.init();  
}else{   
    document.addEventListener( "plusready", App.init, false );  
}

2. js层代码插件facebookplug.js

document.addEventListener( "plusready",  function()  
{  
    var facebookplug =  
    {  
        logIn : function ()  
        {  
            window.plus.bridge.exec('facebookplug', "logIn", []);  
        },  
        logOut : function ()  
        {  
            window.plus.bridge.exec('facebookplug', "logOut", []);  
        },  
    };  

    window.plus.facebookplug = facebookplug;  
}, true );

3. 原生层FacebookLoginPlugin.java

package cn.your.app.plugin.facebook;  

import cn.your.app.SDK_WebApp;  
import cn.your.app.plugin.facebook.callbacks.GetUserCallback;  
import cn.your.app.plugin.facebook.requests.UserRequest;  
import io.dcloud.common.DHInterface.IWebview;  
import io.dcloud.common.DHInterface.StandardFeature;  
import io.dcloud.common.util.JSUtil;  

import org.json.JSONArray;  

import android.content.Context;  
import android.os.Bundle;  

import com.facebook.AccessToken;  
import com.facebook.login.LoginManager;  

import java.util.Arrays;  

public class FacebookLoginPlugin extends StandardFeature  
{  

    public void onStart(Context pContext, Bundle pSavedInstanceState, String[] pRuntimeArgs) {  

        /**  
         * 如果需要在应用启动时进行初始化,可以继承这个方法,并在properties.xml文件的service节点添加扩展插件的注册即可触发onStart方法  
         * */  
    }  

    public void logIn(IWebview pWebview, JSONArray array)  
    {  
        if (AccessToken.getCurrentAccessToken() == null) {  

            /*  
             * 获取用户属性的权限,参考官方文档:https://developers.facebook.com/docs/facebook-login/permissions/  
             * 有2中属性,一种是默认公开的(Default fields),参考: https://developers.facebook.com/docs/facebook-login/permissions/#reference-default_fields  
             * 另一种是需要在应用控制面板提交审查才可以获取的字段(Read Permissions)  
             */  
            // 只获取公开的属性  
            //LoginManager.getInstance().logInWithReadPermissions(pWebview.getActivity(), Arrays.asList("public_profile"));  

            // 获取更多属性(应用处于开发阶段时,不需要提交审查即可获取所有字段权限)  
            LoginManager.getInstance().logInWithReadPermissions(pWebview.getActivity(),Arrays.asList("email", "user_age_range", "user_birthday", "user_gender", "user_hometown", "user_link", "user_location"));  

        } else {  

            UserRequest.makeUserRequest(new GetUserCallback((SDK_WebApp)pWebview.getActivity()).getCallback());  
        }  

    }  

    public void logOut(IWebview pWebview, JSONArray array)  
    {  
        LoginManager.getInstance().logOut();  
    }  

}

4. 原生层SDK_WebApp.java (在官方给出的android SDK集成代码基础上增加如下代码)

package cn.your.app;  

import io.dcloud.EntryProxy;  
import io.dcloud.RInformation;  
import io.dcloud.common.DHInterface.IApp;  
import io.dcloud.common.DHInterface.IApp.IAppStatusListener;  
import io.dcloud.common.DHInterface.ICore;  
import io.dcloud.common.DHInterface.ICore.ICoreStatusListener;  
import io.dcloud.common.DHInterface.IOnCreateSplashView;  
import io.dcloud.common.DHInterface.ISysEventListener.SysEventType;  
import io.dcloud.common.DHInterface.IWebview;  
import io.dcloud.common.DHInterface.IWebviewStateListener;  
import io.dcloud.common.util.BaseInfo;  
import io.dcloud.common.util.ImageLoaderUtil;  
import io.dcloud.feature.internal.sdk.SDK;  
import android.app.Activity;  
import android.app.ProgressDialog;  
import android.content.Context;  
import android.content.Intent;  
import android.content.res.Configuration;  
import android.os.Build;  
import android.os.Bundle;  
import android.view.KeyEvent;  
import android.view.Menu;  
import android.view.View;  
import android.view.ViewGroup;  
import android.view.Window;  
import android.widget.FrameLayout;  
import android.widget.Toast;  

import com.facebook.CallbackManager;  
import com.facebook.FacebookCallback;  
import com.facebook.FacebookException;  
import com.facebook.login.LoginManager;  
import com.facebook.login.LoginResult;  

import cn.your.app.plugin.facebook.callbacks.GetUserCallback;  
import cn.your.app.plugin.facebook.entities.FacebookUser;  
import cn.your.app.plugin.facebook.requests.UserRequest;  
import java.util.ArrayList;  

/**  
 * 本demo为以WebApp方式集成5+ sdk,   
 *  
 */  
public class SDK_WebApp extends Activity implements GetUserCallback.IGetUserResponse {  

    boolean doHardAcc = true;  
    EntryProxy mEntryProxy = null;  
    CallbackManager callbackManager;  

    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        requestWindowFeature(Window.FEATURE_NO_TITLE);  
        if (mEntryProxy == null) {  
            FrameLayout f = new FrameLayout(this);  
            // 创建5+内核运行事件监听  
            WebappModeListener wm = new WebappModeListener(this, f);  
            // 初始化5+内核  
            mEntryProxy = EntryProxy.init(this, wm);  
            // 启动5+内核  
            mEntryProxy.onCreate(this, savedInstanceState, SDK.IntegratedMode.WEBAPP, null);  
            setContentView(f);  
        }  

        // 注册Facebook授权登录回调方法  
        registerFacebookAuthCallback();  
    }  

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        mEntryProxy.onActivityExecute(this, SysEventType.onActivityResult, new Object[] { requestCode, resultCode, data });  

        // facebook授权成功通知  
        callbackManager.onActivityResult(requestCode, resultCode, data);  
    }  

    /** ================Facebook Login START============== **/  
    protected void registerFacebookAuthCallback()  
    {  
        callbackManager = CallbackManager.Factory.create();  

        LoginManager.getInstance().registerCallback(callbackManager, new FacebookCallback<LoginResult>() {  
            @Override  
            public void onSuccess(LoginResult loginResult) {  
                UserRequest.makeUserRequest(new GetUserCallback(SDK_WebApp.this).getCallback());  
            }  

            @Override  
            public void onCancel() {  
                excuteJSCode("App.facebookAuthCancelCallBack()");  
            }  

            @Override  
            public void onError(FacebookException error) {  
                excuteJSCode("App.facebookAuthErrorCallBack('" + error.getMessage() + "')");  
            }  
        });  
    }  

    @Override  
    public void onCompleted(FacebookUser user) {  

        excuteJSCode("App.facebookAuthSuccessCallBack('" + user.getName() + "')");  
    }  

    @Override  
    public void onGotFacebookUserData(String user)  
    {  
        excuteJSCode("App.facebookAuthSuccessCallBack('" + user + "')");  
    }  

    // 调用login.html内的JS函数, 参考引用的 /js/login.js 文件  
    protected void excuteJSCode(String jsCode)  
    {  
        ArrayList<IWebview> ss = SDK.obtainAllIWebview();  
        for (IWebview iWebview : ss) {  
            if (iWebview.getOriginalUrl().equals("login.html")) {  
                iWebview.evalJS(jsCode);  
                break;  
            }  
        }  
    }  

    /** ================Facebook Login END============== **/  

}  

}

5. 配置文件/app/src/main/res/values/strings.xml

<resources>  
    <string name="app_name">应用名</string>  
    <string name="facebook_app_id">193445774573048</string>  
    <string name="fb_login_protocol_scheme">fb121135774573048</string>  
</resources>

6. 配置文件/app/src/main/assets/data/dcloud_properties.xm(在原基础上新增的部分)

<properties>  
    <features>  
        <feature  
            name="facebookloginplugin"  
            value="cn.shaketowin.app.plugin.facebook.FacebookLoginPlugin" />  
    </features>   

    <services>  
        <!--<service name="facebookloginplugin" value="cn.shaketowin.app.plugin.facebook.FacebookLoginPlugin"/>-->  
    </services>  
</properties>

7. 配置文件/app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="cn.shaketowin.app">  

    <supports-screens  
        android:anyDensity="true"  
        android:largeScreens="true"  
        android:normalScreens="true"  
        android:resizeable="true"  
        android:smallScreens="true" />  

    <!-- facebook login begin -->  
    <uses-permission android:name="android.permission.INTERNET"/>  
    <!-- facebook login end -->  

    <application  
        android:name="io.dcloud.application.DCloudApplication"  
        android:allowBackup="true"  
        android:allowClearUserData="true"  
        android:icon="@drawable/icon"  
        android:label="@string/app_name"  
        android:largeHeap="true"  
        android:supportsRtl="true">  

        <!-- facebook login begin -->  
        <meta-data android:name="com.facebook.sdk.ApplicationId"  
            android:value="@string/facebook_app_id"/>  

        <activity android:name="com.facebook.FacebookActivity"  
            android:configChanges=  
                "keyboard|keyboardHidden|screenLayout|screenSize|orientation"  
            android:label="@string/app_name" />  
        <activity  
            android:name="com.facebook.CustomTabActivity"  
            android:exported="true">  
            <intent-filter>  
                <action android:name="android.intent.action.VIEW" />  
                <category android:name="android.intent.category.DEFAULT" />  
                <category android:name="android.intent.category.BROWSABLE" />  
                <data android:scheme="@string/fb_login_protocol_scheme" />  
            </intent-filter>  
        </activity>  
        <!-- facebook login end -->  

        <activity  
            android:name="cn.shaketowin.app.ActivityEntry"  
            android:configChanges="orientation|keyboardHidden"  
            android:label="@string/app_name"  
            android:launchMode="singleTask"  
            android:screenOrientation="user"  
            android:windowSoftInputMode="adjustResize">  
                <!--<intent-filter>-->  
                    <!--<action android:name="android.intent.action.MAIN" />-->  
                    <!--<category android:name="android.intent.category.LAUNCHER" />-->  
                <!--</intent-filter>-->  
        </activity>  
        <activity  
            android:name="cn.shaketowin.app.SDK_WebApp"  
            android:configChanges="orientation|keyboardHidden|screenSize|keyboard|navigation|mcc|mnc|fontScale"  
            android:screenOrientation="user"  
            android:theme="@style/DCloudTheme"> <!-- 离线配置沉浸式  SDK_WebApp  作为apk入口时  必须设置 SDK_WebApp   的主题为android:theme="@style/DCloudTheme" -->  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  

    </application>  

</manifest>
继续阅读 »

贴出源码回报社区!

下面贴出关键代码,稍后完善更多细节步骤。个人android开发经验不多,如果有更好的集成方式,请大家分享!

前提:

  1. 能在android stuido成功运行官方给出的android SDK集成和插件示例程序和离线打包
  2. 能在android stuido成功运行官方给出的集成示例facebook-android-sdk(https://github.com/facebook/facebook-android-sdk) 中的 FBLoginSample 示例程序
  3. 注册了facebook开发者平台,并完成了官方android集成步骤(https://developers.facebook.com/docs/facebook-login/android?locale=zh_CN)
  4. 下面的部分.facebook.plugin.的代码采用了官方示例代码,参考:https://github.com/facebook/facebook-android-sdk/tree/master/samples/FBLoginSample

源代码目录结构

1.js层代码调用插件login.js

var App ={  

    loginWithFacebook: function()  
    {  
        Common.showLoading();  

        setTimeout(function(){  
            if(App.timeout)  
            {  
                Common.hideLoading();  
                mui.toast(Common.messages.LOGIN_TIMEOUT,{ duration:'short', type:'div' });  
            }  
        }, 5000);  

        if(Common.debug)  
        {  
            setTimeout(function(){  
                App.facebookAuthSuccessCallBack('{"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1791872767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1791872767544170","email":"your@email.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"","gender":"male","birthday":"01\/01\/1991","location":{"id":"106324046073002","name":"Shanghai, China"}}');  
                //App.facebookAuthCancelCallBack();  
                //App.facebookAuthErrorCallBack("auth fail!");  
            }, 2000);  

        }else  
        {  
            //plus.facebookplug.logOut();  
            plus.facebookplug.logIn();  
        }  

    },  

    // 这个回调函数会在 cn.your.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthSuccessCallBack: function(data)  
    {  
        App.timeout = false;  

        if(data != null)  
        {  
         // data example:  '{"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1791872767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1791872767544170","email":"your@email.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"","gender":"male","birthday":"01\/01\/1991","location":{"id":"106324046073002","name":"Shanghai, China"}}'  
            try{  
                var facebookUser = JSON.parse(data);  
                facebookUser.unionid = facebookUser.id;  
                mui.toast(Common.messages.LOGIN_SUCCESS,{ duration:'short', type:'div' });  
                App.checkUserIsExist(facebookUser, "facebook");  
            }catch(err)  
            {  
                Common.hideLoading();  
                mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' });  
                Common.log("====login.js, facebookAuthSuccessCallBack(), decode facebook user JSON string fail!");  
            }  
        }else  
        {  
            Common.hideLoading();  
            App.facebookAuthErrorCallBack();  
        }  
    },  

    // 这个回调函数会在 cn.your.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthCancelCallBack: function()  
    {  
        App.timeout = false;  
        Common.hideLoading();  

        mui.toast(Common.messages.LOGIN_CANCEL,{ duration:'long', type:'div' });  
    },  

    // 这个回调函数会在 cn.your.app.SDK_WebApp.java 原生类中调用, 参考类中的excuteJSCode()方法  
    facebookAuthErrorCallBack: function(error)  
    {  
        App.timeout = false;  
        Common.hideLoading();  

        mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' });  
    }  

};  

if(window.plus){  
    App.init();  
}else{   
    document.addEventListener( "plusready", App.init, false );  
}

2. js层代码插件facebookplug.js

document.addEventListener( "plusready",  function()  
{  
    var facebookplug =  
    {  
        logIn : function ()  
        {  
            window.plus.bridge.exec('facebookplug', "logIn", []);  
        },  
        logOut : function ()  
        {  
            window.plus.bridge.exec('facebookplug', "logOut", []);  
        },  
    };  

    window.plus.facebookplug = facebookplug;  
}, true );

3. 原生层FacebookLoginPlugin.java

package cn.your.app.plugin.facebook;  

import cn.your.app.SDK_WebApp;  
import cn.your.app.plugin.facebook.callbacks.GetUserCallback;  
import cn.your.app.plugin.facebook.requests.UserRequest;  
import io.dcloud.common.DHInterface.IWebview;  
import io.dcloud.common.DHInterface.StandardFeature;  
import io.dcloud.common.util.JSUtil;  

import org.json.JSONArray;  

import android.content.Context;  
import android.os.Bundle;  

import com.facebook.AccessToken;  
import com.facebook.login.LoginManager;  

import java.util.Arrays;  

public class FacebookLoginPlugin extends StandardFeature  
{  

    public void onStart(Context pContext, Bundle pSavedInstanceState, String[] pRuntimeArgs) {  

        /**  
         * 如果需要在应用启动时进行初始化,可以继承这个方法,并在properties.xml文件的service节点添加扩展插件的注册即可触发onStart方法  
         * */  
    }  

    public void logIn(IWebview pWebview, JSONArray array)  
    {  
        if (AccessToken.getCurrentAccessToken() == null) {  

            /*  
             * 获取用户属性的权限,参考官方文档:https://developers.facebook.com/docs/facebook-login/permissions/  
             * 有2中属性,一种是默认公开的(Default fields),参考: https://developers.facebook.com/docs/facebook-login/permissions/#reference-default_fields  
             * 另一种是需要在应用控制面板提交审查才可以获取的字段(Read Permissions)  
             */  
            // 只获取公开的属性  
            //LoginManager.getInstance().logInWithReadPermissions(pWebview.getActivity(), Arrays.asList("public_profile"));  

            // 获取更多属性(应用处于开发阶段时,不需要提交审查即可获取所有字段权限)  
            LoginManager.getInstance().logInWithReadPermissions(pWebview.getActivity(),Arrays.asList("email", "user_age_range", "user_birthday", "user_gender", "user_hometown", "user_link", "user_location"));  

        } else {  

            UserRequest.makeUserRequest(new GetUserCallback((SDK_WebApp)pWebview.getActivity()).getCallback());  
        }  

    }  

    public void logOut(IWebview pWebview, JSONArray array)  
    {  
        LoginManager.getInstance().logOut();  
    }  

}

4. 原生层SDK_WebApp.java (在官方给出的android SDK集成代码基础上增加如下代码)

package cn.your.app;  

import io.dcloud.EntryProxy;  
import io.dcloud.RInformation;  
import io.dcloud.common.DHInterface.IApp;  
import io.dcloud.common.DHInterface.IApp.IAppStatusListener;  
import io.dcloud.common.DHInterface.ICore;  
import io.dcloud.common.DHInterface.ICore.ICoreStatusListener;  
import io.dcloud.common.DHInterface.IOnCreateSplashView;  
import io.dcloud.common.DHInterface.ISysEventListener.SysEventType;  
import io.dcloud.common.DHInterface.IWebview;  
import io.dcloud.common.DHInterface.IWebviewStateListener;  
import io.dcloud.common.util.BaseInfo;  
import io.dcloud.common.util.ImageLoaderUtil;  
import io.dcloud.feature.internal.sdk.SDK;  
import android.app.Activity;  
import android.app.ProgressDialog;  
import android.content.Context;  
import android.content.Intent;  
import android.content.res.Configuration;  
import android.os.Build;  
import android.os.Bundle;  
import android.view.KeyEvent;  
import android.view.Menu;  
import android.view.View;  
import android.view.ViewGroup;  
import android.view.Window;  
import android.widget.FrameLayout;  
import android.widget.Toast;  

import com.facebook.CallbackManager;  
import com.facebook.FacebookCallback;  
import com.facebook.FacebookException;  
import com.facebook.login.LoginManager;  
import com.facebook.login.LoginResult;  

import cn.your.app.plugin.facebook.callbacks.GetUserCallback;  
import cn.your.app.plugin.facebook.entities.FacebookUser;  
import cn.your.app.plugin.facebook.requests.UserRequest;  
import java.util.ArrayList;  

/**  
 * 本demo为以WebApp方式集成5+ sdk,   
 *  
 */  
public class SDK_WebApp extends Activity implements GetUserCallback.IGetUserResponse {  

    boolean doHardAcc = true;  
    EntryProxy mEntryProxy = null;  
    CallbackManager callbackManager;  

    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        requestWindowFeature(Window.FEATURE_NO_TITLE);  
        if (mEntryProxy == null) {  
            FrameLayout f = new FrameLayout(this);  
            // 创建5+内核运行事件监听  
            WebappModeListener wm = new WebappModeListener(this, f);  
            // 初始化5+内核  
            mEntryProxy = EntryProxy.init(this, wm);  
            // 启动5+内核  
            mEntryProxy.onCreate(this, savedInstanceState, SDK.IntegratedMode.WEBAPP, null);  
            setContentView(f);  
        }  

        // 注册Facebook授权登录回调方法  
        registerFacebookAuthCallback();  
    }  

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        mEntryProxy.onActivityExecute(this, SysEventType.onActivityResult, new Object[] { requestCode, resultCode, data });  

        // facebook授权成功通知  
        callbackManager.onActivityResult(requestCode, resultCode, data);  
    }  

    /** ================Facebook Login START============== **/  
    protected void registerFacebookAuthCallback()  
    {  
        callbackManager = CallbackManager.Factory.create();  

        LoginManager.getInstance().registerCallback(callbackManager, new FacebookCallback<LoginResult>() {  
            @Override  
            public void onSuccess(LoginResult loginResult) {  
                UserRequest.makeUserRequest(new GetUserCallback(SDK_WebApp.this).getCallback());  
            }  

            @Override  
            public void onCancel() {  
                excuteJSCode("App.facebookAuthCancelCallBack()");  
            }  

            @Override  
            public void onError(FacebookException error) {  
                excuteJSCode("App.facebookAuthErrorCallBack('" + error.getMessage() + "')");  
            }  
        });  
    }  

    @Override  
    public void onCompleted(FacebookUser user) {  

        excuteJSCode("App.facebookAuthSuccessCallBack('" + user.getName() + "')");  
    }  

    @Override  
    public void onGotFacebookUserData(String user)  
    {  
        excuteJSCode("App.facebookAuthSuccessCallBack('" + user + "')");  
    }  

    // 调用login.html内的JS函数, 参考引用的 /js/login.js 文件  
    protected void excuteJSCode(String jsCode)  
    {  
        ArrayList<IWebview> ss = SDK.obtainAllIWebview();  
        for (IWebview iWebview : ss) {  
            if (iWebview.getOriginalUrl().equals("login.html")) {  
                iWebview.evalJS(jsCode);  
                break;  
            }  
        }  
    }  

    /** ================Facebook Login END============== **/  

}  

}

5. 配置文件/app/src/main/res/values/strings.xml

<resources>  
    <string name="app_name">应用名</string>  
    <string name="facebook_app_id">193445774573048</string>  
    <string name="fb_login_protocol_scheme">fb121135774573048</string>  
</resources>

6. 配置文件/app/src/main/assets/data/dcloud_properties.xm(在原基础上新增的部分)

<properties>  
    <features>  
        <feature  
            name="facebookloginplugin"  
            value="cn.shaketowin.app.plugin.facebook.FacebookLoginPlugin" />  
    </features>   

    <services>  
        <!--<service name="facebookloginplugin" value="cn.shaketowin.app.plugin.facebook.FacebookLoginPlugin"/>-->  
    </services>  
</properties>

7. 配置文件/app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="cn.shaketowin.app">  

    <supports-screens  
        android:anyDensity="true"  
        android:largeScreens="true"  
        android:normalScreens="true"  
        android:resizeable="true"  
        android:smallScreens="true" />  

    <!-- facebook login begin -->  
    <uses-permission android:name="android.permission.INTERNET"/>  
    <!-- facebook login end -->  

    <application  
        android:name="io.dcloud.application.DCloudApplication"  
        android:allowBackup="true"  
        android:allowClearUserData="true"  
        android:icon="@drawable/icon"  
        android:label="@string/app_name"  
        android:largeHeap="true"  
        android:supportsRtl="true">  

        <!-- facebook login begin -->  
        <meta-data android:name="com.facebook.sdk.ApplicationId"  
            android:value="@string/facebook_app_id"/>  

        <activity android:name="com.facebook.FacebookActivity"  
            android:configChanges=  
                "keyboard|keyboardHidden|screenLayout|screenSize|orientation"  
            android:label="@string/app_name" />  
        <activity  
            android:name="com.facebook.CustomTabActivity"  
            android:exported="true">  
            <intent-filter>  
                <action android:name="android.intent.action.VIEW" />  
                <category android:name="android.intent.category.DEFAULT" />  
                <category android:name="android.intent.category.BROWSABLE" />  
                <data android:scheme="@string/fb_login_protocol_scheme" />  
            </intent-filter>  
        </activity>  
        <!-- facebook login end -->  

        <activity  
            android:name="cn.shaketowin.app.ActivityEntry"  
            android:configChanges="orientation|keyboardHidden"  
            android:label="@string/app_name"  
            android:launchMode="singleTask"  
            android:screenOrientation="user"  
            android:windowSoftInputMode="adjustResize">  
                <!--<intent-filter>-->  
                    <!--<action android:name="android.intent.action.MAIN" />-->  
                    <!--<category android:name="android.intent.category.LAUNCHER" />-->  
                <!--</intent-filter>-->  
        </activity>  
        <activity  
            android:name="cn.shaketowin.app.SDK_WebApp"  
            android:configChanges="orientation|keyboardHidden|screenSize|keyboard|navigation|mcc|mnc|fontScale"  
            android:screenOrientation="user"  
            android:theme="@style/DCloudTheme"> <!-- 离线配置沉浸式  SDK_WebApp  作为apk入口时  必须设置 SDK_WebApp   的主题为android:theme="@style/DCloudTheme" -->  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  

    </application>  

</manifest>
收起阅读 »

自定义原生二维码扫描样式

HBuilder 二维码

。。。。。这个 好像只能等官方改了!
不过我们可以从另一个方面来写。

先看效果(阿里云学生服务器哈,总出口就100kb)

!
我讲下大概的实现吧,双webview,最上面webview背景透明,webview之间通信用evalJS。

贴点代码?!
1.加载子页面(最上层webview),背景透明

mui.init({  
        subpages: [{  
                url: 'scan_main.html',  
                id: 'scan_main.html',  
                styles: {  
                    top: 0,  
                    bottom: 0,  
                    background: 'transparent'  
                },  
            }]  
        });

2.webview通信

var scan_win = null;  
mui.plusReady(function() {  
    scan_win = plus.webview.getWebviewById('html/scan_win.html');  
})  
scan_win.evalJS('XXXXXXXXXXXXXX');//打开关闭闪光灯、图库啊等等

3.虽然中间那个扫码框和扫码条暂时只能改颜色,但是通过这样也可以做的跟大厂一样的效果了

继续阅读 »

。。。。。这个 好像只能等官方改了!
不过我们可以从另一个方面来写。

先看效果(阿里云学生服务器哈,总出口就100kb)

!
我讲下大概的实现吧,双webview,最上面webview背景透明,webview之间通信用evalJS。

贴点代码?!
1.加载子页面(最上层webview),背景透明

mui.init({  
        subpages: [{  
                url: 'scan_main.html',  
                id: 'scan_main.html',  
                styles: {  
                    top: 0,  
                    bottom: 0,  
                    background: 'transparent'  
                },  
            }]  
        });

2.webview通信

var scan_win = null;  
mui.plusReady(function() {  
    scan_win = plus.webview.getWebviewById('html/scan_win.html');  
})  
scan_win.evalJS('XXXXXXXXXXXXXX');//打开关闭闪光灯、图库啊等等

3.虽然中间那个扫码框和扫码条暂时只能改颜色,但是通过这样也可以做的跟大厂一样的效果了

收起阅读 »

广州,混合app开发,来搞事情呢,8k-15k

混合开发 移动APP

派我科技找你来做事呢:

1、负责Android及IOS移动App平台应用程序的前端开发,实现各模块功能,并确保开发质量与进度;
2、根据开发流程、规范要求完成相应开发文档;
3、持续的优化前端体验和页面响应速度,并保证兼容性和执行效率;
4、熟悉HTML/CSS/Javascript等前端技术;
5、对前端框架设计有一定见解,可构建高性能的 Web 应用程序;
6、1-2年经验。

ok就发信息来,给个机会让我撩撩你!

继续阅读 »

派我科技找你来做事呢:

1、负责Android及IOS移动App平台应用程序的前端开发,实现各模块功能,并确保开发质量与进度;
2、根据开发流程、规范要求完成相应开发文档;
3、持续的优化前端体验和页面响应速度,并保证兼容性和执行效率;
4、熟悉HTML/CSS/Javascript等前端技术;
5、对前端框架设计有一定见解,可构建高性能的 Web 应用程序;
6、1-2年经验。

ok就发信息来,给个机会让我撩撩你!

收起阅读 »

快速上手 - wap2app教程

入门 wap2app

下载/登录开发工具

前往 dcloud.io 根据自己的操作系统,下载对应的 HBuilder 安装包,解压即可使用。

PS:首次使用需注册一个开发者账号,方便后续的打包以及应用管理。

创建项目

按照如下步骤在HBuilder中新建 wap2app 工程

  • 顶部菜单,文件 -> 新建 -> Wap2App
  • 输入应用名称、wap 站首页地址,点击“完成”

工程建成后,默认目录结构如下:

配置应用图标及启动图

应用图标

应用图标是App安装到手机后,在手机桌面显示的图标;按照如下步骤配置应用图标:

双击打开manifest.json文件,点击下方“图标配置”选项卡

在图标配置页面,按照要求配置各种分辨率的图标

建议配置1024*1024的大图标,然后自动生成并替换各种分辨率的图标。

启动图片

启动图片是用户从点击桌面图标到进入应用首页中间显示的欢迎图片;按照如下步骤配置应用图标。

点击“启动图片(splash)配置”选项卡,会打开启动图配置界面

根据需要配置不同分辨率的启动图片,例如仅生成Android安装包,则仅配置Android启动图片即可。

应用发布

wap2app项目可以打包成iOS平台的ipa安装包、Android平台的apk安装包。

但注意:iOS平台只能发布企业版或越狱包,无法上Appstore。因为苹果要求强制wkwebview,而wap2app需要使用uiwebview。导致iOS上的wap2app可以真机运行,但没法上Appstore。

在HBuilder中,点击顶部“发行”菜单,点击“云打包-打原生安装包”,如下:

打开原生打包配置界面,如下图所示:

选择需要打包的平台(iOS平台打包需要配置iOS证书),点击“打包”按钮即可提交云端打包,打包完成后会自动下载安装包,安装到手机即可体验。

好了,到此为止,你已经快速体验了wap2app的创建到发布过程,更多强化功能实现请继续阅读wap2app的相关教程。

继续阅读 »

下载/登录开发工具

前往 dcloud.io 根据自己的操作系统,下载对应的 HBuilder 安装包,解压即可使用。

PS:首次使用需注册一个开发者账号,方便后续的打包以及应用管理。

创建项目

按照如下步骤在HBuilder中新建 wap2app 工程

  • 顶部菜单,文件 -> 新建 -> Wap2App
  • 输入应用名称、wap 站首页地址,点击“完成”

工程建成后,默认目录结构如下:

配置应用图标及启动图

应用图标

应用图标是App安装到手机后,在手机桌面显示的图标;按照如下步骤配置应用图标:

双击打开manifest.json文件,点击下方“图标配置”选项卡

在图标配置页面,按照要求配置各种分辨率的图标

建议配置1024*1024的大图标,然后自动生成并替换各种分辨率的图标。

启动图片

启动图片是用户从点击桌面图标到进入应用首页中间显示的欢迎图片;按照如下步骤配置应用图标。

点击“启动图片(splash)配置”选项卡,会打开启动图配置界面

根据需要配置不同分辨率的启动图片,例如仅生成Android安装包,则仅配置Android启动图片即可。

应用发布

wap2app项目可以打包成iOS平台的ipa安装包、Android平台的apk安装包。

但注意:iOS平台只能发布企业版或越狱包,无法上Appstore。因为苹果要求强制wkwebview,而wap2app需要使用uiwebview。导致iOS上的wap2app可以真机运行,但没法上Appstore。

在HBuilder中,点击顶部“发行”菜单,点击“云打包-打原生安装包”,如下:

打开原生打包配置界面,如下图所示:

选择需要打包的平台(iOS平台打包需要配置iOS证书),点击“打包”按钮即可提交云端打包,打包完成后会自动下载安装包,安装到手机即可体验。

好了,到此为止,你已经快速体验了wap2app的创建到发布过程,更多强化功能实现请继续阅读wap2app的相关教程。

收起阅读 »

力谱云V2.0新发布,分销&外卖配送大升级!

移动APP

力谱云厚积薄发,隆重上线V2.0版本咯!
在本次版本中,我们将重点针对B2C分销、B2B2C配送、B2B2C外卖等三大方面进行App平台功能大升级!并对App商品分享、订单预约等功能进行了优化。另对14项细节处,进行了新功能追加与更新!赶紧抢先阅读吧!

以下为本次重点App更新功能↓↓↓
B2C分销功能大升级
B2B2C配送功能大升级,支持生鲜和外卖行业抢单和派单
B2B2C外卖行业优化,支持外卖模式的店铺布局,并可自动打印订单
支持App中的商品,以小程序卡片方式分享到微信
电商大管家和商户端支持服务预约的订单处理。针对多次型消费服务模式,店铺方可进行多次核销。
B2C分销

可对每个商品设置三级分销提成比例
有分销提成的用户可在移动端界面看到每个订单的分销提成
分销提成方式支持余额,提现更方便
支持通过推荐码建立分销体系。用户在注册平台时,输入推荐码,即可在注册同时,即能加入上级进行分销
管理后台可以修改会员的推荐人。可防止因推荐人退团,导致分销团队离散。
B2B2C配送

支持派单模式。当无人抢单时,商家可通过查看配送员的离店距离、当前接单数等信息,进行筛查,选择最合适的配送员进行手动派单。
配送抢单可以设置抢单范围
B2B2C外卖

店铺支持外卖布局,如美团/饿了么等模式
店铺管家和店铺管理后台支持一键打烊
订单可以自动打印到热敏打印机
其他4项新功能和优化
App可以以小程序卡片的方式分享商品到微信
优化移动端下单体验
支付更流畅
商品待发货时,显示【申请退款】。交易成功时,显示【申请售后】。优化用户购物体验,并提升售后服务。
B2B2C每个店铺可以设置不一样的消费返积分比例
改进订单详情界面物流信息显示
其他14处细节新功能和优化

B2B2C平台可以发放代金券给每个用户
为B2B2C店铺生成二维码,支持扫一扫进入店铺首页
店铺管家可以处理预定订单
B2B2C可以设置某些商品的排序权重,可在分类页和搜索页优先排序
B2C和B2B2C商品支持显示起订量,方便商品批发
B2C商城可对未登陆用户隐藏价格
管理后台可以查看用户余额变更明细
管理后台支持批量导入订单物流信息
配送端多规格商品在详情页显示多规格属性
优化管理后台B2C、B2B2C订单详情和导出
优化管理后台订单支付流水界面
会员导出增加会员自定义属性
文章列表显示阅读量
付费文章在列表页显示积分价格
力谱云助力企业轻松打造专属移动电商平台

再次感谢各位新老用户对力谱云平台的信赖与支持!在上半年度,力谱云主要集中精力打通了小程序平台等各项功能,并针对电商分销系统、外卖配送等功能进行了整体优化与升级。
目前,力谱云倾力打造移动电商App平台,终于厚积薄发,全新推出关于社交分销、多商户社交电商平台、新零售、移动订货平台等4大移动解决方案,采用云集、拼多多、生鲜/外卖配送、裂变新渠道等丰富新电商元素,以期让企业运营、销售、营销迸发出更多商业新活力!此外,我们还将再接再厉,持续攀登行业高峰,努力为企业用户们带来更多前沿、专业、高效、盈利丰厚的移动电商解决方案。
选择力谱云,让我们为您提供最专业的专属移动电商解决方案吧!

继续阅读 »

力谱云厚积薄发,隆重上线V2.0版本咯!
在本次版本中,我们将重点针对B2C分销、B2B2C配送、B2B2C外卖等三大方面进行App平台功能大升级!并对App商品分享、订单预约等功能进行了优化。另对14项细节处,进行了新功能追加与更新!赶紧抢先阅读吧!

以下为本次重点App更新功能↓↓↓
B2C分销功能大升级
B2B2C配送功能大升级,支持生鲜和外卖行业抢单和派单
B2B2C外卖行业优化,支持外卖模式的店铺布局,并可自动打印订单
支持App中的商品,以小程序卡片方式分享到微信
电商大管家和商户端支持服务预约的订单处理。针对多次型消费服务模式,店铺方可进行多次核销。
B2C分销

可对每个商品设置三级分销提成比例
有分销提成的用户可在移动端界面看到每个订单的分销提成
分销提成方式支持余额,提现更方便
支持通过推荐码建立分销体系。用户在注册平台时,输入推荐码,即可在注册同时,即能加入上级进行分销
管理后台可以修改会员的推荐人。可防止因推荐人退团,导致分销团队离散。
B2B2C配送

支持派单模式。当无人抢单时,商家可通过查看配送员的离店距离、当前接单数等信息,进行筛查,选择最合适的配送员进行手动派单。
配送抢单可以设置抢单范围
B2B2C外卖

店铺支持外卖布局,如美团/饿了么等模式
店铺管家和店铺管理后台支持一键打烊
订单可以自动打印到热敏打印机
其他4项新功能和优化
App可以以小程序卡片的方式分享商品到微信
优化移动端下单体验
支付更流畅
商品待发货时,显示【申请退款】。交易成功时,显示【申请售后】。优化用户购物体验,并提升售后服务。
B2B2C每个店铺可以设置不一样的消费返积分比例
改进订单详情界面物流信息显示
其他14处细节新功能和优化

B2B2C平台可以发放代金券给每个用户
为B2B2C店铺生成二维码,支持扫一扫进入店铺首页
店铺管家可以处理预定订单
B2B2C可以设置某些商品的排序权重,可在分类页和搜索页优先排序
B2C和B2B2C商品支持显示起订量,方便商品批发
B2C商城可对未登陆用户隐藏价格
管理后台可以查看用户余额变更明细
管理后台支持批量导入订单物流信息
配送端多规格商品在详情页显示多规格属性
优化管理后台B2C、B2B2C订单详情和导出
优化管理后台订单支付流水界面
会员导出增加会员自定义属性
文章列表显示阅读量
付费文章在列表页显示积分价格
力谱云助力企业轻松打造专属移动电商平台

再次感谢各位新老用户对力谱云平台的信赖与支持!在上半年度,力谱云主要集中精力打通了小程序平台等各项功能,并针对电商分销系统、外卖配送等功能进行了整体优化与升级。
目前,力谱云倾力打造移动电商App平台,终于厚积薄发,全新推出关于社交分销、多商户社交电商平台、新零售、移动订货平台等4大移动解决方案,采用云集、拼多多、生鲜/外卖配送、裂变新渠道等丰富新电商元素,以期让企业运营、销售、营销迸发出更多商业新活力!此外,我们还将再接再厉,持续攀登行业高峰,努力为企业用户们带来更多前沿、专业、高效、盈利丰厚的移动电商解决方案。
选择力谱云,让我们为您提供最专业的专属移动电商解决方案吧!

收起阅读 »

闭包 作用域 原型链

js
// 代码1  
if(!("userName" in window)) {  
  var userName = "yang"  
}  
console.log(userName) // undefined  
if是一个块,在ES6里有块级作用域,在ES5里是没有这个概念的。  
// 代码2  
var obj = {  
  user: "yang",  
  getName: function () {  
    return this.user;  
  }  
}  
var getNameFn = obj.getName;  
console.log(getNameFn()) // Uncaught ReferenceError: user is not defined  
console.log(obj.getName()) // Uncaught ReferenceError: user is not defined  

var obj = {  
  user: "yang",  
  getName: function () {  
    return this.user;  
  }  
}  
var getNameFn = obj.getName;  
console.log(getNameFn()) // Uncaught ReferenceError: user is not defined  
console.log(obj.getName()) // "yang"  

作用域
1、程序级
2、文件级
3、函数级
4、块级

全局作用域、函数作用域、块级作用域(es6)
js中只有一个局部作用域就是函数作用域

// 代码3  
var global  = 1;  
function doSomething() {  
  var inner = 2;  
 globalA = 3;  
}  
doSomething();  
alert(global);  // 1  
alert(globalA); // 3  
alert(inner);  // undefined

什么是作用域链?
在js中,函数也是对象,函数对象和其他对象一样,拥有可以通过代码访问的属性和一系列仅供js引擎访问的内部函数,其中一个内部属性是【scope】,由ECMA-262标准第三版定义,该内部属性包含了函数被创建作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

var x = 1;  
function foo () {  
  var y = 1 + x;  
  return function () {  
    var z = 1 + y;  
    return z;  
  }  
}  
foo()();  

// 代码4  
var test = "aa"  
function doSomething() {  
  alert(test) // undefined 变量与函数的声明提前,永远访问不到外部的test  
  var test = "bb"  
  alert(test) // bb  
}  
doSomething()  
alert(test) //aa  

**变量与函数的声明提前**  

function foo () {  
  alert(test);  
  var test = "bb"  
  alert(test)  
}  
foo()  
执行顺序:  
1.声明函数foo  
2. 调用函数foo  
3. 声明变量test  
4. alert(test)  
5.test变量赋值为bb  
6.alert(test)  

js中this关键字
this指向哪里?
在js中,this指向函数执行时的当前对象。
this的使用场景
》普通函数中:
严格模式中:undefined
非严格模式中:全局对象(window)
》构造函数中:对象的实例
》对象方法:对象本身
call、apply、bind
通过这三个方法可以改变被调用函数中this指向的对象

原型对象是什么
在js中,每定义一个对象(函数)时,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象prototype。普通对象没有prototype属性,但有proto属性

function f1() {};  
console.log(typeof f1.prototype) // Object  
console.log(typeof Function.prototype) // Function,这个特殊  
console.log(typeof Object.prototype) // Object  
console.log(typeof Function.prototype.prototype) //undefined

原型对象有什么用?
面向对象开发、类的继承

function Person(name) {  
  this.name = name;  
}  
Person.prototype.getName = function () {  
  return this.name;  
}  
var sub = new Person("yang")  
sub.getName() //yang

构造函数
使用new关键字调用一个函数的时候,这个函数就叫构造函数
构造函数可以实例化一个对象
返回值,默认返回类的实例
特殊情况:没有返回值、简单数据类型、对象类型
原型链
1.每个函数都有一个prototype的对象属性

  1. 每个对象都有一个 proto 属性,该属性指向其父类的prototype对象
    原型对象中的constructor
    每个原型对象prototype中都有一个constructor属性,默认指向函数本身
    
    // test 1  
    function make(num) {  
    return function () {  
    return num;  
    }  
    }  

var arr = [make(0),make(1),make(2)]
alert(arr[0]()) // 0
alert(arr[1]()) // 1
alert(arr[2]()) // 2

// test 2
var name = "global"
function A(name) {
// 参数name就是一个变量声明
alert(name) // 3
this.name = name
var name = "1" // 这个name完全不影响
}
A.prototype.name = "2" // 在原型链上后调用
var a = new A("3")
alert(a.name) // 3
delete a.name
alert(a.name) // 2

// test 3
function fun (n,o) {
console.log(o)
return {
fun: function(m) {
return fun(m,n) //访问的是最外层的fun,如果要访问return里的fun,一定是XXX.fun(),一定有“.”这个点
}
}
}
var a = fun(0) // undefined
a.fun(1) // 一定是通过这种方法才能访问到对象的fun,而函数fun不同
a.fun(2)
var b = fun(0).fun(1).fun(2).fun(3)
var c = fun(0).fun(1)
c.fun(2)
c.fun(3)
相关说明:https://segmentfault.com/a/1190000004187681
其实是这样的
function _fun (n,o) {
console.log(o)
return {
fun: function(m) {
return _fun(m,n) //访问的是最外层的fun,如果要访问return里的fun,一定是XXX.fun(),一定有“.”这个点
}
}
}


chrome调试作用域链  
闭包就是一个函数一个作用域,使用场景  
使用的i函数就是一个局部作用域,就是一个闭包  
闭包会引起内存泄漏为什么还要用它?  
涉及到js的垃圾回收,循环引用,会在闭包执行完之后进行回收,内存泄漏是认为写代码不当导致的,函数式编程中闭包更常用。  

变量的提前声明  
《函数式编程》  
lodash  
继续阅读 »
// 代码1  
if(!("userName" in window)) {  
  var userName = "yang"  
}  
console.log(userName) // undefined  
if是一个块,在ES6里有块级作用域,在ES5里是没有这个概念的。  
// 代码2  
var obj = {  
  user: "yang",  
  getName: function () {  
    return this.user;  
  }  
}  
var getNameFn = obj.getName;  
console.log(getNameFn()) // Uncaught ReferenceError: user is not defined  
console.log(obj.getName()) // Uncaught ReferenceError: user is not defined  

var obj = {  
  user: "yang",  
  getName: function () {  
    return this.user;  
  }  
}  
var getNameFn = obj.getName;  
console.log(getNameFn()) // Uncaught ReferenceError: user is not defined  
console.log(obj.getName()) // "yang"  

作用域
1、程序级
2、文件级
3、函数级
4、块级

全局作用域、函数作用域、块级作用域(es6)
js中只有一个局部作用域就是函数作用域

// 代码3  
var global  = 1;  
function doSomething() {  
  var inner = 2;  
 globalA = 3;  
}  
doSomething();  
alert(global);  // 1  
alert(globalA); // 3  
alert(inner);  // undefined

什么是作用域链?
在js中,函数也是对象,函数对象和其他对象一样,拥有可以通过代码访问的属性和一系列仅供js引擎访问的内部函数,其中一个内部属性是【scope】,由ECMA-262标准第三版定义,该内部属性包含了函数被创建作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

var x = 1;  
function foo () {  
  var y = 1 + x;  
  return function () {  
    var z = 1 + y;  
    return z;  
  }  
}  
foo()();  

// 代码4  
var test = "aa"  
function doSomething() {  
  alert(test) // undefined 变量与函数的声明提前,永远访问不到外部的test  
  var test = "bb"  
  alert(test) // bb  
}  
doSomething()  
alert(test) //aa  

**变量与函数的声明提前**  

function foo () {  
  alert(test);  
  var test = "bb"  
  alert(test)  
}  
foo()  
执行顺序:  
1.声明函数foo  
2. 调用函数foo  
3. 声明变量test  
4. alert(test)  
5.test变量赋值为bb  
6.alert(test)  

js中this关键字
this指向哪里?
在js中,this指向函数执行时的当前对象。
this的使用场景
》普通函数中:
严格模式中:undefined
非严格模式中:全局对象(window)
》构造函数中:对象的实例
》对象方法:对象本身
call、apply、bind
通过这三个方法可以改变被调用函数中this指向的对象

原型对象是什么
在js中,每定义一个对象(函数)时,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象prototype。普通对象没有prototype属性,但有proto属性

function f1() {};  
console.log(typeof f1.prototype) // Object  
console.log(typeof Function.prototype) // Function,这个特殊  
console.log(typeof Object.prototype) // Object  
console.log(typeof Function.prototype.prototype) //undefined

原型对象有什么用?
面向对象开发、类的继承

function Person(name) {  
  this.name = name;  
}  
Person.prototype.getName = function () {  
  return this.name;  
}  
var sub = new Person("yang")  
sub.getName() //yang

构造函数
使用new关键字调用一个函数的时候,这个函数就叫构造函数
构造函数可以实例化一个对象
返回值,默认返回类的实例
特殊情况:没有返回值、简单数据类型、对象类型
原型链
1.每个函数都有一个prototype的对象属性

  1. 每个对象都有一个 proto 属性,该属性指向其父类的prototype对象
    原型对象中的constructor
    每个原型对象prototype中都有一个constructor属性,默认指向函数本身
    
    // test 1  
    function make(num) {  
    return function () {  
    return num;  
    }  
    }  

var arr = [make(0),make(1),make(2)]
alert(arr[0]()) // 0
alert(arr[1]()) // 1
alert(arr[2]()) // 2

// test 2
var name = "global"
function A(name) {
// 参数name就是一个变量声明
alert(name) // 3
this.name = name
var name = "1" // 这个name完全不影响
}
A.prototype.name = "2" // 在原型链上后调用
var a = new A("3")
alert(a.name) // 3
delete a.name
alert(a.name) // 2

// test 3
function fun (n,o) {
console.log(o)
return {
fun: function(m) {
return fun(m,n) //访问的是最外层的fun,如果要访问return里的fun,一定是XXX.fun(),一定有“.”这个点
}
}
}
var a = fun(0) // undefined
a.fun(1) // 一定是通过这种方法才能访问到对象的fun,而函数fun不同
a.fun(2)
var b = fun(0).fun(1).fun(2).fun(3)
var c = fun(0).fun(1)
c.fun(2)
c.fun(3)
相关说明:https://segmentfault.com/a/1190000004187681
其实是这样的
function _fun (n,o) {
console.log(o)
return {
fun: function(m) {
return _fun(m,n) //访问的是最外层的fun,如果要访问return里的fun,一定是XXX.fun(),一定有“.”这个点
}
}
}


chrome调试作用域链  
闭包就是一个函数一个作用域,使用场景  
使用的i函数就是一个局部作用域,就是一个闭包  
闭包会引起内存泄漏为什么还要用它?  
涉及到js的垃圾回收,循环引用,会在闭包执行完之后进行回收,内存泄漏是认为写代码不当导致的,函数式编程中闭包更常用。  

变量的提前声明  
《函数式编程》  
lodash  
收起阅读 »

支持mui的vue脚手架,可下载apk体验,支持ES6/7/8,支持多页面打包

Vue

mogoH5+ v0.2 脚手架发布啦,支持vue,多页面,ES6/7/8,压缩/打包,完全支持mui.可以下载体验apk
体验apk:https://fir.im/p52j
脚手架地址:https://github.com/tyaqing/mogo-h5plus
欢迎各位提出宝贵意见

  • 支持 Npm 生态
  • 支持 vue 语法,以及 vue 生态,比如 vux,mint,vant
  • 支持 ES6/ES7 语法
  • 使用 VConsole 调试
  • VSCode 友好
  • 局域网便捷调试,不插数据线也可以调试
  • 支持mui的使用
继续阅读 »

mogoH5+ v0.2 脚手架发布啦,支持vue,多页面,ES6/7/8,压缩/打包,完全支持mui.可以下载体验apk
体验apk:https://fir.im/p52j
脚手架地址:https://github.com/tyaqing/mogo-h5plus
欢迎各位提出宝贵意见

  • 支持 Npm 生态
  • 支持 vue 语法,以及 vue 生态,比如 vux,mint,vant
  • 支持 ES6/ES7 语法
  • 使用 VConsole 调试
  • VSCode 友好
  • 局域网便捷调试,不插数据线也可以调试
  • 支持mui的使用
收起阅读 »

老帖子已经关闭

phpgrace PHP

老帖子已经关闭

老帖子已经关闭

h5+、mui、app开发基础+实战教程都在这儿!

mui h5+


《JavaScript 快速提高视频教程》 js基础快速提高课程 【免费】
http://www.hcoder.net/course/info_229.html

《MUI 视频教程》【免费】
http://www.hcoder.net/course/info_211.html

《h.js 视频教程》【免费】
http://www.hcoder.net/tutorials/info_147.html

《HTML 5 开发教程》【免费】
http://www.hcoder.net/course/info_212.html

《APP开发实例教程 - 窗口切换 》【免费】
http://www.hcoder.net/course/info_218.html

《HBuilder 8.0.1 APP开发 - 新功能全接触》【免费】
http://www.hcoder.net/course/info_227.html

--------- 实战收费教程 ------------------------
《仿腾讯新闻客户端》- MUI+VUE.JS H5+ APP 实战教程
http://www.hcoder.net/course/info_240.html

MUI、H5 APP 实战教程 - 仿《有道词典》
https://ke.qq.com/course/194834

H5 跨平台APP开发电商项目实战教程 -《仿京东优选商城》
https://ke.qq.com/course/225830

更多课程中心
http://www.hcoder.net/course

继续阅读 »


《JavaScript 快速提高视频教程》 js基础快速提高课程 【免费】
http://www.hcoder.net/course/info_229.html

《MUI 视频教程》【免费】
http://www.hcoder.net/course/info_211.html

《h.js 视频教程》【免费】
http://www.hcoder.net/tutorials/info_147.html

《HTML 5 开发教程》【免费】
http://www.hcoder.net/course/info_212.html

《APP开发实例教程 - 窗口切换 》【免费】
http://www.hcoder.net/course/info_218.html

《HBuilder 8.0.1 APP开发 - 新功能全接触》【免费】
http://www.hcoder.net/course/info_227.html

--------- 实战收费教程 ------------------------
《仿腾讯新闻客户端》- MUI+VUE.JS H5+ APP 实战教程
http://www.hcoder.net/course/info_240.html

MUI、H5 APP 实战教程 - 仿《有道词典》
https://ke.qq.com/course/194834

H5 跨平台APP开发电商项目实战教程 -《仿京东优选商城》
https://ke.qq.com/course/225830

更多课程中心
http://www.hcoder.net/course

收起阅读 »

直播业务指南

video 直播

*注意要使用HBuilderX而不是HBuilder

引言

直播是很多开发者的需求。
HTML5的video,虽然可以播放流媒体,但不支持rtmp等常用直播格式。更无法实现推流,就是实时流式上传视频。
5+App、uni-app给开发者提供了完整的直播解决方案。
HTML5Plus规范提供了plus.video扩展规范,实现了原生的视频播放和推流录制上传。

HTML5Plus的规范是通行规范,可支持任何直播云服务厂商的直播服务器。
但又拍云公司作为HTML5中国产业联盟成员,为HTML5plus的开发者提供了更多完善服务。

  1. 注册优惠
    点此链接注册,可得众多优惠。
    a) 认证完毕后赠送 61 元免费代金券;
    b) 首次充值后,奖励充值金额 10% 代金券
    c) 一周后再赠送200元的代金劵
    以上优惠不是三选一,是全都有。有效期1个月时间。
  2. 便捷的服务器搭建
    无需关心rtmp服务器的搭建和网络cdn的优化,注册后提供自己的直播推流和播放的域名,做cname映射到又拍云的服务器,然后自己随便定义二级目录地址就可以用了。
    详见又拍云文档http://docs.upyun.com/live/guide/

代码示例-推流上传(live-pusher)

直播推流控件会调用手机摄像头采集视频流,编码后通过rtmp协议提交到直播服务器。

并实现了一套代码兼容App和小程序,具体参考上述组件及API文档。

  1. uni-app下的vue页面
    app平台的vue页面中使用直播推流,不能使用uni的api,而需要使用plus的API

    const currentWebview = this.$mp.page.$getAppWebview()  
    var pusher = plus.video.createLivePusher("", {    
    url:'rtmp://testlivesdk.v0.upaiyun.com/live/upyunb',    
    top:'100px',    
    left:'0px',    
    width: '100%',    
    height: '300px',    
    position: 'static'    
    });    
    currentWebview.append(pusher);  
  2. 5+App模式示例:
    在页面中直播推流控件需要绑定div确定位置及大小,创建直播推流控件需要传入div的id,示例如下:

    <html>  
       <head>  
              <meta charset="utf-8"/>  
              <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>  
              <title>Hello H5+</title>  
              <script type="text/javascript">  
    var pusher = null;  
    // H5 plus事件处理  
    function plusReady(){  
       // 创建直播推流控件  
       pusher = new plus.video.LivePusher('pusher',{url:'rtmp://testlivesdk.v0.upaiyun.com/live/upyunb'});  
       // 监听状态变化事件  
       pusher.addEventListener('statechange', function(e){  
              console.log('statechange: '+JSON.stringify(e));  
       }, false);  
    }  
    document.addEventListener('plusready', plusReady, false);  
              </script>  
       </head>  
       <body>  
              <br/>  
              <div id="pusher" style="width:300px;height:400px;background-color:#000000;margin:auto"></div>  
       </body>  
    </html>

    其中new plus.video.LivePusher()第一个参数'pusher'为绑定div的id值,第二个参数中的url属性值为直播推流服务器地址,必须为rtmp协议。创建后返回直播推流控件(LivePusher)对象,后续可通过此对象进行操作控制。

使用HBuilderX新建5+App项目时自带的Hello H5+模板,真机运行,可以看到video示例,里面有推流和播放的示例代码。

  • 采集直播推流提交到服务器
    创建直播推流控件后可通过setOptions方法更新直播控件参数,如下更新直播推流服务器地址:
    pusher.setOptions({url:'rtmp://新的直播推流服务器地址'});

    配置完成后调用start方法开始采集直播流提交到服务器:

    pusher.start();

推流时支持美颜、摄像头切换。不支持其他特效滤镜。
目前不支持录屏直播,无法实现手机录制。除非使用原生sdk自行开发离线打包。
目前暂不支持直播连麦功能。

更多API参考文档http://www.html5plus.org/doc/zh_cn/video.html#plus.video.LivePusher
推流到直播服务器后可通过原生视频播放控件进行播放。

代码示例-使用视频播放控件播放直播视频流

HTML5标准的video是无法播放直播视频流的,所以需要使用原生增加的播放控件。
HTML5plus扩展的原生视频播放控件(也是uni-app的App端自带的)支持视频格式包括mp4和flv,视频传输协议支持http/https和rtmp/hls/rtsp(直播流)

  • 创建视频播放控件

    1. uni-app模式
      uni-app模式直接使用<video>组件进行播放,无需其他复杂设置。
    2. 5+App模式
      在页面中视频播放控件需要绑定div确定位置及大小,创建视频播放控件需要传入div的id,示例如下:
      <!DOCTYPE HTML>  
      <html>  
      <head>  
            <meta charset="utf-8"/>  
            <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>  
            <title>Hello H5+</title>  
            <script type="text/javascript">  
      var video = null;  
      // H5 plus事件处理  
      function plusReady(){  
      // 创建视频播放控件  
      video = new plus.video.VideoPlayer('video',{  
            src:'rtmp://live.hkstv.hk.lxdns.com/live/hks'  
      });  
      }  
      document.addEventListener('plusready', plusReady, false);  
            </script>  
      </head>  
      <body>  
            <br/>  
            <div id="video" style="width:98%;height:300px;background-color:#000000;margin:auto"></div>  
      </body>  
      </html>

      其中new plus.video.VideoPlayer()第一个参数'video'为绑定div的id值,第二个参数中的src属性值为要播放的直播视频地址,直播视频必须为rtmp协议。创建后返回视频播放控件(VideoPlayer)对象,后续可通过此对象进行操作控制。

  • 播放视频流
    创建视频播放控件后可通过setOptions方法更新播放控件参数,如下更新视频服务器地址:

    video.setOptions({src:'新的视频服务器地址'});

    用户可以点击播放控件上的播放按钮进行播放,也可调用play方法进行播放:

    video.play();

如果想在视频上绘制一些内容,比如滚动的聊天记录,5+App可以在上面创建一个透明webview,具体代码在Hello H5+的video示例里有;uni-app可以在上面创建一个subnvue,在HBuilderX1.9.10+版本中自带的hello uni-app - API - 界面 - 原生子窗体中也有示例。

包括防盗播的跑马灯也可以这么做。

更多API参考文档http://www.html5plus.org/doc/zh_cn/video.html#plus.video.VideoPlayer

三方示例模板

这个是插件市场里三方开发者做的直播业务模板,开源,可以参考:http://ext.dcloud.net.cn/plugin?id=226

还有一些开发者封装了三方原生sdk,比如腾讯直播sdk,可以在插件市场获取

继续阅读 »

*注意要使用HBuilderX而不是HBuilder

引言

直播是很多开发者的需求。
HTML5的video,虽然可以播放流媒体,但不支持rtmp等常用直播格式。更无法实现推流,就是实时流式上传视频。
5+App、uni-app给开发者提供了完整的直播解决方案。
HTML5Plus规范提供了plus.video扩展规范,实现了原生的视频播放和推流录制上传。

HTML5Plus的规范是通行规范,可支持任何直播云服务厂商的直播服务器。
但又拍云公司作为HTML5中国产业联盟成员,为HTML5plus的开发者提供了更多完善服务。

  1. 注册优惠
    点此链接注册,可得众多优惠。
    a) 认证完毕后赠送 61 元免费代金券;
    b) 首次充值后,奖励充值金额 10% 代金券
    c) 一周后再赠送200元的代金劵
    以上优惠不是三选一,是全都有。有效期1个月时间。
  2. 便捷的服务器搭建
    无需关心rtmp服务器的搭建和网络cdn的优化,注册后提供自己的直播推流和播放的域名,做cname映射到又拍云的服务器,然后自己随便定义二级目录地址就可以用了。
    详见又拍云文档http://docs.upyun.com/live/guide/

代码示例-推流上传(live-pusher)

直播推流控件会调用手机摄像头采集视频流,编码后通过rtmp协议提交到直播服务器。

并实现了一套代码兼容App和小程序,具体参考上述组件及API文档。

  1. uni-app下的vue页面
    app平台的vue页面中使用直播推流,不能使用uni的api,而需要使用plus的API

    const currentWebview = this.$mp.page.$getAppWebview()  
    var pusher = plus.video.createLivePusher("", {    
    url:'rtmp://testlivesdk.v0.upaiyun.com/live/upyunb',    
    top:'100px',    
    left:'0px',    
    width: '100%',    
    height: '300px',    
    position: 'static'    
    });    
    currentWebview.append(pusher);  
  2. 5+App模式示例:
    在页面中直播推流控件需要绑定div确定位置及大小,创建直播推流控件需要传入div的id,示例如下:

    <html>  
       <head>  
              <meta charset="utf-8"/>  
              <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>  
              <title>Hello H5+</title>  
              <script type="text/javascript">  
    var pusher = null;  
    // H5 plus事件处理  
    function plusReady(){  
       // 创建直播推流控件  
       pusher = new plus.video.LivePusher('pusher',{url:'rtmp://testlivesdk.v0.upaiyun.com/live/upyunb'});  
       // 监听状态变化事件  
       pusher.addEventListener('statechange', function(e){  
              console.log('statechange: '+JSON.stringify(e));  
       }, false);  
    }  
    document.addEventListener('plusready', plusReady, false);  
              </script>  
       </head>  
       <body>  
              <br/>  
              <div id="pusher" style="width:300px;height:400px;background-color:#000000;margin:auto"></div>  
       </body>  
    </html>

    其中new plus.video.LivePusher()第一个参数'pusher'为绑定div的id值,第二个参数中的url属性值为直播推流服务器地址,必须为rtmp协议。创建后返回直播推流控件(LivePusher)对象,后续可通过此对象进行操作控制。

使用HBuilderX新建5+App项目时自带的Hello H5+模板,真机运行,可以看到video示例,里面有推流和播放的示例代码。

  • 采集直播推流提交到服务器
    创建直播推流控件后可通过setOptions方法更新直播控件参数,如下更新直播推流服务器地址:
    pusher.setOptions({url:'rtmp://新的直播推流服务器地址'});

    配置完成后调用start方法开始采集直播流提交到服务器:

    pusher.start();

推流时支持美颜、摄像头切换。不支持其他特效滤镜。
目前不支持录屏直播,无法实现手机录制。除非使用原生sdk自行开发离线打包。
目前暂不支持直播连麦功能。

更多API参考文档http://www.html5plus.org/doc/zh_cn/video.html#plus.video.LivePusher
推流到直播服务器后可通过原生视频播放控件进行播放。

代码示例-使用视频播放控件播放直播视频流

HTML5标准的video是无法播放直播视频流的,所以需要使用原生增加的播放控件。
HTML5plus扩展的原生视频播放控件(也是uni-app的App端自带的)支持视频格式包括mp4和flv,视频传输协议支持http/https和rtmp/hls/rtsp(直播流)

  • 创建视频播放控件

    1. uni-app模式
      uni-app模式直接使用<video>组件进行播放,无需其他复杂设置。
    2. 5+App模式
      在页面中视频播放控件需要绑定div确定位置及大小,创建视频播放控件需要传入div的id,示例如下:
      <!DOCTYPE HTML>  
      <html>  
      <head>  
            <meta charset="utf-8"/>  
            <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>  
            <title>Hello H5+</title>  
            <script type="text/javascript">  
      var video = null;  
      // H5 plus事件处理  
      function plusReady(){  
      // 创建视频播放控件  
      video = new plus.video.VideoPlayer('video',{  
            src:'rtmp://live.hkstv.hk.lxdns.com/live/hks'  
      });  
      }  
      document.addEventListener('plusready', plusReady, false);  
            </script>  
      </head>  
      <body>  
            <br/>  
            <div id="video" style="width:98%;height:300px;background-color:#000000;margin:auto"></div>  
      </body>  
      </html>

      其中new plus.video.VideoPlayer()第一个参数'video'为绑定div的id值,第二个参数中的src属性值为要播放的直播视频地址,直播视频必须为rtmp协议。创建后返回视频播放控件(VideoPlayer)对象,后续可通过此对象进行操作控制。

  • 播放视频流
    创建视频播放控件后可通过setOptions方法更新播放控件参数,如下更新视频服务器地址:

    video.setOptions({src:'新的视频服务器地址'});

    用户可以点击播放控件上的播放按钮进行播放,也可调用play方法进行播放:

    video.play();

如果想在视频上绘制一些内容,比如滚动的聊天记录,5+App可以在上面创建一个透明webview,具体代码在Hello H5+的video示例里有;uni-app可以在上面创建一个subnvue,在HBuilderX1.9.10+版本中自带的hello uni-app - API - 界面 - 原生子窗体中也有示例。

包括防盗播的跑马灯也可以这么做。

更多API参考文档http://www.html5plus.org/doc/zh_cn/video.html#plus.video.VideoPlayer

三方示例模板

这个是插件市场里三方开发者做的直播业务模板,开源,可以参考:http://ext.dcloud.net.cn/plugin?id=226

还有一些开发者封装了三方原生sdk,比如腾讯直播sdk,可以在插件市场获取

收起阅读 »

免费赠送前端各种学习资料

ajax jquery nodejs Vue

1:微信小游戏入门与实战 点击跳转

2:黑马程序员_node.js视屏教程详解(内含所有源码) 点击跳转

3:jQuery基础视频集合 点击跳转

4:黑马程序员_2018版Ajax教程精讲 点击跳转

5:黑马程序员_2018版Html5+Css3由浅入深 点击跳转

6:黑马程序员_canvas详解 点击跳转

7:黑马程序员_电商实战项目视频(含源码) 点击跳转

8:黑马程序员_ 2018移动web进阶教程 点击跳转

9:ajax详解_视屏教程 点击跳转

10:vue详解 点击跳转

11:毕向东JavaScript视频教程 点击跳转

12:黑马程序员_超全面的javaWeb视频教程点击跳转

13:黑马程序员-JavaScript-高级视频点击跳转

14:黑马程序员:reactjs精品教程视频点击跳转

15:传智播客-黑马程序员最新视频:轻松玩转微信小程序点击跳转

16:vue 2.0 入门及到实战开发点击跳转

继续阅读 »

1:微信小游戏入门与实战 点击跳转

2:黑马程序员_node.js视屏教程详解(内含所有源码) 点击跳转

3:jQuery基础视频集合 点击跳转

4:黑马程序员_2018版Ajax教程精讲 点击跳转

5:黑马程序员_2018版Html5+Css3由浅入深 点击跳转

6:黑马程序员_canvas详解 点击跳转

7:黑马程序员_电商实战项目视频(含源码) 点击跳转

8:黑马程序员_ 2018移动web进阶教程 点击跳转

9:ajax详解_视屏教程 点击跳转

10:vue详解 点击跳转

11:毕向东JavaScript视频教程 点击跳转

12:黑马程序员_超全面的javaWeb视频教程点击跳转

13:黑马程序员-JavaScript-高级视频点击跳转

14:黑马程序员:reactjs精品教程视频点击跳转

15:传智播客-黑马程序员最新视频:轻松玩转微信小程序点击跳转

16:vue 2.0 入门及到实战开发点击跳转

收起阅读 »