I planned to write an essay that covered all the knowledge points, but during the writing process, I found that the content was too much, so I planned to split it into four parts:

  1. It mainly introduces the problems and solutions encountered in routing, communication and development.
  2. It mainly introduces component development and several component plug-ins written by itself.
  3. It mainly introduces canvas and how to realize pie chart and bar chart by itself.
  4. This section describes the process of going online and the problems that may occur during going online

Uni-app is a cross-platform development framework based on VUe.js, which means that as long as you know Vue, you can write a set of code using the framework and publish it to ios, Android, Web, various small programs (wechat/Alipay/Baidu/Toutiao /QQ/ Dingding/Taobao), kuaiapp and many other platforms.

The purpose of this article is to share some of my experiences with uni-App. For more information about uni-App, you can visit the uni-App website to find out more about the framework. You can install the development tool HBuilder X on the website to develop projects.

This article is mainly for my development often used methods, examples are for App, of course, also applies to small programs

Application life cycle

  • onLaunch
    • Triggered when the initialization is complete: just opened the APP, but did not see the contents inside, you can carry out version update prompt in this method
  • onShow
    • Start or enter foreground from background display: App starts to see the contents inside
  • onHide
    • From the foreground to the background: Application in the background (e.g. other apps opened, phone calls coming, etc.)

routing

For APP, each page corresponds to a route. In uni-app, routes need to be configured in the Pages field of pages. Json in the root directory.

Routing hop

Address: official API uniapp. Dcloud. IO/API/router

  • Uni. NavigateTo: Leave the current page and jump to a page in the application with a parameter URL, similar to router.push in vue-Router
  • Uni. redirectTo: Closes the current page and redirects to a page in the application. The URL can take parameters, similar to router.replace in VUe-router
  • Uni. reLaunch: Close all pages and open to a page within the app with a parameter URL
  • Uni. SwitchTab: Jumps to the tabBar page and closes all other non-tabbar pages. The URL cannot take parameters.
  • NavigateBack: close the current page and return to the previous page or multi-level page, similar to router.go(-n) in vue-router.

Pay attention to

Urls have length limits, and strings that are too long will fail to be delivered. This can be resolved through window communication (described below), global variables, or encodeURIComponent

Example:

/ / pageA page
const data = {
    // Multiple parameters
}
uni.navigateTo({
    url: `/pages/pageB/pageB? data=encodeURIComponent(JSON.stringify(data))`
})

/ / pageB page
onLoad(option){
    const data = JSON.parse(decodeURIComponent(option.data));
}

Copy the code

Page life cycle

  • onLoad

    • Listen to the page load, its parameters are the data passed from the previous page, often used to get the parameters passed from the previous page
  • onShow

    • Listen to the page display. Fires each time a page appears on the screen, including a return to the current page from a subordinate page
  • onReady

    • The listener page is rendered for the first time. Note that if the rendering is fast, it will trigger before the page is animated
  • onHide

    • The listening page is hidden. NavigateTo jump to the next page or run the app in the background
  • onUnload

    • Listen for page unmount. Uni. NavigateBack is executed on the current page
  • onPullDownRefresh

    • Monitor the user’s pull-down actions, generally used for pull-down refresh.
  • onReachBottom

    • An event to scroll to the bottom of a page (not to the bottom of a scroll view), usually used to pull down data from the next page
  • onTabItemTap

    • Triggered when you click TAB or run uni. SwitchTab. The parameter is Object
  • onBackPress

    • Event = {from:backbutton, navigateBack}; backbutton = {from:backbutton, navigateBack}; NavigateBack indicates the source is uni. NavigateBack. If you want to make some logical judgments in the callback function, you can dynamically disable the previous page by setting return true and return false
    onBackPress(event){
        const { from } = event;
        if(from= = ='backbutton') {// Make a logical decision
            return true;
        }else if(from= = ='navigateBack') {return false; }}Copy the code

Component life cycle

Component life cycle is the same as vUE

For more information:

  • Vue official website life cycle
  • Uni-app Component life cycle

communication

globalData

Configuration for globalData defined in app.vue:

//App.vue
<script>
    export default{
        globalData: {
            msg: "hello world!"
        }
    }
</script>

/ / in the page
// Get data
getApp().globalData.msg
// Change the data
getApp().globalData.msg = 'uni-app! '
Copy the code

Route forwarding parameters

/ / pageA page
uni.navigateTo({
    url: `/pages/pageB/pageB? name=uni-app&age=18`
})

/ / pageB page
onLoad(option){
    const { name, age } = option;
}

Copy the code

EventChannel

