这是本系列的最后一篇,由于之后就是机密了。但这篇会公开一些很是有用的思路。小程序封死了操做DOM的可能性,而且也不让咱们操做视图,全部与视图有关的东西一概接触不了。而它的自定义组件是很是恶心,基本不配叫组件,不能继承叫什么组件。所以咱们使用它更早期的动态模板技术,template。javascript
个人思路以下,经过编译组件的render方法,将里面的自定义组件变成template类,而后在template类中本身初始化,获得props, state再传给原来的模板。换言之,有两套模板。css
//源码 import { Page } from "../wechat"; import "./page.css"; import Dog from "../components/dog/dog"; const e = "e"; class P extends Page { constructor(props) { super(props); this.state = { name: 'hehe', array: [ {name: "dog1",text: "text1"}, {name: "dog2",text: "text2"}, {name: "dog3",text: "text3"}, ] }; } onClick() { console.log("test click1" + e); } render() { return ( <div> <div> {this.state.array.map(function(el) { return <Dog name={el.name}>{el.text}</Dog>; })} </div> <Dog name={this.state.name} /> </div> ); } } export default P;
咱们先无论Dog组件长得怎么样。
为了让它同时支持小程序与React的render函数,咱们须要对render进行改造。将Dog,div等改形成小程序能能认识的类型,如java
<view> <view> {this.state.array.map(function(el) { return <template is={Dog} name={el.name}>{el.text}</template>; })} </view> <template is="Dog" name={this.state.name} /> </view>
这个转译是如何实现呢,咱们能够通一个插件 syntax-jsx, 它会在visitor遍历出JSX的开标签,闭标签,属性及{}
容器。react
但React没法认识template标签,所以还要改造git
//React专用 <view> <view> {this.state.array.map(function(el) { return <React.template is={Dog} name={el.name}>{el.text}</React.template>; })} </view> <React.template is={Dog} name={this.state.name} /> </view>
如今看小程序这边
小程序没法认识{},须要改变成wx:for指令es6
//小程序专用 <view> <view> <block wx:for="{{this.state.array}}" wx:for-item="el"> <template is="Dog" name={el.name}>{el.text}</template>; </block> </view> <template is="Dog" name={this.state.name} /> </view>
小程序的template有个缺憾,它没法认识name这样的属性的,所以咱们须要一个东西装着它。那么咱们动态建立一个数组吧,改一改React那边github
//React专用 <view> <view> {this.state.array.map(function(el) { return <React.template is={Dog} name={el.name} templatedata="data123124342">{el.text}</React.template>; })} </view> <React.template is={Dog} name={this.state.name} templatedata="data34343433" /> </view>
templatedata这个属性及它的值是babel在编译时建立的,React.template到时会在this.data.state添加data123124342数组,内容为一个个对象,这些对象是经过Dog.props, Dog.defaultProps, Dog.state组成。结构大概是{ props: {}, state: {} }
那么小程序的模板变成小程序
//小程序专用 <import src="../../components/dog/dog.wxml" /> <view> <view> <block wx:for="{{this.state.array}}" wx:for-item="el"> <template is="Dog" wx:for="data123124342" wx:for-item="data" data="{{...data}}"></template>; </block> </view> <template is="Dog" wx:for="data34343433" wx:for-item="data" data="{{...data}}" /> </view>
而咱们的render再通过编译变成()数组
import { Page } from "../wechat"; import "./page.css"; import Dog from "../components/dog/dog"; const e = "e"; class P extends Page { constructor(props) { super(props); this.state = { name: 'hehe', array: [ {name: "dog1",text: "text1"}, {name: "dog2",text: "text2"}, {name: "dog3",text: "text3"}, ] }; } onClick() { console.log("test click1" + e); } render() { return ( React.createElement( "div", null, React.createElement( "div", null, this.state.array.map(function(el) { return React.createElement(React.template, { name: el.name, children: el.text, is: Dog, templatedata:"data34343433" }); }) ), React.createElement(React.template, { is: Dog, name: this.state.name, templatedata:"data34343433" }) ); } export default P;
上面的转译工做能够经过transform-react-jsxbabel插件实现babel
class P extends Page
这种es6定义类的方式,小程序可能也不认识,或者经过babel编译后也太复杂。好比说taro将Dog这个类变成这样:
所以咱们最好在React中提供一个定义类的方法,叫miniCreateClass。如此一来咱们就能将Dog转换得很简洁
var React = require("../../wechat"); var Component = React.Component var miniCreatClass = React.miniCreatClass function Dog() {} let Dog = miniCreatClass(Dog, Component, { render: function () { return React.createElement("view", null, this.state.name ) } }, {}); module.exports.default = Dog;
咱们再看Page类。小程序定义页面是经过 Page 工厂实现的,大概是Page({data: {}})
。小程序在这里的令计很方便咱们进行hack,由于一个Page类只会有一个实例。
Page(createPage(P))
再看createPage的实现:
function createPage(PageClass) { var instance = ReactDOM.render(React.createElement(PageClass), { type: "div", root: true }); var config = { data: { state: instance.state, props: instance.props }, onLoad: function() { instance.$wxPage = this; }, onUnload: function() { instance.componentWillUnmount && instance.componentWillUnmount(); } }; instance.allTemplateData.forEach(function(el) { if (config.data[el.templatedata]) { config.data[el.templatedata].push(el); }else{ config.data[el.templatedata] = [el]; } }); return config; }
最后是React.template的实现,它负责组装给template的数据,这个template是小程序的标签。
React.template = function(props){ //这是一个无状态组件,负责劫持用户传导下来的类,修改它的原型 var clazz = props.is; var a = classzz.prototype; var componentWillMount = a.componentWillMount; a.componentWillMount = function(){ var ref = this._reactInternalRef; var arr = ref._owner.allTemplateData || (ref._owner.allTemplateData = []); arr.push({ props: this.props, state: this.state, templatedata: props.templatedata }) componentWillMount && componentWillMount.call(this) } var componentWillUpdate = a.componentWillUpdate; //...再上面同样 return React.createElement(clazz, props) }
好了,个人方案就介绍到这里了。若是有人愿意与我一开始搞这东西了,欢迎在github找我。