preface
Recently, because of business needs, to start a new small program project, so there is this selection of the article, this will be a brief description of the basic framework construction and part of the compatibility problems and matters needing attention, welcome to read after guidance.
The selection
Although it is a small program project, the page tag is written in native HTML, that is, the traditional div SPAN tag, considering the possibility of additional extensions (H5, etc., is actually quite a small possibility, but should be considered).
Taro Taro and Uniapp, I choose Uniapp. First of all, the ecosystem of the two is richer and there are more solutions. After all, Uniapp is based on Vue, and There are many followers of Vue in China.
Taro does not know much about Vue due to time constraints. Although he supports Vue, most of the solutions are based on React. However, most of the team members are familiar with Vue and consider the inconsistency of technology stack, so they finally choose Uniapp.
Uniapp
After really good main framework, I use vue-CLI own Uniapp template (Vue3.0 + TS) as the technology stack.
vue create -p dcloudio/uni-preset-vue#vue3 <project Name>
Copy the code
After the establishment, the structure is as follows
Simply adjust the directory
- API specific requests are put here, which can be broken down by business module, needless to say.
- Assets Static resources, storing pictures.
- Components Public components.
- Constant is used to store the most commonly used constants such as item type (1: physical, 2: virtual).
- Layout is a global page container, which is wrapped with a layer of layout. It is mainly designed to adapt to various models (such as IphoneX bangs).
- Lib, which I have not marked above (optional), is used to store some structure defined by Ts.
- 例 句 : Packages are used as demos for testing.
- Pages Indicates the service page.
- The router routing.
- Serve public request methods, request interceptors and so on are implemented here.
- Static static resources, forget to delete, see personal naming preferences.
- Store Global status management module, Vuex.
- Utils utility functions.
A brief introduction to some of the problems encountered in mobile terminals, such as common components, requests, etc., no longer verbose.
layout
First of all, let’s talk about the meaning of layout. It exists for global adaptation. It only needs to pay attention to the business level, and there is no need to do adaptation.
Uniapp is very friendly and provides a method called getSystemInfoSync to get system information. This method returns a safeArea, a safeArea under portrait direction, which we can build on.
After we get WeChat applet by getMenuButtonBoundingClientRect location information in the top right corner of the capsule, simple to fit the safety area.
/ * * *@feat < get the top value of the action bar on the view > *@param {number} Height The height of the action bar aligned with the applet menu button */
export function getBarClientTop(height: number) :number {
let top: number;
const { safeArea } = uni.getSystemInfoSync();
const safeAreaInsets = safeArea ? safeArea.top : 0;
// #ifdef MP
if (height > 0) {
const menuBtnRect =
uni.getMenuButtonBoundingClientRect &&
uni.getMenuButtonBoundingClientRect();
top = menuBtnRect.height / 2 + menuBtnRect.top - height / 2;
} else {
top = safeAreaInsets;
}
// #endif
// #ifdef H5 || APP-PLUS
top = safeAreaInsets;
// #endif
return top;
}
Copy the code
The default height is 44, depending on the actual result. We get height information from these apis for the spacing of our elements.
.eslintrc.js
Since the project is based on TS in the Node environment, some UNI and WX global rules need to be added in order for TS to work properly.
module.exports = {
globals: {
uni: true.wx: true,}}Copy the code
postcss
Since the design is a double image (the width of the design is 750px, the actual size is 375px), and RPX is needed to fit the applet.
const path = require("path");
module.exports = {
parser: require("postcss-comment"),
plugins: [
require("postcss-pxtorpx-pro") ({// The conversion unit
unit: "rpx".// Unit precision
unitPrecision: 2.// The minimum pixel value that needs to be converted, the px unit below this value will not be converted
minPixelValue: 1.// Unprocessed files
exclude: /node_modules|components/gi.// The default design is 750 width, 2 times the output
/ / 0.85 640
transform: (x) = > x * 2,}).require("postcss-import") ({resolve(id) {
if (id.startsWith("~ @ /")) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3));
} else if (id.startsWith("@ /")) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2));
} else if (id.startsWith("/") && !id.startsWith("/ /")) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1));
}
returnid; }}),require("autoprefixer") ({remove: process.env.UNI_PLATFORM ! = ="h5",}).require("@dcloudio/vue-cli-plugin-uni/packages/postcss")]};Copy the code
assets/css/constant.scss
Here, define some common style variables, if the later skin function can be easily switched, specific rules can be given by the design.
// Theme color $main-color: #EE5A61; // $sub-color: #FFA264; // Background color $page-background-color: #F2F4F5; // Page background color // More...Copy the code
Camera assembly && Audio assembly
The camera component
Let’s start with the camera component because it involves recording video and so on. Uniapp also provides createCameraContext so that we can get the Camera context. However, the API is not compatible with H5 and APPS. If you want to do a lot of things involving the H5 this is very troublesome, may need to tune the native camera (not redundant).
The component is also not a full-screen camera and can be styled itself. So, here’s a simple Demo.
<template>
<LayoutMain>
<template v-slot:mains>
<div class="carmare-wrapper">
<camera
:device-position="cameraConfig.devicePosition"
:flash="cameraConfig.flash"
binderror="error"
@error="handleOnError"
style="width: 100%; height: 300px"
></camera>
<button @click="handleTakePhoto">Taking pictures</button>
<button @click="handleStartReord">Start the video</button>
<button @click="handleStopRecord">Stop the video</button>
<button @click="handleSwitchDevicePosition">Switch the camera toward the {{cameraConfig. DevicePosition}}</button>
<button @click="handleSwitchFlashLight">{{cameraconfig. flash}} Flash</button>
<div v-if="photoList.length > 0">Photos that have been taken<div v-for="(item, index) in photoList" :key="index">
<img :src="item" alt="" />
</div>
</div>
<div v-if="videoSrc">Recorded video<video :src="videoSrc" style="width: 100px; height: 100px"></video>
</div>
</div>
</template>
</LayoutMain></template> <script lang="ts"> import { onReady } from "@dcloudio/uni-app"; import { defineComponent, reactive, ref, Ref } from "vue"; import LayoutMain from "@/layout/layoutMain.vue"; export default defineComponent({ setup() { let carmareContext: any; let videoSrc: Ref<string> = ref(""); let currentFlashLightStatus = 0; const statusList = ["off", "on", "auto"]; const cameraConfig: any = reactive({ devicePosition: "back", flash: statusList[currentFlashLightStatus], }); onReady(() => { carmareContext = uni.createCameraContext(); }); const photoList: Ref<string[]> = ref([]); return { cameraConfig, photoList, videoSrc, handleOnError: (error: any) => { console.error("handleOnError-eerror", error); }, handleTakePhoto: () => { carmareContext.takePhoto({ quality: "high", success: (res: any) => { console.info("res", res); photoList.value.push(res.tempImagePath); }}); }, handleSwitchDevicePosition: () => { console.info("cameraConfig.devicePosition"); cameraConfig.devicePosition = cameraConfig.devicePosition === "back" ? "front" : "back"; }, handleSwitchFlashLight: () => { const lastStatus = statusList.length - 1; console.info("333handleSwitchFlashLight"); if (currentFlashLightStatus < lastStatus) { cameraConfig.flash = statusList[(currentFlashLightStatus += 1)]; } else { currentFlashLightStatus = 0; cameraConfig.flash = statusList[currentFlashLightStatus]; }}, / / video handleStartReord: () = > {carmareContext. StartRecord ({success: (res: any) => { console.log("handleStartReord-success", res); Uni. ShowToast ({title: "Start recording ",}); }, fail: (error: any) => { console.error("handleStartReord-error", error); }}); } / / stop video handleStopRecord: () = > {carmareContext. StopRecord ({success: (res: any) => { console.log("handleStopRecord-success", res); Uni.showtoast ({title: "Stop recording ",}); videoSrc.value = res.tempVideoPath; }, fail: (error: any) => { console.error("handleStopRecord-error", error); }}); }}; }, components: { LayoutMain }, }); </script>Copy the code
Click the real machine debugging on the wechat developer tool, and the result is shown in the picture:
Audio components
Similarly, Uniapp provides createInnerAudioContext(), which creates and returns the innerAudioContext object of the innerAudioContext.
This API is compatible with H5 and App. On IOS, this component supports only M4A, WAV, MP3, AAC, AIFF, AND CAF formats. But there are more on Android. Also give a brief answer to the Demo for debugging.
<template>
<LayoutMain>
<template v-slot:container>
<button @click="handleStartRecord">Start the recording</button>
<button @click="handleEndRecord">End of the tape</button>
<button @click="handlePlay">Play the tape</button>
<button @click="handlePausePlay">Pause the Recording</button>
<button @click="handlePausePlay">Pause the Recording</button>
<button @click="handleEndPlay">End Playing a Recording</button>
<div>Operation records<div v-for="(item, index) in operateRecordList" :key="index">
{{ item }}
</div>
</div>
</template>
</LayoutMain>
</template>
<script lang="ts">
import { ref, onMounted, Ref } from "vue";
export default {
data() {
return {};
},
setup() {
const operateRecordList: Ref<string[]> = ref([]);
// let getRecorderManager;
let recorderManager: any;
let innerAudioContext: any;
let voicePath: string;
onMounted(() = > {
const current = (recorderManager = uni.getRecorderManager());
operateRecordList.value.push("prending");
current.onError(function (e: unknown) {
uni.showToast({
title: "getRecorderManager.onError"});console.error("getRecorderManager.onError", e);
});
current.onStart(function () {
operateRecordList.value.push("Start recording.");
});
current.onStop(function (res: any) {
operateRecordList.value.push("End of the recording.");
console.log("recorder stop" + JSON.stringify(res));
voicePath = res.tempFilePath;
});
current.onPause(function () {
operateRecordList.value.push("Pause recording");
});
});
onMounted(() = > {
const current = (innerAudioContext = uni.createInnerAudioContext());
current.obeyMuteSwitch = false;
uni.setInnerAudioOption({
obeyMuteSwitch: false}); current.onError((res) = > {
console.error("innerAudioContext-onError", res);
});
current.onPlay(() = > {
operateRecordList.value.push("Start playing");
});
current.onPause(() = > {
operateRecordList.value.push("Pause play");
});
current.onStop(() = > {
operateRecordList.value.push("End of play");
});
});
return {
operateRecordList,
handleStartRecord: () = > {
recorderManager.start({
duration: 60000.// Recording duration in ms, maximum 600000 (10 minutes)
format: "mp3"}); },handleEndRecord: () = > {
recorderManager.stop();
},
handlePlay: () = > {
innerAudioContext.src = voicePath;
innerAudioContext.play();
},
handleEndPlay: () = > {
innerAudioContext.stop();
},
handlePausePlay: () = >{ innerAudioContext.pause(); }}; }};</script>
Copy the code
Recording and playing are not easy to show, so there are no screenshots, interested friends can copy to use.
In the pit of
layout
When the layout components mentioned above are running on the computers of different colleagues, it is found that a few of them have layout that does not take effect, that is, the page does not wrap the layout layer.
Register globally in main.ts:
import { createApp } from "vue";
import Layout from "@/layout/layoutMain.vue";
import store from "@/store/index";
import App from "./App.vue";
const app = createApp(App);
app.use(store);
// Globally register components
app.component("Layout", Layout);
app.mount("#app");
Copy the code
Used in a page (layout doesn’t work here, but on my computer) :
<template>
<Layout>
<template v-slot:mains>
<div>Category pages</div>
</template>
</Layout>
</template>
Copy the code
This is still the case when the environment is the same and plug-ins and other aspects are not affected. It is not clear how this will work, but the current solution is to rename the Layout to LayoutMain. (Black question mark?)
image
The lazy loading of the Uniapp Image component does not take effect, which is suspected to be A decoration after testing.
See their own manual image lazy loading.
About component passing values
Assume the following components:
<template>
<div>{{ hello }}</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
import { Props } from "./interface";
export default defineComponent({
props: {
hello: {
type: String as PropType<Props["hello"] >,default: 'fff',}}})</script>
Copy the code
You can see that Hello is of type String and the default value is FFF.
But when hello = undefined, hello will display an empty string “”. If hello does not pass, it will be “FFF”.
The last
Are you gonna leave here without liking it?
Attention to the public number: biscuit front, close the distance between you and me (^▽^)