preface

After some research (research article here: small program framework research), the final decision is to use the native development of small programs. But the native development engineering ability is relatively poor, directly using the official template to write demo is ok, to continue iterative project development needs to be armed, typescript, internationalization, SCSS preprocessing, setting environment variables, and so on, the final template is put on Github. Those in need can use Useful – CLI to create an enhanced version of the native wechat applet project.

The project was built using the Gulp build tool, and some of the glUP details are not covered in this article, so it is better for those who have not been involved in glup to learn about it before coming back. If you are already familiar with gulp, you can see what is configured by looking at the gulpfile in your project.

Use the Useful -cli create [app-name] command to create a small program project

Small program native template build

Small program capability

The final applet native development template integrates the following features:

  • supporttypescriptAnd you can import a file using alias
  • supportinternationalization
  • supportSCSS precompiled
  • supportSetting environment variables
  • supportHot update(When yarn dev is executed, the file is compiled automatically.)
  • Use Promise to wrap wX.request and create an httpManager singleton
  • Use the esLint + prettier + Husky specification code format
  • Introducing the weUI library
  • GlobalData based state management solution

The above ability is in the official template based on step by step practice, the main purpose is to be able to make small program native development you provide a little reference, of course, if found a better solution can be discussed in the comments below. The introduction process for each capability is described in detail below.

Create a project

  • Apply for a small program account
  • Use official wechat developer tools to quickly create a small program project

Select Typescript for your project language. After you create your project, you’ll find that the official applet template directory structure is very simple. A applet page consists of four files:

  1. .jsonThe suffixJSONThe configuration file
  2. .wxmlThe suffixWXMLTemplate file
  3. .wxssThe suffixWXSSThe style file
  4. .jsThe suffixJSScript logic file (if it is a TS template, the JS file is compiled from the TS file)

configurationTypescript

After creating the project, you can find the so-called support for Typescript, that is, the official provide the small program interface file typings, and each page under a ts file, and then through the TSC command to compile the TS file into JS file. Em… That’s a little rough. But that’s ok, as long as we have the Typings file, we can do the rest ourselves.

  1. Change the ts output path to dist

    Ts files and JS files would be very messy together, so we changed the outDir path in tsconfig to dist, so that the compiled JS files are placed in dist directory

  2. Copy JSON, WXML, WXSS to dist directory using Glup build tool

    These files will be moved to dist during configuration nationalization and SCSS precompilation. You can see the final template code for the logic, but this is just to show the implementation steps to support typescript in the first place

  3. By setting the miniprogramRoot of project.config.json to dist, the wechat developer tool can correctly find dist to load the required files

  4. Configuration alias

    Since aliases cannot be parsed properly with typescript compilation, reference ttypescript + typescript-transform-paths to solve this problem. For example, refer to the methods in utils to import {… } from ‘@utils/util’ or import {… } from ‘utils/util’

    A. Install ttypescript + typescript-transform-Paths dependencies

    // Install dependencies
    yarn add -D ttypescript
    yarn add -D typescript-transform-paths
    Copy the code

    B. configure tsconfig

    // tsconfig{..."baseUrl": "miniprogram" /* Base directory to resolve non-absolute module names. */."paths": {
          "@pages/*": ["page/*"]."@utils/*": ["utils/*"]."@npm/*": ["miniprogram_npm/*"]... }/* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */."plugins": [{ "transform": "typescript-transform-paths"}],... }Copy the code

    C. Compile using TTSC

    // package.json."scripts" {
           "tsc": "ttsc". }...Copy the code

    D. use alias

    // Reference the method in utils
    import{... }from '@utils/util'
    / / or
    import{... }from 'utils/util'
    
    // Reference the NPM module
    import{... }from '@npm/***'
    / / or
    import{... }from 'miniprogram_npm/***'
    Copy the code

Tip: Small programs can only run JS files, so all TS files are not packaged and uploaded by default.

Internationalization of configuration

There is an official reference scheme for internationalization configuration.

Usage in WXML

  1. Introduce the internationalization runtime in the JavaScript file corresponding to the WXML file.

Note: It is recommended that both Page and Component be defined with the Component constructor so that the I18n Behavior can be used. If you want to use I18n on the Page construct then you need to introduce I18nPage instead of the Page constructor.

<! -- pages/index/index.ts -->import { I18nPage } from '@miniprogram-i18n/core'I18nPage({ ... }) orimport { I18n } from '@miniprogram-i18n/core'
Component({
  behaviors: [I18n]
})
Copy the code
  1. Use the t function (or whatever function name you specify) in WXML to get the text.
