component

If we put all the processing logic on a page together, it would become very complex to process, and not conducive to subsequent management and extension

However, if we break a page into small function blocks, each function block completes its own part of the independent function, then the whole page management and maintenance becomes very easy

  • We split a complete page into many components

  • Each component is used to implement a functional block of the page

  • Each component can be subdivided

The custom component consists of json WXML WXSS JS files.

The structure of a component in an applet is exactly the same as the structure of a page

It’s just that when the Component field in json is set to true,

This is a component, not a page

Json file for the component

{
  "component": true.// Indicate that this is a component
  "usingComponents": {} // Other components can also be used inside components
}
Copy the code

Using the component

<myCpn />
Copy the code
{
  // All components that are used need to be registered here
  "usingComponents": {
    // Component name: component path
    "myCpn": "/components/my-cpn/my-cpn"}}Copy the code

Attention to details:

  • Because WXML node label names can only be lowercase letters, hyphens, and underscores, custom component label names can only contain these characters
  • Custom components can also refer to custom components in a manner similar to how pages refer to custom components (using the usingComponents field)
  • The name of the root directory where custom components and pages reside cannot be prefixed with wx-; otherwise, an error message will be reported
  • If you declare a component in app.json’s usingComponents, that component is a global component

style

By default, class styles within a component and class styles outside of a component have a separate effect,

The default component style and page style do not conflict

If you need to have the same style between the component and the page interact

In the Component object, you can pass in an Options property with a styleIsolation property.

StyleIsolation has three values:

value instructions
isolated Default values The styles between components and pages do not affect each other
apply-shared The style of the page can affect the component, but the style of the component cannot affect the page

If the component internal style and the page style conflict, the component internal style overrides the page style
shared Component styles and page styles can influence each other
Component({
  options: {
    styleIsolation: 'apply-shared'}})Copy the code
  1. You cannot use id selectors, attribute selectors, or label selectors within a component. You can only use class selectors

  2. You can use class selectors, ID selectors, attribute selectors, and tag selectors in a page

  3. By default, if a label selector is used externally (similar to view {… }) will affect the internal style of the component

    But class selectors, ID selectors, and attribute selectors have no effect on the internal styles of components

    Therefore, it is recommended to use the class selector for style Settings in applets

The page

In many cases, the content presented within a component (data, styles, tags) is not written down within the component and can be determined by the consumer

properties

For the most part, components are only responsible for layout and styling, and the content is determined by the objects that use them

We often need to pass data externally to our components for presentation

At this point we can use the properties property

Properties Supported types; String, Number, Boolean, Object, Array, null(unrestricted type)

The parent component

<myCpn name="klaus" />
<myCpn />
Copy the code

Child components

<view class="title">{{ name }}</view>
Copy the code
Component({
  properties: {
    name: String}})Copy the code

The above is a simplification. In practical development, the more common way to write is as follows:

Component({
  properties: {
    name: {
      type: String.// Customize the default value to override the original default value
      value: 'defaule name'.// The watch option for the property
      // Deep listening is enabled by default
      // The immediate option defaults to true
      // The first oldValue is automatically initialized according to the type (e.g., string is empty and value is 0).
      If the type of the props passed is different from that of the props needed, the type conversion is performed as much as possible
      If the conversion succeeds, the converted value is used. If the conversion fails, the default value is used
      observer(newName, oldName) {
        console.log(newName, oldName)
      }
    }
  }
})
Copy the code
Component({
  properties: {
    foo: {
      // If multiple types of props can be used, use type and optionalTypes together
      // type Mandatory -- This parameter is used to set the default initial value for multiple types. For example, if this parameter is not set, the default initial value is 0
      type: Number.// Optional other types -- value is of the type array
      optionalTypes: [String.Boolean]}}})Copy the code

externalClasses

Sometimes we don’t want styles to be fixed within a component, but externally

That is, you pass a style name to the child, and the style is defined in the parent component

The parent component

<! The title attribute is defined in externalClasses in the child component, so the comforter component will think of it as the value of the class style attribute that the parent component passed in ----.
<myCpn title="red" foo="Klaus" />
Copy the code
.red {
  color: red;
}
Copy the code

Child components

