"解锁高效开发:运用Uni-app构建微信小程序与H5应用的经验分享"

1,207 阅读6分钟

项目搭建

所用技术: uni-app vue3 javaScript pinia axios uView-plus

使用vscode开发uni-app项目

  1. 下载需要的技术的版本(uni-app官网)👇👇👇👇

uni-app官网

  1. 使用vscode引入,并安装依赖,并安装对应插件
npm install
  • 👉 安装 uni-app 开发插件

  • 👉 JSON 注释问题

    • 设置文件关联,把 manifest.json 和 pages.json 设置为 jsonc

修改路径:左上角文件-首选项-设置

image.png

{
  "editor.formatOnSave": true,
  "files.autoSave": "afterDelay",
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
   // 配置语言的文件关联
   "files.associations": {
    "pages.json": "jsonc", // pages.json 可以写注释
    "manifest.json": "jsonc" // manifest.json 可以写注释
  },
  "Codegeex.Privacy": false,
  "[jsonc]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

  1. 尝试启动项目,启动项目的配置在项目根目录下的package.json

image.png

  1. 启动成功是这样子

image.png

  1. 如果你想运行成小程序需要在manifest.json配置文件中添加appId和其它的单独的设置
"mp-weixin": {
    "appid": "你自己的appid",
    "setting": {
      "urlCheck": false
    },
    "usingComponents": true,
    // 配置位置服务信息
    "permission": {
      "scope.userLocation": {
        "desc": "您的位置信息将用于提供相关服务"
      }
    }
  },

开发中遇到的单位问题

如果你是开发小程序,后者同时开发h5页面手机端和小程序,那么单位推荐你使用rpx作为首选单位。 选择好单位后就是下载对应的自动转换的插件,我这里是准备的将px转为rpx单位

  1. vscode中搜索插件:

image.png

  1. 安装好后在设置中搜索这个:

image.png

  1. 然后修改设计稿的宽度为基准值即可:

image.png

  1. 设置完就可以使用了(如果没有自动转换提示,可以重启,或者Ctlr + i快捷键试试)

image.png

引入pinia和pinia持久化

  1. 下载插件
npm i pinia-plugin-persistedstate

2. 多端持久化

// 兼容多端API
uni.setStorageSync()
uni.getStorageSync()
  1. 配置自动持久化
export const useMemberStore = defineStore(
  'member',
  () => {
    //…省略
  },
  {
    // 配置持久化
    persist: {
      // 调整为兼容多端的API
      storage: {
        setItem(key, value) {
          uni.setStorageSync(key, value) 
        },
        getItem(key) {
          return uni.getStorageSync(key) 
        },
      },
    },
  },
)

注意在我们配置pinia的时候会可能出现pinia报hasInjectionContext问题

原因是pinia的版本跟vue的版本对不上,我们可以手动的更改一下版本就可以了

npm i pinia@2.0.36

如果你还是报这个错误请自己去官网查询解决版本对应的问题

引入框架

前端框架:uView-plus(支持vue3)

  1. npm 下载
npm install uview-plus

2. 安装配置

// 安装sass
npm i sass -D

// 安装sass-loader,注意需要版本10,否则可能会导致vue与sass的兼容问题而报错
npm i sass-loader@10 -D

// 安装
npm install dayjs
npm install clipboard

3. 引入uview-plus主JS库(main.js)

import { createSSRApp } from "vue";
import uviewPlus from "uview-plus";
import pinia from "./stores/index";

import App from "./App.vue";
export function createApp() {
  const app = createSSRApp(App);
  app.use(uviewPlus);
  app.use(pinia);

  return {
    app,
  };
}
  1. 引入样式 在uni.scss引入或者在APP.vue的样式表的第一样引入都可以
@import 'uview-plus/theme.scss';
  1. 配置easycom组件模式
// pages.json
{
	"easycom": {
		// 注意一定要放在custom里,否则无效,https://ask.dcloud.net.cn/question/131175
		"custom": {
			"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
			"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
	        "^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
		}
	},
	
	// 此为本身已有的内容
	"pages": [
		// ......
	]
}

这样就可以使用uView-plus组件库了

封装自定义头部导航栏

  1. 安全区域

不同手机的安全区域不同,适配安全区域能防止页面重要内容被遮挡。

可通过 uni.getSystemInfoSync() 获取屏幕边界到安全区的距离。

image.png

  1. 自定义导航配置
// src/pages.json
{
  "path": "pages/index/index",
  "style": {
    "navigationStyle": "custom", // 隐藏默认导航
    "navigationBarTextStyle": "white",
    "navigationBarTitleText": "首页"
  }
}
  1. 组件适配安全区域
<script>
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>

<template>
  <!-- 顶部占位 -->
  <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
    <!-- ...省略 -->
  </view>
</template>
<template>
  <div class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
    <div @click="goToHome">
      <img class="nav-img" src="../static/home.png" alt="点击返回首页" />
    </div>
    <div class="nav-title">
      <span class="sp">XXXX</span>
    </div>
    <div></div>
  </div>
</template>

<script setup>
import {} from "vue";
import { onShow } from "@dcloudio/uni-app";

const { safeAreaInsets } = uni.getSystemInfoSync();

onShow(() => {
  // console.log("我被执行", safeAreaInsets);
});
const goToHome = () => {
  uni.switchTab({
    url: "/pages/index", // 首页的路由路径
  });
};
</script>

<style lang="scss" scoped>
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 80rpx;
  width: 100%;
  // position: fixed;
  top: 0;
  // height: 40px;
  background-color: #fff;
  // background-color: aqua;

  .nav-img {
    width: 34rpx;
    height: 32rpx;
    margin-left: 24rpx;
  }
  .nav-title {
    .sp {
      width: 122rpx;
      height: 24rpx;
      font-size: 25rpx;
      font-family: SourceHanSansCN;
      font-weight: 300;
      color: #000000;
    }
  }
}
</style>