<! -- pages/index/index.wxml --><view>{{ t('helloWorld') }}</view>
Copy the code

Usage in JavaScript

Miniprogram /utils/i18n encapsulates Message and can be used directly

import { message, toggleLanguage } from '@utils/i18n'
console.log(message('helloWorld'))
toggleLanguage() // Switch the language
console.log(message('helloWorld'))
Copy the code

Note

  • NPM needs to be built before @miniProgram-i18n /core can be used
  • Use gulp to resolve the problemi18nIn theJson fileandpageIn theWXML fileOutput to dist

Configuration SCSS

The SCSS precompiled configuration is very simple, using gulp-sass. The only thing to note is the use of @import. If you use gulp-sass to parse @import, sass will package the import into the current file. A typical approach is to comment out @import statements before compiling and open @import after compiling. (WXSS supports style imports and can recognize @import.)

However, commenting out @import makes it impossible to @import a variable declaration (such as $black) or @mixin in a sass file, because WXSS does not support this. Considering that a single package of style files for small programs should not exceed 2M, we do not comment out @import, but use gulp-sass to handle @import directly.

For detailed implementation steps, see gulP configuration below (see this article for an overall idea). After configuration, the following functions are supported:

  • Support for.scSS style files
  • Support @import and configure aliases (see styleAliases configuration below)
  • Supports converting px to RPX
// gulpfile.js
const scss = require('gulp-sass'); // SCSS compiles the plug-in
const postcss = require('gulp-postcss'); // Powerful CSS handling plug-ins
const pxtorpx = require('postcss-px2rpx'); // Convert px to RPX
const styleAliases = require('gulp-style-aliases'); // SCSS Sets the alias
const rename = require('gulp-rename'); // Change the file name
const replace = require('gulp-replace'); // Replace the content
const changed = require('gulp-changed'); // Check for changes
const autoprefixer = require('autoprefixer'); // Add the prefix automatically

/ * * * compile sass files for WXSS * refrence: https://juejin.cn/post/6844903778496282632 * /
function compileStyle() {
    /** * The steps are as follows: * Specify the file processing directory * gulp-replace matches through regular matches@import* Enable gulp-sass to compile SCSS files, * use postCSS to work with compatible styles for earlier versions of ios and Android, * gulp-rename to change the file suffix to. WXSS * gulp-replace to match through regex@importStatements open comments * and finally enter into the dist directory */
    return src(['miniprogram/**/*.scss'])
      .pipe(replace(/\@(import\s[^@;] *) + (; import|\bimport|; |\b)? /g.($1) = > {
        // Unlike import, sass packs @improt's contents into the current file, so the package will be larger
        If the package size is too large due to @import, you need to comment out @import(use the built-in import) and open it again after sass is finished compiling
        // Given that the size of the style file for a small program should not exceed 2M per package, and that commenting @import would disable variable declarations and @mixin functionality in sass, use @import instead
        return $1 // `\/*T${$1}T*\/`
      }))
      .pipe(styleAliases({
        "@miniprogram": 'miniprogram'."@page": 'miniprogram/page'."@styles": 'miniprogram/styles'
      }))
      .pipe(scss())
      .pipe(postcss([autoprefixer(['iOS >= 8'.'Android > = 4.1']), pxtorpx()]))
      .pipe(
        rename(function(path) {
          path.extname = '.wxss'
      }))
      .pipe(changed('dist'))
      .pipe(replace(/.scss/g.'.wxss'))
      .pipe(replace(/\/\*T(@import\s[^@;] *;) ? (T\*\/)/g.'$1'))
      .pipe(dest('dist'))}Copy the code

Setting environment variables

  1. Define ts files for different environments in miniProgram /config

  2. Use cross-env to inject environment variables

// package.json{..."scripts": {..."dev": "cross-env NODE_ENV=development gulp watch"."build:test": "cross-env NODE_ENV=test gulp"."build:prod": "cross-env NODE_ENV=production gulp". },... }Copy the code
  1. The environment variable process.env.node_env is obtained from gulpfile

  2. Generate env.ts from process.env.node_env (copied from miniprogram/config)

  3. Use the following:

import ENV from 'env';
console.log('Current environment variable', ENV)
Copy the code

Hot update support

During development (with YARN dev), the page is reloaded when the code changes.

