uniapp入门-移动端的前端之路

学习导览

uniapp调研资料

1.认识vue

2.搭建vue应用

3.组件与api

4.生命周期

5.条件编译

6.原生APP中集成uni小程序

7.小程序打开集成原生App插件

8.原生插件开发

9.热更新

10.优缺点

demo运行指南

首先安装HBuilderX和微信开发者程序

开发工具下载

https://www.dcloud.io/hbuilderx.html

微信开发者工具下载(小程序预览需要)

https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

然后导入项目并运行

项目git地址:https://github.com/willShuhuan/DSH_Uni.git

uniapp入门-移动端的前端之路_第1张图片
image

1.认识vue


Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

uniapp是什么

uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)、快应用等多个平台。

多端运行的原因->目标平台安装文件打包

uniapp入门-移动端的前端之路_第2张图片
image

uniapp能力

能力 Y/N 说明
跨端能力 安卓、ios、微信小程序、其他小程序
原生App唤起uni小程序能力 需要集成uni小程序SDK并导入小程序wgt包
原生App跳转小程序二级页面 可以传递json、字符串等
uni小程序唤起原生App能力 需要开发对应的Android/iOS插件,或通过插件市场下载,支持json、字符串等通用数据类型的传递
第三方服务 支付/推送/三方登录/分享/地图等
HBuilderX热编译 支持手机、微信、浏览器实时预览
fragment/viewController嵌套小程序页面 启动小程序相当于打开一个新的Activity,详见页面最下说明
uniapp页面加载原生fragment/viewController uni小程序在App端实际上是运行在一个WebActivity之上,可以拿到ActivityContext进而获取FragmentManager处理Fragment的展示隐藏,虽然这种方式可行,但是在uniapp上切换选项卡的时候要不断处理fragment的内部逻辑,相当于fragment生命周期的监控任务交给uniapp来执行而不是fragment本身

Vue.js优点

  1. 体积小
    压缩后33K;
  2. 更高的运行效率
    基于虚拟dom,一种可以预先通过JavaScript进行各种计算,把最终的DOM操作计算出来并优化的技术,由于这个DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM。
  3. 双向数据绑定
    让开发者不用再去操作dom对象,把更多的精力投入到业务逻辑上;
  4. 生态丰富、学习成本低
    市场上拥有大量成熟、稳定的基于vue.js的ui框架、常用组件!拿来即用实现快速开发!
    对初学者友好、入门容易、学习资料多;

2.搭建vue应用

2.1 html内嵌式

使用cdn或者本地文件的形式引入vue(不建议使用,知道就好)


    
        
        vue实例
        
        
        
        
        
        
        
        
        
    
    
    


2.2 vue cli 终端命令行安装(需要掌握)

https://uniapp.dcloud.io/quickstart?id=_2-通过vue-cli命令行

  1. 安装npm
    npm全称为Node Package Manager, 是一个基于Node . js的包管理器,也是整个Node . js社区最流行、支持的第三方模块最多的包管理器。
    npm -v 查看npm
  2. 安装cnpm
    npm install -g cnpm -registry=https://registry.npm. taobao.org
  3. 安装vue一cli
    cnpm install -g @vue/cli
  4. 安装webpack
    cnpm install -g webpack
    webpack是JavaScript 打包器(module bundler)
  5. vue ui —> 浏览器可视化页面 —> 搭建vue 应用 -> 使用HBuilder X打开

2.3 HBuilder X 搭建uniapp(建议使用)

https://uniapp.dcloud.io/quickstart?id=_1-通过-hbuilderx-可视化界面

创建

  • HBuilderX->文件->新建项目->选择模板
    uniapp入门-移动端的前端之路_第3张图片
    image

运行

https://uniapp.dcloud.io/quickstart?id=运行uni-app

uniapp入门-移动端的前端之路_第4张图片
image

uniapp入门-移动端的前端之路_第5张图片
image

uniapp入门-移动端的前端之路_第6张图片
image

发布

uniapp入门-移动端的前端之路_第7张图片
image

3.组件与api


https://uniapp.dcloud.io/component/README
https://uniapp.dcloud.io/api/README

3.1 组件的使用

uni-app为开发者提供了一系列基础组件,类似HTML里的基础标签元素。
但uni-app的组件与HTML不同,而是与小程序相同,更适合手机端使用。

  • 视图容器:view scroll-view等
  • 基础内容:text icon等
  • 表单组件:button form radio checkbox等
  • 媒体组件:video 等
  • 其他:地图,导航,画布,扩展组件等