<! -- The style is used in the child component -- note: the style name used here is title not red (★★★) -->
<view class="title">{{ name }}</view>
Copy the code
Component({
  data: {
    name: 'Klaus'
  },

  // Receive the style passed in by the parent - the value is of type array
  externalClasses: ['title']})Copy the code

Custom events

The parent component

<view>{{ counter }}</view>

<! -- Listen for custom events -->
<cpn  bindincrement="increment" />
Copy the code
Page({
  data: {
    counter: 0
  },

  increment() {
    this.setData({
      counter: this.data.counter + 1})}})Copy the code

Child components

<button size="mini" bindtap="increment">increment</button>
Copy the code
Component({
  // Component methods need to be defined in the methods option
  methods: {
    increment() {
      // Use triggerEvent to trigger custom events
      // Parameter 1 -- Custom event name
      // Argument 2 - the argument to be passed ---- is like an object
      // parameter 3 ---- configuration object ---- is not usually used - pass an empty object
      this.triggerEvent('increment', {}, {})}}})Copy the code

Phase case

To achieve a simple TAB switch components

The parent component

<tabs 
  users="{{ users }}" 
  bind:changeTab="changeTab"
/>

<text>{{ user }}</text>
Copy the code
Page({
  data: {
    users: ['Klaus'.'Alex'.'Steven'],
    user: 'Klaus'
  },

  changeTab(e) {
    this.setData({
      user: e.detail.user
    })
  }
})
Copy the code

Child components

<view class="users">
 <! -- You can dynamically bind the corresponding style in class -->
 <text 
  wx:for="{{ users }}" 
  wx:key="user"
  wx:for-item="user"
  class="user {{ user === activeUser ? 'active' : ''}}"
  bindtap="changeActiveTab"
  data-user="{{ user }}"
 >
  {{ user }}
 </text>
</view>
Copy the code
Component({
  properties: {
    users: {
      type: Array.value: []}},data: {
    activeUser: 'Klaus'
  },

  methods: {
    changeActiveTab(e) {
      const user = e.currentTarget.dataset.user

      this.setData({ 
        activeUser: user
      })
      this.triggerEvent('changeTab', {
        user
      })
    }
  }
})
Copy the code

selectComponent

You can select the component used in the current page or component from the page or component

The arguments are id or class selectors

<tabs id="cpn" />
Copy the code
Page({
  onReady() {
    console.log(this.selectComponent('#cpn'))}})Copy the code

slot

Component slots:

  • Component slots are also designed to make the components we package more extensible
  • Let the user decide what some of the content inside the component actually shows

A single slot

The parent component

<cpn>
  <slider value="60" />
</cpn>
Copy the code

Child components

<view>start</view>

<! Unable to set component default value in applet -->

<! If there are multiple default slots in the Vue, only the first default slot will be valid, and the following default slots will be invalid.
<slot />

<view>end</view>
Copy the code

Multiple slots

The parent component

<cpn>
  <view slot="left">left</view>
  <view slot="center">center</view>
  <view slot="right">right</view>
</cpn>
Copy the code

Child components

<slot name="left" />
<slot name="center" />
<slot name="right" />
Copy the code
Component({
  options: {
    // Multiple slots can only be used if this option is enabled in child components
    multipleSlots: true}})Copy the code

Component

Component({
  // Options for receiving props
  properties: {},

  // Options for defining data inside the component
  data: {},

  // External style passed in
  externalClasses: [].// The method inside the component
  methods: {},

  // Watch option - can listen for state changes in data/properties
  // But the observer method in this option takes only newValue
  / / not oldValue
  observers: {},

  // Configuration options within the component
  For example multipleSlots and styleIsolation
  options: {
    multipleSlots: true
  },

  // The page life cycle
  pageLifetimes: {
    show() {
      console.log('Page is displayed')},hide() {
      console.log('Page hidden')},resize() {
      console.log('Page size has changed')}},// The component lifecycle function
  lifetimes: {
    created() {
      console.log('Component created')},attached() {
      console.log('Component added to page')},ready() {
      console.log('Component rendered complete')},moved() {
      console.log('Component has moved')},detached() {
      console.log('Component removed')}}})Copy the code

System apis

Network request

By default, the interface API domain address requested by the applet must be the domain name configured in the applet background