Since we have set the miniprogramRoot of project.config.json to dist, whenever the files in dist change, the wechat developer tool will rebuild the loading code. Therefore, what we need to do is to use the gulp Watch function to listen to whether the files in miniProgram have changed, and update dist as long as the files have changed, so as to achieve the effect of hot update. The concrete code implementation can see the gulpFile configuration in the template.

yarn dev // Run the yarn dev command to enable the Glup watch function
Copy the code

Encapsulation wx. Request

Use Promise to wrap wX.request and create an httpManager singleton. See the httpManager in the template for the code.

Use the esLint + prettier + Husky specification code format

The esLint, prettire, and Husky templates are integrated into the vsCode template. The configuration in vsCode can be used to format the project code, which is partially implemented in this article.

Introducing the weUI library

The selection

Wechat itself provides a rich set of components, as well as some third-party UI libraries:

  • WeUI(Official style library)
    • Small program UI library designed by the official wechat team
    • Can be introduced using useExtendedLib and does not take up the small package size
  • iView Weapp
  • vantUI
  • Omim/Omiu – UI component provided by Tencent cross-framework OMI

WeUI is supported using useExtendedLib, which does not take up the size of small packages

configuration

  1. Configure useExtendedLib in app.json
// app.json{..."useExtendedLib": {
    "weui": true}... }Copy the code
  1. Add the components to be used in the usingComponents configuration field of the corresponding JSON file on the page
// ***.json
{
  "usingComponents": {
    "mp-dialog": "weui-miniprogram/dialog/dialog"}}Copy the code
  1. The component is used directly in the WXML of the corresponding page
<mp-dialog title="test" show="{{true}}" bindbuttontap="tapDialogButton" buttons="{{[{text: 'cancel '}, {text:' confirm '}]}}">
    <view>test content</view>
</mp-dialog>
Copy the code

GlobalData based state management solution

State management scheme selection

Why is state management needed?

  1. When a component needs to dispatch an event multiple times. For example, add or subtract the number of shopping carts.
  2. Sharing data across components, sharing data across pages. For example, order status updates.
  3. Data that requires persistence. For example, the user information after login.
  4. When you need to develop medium to large applications that are suitable for complex multi-module, multi-page data interactions, consider how best to manage state outside of components.

Wechat small program official does not provide state management scheme, which is also a big pain point of small program native development.

  • Custom globalData: it is equivalent to setting a global variable and updating its function, which is relatively simple, but difficult to manage when the volume of business increases.
  • Behaviors manage global state: The ability to refactor components using behaviors in small program custom components is a bit cumbersome without looking closely at it.
  • Westore: Tencent produced micro channel small program solution.
  • Omix: Also Tencent’s native miniprogram framework, an evolution of WeStore, supports typescript (Omix-TS), but ts support is not good
  • Use reduxRedux, as a well-known and widely used state management solution in react projects, has no problems dealing with complex business requirements and is friendly to front-end development. Available online solutions include:
    • minapp-redux / mp-redux: Minapp-redux/mp-redux SRC /index to use redux connect, connectComponent, use methods, but do not support ts, you need to define the interface of these methods.
    • Wxa/Redux: Webank produced, open source a small program development framework WXA, documentation is good, but to use the wXA complete solution.

The native framework has no separate optimizations for setdata. You need to manually write optimizations to control setdata. Using state management libraries (minapp-Redux, Omix, etc.) diff calculations before calling setdata to improve performance.

We originally intended to use redux for state management, but when we introduced redux, we found that the support for TS is not very good in applets. If you want to use redux, you need to write your own interface or you will lose type checking. Besides, applets don’t persist and share much data. The decision was made to use globalData for development.

GlobalData configuration

  1. Use the getApp ()orUse this inside the App() functionObtain the globally unique App instance of the applet.

Note:

  • Do not call getApp() inside a function defined in App() or before calling App(). Use this to get the App instance.
  • After obtaining an instance through getApp(), do not use the instance to invoke lifecycle functions privately.
  1. To set global properties, get the globalData properties of the App instance directly and initialize globalData in the App() function
// app.ts
App<IMiniAppOption>({
    globalData: {
      test: '123'
    },
    onLaunch() {
      this.globalData.test = '456'}})// pages/**/**.ts
const app = getApp()
Page({
    data: {},onLoad() {
	app.globalData.test = '789'}})Copy the code

To manage global properties for the entire project, we define a singleton, StoreManager, that assumes that all globalData operations are performed through the StoreManager.