3.2 API调用

uni-app的js API由标准ECMAScript的js API 和 uni 扩展 API 这两部分组成。
以上述代码为例,处理表单提交时,可以使用uni.showModal弹出一个提示框

formSubmit: function(e) {
    console.log('form发生了submit事件,携带数据为:' + JSON.stringify(e.detail.value))
    var formdata = e.detail.value
    uni.showModal({
        content: '表单数据内容:' + JSON.stringify(formdata),
        showCancel: false
    });
}

如果想模拟一个网络请求,可以这样

uni.request({
    url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
    data: {
        text: 'uni.request'
    },
    header: {
        'custom-header': 'hello' //自定义请求头信息
    },
    success: (res) => {
        console.log(res.data);
        this.text = 'request success';
    }
});
uniapp入门-移动端的前端之路_第8张图片
image

uniapp入门-移动端的前端之路_第9张图片
image

3.3 自定义组件

封装可复用的组件,可以在其他的页面使用它

  • 首先编写一个组件





  • 在其他的组件中导入、声明、传入必要的参数并使用它




uniapp入门-移动端的前端之路_第10张图片
image

4.生命周期


https://uniapp.dcloud.io/collocation/frame/lifecycle

4.1 整个应用的生命周期

只能在App.vue中监听,

  • onLaunch 当uni-app 初始化完成时触发(全局只触发一次)
  • onShow 当 uni-app 启动,或从后台进入前台显示
  • onHide 当 uni-app 从前台进入后台
  • onError 当 uni-app 报错时触发
  • onUniNViewMessage 对 nvue 页面发送的数据进行监听

下面的代码中,在App启动时,为应用底部tabBar设置了消息气泡,在实际的开发中,也可以做一些资源和SDK的初始化工作,比如为安卓和iOS原生插件内部的SDK做一些初始化工作


4.2 每个页面的生命周期

onLoad、onShow、onReady、onHide等

  • onPageScroll 页面在垂直方向已滚动的距离(单位px)
    onPageScroll:function(e){
        this.scrollTop = e.scrollTop;
    }
  • onBackPress 触发返回行为的来源:'backbutton'——左上角导航栏按钮及安卓返回键;'navigateBack'——uni.navigateBack() 方法。
  • onTabItemTap 当前点击的tabItem
    • 返回对象 {"index":4,"text":"我的","pagePath":"pages/main/tab_main_mine"}
      uniapp入门-移动端的前端之路_第11张图片
      image

4.3 通过页面生命周期控制原生插件的生命周期

在需要与原生插件进行交互时,下面的代码可以通过监听页面生命周期控制安卓原生插件fragment的显示隐藏

onHide() {
    console.log("onHide");
    testModule.hideFragment();
},
onShow() {
    console.log("onShow");
    testModule.showFragment();
}

5.条件编译


【官网介绍】 https://uniapp.dcloud.io/platform

5.1 简介

语法: 以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。

  • ifdef:if defined 仅在某平台存在
  • ifndef:if not defined 除了某平台均存在
  • %PLATFORM%:平台名称
#ifdef APP-PLUS
需条件编译的代码
#endif

#ifndef H5
需条件编译的代码
#endif

#ifdef H5 || MP-WEIXIN
需条件编译的代码
#endif

在vue中的使用,与注释代码的语法一致,mac下command+/或者control+alt+/提示







5.2 pages.json条件编译

例:实现微信小程序和客户端的差异化显示
pages.json中使用条件编译注释微信平台需要屏蔽的页面

{
    "pages":[
        //#ifndef MP-WEIXIN
        {
            "path": "pages/main/tab_main_video",
            "style": {}
        },
        {
            "path" : "pages/main/tab_main_game",
            "style" : {}
        },
        //#endif
        ...
    ],
    "tabBar" : {
        ...
        "list":[
            //#ifndef MP-WEIXIN
            {
                "pagePath" : "pages/main/tab_main_video",
                "iconPath" : "static/mipmap/tabbar/ic_nav_video_unselected.png",
                "selectedIconPath" : "static/mipmap/tabbar/ic_nav_video_selected.png",
                "text" : "刷视频"
            },
            {
                "pagePath" : "pages/main/tab_main_game",
                "iconPath" : "static/mipmap/tabbar/ic_nav_game_unselected.png",
                "selectedIconPath" : "static/mipmap/tabbar/ic_nav_game_selected.png",
                "text" : "玩游戏"
            },
            // #endif
        ]
        
        ...
    }
}

注意:pages中条件编译的代码要与tabbar中的条件编译代码要一致,否则运行时会报错