This mode is used for inter-page event communication. Page A is redirected to page B for parameter transfer, and page B returns to Page A for parameter transfer

//pageA uni.navigateTo({ url: '/pages/pageB ', events:{fromBackPageData: function(data){console.log('fromBackPageData',data); } }, success(res){ res.eventChannel.emit('passToNextPage',{name: 'pageA'}) } }) //pageB onLoad(){ const eventChannel = this.getOpenerEventChannel(); <! Eventchannel. on('passToNextPage',data => {console.log('passToNextPage', data); }) <! Eventchannel. emit('fromBackPageData',{name: 'backData'})}Copy the code

The page

  • uni.$emit(Event name, parameter), trigger the global custom event;
  • uni.$on(event name, callback function), listen for global custom events, events can be defined byuni.$emitThe trigger;
  • uni.$once(event name, callback function), listen for global custom events, events can be defined byuni.$emitTrigger, but only once, and remove the listener after the first trigger;
  • uni.$off(event name, callback function) to remove the global custom event listener

Note: commonly used for cross-page, cross-component communication; Destroy event listeners in a timely manner

Example:

//pageA
<template>
    <button @click='handleEvent'>pageA</button>
</template>
<script>
    export default {
        methods: {
          handleEvent(){
            uni.$emit('pageAEvent',{name: 'pageA'})
          }  
        }
    }
</script>
Copy the code
//pageB <template> <button @click='handleEvent'>pageA</button> </template> <script> export default { onLoad(){ uni.$on('pageAEvent',data => { console.log(data) }) }, OnUnload (){uni.$off('pageAEvent',() => {console.log(' pageAEvent'); }) } } </script>Copy the code

Intercomponent communication can also be used this way, depending on the situation

Other communication modes are the same as those of VUE

1. Event bus: across pages and components

class Bus{
    constructor(){
        this.callbacks = {};
    } 
    $on(eventName,cb){
        this.callbacks[eventName] = this.callbacks[name] || [];
        this.callbacks[name].push(cb);
    }
    $emit(eventName,args){
        if(this.callbacks){
            this.callbacks[name].forEach(cb= > cb(args))
        }
    }
}
Copy the code
// main.js
Vue.prototype.$bus = newBus () or Vue. Prototype. $Bus =new Vue()
Copy the code
/ / pageA or component
this.$bus.$emit(event name, parameter)Copy the code
/ / pageB or componentB
this.$bus.$on(event name, callback function)Copy the code

Vuex: Across pages and components, create a unique global data manager store that manages data and notifies components of state changes

const store = new Vuex.Store({
    state: {},
    getters: {},actions: {},mutations: {}})Copy the code

3. Props: The parent passes a value to the child

// Parent <template> <child name='uni-app'></child> </template>Copy the code
// Subcomponent <template> <view>{{name}}</view> </template> <script> export default{props: {name: String, default: '' } } </script>Copy the code

4. Custom event: The child passes a value to the parent

// parent <template> parent < child-@onclick ='handleEvent'></child> </template> <script> export default{methods: {handleEvent(value){console.log(' data from child component ',value)}}} </script>Copy the code
// subcomponent <template> <view> < button@click ='handleEvent'> subcomponent </button> </view> </template> <script> export default{methods: { handleEvent(){ this.$emit('onClick',{name: 'uni-app'}); } } } </script>Copy the code

5. Slot

Anonymous slot
// Parent <template> <child>hello world! uni-app!!! </child> </template>Copy the code
/ / subcomponents < template > < view > < slot > < / slot > < / view > < / template >Copy the code
A named slot
// Parent <template> <child> <template V-slot :header> </template> Hello world! uni-app!!! <template v-slot:footer> tail </template> </child> </template>Copy the code
// Subcomponent <template> <view> <slot name='header'></slot> <slot></slot> <slot name='footer'></slot> </view> </template>Copy the code
Scope slot
// Parent component <template> <view> <child> <template V-slot :default='{user}'> {{user.name}} </template> <template v-slot:category='{list}'> <ul> <li v-for="item in list" :key='item'>{{item}}</li> </ul> </template> </child> </view> </template>Copy the code
// Subcomponent <template> <view> <slot :user='user'></slot> <slot name='category' :list='list'></slot> </view> </template> <script> export default{ data(){ return { user: { name: 'uni-app', age: 18 }, list:['vue','react','node'] } } } </script>Copy the code
$refs: Get child node reference: parent passes child
<template> <view> <child ref='child'></child> </view> </template> <script> export default {mounted(){ this.$refs.child.passData('child') } } </script>Copy the code
// subcomponent <script> export default {methods: {passData(data) {console.log(" parameters from parent component ",data); } } } </script>Copy the code
$parentand$root: Sibling components communicate via common ancestor bypass
// Parent component <template> <view> <child1></child1> </child2> </view> </template>Copy the code
Child1 <template> <view> < view@click ="handleEvent"></view> </template> <script> export default {methods: {handleEvent(){this.$parent.$emit(event name, parameter)}}} </script>Copy the code
// child2 <script> export default {mounted(){this.$parent.$on(eventname, callback)}} </script>Copy the code
$children: The parent component can pass$childrenAccess child components for parent-child communication
<template> <view> <child></child> </view> </template> <script> export default {mounted(){this.$children[0].title  = 'uni-app' } } </script>Copy the code
// Child <template> <view> {{title}} </view> </template> <script> export default {data() {return {title: 'vue' } } } </script>Copy the code