During local debugging, you can temporarily disable domain name verification

wx.request

wx.request({
  url: 'https://httpbin.org/get'.// Request parameters - For GET requests, parameters can be written in the data option or after the URL request domain name
  data: {
    name: 'Klaus'.age: 23
  },
  
  // Successful callback
  success(res) {
    console.log(res.data.args)
  }
})
Copy the code
wx.request({
  url: 'https://httpbin.org/post'.// Request mode -- the default value is GET
  method: 'POST'.// The parameters of the POST request must be placed in the data option
  data: {
    name: 'Klaus'.age: 23
  },

  success(res) {
    console.log(res.data.json)
  }
})
Copy the code

In applets, there may be multiple places to send network requests,

Therefore, we need to encapsulate network requests so that network requests throughout the project call their own encapsulated interfaces

In order to achieve the decoupling of the request and API, if the later need to modify, directly modify the encapsulated API request method

encapsulation

import { BASE_URL } from './consts'

class Api {
  request(path, method, params) {
    return new Promise((resolve, reject) = > {
      wx.request({
        url: BASE_URL + path,
        method: method || 'get'.data: params || {},
        success: resolve,
        fail: reject
      })
    })
  }

  get(path, query) {
    return this.request(path, 'get', query)
  }

  post(path, params) {
    return this.request(path, 'post', params)
  }
}

export default new Api()
Copy the code

test

// Import files must use relative paths, not absolute paths
import api from '.. /lib/api'

export function fetchTopMv(offset = 0, limit = 10) {
  return api.get('/top/mv', {
    offset,
    limit
  })
}
Copy the code

Popup window

showToast

wx.showToast({
  title: 'toast'.icon: 'loading'.duration: 3000.// The duration for which toast appears. The default value is 1500
  mask: true // When a toast appears, it is not allowed to interact with elements below the toast layer
})
Copy the code

showModal

wx.showModal({
  title: 'title'.content: 'content'.success(res) {
    // There are attributes in the res object
    // 1. cancel -- The user exits the popover when the value is true
    // 2. confirm - the user confirms the popup when the value is true
    console.log(res)
  }
})
Copy the code

showLoading

ShowLoding and showToast whose icon property is Loading have the same display effect

The only difference is that shoToast automatically shuts down after a certain amount of time

When showLoading is disabled, you need to manually invoke the hideLoading method

wx.showLoading({
  title: 'loading... ',})// You need to manually call the hideLoading method to close the loading popup
setTimeout(() = > wx.hideLoading({}), 3000)
Copy the code

showActionSheet

wx.showActionSheet({
  itemList: ['photos'.'gallery'].success(res) {
    // there is a tapIndex attribute in res whose index value corresponds to the element in the index position of the itemList
    console.log(res)
  }
})
Copy the code

share

Sharing is an important way of spreading small programs. There are two ways of sharing in small programs:

  1. Click the menu button in the upper right corner and then click Forward
Page({
  // onShareAppMessage is the equivalent of the Page and lifecycle functions
  onShareAppMessage() {
    // Returns a custom configuration item
    return {
      title: 'sharing'.// Share is displayed on the home page by default
      // The path property can determine the shared applet and click enter to enter the specific page
      path: '/pages/about/about.js'}}})Copy the code

OnShareAppMessage can be set to the following values:

  1. Click a button, direct forward
<! OnShareAppMessage is automatically called when a button component's open-type property is set to share.
<button open-type="share">share</button>
Copy the code

The login

// The constants are separated into separate locations for later maintenance and modification
const TOKEN = 'token'