注意,因为小程序中不支持标签名选择器,所以在vscode中写的css样式最好不用标签名选择器

使用uView-plus组件时样式不会同时生效

我们在给按钮组件设置样式的时候,发现同样的代码在不同的平台展示的效果不一样,在微信小程序中就完全不显示样式 image.png 查询文档 uView-plus 3.0后才发现,我们需要custom-style属性来定义外部样式: image.png 按着文档中提到的我们设置了一个custom-styles属性,在里面设置自定义的样式就可以了

 <up-button
    class="search-btn"
    text="确定"
    :custom-style="{
      color: '#fff',
      background: '#ff960c',
      borderRadius: '28rpx',
     }"
></up-button>

页面之间传参

使用uni.$onuni.$emit进行页面之间的传值

  1. 页面配合uni.navigateBack使用
 uni.navigateBack({
    delta: 1, // 回退的层数
    success: () => {
      uni.$emit("dataBack", {
        regionId: id,
        titleFull: showCity.value,
      });
    },
  });
  1. 接收参数的页面在钩子函数中
onLoad( () => {
    uni.$on("dataBack", function (data) {
        searchObject.value.region = data.regionId;
        address.value = data.titleFull;
  });
})

配置小程序界面分享

image.png

  1. 给单独页面设置允许分享

需要再钩子函数中调用并配置下面代码(配置完成后就可以分享给好友和朋友圈了)

onLoad((options) => {
  getResourceId(options);
  HouseDetail();
  // #ifdef MP-WEIXIN
  uni.showShareMenu({
    withShareTicket: true, // 是否使用带 shareTicket 的转发
    menus: ['shareAppMessage', 'shareTimeline'], // 需要显示的菜单列表,例如分享到朋友圈等
  });
  // #endif
});

如果你想自定义修改标题和封面图片可以利用钩子函数

!!!!跟onLoad同级,并且vue3开发需要引入

import { onShareAppMessage } from '@dcloudio/uni-app';  // 这句话很重要

