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

image

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

uniapp是什么

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

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

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框架、常用组件!拿来即用实现快速开发!
    对初学者友好、入门容易、学习资料多;

<h2 id="chapacter2">2.搭建vue应用</h2>

2.1 html内嵌式

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

<html>
    <head>
        <meta charset="utf-8">
        <title>vue实例</title>
        <!-- 直接引入方式 -->
        <!-- <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> -->
        <!-- <script src="../js/vue.min.js" type="text/javascript" charset="utf-8"></script> -->
        
        <!-- cdn方式 -->
        <!-- 开发环境版本,包含了有帮助的命令行警告 -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <!-- 生产环境版本,优化了尺寸和速度 -->
        <!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
    </head>
    <body>
    </body>
<html>

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->文件->新建项目->选择模板
    image

运行

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

image

image

image

发布

image

<h2 id="chapacter3">3.组件与api</h2>
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 等
  • 其他:地图,导航,画布,扩展组件等
<template>
    <view class="content">
        <scroll-view show-scrollbar="false">

            <navigator style="width: 100%; margin-top: 10rpx;" url="./tab_main_task" hover-class="navigator-hover" open-type="switchTab">
                <button type="primary">navigator跳转到新页面</button>
            </navigator>

            <video style="width: 100%; margin-top: 10rpx;" src="http://gusteau-test.oss-cn-hangzhou.aliyuncs.com/material/transcode/video/2020/07/24/HK2mmdYW_1595570205480.mp4"></video>

            <form @submit="formSubmit" @reset="formReset" class="form">
                <view class="title">{{switchvalue}}</view>
                <switch @change="switchChange" color="#FF6723" name="switch"></switch>
                <view class="title">radio单选框</view>
                <radio-group name="radio">
                    <label>
                        <radio value="radio1" /><text>选项一</text>
                    </label>
                    <label>
                        <radio value="radio2" /><text>选项二</text>
                    </label>
                </radio-group>
                <view class="title">checkbox复选框</view>
                <checkbox-group name="checkbox">
                    <label>
                        <checkbox value="checkbox1" /><text>选项一</text>
                    </label>
                    <label>
                        <checkbox value="checkbox2" /><text>选项二</text>
                    </label>
                </checkbox-group>
                <view class="title">slider</view>
                <slider value="50" name="slider" show-value></slider>
                <view class="title">input输入框</view>
                <input class="uni-input" name="input" placeholder="这是一个输入框" />
                <button form-type="submit" type="primary" class="button">Submit</button>
                <button type="warn" form-type="reset" class="button">Reset</button>
            </form>


        </scroll-view>

    </view>
</template>

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';
    }
});
image

image

3.3 自定义组件

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

  • 首先编写一个组件
<template>
    <h3 @click='clickFun'>我是自定义组件:{{msg}}</h3>
</template>

<script>
    export default {
        name:'testComponent',
        props:{
            msg:{
                type:String,
                default:'default msg'
            }
        },
        methods:{
            clickFun:function(){
                this.msg = '改变后的组件属性',
                this.$emit('clickFun',this.msg);
            }
        }
    }
</script>
<style>
</style>

  • 在其他的组件中导入、声明、传入必要的参数并使用它
<template>
    <view>
        <!-- 3.自定义组件使用 msg是自定义组件的一个属性-->
        <testComponent style="margin-top: 50px;" msg="默认属性"  @clickFun="sonComponentClickCallback"></testComponent>
        <div>{{callback}}</div>
    </view>
</template>
<script>
    //1.导入
    import testComponent from '../components/testcomponent.vue'
    
    export default{
        components:{
            //2.声明
            testComponent
        },
        data() {
            return {
                callback: ''
            }
        },
        methods:{
            sonComponentClickCallback:function(e){
                // this.callback = e;
                console.log(e);
            }
        }
    }
</script>

<style>

</style>
image

<h2 id="chapacter4">4.生命周期</h2>
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做一些初始化工作

<script>
    export default {
        onLaunch: function() {
            console.log('App Launch')
            setTimeout(() => {
                uni.setTabBarBadge({
                    index: 1,
                    text: '9'
                });
                uni.showTabBarRedDot({
                    index: 3
                });
            }, 1000);
        },
        onShow: function() {
            console.log('App Show')
        },
        onHide: function() {
            console.log('App Hide')
        },
    }
</script>

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"}
      image

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

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

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