// storeManager.ts
import { IMiniApp } from 'app'

export interface IGlobalData {
    test: string
}

export function initGlobalData() {
    return {
        test: ' ',}}export default class StoreManager {
    // StoreManager instance
    private static _storeManager: StoreManager

    private _app: IMiniApp

    constructor(app? : IMiniApp) {
        this._app = app || getApp()
        if (!this._app) {
            throw 'StoreManager Error: Failed to get app instance '
        }
    }

    public staticgetInstance(app? : IMiniApp): StoreManager {if (!this._storeManager) {
            this._storeManager = new StoreManager(app)
        }

        return this._storeManager
    }

    public setTest(test) {
        this._app.globalData.test = test
    }

    public getLogs() {
        return this._app.globalData.test
    }
}

// app.ts
import StoreManager, { IGlobalData, initGlobalData } from 'store/storeManager'
interface IMiniAppOption {
    globalData: IGlobalData userInfoReadyCallback? : WechatMiniprogram.GetUserInfoSuccessCallback }export type IMiniApp = WechatMiniprogram.App.Instance<IMiniAppOption>

App<IMiniAppOption>({
    globalData: initGlobalData(),
    onLaunch() {
        StoreManager.getInstance(this).initLogs()
    }
})
Copy the code

Small program template use notes

The routing configuration

The pages field in the app.json file can be used to configure the route of the project directly. The wX. navigateTo can be used to redirect the route.

wx.navigateTo({
    url: '.. /logs/logs',
})
或者
wx.navigateTo({
    url: '/pages/logs/logs',})Copy the code

Introducing third-party libraries

There are two ways to introduce third-party dependencies:

  1. Copy the dependent output file directly

  2. Use the NPM build tool

Copy the dependent output file directly

For example, the ali-oss file can be github.com/ali-sdk/ali… Copy dist/ aliyun-oss-sdk-min.js from git repository to project lib folder

Build the NPM

Small programs support using NPM, but you need to use the build NPM function of wechat developer tools

When we need to import the NPM package, we need to do the following

  1. Add the dependency in the miniProgram directory
cd miniprogram
yarn add ***
Copy the code
  1. After adding the dependency, there will be node_modules folder in the miniprogram, which will be put through the wechat developer toolnode_modulesTo build aminiprogram_npm

Wechat Developer Tools menu -> Tools -> Build NPM

  1. After the buildminiprogram_npmThe corresponding NPM package is generated in the directory and can be referenced directly in the project
import{... }from 'miniprogram_npm/***'
Copy the code

Small programs support using NPM, but you need to use the build NPM function of wechat developer tools

When we need to import the NPM package, we need to do the following

  1. Modify project. Config. Json.

    A. Set setting. PackNpmManually to true for project.config.json and enable the customized configuration of node_modules and miniprogram_npm paths.

    B. configure project. Config. The json setting. The packNpmRelationList, packageJsonPath and miniprogramNpmDistDir specified path.

"packNpmManually": true."packNpmRelationList": [{
  "packageJsonPath": "miniprogram/package.json"."miniprogramNpmDistDir": "miniprogram"}].Copy the code
  1. Add the dependency in the miniProgram directory
cd miniprogram
yarn add ***
Copy the code
  1. After adding dependencies, there will be node_modules folder in the miniprogram. Build node_modules into miniProgram_npm through wechat developer tools. Wechat Developer Tools menu -> Tools -> Build NPM

  2. After the build, the corresponding NPM package is generated in the miniprogram_npm directory, which can be directly referenced in the project

import{... }from '@npm/***'
/ / or
import{... }from 'miniprogram_npm/***'
Copy the code

There are limits to the size of small programs

  • When using subcontract loading, the subcontract size of the whole small program is not more than 20M
  • The size of a single subcontract or main package cannot exceed 2M

Therefore, in order to avoid the small program size is too large, it is recommended that the images are put in CDN, and some third-party dependencies can be processed on the back end (such as Ali-OSS to obtain the signature URL can make the back end provide API, so that the small program does not need to introduce Ali-OSS dependencies).

WeChat pay

Wechat mini program officially provides the corresponding [payment scheme]. (developers.weixin.qq.com/miniprogram…).

Development and debugging

Can be directly provided in the official wechat developer tools for development, preview, debugging.

reference

  • Mini program official website
  • Small program development guide
  • Arm your applets – a guide to the development process
  • Use the ESLint+Prettier specification for React+Typescript projects