Note: $children does not guarantee child order

$attrsand$listeners: parent and child components

$attrs: contains attribute bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, all parent-scoped bindings (except class and style) are included, and internal components can be passed in via V-bind =”$attrs” — useful when creating higher-level components.

Example:

// parent <template> <child1 title='uni-app' :name='name' V-bind ='$attrs'></child1> </template> export default {components:  { Child }, data() { return { name: 'vue' } } }; </script>Copy the code
<template> <view> from $attrs: {{$attrs.title}} from prop: {{ name }} </view> </template> <script> export default { name: 'Child1' props: { name: { type: String, default: '' } }, mounted () { console.log(this.$attrs); }, } </script>Copy the code

$Listeners: Contains V-on event listeners in the parent scope (without.native modifiers). Internal components can be passed in via V-on =”$Listeners “– useful for creating higher-level components.

Child @click='handleEvent' @change='handleChange' V -on='$listeners'></child> </template> < process > export default { components: { Child }, methods: { handleEvent() { console.log('handleEvent'); }, handleChange(){ console.log('handleChange'); }}}; </script>Copy the code
/ / child component
<script>
    export default {
      mounted () {
           for(let eventName in this.$listeners){
               this.$listeners[eventName]()
            }
        },
    }
</script>
Copy the code
Provide and Inject enable value transmission between ancestors and descendants
// Ancestor component
export default {
   provide(){
    return {
        name: 'uni-app'}}}Copy the code
<template> <view> {{name}} </view> </template> export default {inject: ['name']}Copy the code

This is covered further in the component sections that follow

Problems encountered in the development process and solutions

1. Picker is implemented on different platforms with different UI, so how to keep it uniform?

Use the Picker-View component instead

2. The app side uses VUE to develop custom popup components, which cannot block navigationBar and Tabbar

The reason: NavigationBar and Tabbar are both native components. In uni-App, native components are separated from the webView rendering process, and the highest level. So the front-end components developed by Vue can’t hide navigationBar and Tabbar

Solution:

  • Can use NVUE development, all native rendering, no hierarchy issues;
    • For details, please refer to the nvUE tutorial provided by UNI-App
  • Plus.nativeobj. view, which is a native canvas-like control that can draw any interface;
    • It is not recommended to use it, because there are three disadvantages: 1. API is very low-level and development is complicated; 2. 2. Animation is not supported; 3. Internal scrolling is not supported
  • SubNVue is a nvUE sub-form of native rendering, which covers an NVUE page in a half-screen mode on a VUE page.
    • SubNVue official details: ask.dcloud.net.cn/article/359…
  • Pop up a partially transparent NVUE page, which appears to pop up an element on the form, but actually pops up a new form with a partially opaque area.
    • You can refer to the official recommended plug-in: ext.dcloud.net.cn/plugin?id=9…

3. Customize loading to drive graph effect

When the interface is requested, uni-app provides uni.showLoading API to avoid loading before data is returned, but if the product manager thinks it is ugly, we have to customize it. However, no relevant interface is provided officially. Therefore, we thought of customizing loading components, but in uni-App, loading components need to be introduced in each page to enable each page to use loading components, and some methods may be needed to control the display and hiding of loading components. If a page has multiple requests, there may be problems (I have experienced this, ios has no problem, Android problem, speculation and the timing of the rendering), through access to information, can plus. By using the method of HTML 5 + provide nativeUI. ShowWaiting to solve, specific refer to: www.html5plus.org/doc/zh_cn/n…

Example:


    #ifdef APP-PLUS
    letLoading;// #ifdef APP-PLUS
		loading = plus.nativeUI.showWaiting('Data loading... ', {// Request started
			background: 'rgba (0,0,0,0.3)'.color: '#fff'.width:'100%'.height: '100%'.size: '15px'.loading: {
				display: 'block'.icon: '/static/11.png'.// Customize the image address
				width: '80px'.// Image width and height
				height: '80px'.interval: '500ms'}});// The request succeeded or failed
		loading.close()
	// #endif
	
