When developing applets, we always expect to use the technical specifications and grammatical features of the past to write the current applet, so there will be a variety of applet frameworks, such as mpvue, taro and other compiled frameworks.Of course, these frameworks themselves are helpful for newly developed projects.And what about old projects that we want to maintain using the grammatical features of vue?
Here I'm going to look at youzan's vant-weapp.It was found that the components in the project were written this way.
import { VantComponent } from '../common/component'; VantComponent({ mixins: [], props: { name: String, size: String }, // You can use watch es to monitor props changes // It's actually extracting observer s from properties watch: { name(newVal) { ... }, // Strings can be used directly instead of function calls size: 'changeSize' }, // Get data using computed attributes, which can be used directly in wxml computed: { bigSize() { return this.data.size + 100 } }, data: { size: 0 }, methods: { onClick() { this.$emit('click'); }, changeSize(size) { // Use set this.set(size) } }, // Corresponding applet component created cycle beforeCreate() {}, // Corresponding applet component attached cycle created() {}, // Corresponding applet component read cycle mounted() {}, // Corresponding applet component detached cycle destroyed: {} });
I found that the component is written in a way that resembles the Vue syntax as a whole.It does not compile itself.It seems the problem lies in the method of importing VantComponet.Let's start by detailing how to use VantComponet to maintain older projects.
TLDR (don't talk a lot, just conclude first)
The widget components are not described here.Here we give the code style for writing Page s using VantComponent.
import { VantComponent } from '../common/component'; VantComponent({ mixins: [], props: { a: String, b: Number }, // Watch is basically useless here on the page, because only watch changes props, page does not change props // The reason is detailed later watch: {}, // Calculated properties are still available computed: { d() { return c++ } }, methods: { onLoad() {} }, created() {}, // Other component life cycle })
Here you may be wondering, is VantComponet not effective for Component?How does this work for Page pages?In fact, we can use components to construct applet pages.
In the official documents, we can see that Using Component Constructor to Construct Pages
In fact, the page of the applet can also be viewed as a custom component.As a result, pages can also be constructed using Component constructors with the same definition segments and instance methods as common components.The code is as follows:
Component({ // You can use the component's behaviors mechanism, although React does not think mixins are a good solution // However, to some extent, the solution does reuse the same logic code behaviors: [myBehavior], // Options corresponding to the page, which is typed by itself, and data obtained from options is of type string // Access page/pages/index/index?ParamA=123¶mB=xyz // If properties paramA or paramB are declared, they are assigned 123 or xyz instead of string type properties: { paramA: Number, paramB: String, }, methods: { // onLoad does not require option // However, page-level life cycles can only be written in methods onLoad() { this.data.paramA // Value 123 for page parameter paramA this.data.paramB // Value'xyz'of page parameter paramB } } })
So how does the life cycle of a component correspond to the life cycle of a page?After some tests, the result is: (For ease of use).Only important life cycles will be listed)
// Component Instance is created into Component Instance Entry Page Node Tree component created -> component attched -> // Page Page Loading to Components Completed in View Layer Layout page onLoad -> component ready -> // Page unload to component instance removed from page node tree page OnUnload -> component detached
Of course, we don't focus on the intermediate state between onload and onunload, because when the intermediate state is in place, we can use the page life cycle to do a better job on the page.
Sometimes some of our initialization code shouldn't be in onload, so we can consider putting it in component creation and even reusing it with behaviors.
In a way, if you don't need the Vue style, it's also a good maintenance solution for us to directly use Component instead of Page in our old projects.After all, there is no need to worry about a series of other follow-up issues by official standards.
VantComponent Source Parsing
VantComponent
At this point, we start parsing the VantComponent
// Assignment, operated on by map's key and value function mapKeys(source: object, target: object, map: object) { Object.keys(map).forEach(key => { if (source[key]) { // The map[key] of the target object corresponds to the key of the source data object target[map[key]] = source[key]; } }); } // ts code, also known as generics function VantComponent<Data, Props, Watch, Methods, Computed>( vantOptions: VantComponentOptions< Data, Props, Watch, Methods, Computed, CombinedComponentInstance<Data, Props, Watch, Methods, Computed> > = {} ): void { const options: any = {}; // Use function to copy new data, which is the Vue style we can use mapKeys(vantOptions, options, { data: 'data', props: 'properties', mixins: 'behaviors', methods: 'methods', beforeCreate: 'created', created: 'attached', mounted: 'ready', relations: 'relations', destroyed: 'detached', classes: 'externalClasses' }); // Edit relationships between components, but page s do not need to, and can be deleted const { relation } = vantOptions; if (relation) { options.relations = Object.assign(options.relations || {}, { [`../${relation.name}/index`]: relation }); } // externalClasses are added by default to components, but page s are not needed and can be deleted // add default externalClasses options.externalClasses = options.externalClasses || []; options.externalClasses.push('custom-class'); // Add basic by default to components, encapsulate $emit and applet node query methods, and can be deleted // add default behaviors options.behaviors = options.behaviors || []; options.behaviors.push(basic); // map field to form-field behavior // Add built-in behavior wx://form-field by default // It makes this custom component behave like a form control. // You can explore the built-in behaviors given below if (vantOptions.field) { options.behaviors.push('wx://form-field'); } // add default options // Add component default configuration, multiple slot s options.options = { multipleSlots: true,// Enable multiple slot support in options when components are defined // If this Component constructor is used to construct pages, the default value is shared // apply-shared of a component to study component style isolation given below addGlobalClass: true }; // Monitor vantOptions observe(vantOptions, options); // Put the currently reconfigured options into Component Component(options); }
Built-in behaviors
Component style isolation
basic behaviors
We just talked about basic behaviors, and the code looks like this
export const basic = Behavior({ methods: { // Calling the $emit component actually uses triggerEvent $emit() { this.triggerEvent.apply(this, arguments); }, // Encapsulator Node Query getRect(selector: string, all: boolean) { return new Promise(resolve => { wx.createSelectorQuery() .in(this)[all ? 'selectAll' : 'select'](selector) .boundingClientRect(rect => { if (all && Array.isArray(rect) && rect.length) { resolve(rect); } if (!all && rect) { resolve(rect); } }) .exec(); }); } } });
observe
Code parsing for applets watch and computed
export function observe(vantOptions, options) { // Get watch computed from the incoming option const { watch, computed } = vantOptions; // Add behavior options.behaviors.push(behavior); ///If there is a watch object if (watch) { const props = options.properties || {}; // For example: // props: { // a: String // }, // watch: { // a(val) { // //Print every time the val ue changes // consol.log(val) // } } Object.keys(watch).forEach(key => { // watch only monitors data in prop if (key in props) { let prop = props[key]; if (prop === null || !('type' in prop)) { prop = { type: prop }; } // The observer for prop is assigned by the watch, which is the function of the applet component itself. prop.observer = watch[key]; // Put the current key in prop props[key] = prop; } }); // By this method // props: { // a: { // type: String, // observer: (val) { // console.log(val) // } // } // } options.properties = props; } // Encapsulate calculated properties if (computed) { options.methods = options.methods || {}; options.methods.$options = () => vantOptions; if (options.properties) { // Monitor props, and if props change, the calculated property itself changes observeProps(options.properties); } } }
observeProps
Now there are two files left, observeProps and behavior, which were generated to compute attributes. Let's first explain the observeProps code
export function observeProps(props) { if (!props) { return; } Object.keys(props).forEach(key => { let prop = props[key]; if (prop === null || !('type' in prop)) { prop = { type: prop }; } // Save the previous observer, the prop generated by the previous code let { observer } = prop; prop.observer = function() { if (observer) { if (typeof observer === 'string') { observer = this[observer]; } // Saved observer before calling observer.apply(this, arguments); } // Call set once to reset calculation properties when a change occurs this.set(); }; // Assign modified props back props[key] = prop; }); }
behavior
The final behavior is also the computed implementation mechanism
// Asynchronous call to setData function setAsync(context: Weapp.Component, data: object) { return new Promise(resolve => { context.setData(data, resolve); }); }; export const behavior = Behavior({ created() { if (!this.$options) { return; } // cache const cache = {}; const { computed } = this.$options(); const keys = Object.keys(computed); this.calcComputed = () => { // Data that needs to be updated const needUpdate = {}; keys.forEach(key => { const value = computed[key].call(this); // Cached data does not equal the current calculated value if (cache[key] !== value) { cache[key] = needUpdate[key] = value; } }); // Return computed for required updates return needUpdate; }; }, attached() { // Call once in the attached cycle to calculate the current computed value this.set(); }, methods: { // set data and set computed data // set can use callback and then set(data: object, callback: Function) { const stack = []; // Place data when set if (data) { stack.push(setAsync(this, data)); } if (this.calcComputed) { // There are calculation properties and they are also placed in the stack, but each set is called once, and props changes are also called stack.push(setAsync(this, this.calcComputed())); } return Promise.all(stack).then(res => { // callback is called when all data and calculated properties are complete if (callback && typeof callback === 'function') { callback.call(this); } return res; }); } } });
Write after
- js is a flexible language (manual comic)
- After the applet Page, the Component itself is more mature and useful than the Page itself. Sometimes new solutions are often hidden in the document. It is never meaningless to read the document several times at a time.
- Applet version 2.6.1 Component now implements observers to listen on props data Data Monitor VantComponent is not currently implemented. Of course, page does not need to listen on prop per se, because entering pages does not change at all, while data changes do not need to listen on themselves and functions can be called directly, so observers may or may not be available to pages.
- The scheme also has a vue style on the js code, and no other articles have been written on template s or styles.
- The performance of this scheme must be missing because computed is calculated every time a set is set, not based on the set's data, and after deletion I think it is acceptable.If you don't need the grammatical features of vue, you can write a Page directly using Component. Choosing a different solution is essentially a trade-off between the pros and cons.If there are other requirements or new projects in itself, it is still recommended to use the new technology, if it is an existing project and needs to be maintained, and also want to have the Vue feature.You can use this scenario because the code itself is small and can be modified to suit your needs.
- At the same time, vant-weapp is a very good project, I recommend you to check it out and star t it.