This framework is Tencent’s internal development framework based on small programs. The design idea basically refers to VUE, and more than 80% of the development mode and coding style is similar to VUE

advantage

Componentized development

Although the small program has tags to realize component reuse, but only at template fragment level reuse, business code and interaction events still need to be processed in the page. Componentized loose coupling and reuse cannot be achieved. Wepy component example

// index.wpy <template> <view> <panel> <h1 slot="title"></h1> </panel> <counter1 :num="myNum"></counter1> <counter2 :num.sync="syncNum"></counter2> <list :item="items"></list> </view> </template> <script> import wepy from 'wepy'; import List from '.. /components/list'; import Panel from '.. /components/panel'; import Counter from '.. /components/counter'; export default class Index extends wepy.page { config = { "navigationBarTitleText": "test" }; components = { panel: Panel, counter1: Counter, counter2: Counter, list: List }; data = { myNum: 50, syncNum: 100, items: [1, 2, 3, 4] } } </script>Copy the code

Support for loading external NPM packages

The biggest drawback of the applet is that it does not support NPM packages, which makes it difficult to directly use a lot of excellent open source content. During compilation, Wepy will recursively traverse the require in the code and copy the corresponding dependency files from node_modules, and modify require to the relative path. This enables support for external NPM packages

The single-file mode makes the directory structure clearer

Json, app.js, and app.wxss. The page has four files: index.json, index.js, index.wxml, and index.wxss. And the text must have the same name. So the comparison of development directories before and after using Wepy is as follows:

Project ├ ─ ─ pages | ├ ─ ─ index | | ├ ─ ─ index. The json configuration index page | | ├ ─ ─ index. The js index page logic | | ├ ─ ─ but WXML | index page structure | └ ─ ─ index. WXSS index page stylesheet | └ ─ ─ the log | ├ ─ ─ the json log configuration page | ├ ─ ─ the WXML log page logic | ├ ─ ─ the js log page structure | └ ─ ─ ├─ ├─ app.json └─ app.wxss Public Style SheetCopy the code

Directory structure with the Wepy framework:

Project └ ─ ─ the SRC ├ ─ ─ pages | ├ ─ ─ index. The wpy index page configuration, structure, style, logic | └ ─ ─ the wpy log page configuration, structure, style, logic └ ─ ─ app. Wpy Applets configuration items (global style configuration, declaration hooks, and so on)Copy the code

How to develop

A quick start

  • NPM install wepy-cli -g applets framework wepy command line tool
  • Create the project wepy New MyProject
  • Switch to the project directory CD myProject
  • Build wepy build-Watch in real time

├─ Dist wechat developer tools specified directory

├ ─ ─ node_modules ├ ─ ─ the SRC code directory | ├ ─ ─ components components folder (not full page) | | ├ ─ ─ com_a. Wpy reusable components a | | └ ─ ─ com_b. Wpy reusable components b | ├ ─ ─ Pages page folder (full page) | | ├ ─ ─ index. The index wpy page | | └ ─ ─ page. The wpy page page | └ ─ ─ app. Wpy small program configuration items (global style configuration, statement hooks, etc.) └ ─ ─ Package. The json package configurationCopy the code

The main differences between Wepy and VUE

1. Both support props, Data, computed, Components, methods, and Watch (Watcher in Wepy). However, methods in WEpy can only be used for page event binding. All methods in VUE are placed under methods

2. In wepy, the.sync modifier (similar to vu1.x) should be added to enable the props to be dynamically updated. After the parent component is passed to the child component, this

3. Wepy supports data bidirectional binding. When defining props, add twoway: True to enable the child component to modify the parent component data

4.VUE2. X recommends using eventBus for component communication, while wepy uses eventBusEmit, $invoke three methods to implement communication

