It has been more than half a year since the micro program was released. With the powerful influence of the micro channel platform, more and more enterprises have joined the development of small programs. Small programs have the following advantages over M pages:
1, small programs have more capabilities, including positioning, recording, files, media, all kinds of hardware capabilities, more imagination space
2. Run inside wechat, and experience is closer to APP
3. In the overly competitive Internet industry, the cost of acquiring an effective APP user is already very high. Compared with APP, small programs are more lightweight, ready-to-use and easier to acquire users
Development of contrast
From the perspective of development, small program official encapsulation of many commonly used components to bring a lot of convenience to development, but also bring a lot of inconvenience:
1, the small program redefine the DOM structure, no window, document, div, SPAN and so on, small program only view, text, image and other packaged components, page layout can only be realized by these basic components, for developers need a certain habit conversion cost
2. Direct DOM manipulation is not recommended for applets (DOM and some properties are only available from July 2017), and there is a high learning cost for developers unfamiliar with the MVVM pattern
3, small program no cookie, can only simulate the cookie operation through storage (including HTTP setCookie also need to process)
wepy
The team has recently developed multiple WeChat small programs, in order to make up for a lack of small programs of various VUE development habits and the continuation of developers, in the team development chose wepy framework, the framework is tencent internal development framework, based on the small program design basic reference VUE, development mode and coding, a more than 80% is close to the VUE, Developers can switch from VUE development to applets development at a fraction of the cost. The main advantages over applets are as follows:
Wepy is packaged again in the original small program development mode, which is closer to the existing MVVM framework development mode. The framework was developed with reference to some of the features of the current framework and incorporated into it. Below is a comparison of the code before and after using Wepy.
Official DEMO code:
/index.js
// Get the application instance
var app = getApp()
Page({
data: {
motto: 'Hello World',
userInfo: {}
},
// Event handlers
bindViewTap: function() {
console.log('button clicked')
},
onLoad: function () {
console.log('onLoad')
}
})
Copy the code
Implementation based on Wepy:
import wepy from 'wepy';
export default class Index extends wepy.page {
data = {
motto: 'Hello World',
userInfo: {}
};
methods = {
bindViewTap () {
console.log('button clicked');
}
};
onLoad() {
console.log('onLoad');
};
}
Copy the code
2. The real component development applet has tags that can realize component reuse, but only at the level of template fragment 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
The biggest drawback of applets is that they don’t support NPM packages, so they can’t 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 be a relative path to implement support for external NPM packages. The diagram below:
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. Therefore, the comparison of development directories before and after wepy development is as follows:
Official DEMO:
project
├ ─ ─ pages
| ├ ─ ─ the index
| | ├ ─ ─ index. Json configuration index page
| | ├ ─ ─ index. Js index page logic
| | ├ ─ ─ index. WXML index page structure
| | └ ─ ─ index. WXSS index page style sheets
| └ ─ ─ the log
| ├ ─ ─ the json log configuration page
| ├ ─ ─ the WXML log page logic
| ├ ─ ─ the js log page structure
| └ ─ ─ the WXSS log page style sheets
├─ App.js Small program logic
├─ App.json Applet Public Settings
└─ app.wxSS Public style sheet for small programs
Copy the code
Directory structure with the Wepy framework:
project
└ ─ ─ the SRC
├ ─ ─ pages
| ├ ─ ─ index. Wpy index page configuration, structure, style, logic
| └ ─ ─ the wpy log page configuration, structure, style, logic
├ ─ └─ app.wpy Small program config (Global style config, declare hooks, etc.)
Copy the code
5. Babel compilation is used by default and some new features of ES6/7 are supported.
6. Wepy supports less
Default enabled uses new features like Promise, async/await, etc
How to develop
A quick start
The installation
npm install wepy-cli -g
Copy the code
The scaffold
wepy new myproject
Copy the code
Switch to the project directory
cd myproject
Copy the code
Real-time compilation
wepy build --watch
Copy the code
The directory structure
├─ dist app Developer tools designated directory
├ ─ ─ node_modules
├─ SRC code for the directory
| ├ ─ ─ components components folder (not full page)
. | | ├ ─ ─ com_a wpy a reusable component
. | | └ ─ ─ com_b wpy reusable component b
| ├ ─ ─ pages page folder (full page)
| | ├ ─ ─ index. The wpy index page
| | └ ─ ─ page. Wpy page page
| └ ─ ─ app. Wpy small application configuration items (global style configuration, statement hooks, etc.)
└─ package.json package config
Copy the code
Wepy and VUE are very similar in their coding styles, and VUE developers can switch seamlessly, so here are the main differences:
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 $broadcast, $emit, and $invoke to communicate
· 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}`);
}
};
// Other properties
}
` ` `
· $broadcast: The parent component fires all child component events
· $emit: Child component fires parent component event
· $invoke: Child component fires child component events
Copy 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
Wepy principle research
Although wepy improves the experience of developing small programs, ultimately wepy needs to be compiled into the format that small programs need to run in, so the core of Wepy is code parsing and compilation.
Wepy project files mainly have two: wepy-cli: used to extract, analyze and compile. Wpy files into WXML, WXSS, JS and JSON formats required by small programs. Wepy: JS framework in compiled JS files
Wepy compilation process
Break down the process core code
// Wepy custom attributes are replaced by applet standard attributes
return content.replace(/<([\w-]+)\s*[\s\S]*? (\/|<\/[\w-]+)>/ig, (tag, tagName) => {
tagName = tagName.toLowerCase();
return tag.replace(/\s+:([\w-_]*)([\.\w]*)\s*=/ig, (attr, name, type) => { // replace :param.sync => v-bind:param.sync
if (type === '.once' || type === '.sync') {
}
else
type = '.once';
return ` v-bind:${name}${type}=`;
}).replace(/\s+\@([\w-_]*)([\.\w]*)\s*=/ig, (attr, name, type) => { // replace @change => v-on:change
const prefix = type ! == '.user' ? (type === '.stop' ? 'catch' : 'bind') : 'v-on:';
return ` ${prefix}${name}=`;
});
});
.
// Parse the wepy file as XML
xml = this.createParser().parseFromString(content);
const moduleId = util.genId(filepath);
// The extracted format
let rst = {
moduleId: moduleId,
style: [],
template: {
code: '',
src: '',
type: ''
},
script: {
code: '',
src: '',
type: ''
}
};
// Recycle the extraction process
[].slice.call(xml.childNodes || []).forEach((child) => {
const nodeName = child.nodeName;
if (nodeName === 'style' || nodeName === 'template' || nodeName === 'script') {
let rstTypeObj;
if (nodeName === 'style') {
rstTypeObj = {code: ''};
rst[nodeName].push(rstTypeObj);
} else {
rstTypeObj = rst[nodeName];
}
rstTypeObj.src = child.getAttribute('src');
rstTypeObj.type = child.getAttribute('lang') || child.getAttribute('type');
if (nodeName === 'style') {
// Add the scoped attribute for style
rstTypeObj.scoped = child.getAttribute('scoped') ? true : false;
}
if (rstTypeObj.src) {
rstTypeObj.src = path.resolve(opath.dir, rstTypeObj.src);
}
if (rstTypeObj.src && util.isFile(rstTypeObj.src)) {
const fileCode = util.readFile(rstTypeObj.src, 'utf-8');
if (fileCode === null) {
Throw 'failed to open file:' + rsttypeobj.src;
} else {
rstTypeObj.code += fileCode;
}
} else {
[].slice.call(child.childNodes || []).forEach((c) => {
rstTypeObj.code += util.decode(c.toString());
});
}
if (! rstTypeObj.src)
rstTypeObj.src = path.join(opath.dir, opath.name + opath.ext);
}
});
.
// Unpack the WXML extraction process
(() = > {
if (rst.template.type ! == 'wxml' && rst.template.type ! == 'xml') {
let compiler = loader.loadCompiler(rst.template.type);
if (compiler && compiler.sync) {
if (rst.template.type === 'pug') { // fix indent for pug, https://github.com/wepyjs/wepy/issues/211
let indent = util.getIndent(rst.template.code);
if (indent.firstLineIndent) {
rst.template.code = util.fixIndent(rst.template.code, indent.firstLineIndent * -1, indent.char);
}
}
// Call the WXML parsing module
let compilerConfig = config.compilers[rst.template.type];
// xmldom replaceNode have some issues when parsing pug minify html, so if it's not set, then default to un-minify html.
if (compilerConfig.pretty === undefined) {
compilerConfig.pretty = true;
}
rst.template.code = compiler.sync(rst.template.code, config.compilers[rst.template.type] || {});
rst.template.type = 'wxml';
}
}
if (rst.template.code)
rst.template.node = this.createParser().parseFromString(util.attrReplace(rst.template.code));
}) ();
// Process of extracting import resource files
(() = > {
let coms = {};
rst.script.code.replace(/import\s*([\w\-\_]*)\s*from\s*['"]([\w\-\_\.\/]*)['"]/ig, (match, com, path) => {
coms[com] = path;
});
let match = rst.script.code.match(/[\s\r\n]components\s*=[\s\r\n]*/);
match = match ? match[0] : undefined;
let components = match ? this.grabConfigFromScript(rst.script.code, rst.script.code.indexOf(match) + match.length) : false;
let vars = Object.keys(coms).map((com, i) => `var ${com} = "${coms[com]}"; `).join('\r\n');
try {
if (components) {
rst.template.components = new Function(`${vars}\r\nreturn ${components}`)();
} else {
rst.template.components = {};
}
} catch (e) {
Util. Output (' error ', path.join(opath. Dir, opath. Base));
Util. Error (${e}\r\n${vars}\r\nreturn ${components} ');
}
}) ();
.
Copy the code
Wepy has special script, style, template, and config parsing modules.
//compile-template.js
.
// Write the disassembled WXML structure to the file
getTemplate (content) {
content = `<template>${content}</template>`;
let doc = new DOMImplementation().createDocument();
let node = new DOMParser().parseFromString(content);
let template = [].slice.call(node.childNodes || []).filter((n) => n.nodeName === 'template');
[].slice.call(template[0].childNodes || []).forEach((n) => {
doc.appendChild(n);
});
.
return doc;
},
// Process to the WXML format required by wechat applet
compileXML (node, template, prefix, childNodes, comAppendAttribute = {}, propsMapping = {}) {
/ / handle slot
this.updateSlot(node, childNodes);
// Handle the data binding bind method
this.updateBind(node, prefix, {}, propsMapping);
/ / handle the className
if (node && node.documentElement) {
Object.keys(comAppendAttribute).forEach((key) => {
if (key === 'class') {
let classNames = node.documentElement.getAttribute('class').split(' ').concat(comAppendAttribute[key].split(' ')).join(' ');
node.documentElement.setAttribute('class', classNames);
} else {
node.documentElement.setAttribute(key, comAppendAttribute[key]);
}
});
}
// Handle the repeat tag
let repeats = util.elemToArray(node.getElementsByTagName('repeat'));
.
// Process components
let componentElements = util.elemToArray(node.getElementsByTagName('component'));
.
return node;
},
// The template file compiles the module
compile (wpy){
.
// Write the compiled content to the file
let plg = new loader.PluginHelper(config.plugins, {
type: 'wxml',
code: util.decode(node.toString()),
file: target,
output (p) {
util.output(p.action, p.file);
},
done (rst) {
// Write operation
Util. output(' write ', rst.file); util.output(' write ', rst.file);
rst.code = self.replaceBooleanAttr(rst.code);
util.writeFile(target, rst.code);
}
});
}
Copy the code
Compare files before and after compilation
Wepy files before compilation:
<scroll-view scroll-y="true" class="list-page" scroll-top="{{scrollTop}}" bindscrolltolower="loadMore">
<! -- Item List component -->
<view class="goods-list">
<GoodsList :goodsList.sync="goodsList" :clickItemHandler="clickHandler" :redirect="redirect" :pageUrl="pageUrl"></GoodsList>
</view>
</scroll-view>
Copy the code
Wepy compiled files:
<scroll-view scroll-y="true" class="list-page" scroll-top="{{scrollTop}}" bindscrolltolower="loadMore">
<view class="goods-list">
<view wx:for="{{$GoodsList$goodsList}}" wx:for-item="item" wx:for-index="index" wx:key="{{item.infoId}}" bindtap="$GoodsList$clickHandler" data-index="{{index}}" class="item-list-container{{index%2==0 ? ' left-item' : ''}}">
<view class="item-img-list"><image src="{{item.pic}}" class="item-img" mode="aspectFill"/></view>
<view class="item-desc">
<view class="item-list-title">
<text class="item-title">{{item.title}}</text>
</view>
<view class="item-list-price">
< view wx: if = "{{item. The price & item. The price > 0}}" class = "item - nowPrice" > < I > $< / I > {{item. Price}} < / view >
<view wx:if="{{item.originalPrice &&item.originalPrice >0}}" class=" item-oriprice ">¥{{item.originalprice}}</view>
</view>
<view class="item-list-local"><view>{{item.cityName}}{{item.cityName&&item.businessName? ' | ':''}}{{item.businessName}} </view>
</view>
</view>
<form class="form" bindsubmit="$GoodsList$sendFromId" report-submit="true" data-index="{{index}}">
<button class="submit-button" form-type="submit"/>
</form>
</view>
</view>
</view>
</scroll-view>
Copy the code
It can be seen that wepy directly writes all the introduced components into the page, and outputs them in the format of wechat applet. Of course, it can also be seen from one side that using wepy framework, the code style is more concise and elegant than the original
The brief analysis of the above is wepy implementation principle, interested friends can go to read the source code (https://github.com/wepyjs/wepy). Generally speaking, the core of Wepy lies in the compilation process, which can compile elegant and concise codes similar to VUE style into complex codes required by wechat applets.
Wepy, as an excellent micro channel small program framework, can help us greatly improve the development efficiency and stand out among the few small program frameworks. I hope more teams will choose Wepy.
PS: Wepy is also in the process of implementing applets and VUE code isomorphism, but it is still in the development stage. It would be very cool if we could implement a development in the future and produce applets and M pages at the same time.