React转微信小程序:从React类界说到Component挪用 。。。。。这是本系列的第二篇,,,,,已往两周,,,,,已经有相当效果出来。。。。。本文介绍其中一部分可靠的思绪,,,,,这个比京东的taro更具可靠性。。。。。若是以为看不过瘾,,,,,可以看anu的源码,,,,,内里包括了miniapp的转换器。。。。。
微信小程序是面向设置工具编程,,,,,不袒露Page,App,Component等焦点工具的原型,,,,,只提供三个工厂要领,,,,,因此无法实现继承。。。。。App,Page,Component所在的JS的依赖处理也很弱智,,,,,你需要声明在统一目录下的json文件中。。。。。
好比说
Component({
properties: {},
data: {},
onClick: function(){}
})
properties与data都是统一个工具,,,,,properties只是用来界说data中的数据的默认值与类型,,,,,相当于React的defaultProps与propTypes。。。。。怎样转换呢?
import {Component} form "./wechat"
Class AAA extends Component{
constructor(props){
super(props);
this.state = {}
}
static propTypes = {}
static defaultProps = {}
onClick(){}
render(){}
}
export AAA;
首先我们要提供一个wechat.js文件,,,,,内里提供Component, Page, App 这几个基类,,,,,现在只是空实现,,,,,但已经足够了,,,,,包管它在调试不会蜕化。。。。。我们要的是`Class AAA extends Component`这个语句的内容。。。。。学了babel,,,,,对JS语法越发熟悉了。。。。。这个语句在babel6中称为ClassExpression,,,,,到babel7中又叫ClassDeclaration。。。。。babel有一个叫"babel-traverse"的包,,,,,可以将pp电子代码的AST,,,,,然后凭证语法的因素举行转换(详见这文章 https://yq.aliyun.com/articles/62671)。。。。。ClassDeclaration的参数为一个叫path的工具,,,,,我们通过 path.node.superClass.name 就能拿到Component这个字样。。。。。若是pp电子类界说是下面的这样,,,,,path.node.superClass.name 则为App。。。。。
Class AAA extends App{
constructor(props){
super(props);
this.state = {}
}
}
App, Page, Component对应的json差别很大,,,,,拿到这个可以利便我们区别看待。。。。。
然后我们继续界说一个ImportDeclaration处理器,,,,,将import语句去掉。。。。。
界说ExportDefaultDeclaration与ExportNamedDeclaration处理器,,,,,将export语句去掉。。。。。
到这里我不得不展示一下我的转码器的全貌了。。。。。我是通过rollup获得所有????榈穆肪队胛募内容,,,,,然后通过babel举行转译。。。。。babel转换是通过babel.transform。。。。。babel原来就有许多叫babel-plugin-transform-xxx的插件,,,,,它是专门处理那些es5无法识别的新语法。。。。。我们需要在这后面加上一个新插件叫miniappPlugin
// https://github.com/RubyLouvre/anu/blob/master/packages/render/miniapp/translator/transform.js
const syntaxClassProperties = require("babel-plugin-syntax-class-properties")
const babel = require('babel-core')
const visitor = require("./visitor");
var result = babel.transform(code, {
babelrc: false,
plugins: [
'syntax-jsx',
// "transform-react-jsx",
'transform-decorators-legacy',
'transform-object-rest-spread',
miniappPlugin,
]
})
function miniappPlugin(api) {
return {
inherits: syntaxClassProperties,
visitor: visitor
};
}
miniappPlugin的结构异常简朴,,,,,它继承一个叫syntaxClassProperties的插件,,,,,这插件原来用来剖析es6 class的属性的,,,,,由于pp电子目的也是抽取React类中的defaultProps, propsTypes静态属性。。。。。
visitor的结构很简朴,,,,,就是种种JS语法的形貌。。。。。
const t = require("babel-types");
module.exports = {
ClassDeclaration: 抽取父类的名字与转换结构器,
ClassExpression: 抽取父类的名字与转换结构器,
ImportDeclaration(path) {
path.remove() //移除import语句,,,,,小程序会自动在外面包一层,,,,,酿成AMD????
},
ExportDefaultDeclaration(path){
path.remove() //AMD不熟悉export语句,,,,,要删掉,,,,,或转换成module.exports
},
ExportNamedDeclaration(path){
path.remove() //AMD不熟悉export语句,,,,,要删掉,,,,,或转换成module.exports
}
}
我再介绍一下visitor的处理器是怎么用的,,,,,处理器着实会执行两次。。。。。pp电子AST树每个节点会被执行两次,,,,,若是学过DFS的同砚会明确,,,,,第一次会见后,,,,,做些处理,,,,,然后举行它内部的节点,,,,,处理后再见见一次。。。。。于是visitor也可以这样界说。。。。。
ClassDeclaration:{
enter(path){},
exit(path){}
}
若是以函数形式界说,,,,,那么它只是作为enter来用。。。。。
AST会从上到下执行,,,,,我们先拿到类名的名字与父类的名字,,,,,我们界说一个modules的工具,,,,,生涯信息。。。。。
enter(path) {
let className = path.node.superClass ? path.node.superClass.name : "";
let match = className.match(/\.?(App|Page|Component)/);
if (match) {
//获取类的组件类型与名字
var componentType = match[1];
if (componentType === "Component") {
modules.componentName = path.node.id.name;
}
modules.componentType = componentType;
}
},
我们在第二次会见这个类界说时,,,,,要将类界说转换为函数挪用。。。。。即
Class AAA extends Component ---> Component({})
实现如下,,,,,将原来的类删掉(因此才在exit时执行),,,,,然后新建一个函数挪用语句。。。。。我们可以通过babel-types这个句实现。。。。。详细看这里。。。。。好比说:
const call = t.expressionStatement(
t.callExpression(t.identifier("Component"), [ t.objectExpression([])])
);
path.replaceWith(call);
就能爆发如下代码,,,,,将pp电子类界说从原位置替换掉。。。。。
Component({})
但我们不可是一个空工具啊,,,,,因此我们需要网络它的要领。。。。。
我们需要在visitors工具添加一个ClassMethod处理器,,,,,网络原来类的要领。。。。。类的要领与工具的要领纷歧样,,,,,工具的要领叫成员表达式,,,,,需要转换一下。。。。。我们首先弄一个数组,,,,,用来下班具。。。。。
var methods = []
module.exports= {
ClassMethod: {
enter(path){
var methodName = path.node.key.name
var method = t.ObjectProperty(
t.identifier(methodName),
t.functionExpression(
null,
path.node.params,
path.node.body,
path.node.generator,
path.node.async
)
);
methods.push(method)
}
}
然后我们在ClassDeclaration或ClassExpression的处理器的exit要领中改成:
const call = t.expressionStatement(
t.callExpression(t.identifier("Component"), [ t.objectExpression(methods)])
);
path.replaceWith(call);
于是函数界说就酿成
Component({
constructor:function(){},
render:function(){},
onClick: function(){}
})
到这里,,,,,我们最先另一个问题了。。。。。小程序虽然是抄React,,,,,但又想别出心裁,,,,,于是一些属性与要领是纷歧样的。。。。。好比说data对应state, setData对应setState,,,,,早期的版本尚有forceUpdate之类的。。。。。data对应一个工具,,,,,你可以有千奇百怪的写法。。。。。
this.state ={ a: 1}
this["state"] = {b: 1};
this.state = {}
this.state.aa = 1
你想hold住这么多希奇的写法是很难题的,,,,,因此我们可以对constructor要领做些处理,,,,,然后其他要领做些约束,,,,,来镌汰转换的本钱。。。。。什么处理constructor呢,,,,,我们可以界说一个onInit要领,,,,,专门挟制constructor要领,,,,,将this.state酿成this.data。。。。。
function onInit(config){
if(config.hasOwnProperty("constructor")){
config.constructor.call(config);
}
config.data = config.state|| {};
delete config.state
return config;
}
Component(onInit({
constructor:function(){},
render:function(){},
onClick: function(){}
}))
详细实现参这里,,,,,本文就不贴上来了。。。。。
RubyLouvre/anu
那this.setState怎么转换成this.setData呢。。。。。这是一个函数挪用,,,,,语法上称之为**CallExpression**。。。。。我们在visitors上界说同名的处理器。。。。。
CallExpression(path) {
var callee = path.node.callee || Object;
if ( modules.componentType === "Component" ) {
var property = callee.property;
if (property && property.name === "setState") {
property.name = "setData";
}
}
},
至少,,,,,将React类界说转换成Component({})挪用方式 乐成了。。。。。剩下就是将import语句处理一下,,,,,由于要小程序中,,,,,若是这个组件引用了其他组件,,,,,需要在其json中添加useComponens工具,,,,,将这些组件名及链接写上去。。。。;;;谎灾,,,,,小程序太懒了,,,,,随处都要手动。。。。。有了React转码器,,,,,这些事可以省掉。。。。。
其次是render要领的转换,,,,,怎么酿成一个wxml文件呢,,,,,`{}单花括号的内容要转换成`"{{}}"`双引号+双花括号 ,,,,,wx:if, wx:for的模拟等等,,,,,且听下回剖析。。。。。
KESION pp电子软件
KESION pp电子软件是海内领先的在线教育软件及私域社交电商软件服务提供商,,,,,恒久专注于为企业提供在线教育软件及社交电商SaaS平台解决方案。。。。。
公司焦点产品云开店SaaS社交电商服务平台、在线教育SaaS服务平台、教育企业数字化SaaS云平台、企微营销助手、私有化自力安排品牌网校和在线教育咨询等。。。。。KESION 一直通过手艺立异,,,,,提供产品和服务,,,,,助力企业向数字化转型,,,,,通过科技驱动商业刷新,,,,,让商业变得更智慧!
详细操作 下载echarts-for-weixin。。。。。 把其 ec-canvas 目录移动到 mpvue 项目的 static 目录下。。。。。 对 ec-canvas/ec-canvas.js 举行小调解,,,,,考
把微信小程序异步API转为Promise,,,,,简化异步编程,,,,,离别层层回调 ... 把微信小程序异步API转化为Promise。。。。。用Promise处理异步操作有多利便,,,,,谁用谁知道...