App({
  // Define token as a global variable
  globalData: {
    token: ' '
  },

  // Applets are recommended for login in the onLaunch method
  onLaunch: function () {
    // 1. Fetch the token from the buffer first
    const token = wx.getStorage(TOKEN)

    // 2. Check whether the token has a value
    if(token && token.length ! = =0) { // The token already exists. Verify whether the token expires
      this.check_token(token) // Verify that the token expires
    } else { // Login without token
      this.login()
    }
  },
  
  check_token(token) {
    wx.request({
      // The backend server
      url: 'http://www.example.com/auth'.method: 'post'.header: {
        token
      },
      success: (res) = > {
        if(! res.data.errCode) {// After obtaining the token, store the token in globalData
          this.globalData.token = token;
        } else {
          // Token expired. Log in again
          this.login()
        }
      },
      fail: function(err) {
        console.log(err)
      }
    })
  },
  login() {
    wx.login({
      // The code obtained from the wechat server is valid for only 5 minutes
      success: (res) = > {
        // 1. Obtain code
        const code = res.code;

        // 2. Send the code to our server
        wx.request({
          url: 'http://www.example.com/login'.method: 'post'.data: {
            code
          },
          success: (res) = > {
            // 1. Retrieve the token
            const token = res.data.token;

            // 2. Save the token in globalData
            this.globalData.token = token;

            // 3. Local storage
            // The storage of an applet can be viewed and modified in the storage TAB of the applet debugger
            wx.setStorage(TOKEN, token)
          }
        })
      }
    })
  }
})

Copy the code

Page jump

navigator

<! The url parameter in navigator must be an absolute path, not a relative path.
<navigator url="/pages/profile/profile" open-type="switchTab">jump</navigator>
Copy the code

Open-type Optional value

value instructions
navigate The default is to keep the original page, and the new page is inserted as a push, but cannot be skipped to the Tabbar page

This operation is a push operation, so a back button appears on the navigation bar
redirectTo Jump to a new page without saving the original page, but not to the Tabbar page

The back button does not appear on the navigation bar
switchTab Jump to the tabBar page and close all other non-Tabbar pages

Multiple tabbars are switched through the bottom TAB bar, so there is no back button in the navigation bar
reLaunch Close all pages before opening a page within the app

The back button does not appear on the navigation bar
navigateBack Close the current page and return to the previous page or multi-level page
<! If the value of delta is greater than the length in the history stack, all pages on the stack will be displayed except the one at the bottom of the stack.
<navigator open-type="navigateBack" delta="2">The fallback</navigator>
Copy the code

Pass parameters when the page jumps

  • Home -> Details page —- page jump — use the query field in the URL
  • Details page -> home page — page back — inside the details page to get the home page object, directly modify the data

Page jump

<! Select * from 'query' to 'route';
<navigator url="/pages/categories/categories? name=Klaus&age=23">jump</navigator>
Copy the code
Page({
  // If the page jumps to the page with a query parameter
  // The corresponding query argument is then converted to an object and passed in as an argument to the onLoad method
  onLoad(options) {
    console.log(options)
  }
})
Copy the code

The page back

The page can be rolled back using a button or the back key on the navigation bar

That is, there are a lot of page back, so if the small program needs to pass in the corresponding parameters when the page back

The corresponding logic can be implemented in the onUnload method

Page({
  onUnload() {
    The getCurrentPages method retrieves all active pages - that is, pages in the history stack
    const pages = getCurrentPages()

    // Pass data to the previous page
    // pages. Length is the number of active pages, and the index of the array starts at 0
    // The index of the last page in pages is page.length-1
    // So the index of the last page in pages is pages.length-2
    const page = pages[pages.length - 2] 

    page.setData({
      msg: {
        name: 'Klaus'.age: 23}})}})Copy the code

Through THE WX API

Each naviagtor hop in the applet corresponds to the corresponding wxAPI

wx.navigateTo({
  url: '/pages/about/about? name=Klaus&age=23'
})
Copy the code
// If the configuration object is not passed, then the default value of the delta property is 1, which means that the page is reverted to the previous page
wx.navigateBack({
  // The value of delta is set the same as that of navigator when open-type is naviagorBack
  delta: 2
})
Copy the code

home.js

Page({
  handleTap() {
    wx.navigateTo({
      url: '/pages/categories/categories? name=Klaus&age=23'.events: {
        // Set the event listener
        getMsg(v) {
          console.log(v)
        }
      }
    })
  }
})
Copy the code

categories.js

Page({
  onLoad(options) {
    // Get the parameters passed in
    console.log(options)

    // Get the corresponding event bus
    const eventChannel = this.getOpenerEventChannel()
    // Go through the event bus to the source page (in this case, the home page)
    // Trigger the events registered in events to return data
    eventChannel.emit('getMsg', {
      msg: 'Hello World'})}})Copy the code