编译之后在微信开发者工具下看到代码不包含被我们条件编译排除掉的代码,可以说HBuilderX在编译过程中帮我们去掉了平台无关代码

uniapp入门-移动端的前端之路_第12张图片
image

效果
其他平台
uniapp入门-移动端的前端之路_第13张图片
image

微信平台
uniapp入门-移动端的前端之路_第14张图片
image

5.3 API条件编译

  • 针对各个平台的特性不同,处理一些交互,比如表单校验提示,在原生APP可以以吐司形式,在H5平台上可以处理为Alert弹出框
  • 控制各个平台业务代码的差异

查看以下代码

  • v-show 属性可以理解为css的display属性,控制组件显示隐藏
  • 通过带参函数show(param)返回值控制v-show的值
  • gotoNativePage()是一个跳转到原生App页面的一个方法,需要原生插件的支持
  • ifios和ifAndroid可以针对移动端平台进行处理






上述代码运行结果就是:

  • 在H5平台下 安卓和 ios跳转按钮都展示
  • 在安卓平台下,只会展示跳转安卓activity按钮
  • 在iOS平台下,只会展示跳转iOS viewController的按钮
uniapp入门-移动端的前端之路_第15张图片
image

其他条件编译的使用场景放在第7小节讲解

6.原生APP中集成uni小程序

https://nativesupport.dcloud.net.cn/UniMPDocs/UseSdk/android

uni小程序SDK,是为原生App打造的可运行基于 uni-app 开发的小程序前端项目的框架,从而帮助原生App快速获取小程序的能力,效果如下:

uniapp入门-移动端的前端之路_第16张图片
image

流程

  • 原生工程中 添加基础依赖库及资源文件
 uniMPSDK-release.aar  //必须集成
 uniapp-release.aar //必须集成
 sqlite-release.aar
 msa_mdid_1.0.13.aar //必须集成 注意(2.8.0版本的SDK及以下版本请集成miit_mdid_1.0.10.aar)
 messaging-release.aar
 iBeacon-release.aar
 fingerprint-release.aar
 contacts-release.aar
 Bluetooth-release.aar
 [email protected] //必须集成

gradle配置

//必须配置
def mfph = [
    //宿主包名
    "apk.applicationId" : "xxx.xxx.xxxxx",
]
android {
    defaultConfig {
        targetSdkVersion 26 //最高28最优26 设置值域超过28可能在android10以上手机出现白屏问题。
        ndk {
            abiFilters 'x86','armeabi-v7a',"arm64-v8a" //不支持armeabi
        }
        manifestPlaceholders = mfph
    }
    //此处配置必须添加 否则无法正确运行
    aaptOptions {
        additionalParameters '--auto-add-overlay'
        //noCompress 'foo', 'bar'
        ignoreAssetsPattern "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
    }
}
//导入aar需要的配置
repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    //导入SDK相关依赖jar、aar
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation fileTree(include: ['*.aar'], dir: 'libs')
    //必须添加的依赖
    implementation 'com.android.support:recyclerview-v7:27.1.0'
    implementation 'com.android.support:support-v4:27.1.0'
    implementation 'com.android.support:appcompat-v7:27.1.0'
    implementation 'com.alibaba:fastjson:1.1.46.android'
    implementation 'com.facebook.fresco:fresco:1.13.0'
    implementation 'com.facebook.fresco:animated-gif:1.13.0'
    implementation 'com.github.bumptech.glide:glide:4.9.0'
}
uniapp入门-移动端的前端之路_第17张图片
image
  • 生成小程序应用资源, 在uniapp中制作wgt包

    uniapp入门-移动端的前端之路_第18张图片
    image

  • 导入小程序应用资源
    打开android原生项目。在主Module模块的assets路径下创建apps/(内置uni小程序的appid)/www 路径。例如:apps/__UNI__04E3A11/www。将之前导出的应用资源包解压释放到apps/__UNI__04E3A11/www路径下。

小程序id在uniapp工程中可以看到

uniapp入门-移动端的前端之路_第19张图片
image

安卓工程配置

uniapp入门-移动端的前端之路_第20张图片
image

注意,以下配置项必须添加,并且按照要求填写,否则不停报错

uniapp入门-移动端的前端之路_第21张图片
image