// 允许页面被分享
onShareAppMessage(() => {
  return {
    title: roomDetail.value.resourceName, // 自定义标题
    imageUrl: roomDetail.value.picArr[0], // 自定义图片 URL
    path: `/pages/Tenant/roomDetail?resourceId=${resourceId.value}&roomPrimaryId=${roomPrimaryId.value}&coreArr=${coreArr.value}&checkInTime=${checkInTime.value}&checkOutTime=${checkOutTime.value}`, // 自定义分享参数
    success: function () {
      console.log('分享成功');
    },
    fail: function () {
      console.log('分享失败');
    },
  };
});
  1. 全局页面都可以分享

首先你需要新建一个js文件

export default {
  onLoad() {
    uni.showShareMenu({
      withShareTicket: true, // 是否使用带 shareTicket 的转发
      menus: ['shareAppMessage', 'shareTimeline'], // 需要显示的菜单列表,例如分享到朋友圈等
    });
  },
};

然后在main.js文件中引入并使用

import { createSSRApp } from 'vue';
import uviewPlus from 'uview-plus';
import pinia from './stores/index';
import App from './App.vue';

// 下面的注释是条件编译,如果你只开发小程序,可以不用条件编译
// #ifdef MP-WEIXIN
import shareMixin from './utils/share';
// #endif

export function createApp() {
  const app = createSSRApp(App);
  app.use(uviewPlus);
  app.use(pinia);

  // #ifdef MP-WEIXIN
  app.mixin(shareMixin);
  // #endif

  return {
    app,
  };
}

这样你的小程序的每个界面都可以分享给好友和朋友圈了。

拨打电话功能

image.png

<view  class="call-msg">
    <text class="call_text" @click="makeCall(roomDetail.servMobile)">
        {{ roomDetail.servMobile }}
    </text>
    <img style="width: 32rpx; height: 32rpx; margin-left: 10rpx" src="电话小图标的互联网那个地址、或者临时目录文件" alt="" />
</view>



// 给房东打电话
const makeCall = (servMobile) => {
    uni.makePhoneCall({
        phoneNumber: servMobile, // servMobile改为需要打的电话号码
    });
};


.call-msg {
   font-size: 13px;
   display: inline-flex;
   align-items: center;
   .call_text {
      text-decoration: underline;
      color: #2196f3;
   }
}

图片保存功能

screenshots.gif

首先你需要在manifest.json文件中添加配置使用vscode开发uni-app

image.png

    const bindSavePic = () => {
        uni.showModal({
            title: '提示',
            content: '是否保存照片到相册',
            cancelText: '是',
            confirmText: '否',
            showCancel: true,
            success: function (res) {
                if (!res.confirm) {
                    uni.downloadFile({
                        url: 'https://s2.loli.net/2022/02/21/wRltb4a2cXoPhCk.jpg', //仅为示例,并非真实的资源
                        success: (res) => {
                            if (res.statusCode === 200) {
                                uni.saveImageToPhotosAlbum({
                                    //保存图片到系统相册。
                                    filePath: res.tempFilePath, //图片文件路径
                                    success: function () {
                                        uni.showToast({
                                            title: '图片保存成功',
                                            icon: 'none',
                                        });
                                    },
                                    fail: function (e) {
                                        uni.showToast({
                                            title: '图片保存失败',
                                            icon: 'none',
                                        });
                                    },
                                });
                            }
                        },
                    });
                }
            },
        });
    };

uni-app中获取dom实例

<template>
  <div class="dropdown-menu"></div>
  <button @click="getDomInstant">点击获取DOM元素实例</button>
</template>

<script setup>
import { ref, onMounted,nextTick } from "vue";
import { getCurrentInstance } from "vue";

let query; // 获取dom实例
const currentInstance = ref(); // 组件实例

onMounted(() => {
  currentInstance.value = getCurrentInstance(); // 获取组件实例
  query = uni.createSelectorQuery().in(currentInstance.value);
});

const getDomInstant = async () => {
  await nextTick()
  query
    .select(".dropdown-menu")
    .boundingClientRect((rect) => {
      console.log(val, rect);
    })
    .exec();
};
</script>

<style lang="scss" scoped></style>