· First, the event listener needs to be written under the events property: ' 'bash import wepy from 'wepy'; export default class Com extends wepy.component { components = {}; data = {}; methods = {}; events = { 'some-event': (p1, p2, p3, $event) => { console.log(`${this.name} receive ${$event.name} from ${$event.source.name}`); }}; $emit: child triggers parent component events. $emit: Child triggers parent component events. $invoke: child triggers child component eventsCopy the code

5. The life cycle of VUE includes Created and Mounted, while wepy only supports the life cycle of onLoad and onReady

6. Wepy does not support VUE features such as filters, keep-alive, ref, transition, global plug-ins, route management, server rendering, etc

Introduce the advanced

Wpy File description

A.wpy file can be divided into three parts, each corresponding to a tag:

  • The script part, the contents of the tag, can be divided into two parts: the logical part, the part other than the Config object, corresponding to the native.js file; The configuration part, the Config object, corresponds to the native.json file.
  • The structural part, the template part, corresponds to the native.wXML file.
  • The style section, the style section, corresponds to the native.wXSS file. The script, template, and style tags in the.wpy file all support lang and SRC attributes. Lang determines the compilation process of the code, SRC determines whether to outline the code, and if the SRC attribute exists and is valid, the inlining code is ignored.
The label Lang, a default value Lang support value
style css CSS, LESS, SASS, stylus
template wxml WXML, XML, puG (formerly Jade)
script babel Babel, TypeScript

Plain component reference

When a page needs to import a component or a component needs to import a child component, it must be in the.wpy file

<template> <! -- Name the custom tag after the component ID declared in the '<script>' script section, To insert the component into the '<template>' template section --> <child></child> </template> <script> import wepy from 'wepy'; Import Child from '.. /components/child'; Export default class Index extends wepy.ponent {// Declare a component and assign the component ID to Child Components = {child: child}; } </script>Copy the code

It is important to note that WePY components are in the static components, is a component ID as a unique identifier, each ID corresponding to a component instance, when the page is introduced into two components of the same ID, these two components share the same instance with the data, when one of the component data changes, another will change together. If you want to avoid this problem, you need to assign multiple component ids and instances.

Loop rendering of components

When WePY components need to be rendered in a loop (similar to rendering native WXML tags through the WX :for loop), auxiliary tags defined by WePY must be used

<template> <! <repeat for="{{list}}" key="index" index="index" item="item"> <! -- Insert the child component declared in the <script> script section and pass item --> <child :item="item"></child> </repeat> </template>Copy the code

Computed properties

Computed property is a function that returns a value and can be used directly as binding data. Therefore, similar to the data property, the code can be referred to by the this. calculation property name, and the template can be bound to the data by {{calculation property name}}. Note that all calculated properties are recalculated whenever any data in the component is changed.

Data = {a: 1} // Calculate the attribute aPlus, which can be referenced by this.aPlus in scripts, and interpolated by {{aPlus}} in templates, computed = {aPlus () {return this.a + 1}}Copy the code

Watcher listener

The watcher listener allows you to listen for numerical updates to any numeric attribute. The listener is declared in the Watch object and its type is function. The function name is the same as the value attribute in the data object to be monitored. Whenever the value attribute being monitored changes once, the listener function will be automatically called and executed once. Listeners are suitable for situations where some additional processing is required when numeric attributes change.

data = { num: 1} // The name of the listener function must be the same as that of the num attribute in the data object to be listened on. Watch = {num (newValue, oldValue) {console.log(' num value: ${oldValue} -> ${newValue} ')}} The corresponding listener function num() is automatically called once onLoad () {setInterval(() => {this.num++; this.$apply(); }}, 1000)Copy the code

Props by value

  • Static value passing Static value passing constant data from a parent component to a child component can only be a String. In the component tag of the parent component’s Template template section, it receives the values passed by the parent component using the property name declared in the child component props object as its property name.
<child title="mytitle"></child>

// child.wpy
props = {
    title: String
};

onLoad () {
    console.log(this.title); // mytitle
}Copy the code
  • Dynamic value transfer Dynamic value transfer refers to the transfer of dynamic data content from the parent component to the child component, and the data of the parent component is completely independent of each other. The parent component data can be bound to the child component by using the.sync modifier, and the child component data can be bound to the parent component by setting the twoWay: true of the child component props. This is done by using the.sync modifier and adding twoWay: true in the props subcomponent. Note: When twoWay is true in the example below, it means that the child sends values dynamically to the parent in one direction, whereas when twoWay is false(the default, no write), it means that the child does not send values to the parent. This is an inconsistency with Vue, and the reason for using twoWay here is simply to be as consistent as possible with Vue in naming identifiers. Use the :prop property (equivalent to the V-bind: Prop property in Vue) to dynamically pass values in the child component tag inserted in the parent component template section.
// parent.wpy <child :title="parentTitle" :syncTitle.sync="parentTitle" :twoWayTitle="parentTitle"></child> data = { parentTitle: 'p-title' }; // child-wpy props = {// Props: String, // syncTitle: {type: String, default: 'null'}, twoWayTitle: { type: Number, default: 50, twoWay: true } }; onLoad () { console.log(this.title); // p-title console.log(this.syncTitle); // p-title console.log(this.twoWayTitle); // 50 this.title = 'c-title'; console.log(this.$parent.parentTitle); // p-title. this.twoWayTitle = 60; this.$apply(); console.log(this.$parent.parentTitle); $parent. ParentTitle = 'p-title-changed'; this.$parent. ParentTitle = 'p-title-changed'; this.$parent.$apply(); console.log(this.title); // 'p-title'; console.log(this.syncTitle); // 'p-title-changed' -- has the props property value of the.sync modifier. When changing in the parent component, it also changes the corresponding value of the child component. }Copy the code

Component communication and interaction

Event handlers that listen for communication and interaction events between components need to be written in the events objects of the component and the page

Broadcast events are initiated by the parent component, and all child components receive the broadcast event unless the event is manually cancelled. Events are broadcast in breadth-first search order

Emit andEmit events.

Invoke is a direct call by a page or component to a method in another component, finding the corresponding component by passing in the component path, and then invoking its method.

For example, you want to call a method of the component ComA in page Page_Index:

this.$invoke('ComA', 'someMethod', 'someArgs');Copy the code

If you want to call a method of the component ComG from component ComA:

this.$invoke('./.. /ComB/ComG', 'someMethod', 'someArgs');Copy the code

Component custom event handlers

You can bind custom component events by using the. User modifier, such as @customEvent. User = “myFn”, where @ represents the event modifier, customEvent represents the event name, and.user represents the event suffix. There are currently three event suffixes:

. Default: bind small program bubbling events, such as bindtap, suffix. Default can be omitted; .stop: bind applets to capture events such as catchtap; .user: Binds user-defined component events that are emitted via $emit.Copy the code
// index.wpy <template> <child @childFn.user="parentFn"></child> </template> <script> import wepy from 'wepy' import Child from '.. /components/child' export default class Index extends wepy.page { components = { child: Child } methods = { parentFn (num, evt) { console.log('parent received emit event, number is: ' + num) } } } </script> // child.wpy <template> <view @tap="tap">Click me</view> </template> <script> import wepy from 'wepy' export default class Child extends wepy.component { methods = { tap () { console.log('child is clicked') this.$emit('childFn', 100) } } } </script>Copy the code

Slot Component content distribution slot

Slot slots in WePY serve as space placeholders for content distribution labels. By inserting and removing content distribution labels equivalent to expansion board cards in the parent component, content distribution for child components is more flexible and convenient. The slot tag is declared as a content slot in the template section of the child component. The slot name must be specified in its name attribute, and the default tag content can be set. The content distribution label for “plug and unplug” is then declared in the parent component template template section that introduces the child component with slots. Note that the content distribution label in these parent components must have a slot attribute and its value must be the name of the corresponding slot in the child component, so that the content in the parent component content distribution label overrides the default content in the corresponding slot in the child component.

In the Panel component there are the following templates: <view class="panel"> <slot name="title"> default title </slot> <slot name="content"> Default content </slot> </view> When using a Pannel child in a parent component, you can use: <panel> <view slot="title"> new title </view> <view slot="content"> <text> New content </text> </view> </panel>Copy the code

hybrid

  • Default blending is used for component data, components, events events, and other custom methods. That is, if the component does not declare the data, component, event, custom method, etc., then the options from the blending object are injected into the component. Options already declared for components are not affected.
// mixins/test.js
import wepy from 'wepy';

export default class TestMixin extends wepy.mixin {
    data = {
        foo: 'foo defined by page',
        bar: 'bar defined by testMix'
    };
    methods: {
    tap () {
      console.log('mix tap');
    }
  }
}

// pages/index.wpy
import wepy from 'wepy';
import TestMixin from './mixins/test';

export default class Index extends wepy.page {
    data = {
        foo: 'foo defined by index'
    };
    mixins = [TestMixin ];
    onShow() {
        console.log(this.foo); // foo defined by index.
        console.log(this.bar); // foo defined by testMix.
    }
}Copy the code
  • Compatible mixing For component methods response events and applet page events, compatible mixing is adopted, that is, responding to the response events of the component itself first, and then responding to the response events in the mixed object.
// mixins/test.js import wepy from 'wepy'; export default class TestMixin extends wepy.mixin { methods = { tap () { console.log('mix tap'); }}; onShow() { console.log('mix onshow'); } } // pages/index.wpy import wepy from 'wepy'; import TestMixin from './mixins/test'; export default class Index extends wepy.page { mixins = [TestMixin]; methods = { tap () { console.log('index tap'); }}; onShow() { console.log('index onshow'); } } // index onshow // mix onshow // ----- when tap // index tap // mix tapCopy the code

The interceptor

You can use the config, fail, Success, and complete methods of the global interceptor configuration API as examples:

import wepy from 'wepy'; export default class extends wepy.app { constructor () { this.intercept('request', { config (p) { p.timestamp = +new Date(); return p; }, success (p) { console.log('request success'); return p; }, fail (p) { console.log('request error'); return p; }}); }}Copy the code

WePY data binding

WePY uses the dirty data check to encapsulate setData, and performs the dirty data check at the end of the function running cycle. On the one hand, it does not need to worry about whether setData will have performance problems on the page multiple times. On the other hand, it can be more concise to modify the data to achieve binding, and it does not need to write the setData method repeatedly.

this.title = 'this is title';Copy the code

To modify data in a function outside the function’s run period, you need to manually call the $apply method

setTimeout(() => {
    this.title = 'this is title';
    this.$apply();
}, 3000);Copy the code

Optimize event parameter passing

<view data-id="{{index}}" data-title="wepy" data-other=" otherParams "bindtap="tapName"> Click me! </view> Page({ tapName: function(event) { console.log(event.currentTarget.dataset.id)// output: 1 console.log(event.currentTarget.dataset.title)// output: wepy console.log(event.currentTarget.dataset.other)// output: otherparams } }); WePY later than 1.1.8 only allows string passing. <view bindtap="tapName({{index}}, 'wepy', 'otherparams')"> Click me! </view> methods: { tapName (id, title, other, event) { console.log(id, title, other)// output: 1, wepy, otherparams } }Copy the code

Change the data binding mode

SetData (target, value) this.setData(object)

OnLoad: function () {this.setData({message: 'hello world'}); } // WePY <view> {{ message }} </view> onLoad () { this.message = 'hello world'; }Copy the code

Important reminder

  • Use wechat developer tools – > Add project, please select Dist directory for project directory.
  • Wechat Developer Tools – > Project – > Close ES6 to ES5. Important: Missing this item will result in an error.
  • Wechat developer tools – > Project – > turn off automatic style completion when uploading code. Important: Missing this item may cause an error in some cases.
  • Wechat developer tools – > Project – > close code compression upload. Major: If this function is enabled, attributes such as computed and props. Sync on the real machine become invalid. (Note: The compression function can be replaced by the build instructions provided by WePY, as described later and the wepy.config.js and package.json files in the Demo project root directory.)
  • Run wepy build-watch in your local project root to enable real-time compilation. (Note: If at the same time in wechat developer tools – > Settings – > editor check automatic compilation of file save small program, it will be a real-time preview, very convenient.)

Pay attention to

  • The methods attribute in WePY can only declare bind and catch events of a page’s WXML tag, not custom methods

Refer to the article

Write micro channel applets like VUE – In-depth study of wePY framework resources summary micro channel applets componentized development framework – WepyJS micro channel applets componentized development framework Wepy official document VUE official document