在原生App中开启小程序

    //配置菜单
    MenuActionSheetItem item = new MenuActionSheetItem("关于", "gy");
    MenuActionSheetItem item1 = new MenuActionSheetItem("获取当前页面url", "hqdqym");
    List sheetItems = new ArrayList<>();
    sheetItems.add(item);
    sheetItems.add(item1);
    DCSDKInitConfig config = new DCSDKInitConfig.Builder()
            .setCapsule(true)
            .setMenuDefFontSize("16px")
            .setMenuDefFontColor("#ff00ff")
            .setMenuDefFontWeight("normal")
            .setMenuActionSheetItems(sheetItems)
            .setEnableBackground(true)//开启后台运行
            .build();
    //开启小程序            
    DCUniMPSDK.getInstance().startApp(mContext,"__UNI__1F21932","/pages/product/product?id=10");
        

7.小程序集成原生App插件


uniapp支持原生插件扩展,包括插件市场云端插件和本地插件两种方式,这节讲解云端插件,本地插件在第8节进行说明

  • 首先在manifest中选择云端插件

    uniapp入门-移动端的前端之路_第22张图片
    image

  • 在插件市场导入


    uniapp入门-移动端的前端之路_第23张图片
    image
  • 回到HBuilderX中导入插件

    uniapp入门-移动端的前端之路_第24张图片
    image

  • 自定义基座

    uniapp入门-移动端的前端之路_第25张图片
    image

  • 打包完成之后,按照插件使用说明在需要的地方引入并使用它,效果如下


uniapp入门-移动端的前端之路_第26张图片
image

8.原生插件开发


当HBuilderX中提供的能力无法满足App功能需求,需要通过使用Andorid/iOS原生开发实现时,可使用App离线SDK开发原生插件来扩展原生能力。
https://nativesupport.dcloud.net.cn/NativePlugin/course/android

  • 扩展 module(偏向功能模块)
  • 扩展组件 component(偏向UI组件)

扩展 module

https://nativesupport.dcloud.net.cn/NativePlugin/course/android?id=创建android-studio的module模块

uniapp入门-移动端的前端之路_第27张图片
image

  • 1.首先创建一个AS工程,工程内创建Android Library,导入uniapp-relase.aar并依赖
  • 2.编写Module类,设计交互场景
public class TestModule extends WXModule {

    private static final String TAG = "uniTestModule";
    FragmentManager fragmentManager;
    FragmentTransaction transaction;
    Fragment fragment;
    Context mContext;
    FrameLayout frameLayout;

    //run ui thread
    @JSMethod(uiThread = true)
    public void testAsyncFunc(JSONObject options, JSCallback callback) {
        Log.e(TAG, "testAsyncFunc--"+options);
        if(callback != null) {
            JSONObject data = new JSONObject();
            data.put("code", "success");
            callback.invoke(data);
            //callback.invokeAndKeepAlive(data);
        }
    }

    //run JS thread
    @JSMethod (uiThread = false)
    public JSONObject testSyncFunc(){
        JSONObject data = new JSONObject();
        data.put("code", "success");
        return data;
    }

    @JSMethod (uiThread = true)
    public void gotoNativeMainPage(JSONObject jsonObject){
        Log.d(TAG, "gotoNativeMainPage: "+jsonObject);
        if(mWXSDKInstance != null) {
            Intent intent = new Intent(mWXSDKInstance.getContext(), TestActivity.class);
            intent.putExtra("data",jsonObject.toString());
            mWXSDKInstance.getContext().startActivity(intent);
        }
    }

    @JSMethod (uiThread = true)
    public void showFragment(){
        mContext = mWXSDKInstance.getContext();
        Log.d(TAG, "showFragment: "+mContext);//PandoraEntryActivity
        fragmentManager = ((Activity) mContext).getFragmentManager();
        transaction = fragmentManager.beginTransaction();  
        fragment = new TestFragment();
        frameLayout = new FrameLayout(mContext);

        int height = Util.getScreenHeidth(mContext);
        int bottomMargin =  Util.dip2px(mContext,50);

        FrameLayout.LayoutParams params1 =new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                height-bottomMargin
        );
        ((Activity) mContext).addContentView(frameLayout,params1);
        frameLayout.setId(View.generateViewId());
        transaction.replace(frameLayout.getId(), fragment);
        transaction.commitAllowingStateLoss();
    }


    @JSMethod (uiThread = true)
    public void hideFragment(){
        transaction = fragmentManager.beginTransaction();
        transaction.hide(fragment).commit();
        fragment.onPause();
    }


}
  • 3 编写通过Module进行交互的业务代码
public class TestFragment extends Fragment {

    private static final String TAG = "uniTestModule";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.mine_layout, container,false);
        return view;
    }
}