Copy the code

Note:

You can customize the path of the loading icon. The path must be in PNG format and must be the address of a local resource. The width of the loading diagram must be an integer multiple of its height. Each frame is refreshed according to the height of the picture when displaying the waiting frame. For example, a GIF is composed of 10 parts, and the 10 images should be assembled into a 100px HIGH and 10*100 wide PNG image. When loading is displayed, the image will be animated at a preset interval. This is similar to changing background-position values in CSS.

4. Create images of the page content

Use the HTML2Canvas third-party library

It applies only to H5 and APP terminals. For other terminals, refer to the APIS of related platforms

This library can be used on the H5 end, but it cannot be used on the APP end without the DOM. Then we will mainly introduce how to use it on the app end.

renderjs

Example:

Download html2Canvas.min. js and place it in the static folder of your project directory

<template> <view class="content"> <view class="main" id="main"> <text> RenderJS example </text> </view> <button type="default" </button> <view class="image-container"> <image: SRC ="imageUrl" mode="" class="img"></image> </view> </view> </template> <script> export default { data() { return { imageUrl: '' } }, methods: { generatorImage(url){ this.imageUrl = url; }} </script> <script module="html2canvas" lang="renderjs"> export default {mounted() {// Script = document.createElement('script') // View layer pages run in WWW root directory, Its relative path relative to the WWW calculation script. The SRC = "static/html2canvas. Min. Js' document. The head. The appendChild (script)}, the methods: { onClick(event, instance) { html2canvas(document.querySelector("#main")).then(canvas => { const url = canvas.toDataURL(); // call instance.callMethod('generatorImage', url)}); } } } </script>Copy the code

Example code address: github.com/BoryLee/uni…

Html5 + Screenshot drawing

Reference: www.html5plus.org/doc/zh_cn/w…

<template> <view class="content"> <view class="main" id="main"> <text> RenderJS example </text> </view> <button class=" BTN" </button> <view class="image-container" v-if='plusUrl'> <image: SRC ="plusUrl" Mode ="" class="img"></image> <text>plus picture </text> </view> </view> </template> <script> export default {data() {return {  plusUrl: '' } }, methods:{ plusClick(){ const self = this; const {statusBarHeight} = uni.getSystemInfoSync() const query = uni.createSelectorQuery().in(this); query.select('#main').boundingClientRect(data => { const { top,left,width,height } = data; // #ifdef APP-PLUS const pages = getCurrentPages(); const page = pages[pages.length - 1]; const ws = page.$getAppWebview(); const bitmap = new plus.nativeObj.Bitmap('test'); ws.draw(bitmap, function() { bitmap.save(`_doc/${new Date().getTime()}.jpg`, { overwrite: true, quality: 100, clip: { top:top + 44 + statusBarHeight, left, width, height }, }, function(result) { self.plusUrl = result.target; }, function(e) {console.log(' failed to save image: '+ json.stringify (e)); }); }, function(e) {console.log(' failed to draw a screenshot: '+ json.stringify (e)); }); // #endif }).exec(); } } } </script>Copy the code

5. App opens the applet

Resources: uniapp. Dcloud. IO/API/other/o…

App platform open wechat mini program, use Plus. share launchMiniProgram. Note that UNI-app does not require Plus Ready, just write the code in Plus Ready to the onLoad life cycle of the page. To use this function, you need to configure wechat to share SDK information in the manifest and package it to take effect.

The sample code

<template> <view class="content"> <button @click="openMini"> </button> </template> <script> export default {  data() { return { sweixin: null } }, methods: { openMini() { const self = this; / / # ifdef MP - WEIXIN uni. NavigateToMiniProgram ({appId: 'open the small program appId}) / / # endif / / # ifdef APP - PLUS self. Sweixin? Self. Sweixin. LaunchMiniProgram ({id: 'open the small program of the original id' path: 'open the specified page? Parameter ', type: '2'// 0- official version; 1- Beta; 2- Experience edition. Default is 0}) : plus.nativeui.alert (' The current environment does not support wechat operation! '); //#endif }, init() { const self = this; // #ifdef APP-PLUS plus.share.getServices(function(s) { const share = {} s.forEach(item => { share[item.id] = item }) const sweixin = share['weixin'] self.sweixin = sweixin }) // #endif } }, mounted() { this.init() } } </script>Copy the code

Example code address: github.com/BoryLee/uni…