<h2 id="chapacter5">5.条件编译</h2>
【官网介绍】 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+/提示

<template>
    <!--  #ifdef  %PLATFORM% -->
    平台特有的组件
    <!--  #endif -->
</template>

<script>
    // #ifdef APP-PLUS
    平台特有API实现
    // #endif
</script>

<style>
    /*  #ifdef  %PLATFORM%  */
    平台特有样式
    /*  #endif  */
</style>

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
        ]
        
        ...
    }
}

<p style="color:red">注意:pages中条件编译的代码要与tabbar中的条件编译代码要一致,否则运行时会报错</p>

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

image

效果
其他平台
image

微信平台
image

5.3 API条件编译

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

查看以下代码

  • v-show 属性可以理解为css的display属性,控制组件显示隐藏
  • 通过带参函数show(param)返回值控制v-show的值
  • gotoNativePage()是一个跳转到原生App页面的一个方法,需要原生插件的支持
  • ifios和ifAndroid可以针对移动端平台进行处理
<template>
    <div>
        <div class="content">
            <button v-show="show('android')"  class="button" type="primary" @click="gotoNativePage">Android跳转原生Activity</button>
            <button v-show="show('ios')" class="button" type="primary" @click="gotoNativePage">iOS跳转原生ViewController</button>
        </div>
    </div>

</template>
<script>
    const testModule = uni.requireNativePlugin("willA-SopModule");
    export default {
        data() {
            return {
                title: 'Hello',
                show: function(plat){
                    // #ifdef H5
                    return true
                    // #endif
                    // #ifdef APP-PLUS
                    if(plat==='android'){
                        if (uni.getSystemInfoSync().platform == "android") {
                            return true
                        }else{
                            return false
                        }
                    }else{
                        if (uni.getSystemInfoSync().platform == "android") {
                            return false
                        }else{
                            return true
                        }
                    }
                    // #endif
                }
                    
            }
        }
    
    };
</script>


<style>
    .content {
        text-align: center;
        height: 100%;
        margin-top: 150upx;
    }

    .button {
        width: 60%;
        margin-top: 100upx;
    }
</style>

上述代码运行结果就是:

  • 在H5平台下 安卓和 ios跳转按钮都展示
  • 在安卓平台下,只会展示跳转安卓activity按钮
  • 在iOS平台下,只会展示跳转iOS viewController的按钮
image

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

<h2 id="chapacter6">6.原生APP中集成uni小程序</h2>

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

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

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
 android-gif-drawable-release@1.2.17.aar //必须集成

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'
}
image
  • 生成小程序应用资源, 在uniapp中制作wgt包

    image

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

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

image

安卓工程配置

image

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

image

在原生App中开启小程序

    //配置菜单
    MenuActionSheetItem item = new MenuActionSheetItem("关于", "gy");
    MenuActionSheetItem item1 = new MenuActionSheetItem("获取当前页面url", "hqdqym");
    List<MenuActionSheetItem> 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");
        

<h2 id="chapacter7">7.小程序集成原生App插件</h2>
uniapp支持原生插件扩展,包括插件市场云端插件和本地插件两种方式,这节讲解云端插件,本地插件在第8节进行说明

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

    image

  • 在插件市场导入


    image
  • 回到HBuilderX中导入插件

    image

  • 自定义基座

    image

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

<script>
    //在页面中引入插件
    const dcRichAlert = uni.requireNativePlugin('DCloud-RichAlert')
    export default{
        onTabItemTap:function(Object){
            console.log(Object);
        },
        methods:{
            showPlugin(){
                //编写调用代码
                dcRichAlert.doSomthing();
            }
        }
    }
</script>
image

<h2 id="chapacter8">8.原生插件开发</h2>
当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模块

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文件
image
image
  • 5 配置package.json


    image
  • 6 将插件集成到项目中
    项目根目录下新建nativeplugins文件夹,将package.json放入此目录,新建android文件夹放入安卓打包aar文件,新建ios文件夹配置放置ios打包.a文件

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

    image

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

    image

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


    image
  • 10 在真机或模拟器上运行,点击加载fragment按钮,效果如下


    image

<h2 id="chapacter9">9.热更新</h2>

小程序平台升级

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

APP端的升级

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) 等热更新方案也是类似的原理
    image

<h2 id="chapacter10">10.优缺点</h2>
<strong style="color:red;font-size:20px">结论:优点>缺点</strong>

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

优点

  • 跨平台性,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的使用
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270