public class TestActivity extends AppCompatActivity {
    private static final String TAG = "uniTestModule";

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String  data = getIntent().getStringExtra("data");
        setContentView(R.layout.activity_test);
        TextView textView = findViewById(R.id.text);
        textView.setText(data);

    }
}

  • 4 通过gradle执行打包构建aar文件
uniapp入门-移动端的前端之路_第28张图片
image
uniapp入门-移动端的前端之路_第29张图片
image
  • 5 配置package.json


    uniapp入门-移动端的前端之路_第30张图片
    image
  • 6 将插件集成到项目中
    项目根目录下新建nativeplugins文件夹,将package.json放入此目录,新建android文件夹放入安卓打包aar文件,新建ios文件夹配置放置ios打包.a文件

  • 7 manifest文件中配置本地模块

    uniapp入门-移动端的前端之路_第31张图片
    image

  • 8 制作自定义基座(云打包)

    uniapp入门-移动端的前端之路_第32张图片
    image

  • 9 等待打包完成,在需要使用的地方调用插件即可


    uniapp入门-移动端的前端之路_第33张图片
    image
  • 10 在真机或模拟器上运行,点击加载fragment按钮,效果如下


    uniapp入门-移动端的前端之路_第34张图片
    image

9.热更新

小程序平台升级

uni-app发布为小程序的升级模式较简单,只需将开发完的代码提交小程序后台,待审核通过后用户将自动升级。

APP端的升级

  • 整包更新,即常规的整个App安装包重新下载安装。(不是热更新)
  • 资源热更新,即App不需要重新安装,里面的js等前端代码进行更新。(是热更新)
    • https://ask.dcloud.net.cn/article/id-35667__page-4
    • https://ask.dcloud.net.cn/article/182
    • https://ask.dcloud.net.cn/article/199

App资源热更新

https://ask.dcloud.net.cn/article/id-35667__page-4
适用小版本升级
通过生成移动APP资源升级包的方式进行差量(增量)升级,将uniapp发布为wgt升级包,通过服务端与前端配合的方式在移动端进行升级

  • 以安卓为例,uniapp打包apk的过程就是把当前uniapp工程包装为一个安卓工程并进行压缩打包的过程,uniapp中的代码运行在一个Webview中(原生插件除外)
  • 实际上,所以我们修改uniapp的代码也只是修改了前端代码,不管打包前还是打包为apk之后,也不可能中间会存在js2java或者js2OC这样的代码转换过程(如果真有这种黑科技,就能真正的一统天下了)
  • 所以,只需要把wgt升级包放在服务端,App端检测到版本升级时,下载wgt资源并替换原来的资源就可以了,实际上,Tinker(https://www.jianshu.com/p/076afb5cdd55) 等热更新方案也是类似的原理
    uniapp入门-移动端的前端之路_第35张图片
    image

10.优缺点


结论:优点>缺点

虽然下面写了更多的缺点,但总体来看,优点远大于缺点

优点

  • 跨平台性,write once ,run everywhere,通过多平台打包发布到对应平台,节约大量开发成本
  • 对于前端开发人员比较友好,vue作为主流前端框架大多数人都掌握,只需要少量原生知识就能够开发跨平台的应用
  • 生态体系逐渐完善,很多大厂也在用,社区成长速度比较快

缺点

  • 正是由于跨平台特性,多端适配会占据相当的工作量,开发人员必须编写很多条件编译代码,这会破坏代码的整体结构(毕竟在业务代码中编写很多平台适配代码看起来太不优雅了),可读性也会受到一定影响,而且坑多是必然的
  • 对于Android和iOS开发人员来讲,转投uniapp学习成本很高(除了vue,还要学习html+js+css等知识),精通更是很难,这有点类似前两年移动端转投react native那样,会而不精,代码质量无法得到充分保证(我也有朋友最近把公司的react native的项目完全重构为原生代码了)
  • 前端人员依然无法解决所有问题,至少在目前的应用场景中,很多需求仍然需要客户端人员配合开发(对于前端人员而言,学习安卓iOS的成本也相当可观)
  • 对于一些棘手的问题,参考的资料并不多,而且官方也很难第一时间回答你,甚至只能自己慢慢探索
  • 陈旧的api:以安卓为例,uniapp云打包apk反编译之后,发现入口程序PandoraEntry实际上是一个Activity而非API22(Android 5.1)后谷歌官方建议使用的v7包下的AppCompactActivity,这会影响ToolBar的使用

你可能感兴趣的:(uniapp入门-移动端的前端之路)