Remax 是一个跨多端小程序 React 开发方案,之因此称其为“方案”而非框架是由于这并不是一个新的框架,其主要能力就是让 React 可以直接运行在 微信小程序/支付宝小程序/字节跳动小程序/H5(固然这个原本就支持) 等环境。html
可能会有人要会问 “React 不是早就能够运行在小程序中了么“?本文会介绍一下现现在的一些小程序框架的解决方案,以及为何咱们认为把 React 直接搬进小程序是个更为合理的方案。react
因为大多开发者都更熟悉 React 和 Vue 的 API 和语法,加上小程序自己的开发方式确实让人痛苦,因而便有了一些框架来将这些熟悉的语法编译到小程序的 WXML/WXSS/JS 上,其中比较具备表明性的例如 taro,其目标就是让开发者可以用 React 的开发方式编写小程序。git
而这类框架的实现原理其实并不是真的是一个 React 或者类 React 框架,而是把看起来像是 JSX 的模板经过静态编译的方式翻译成小程序自身的模板。github
这样作的限制很是明显,那就是 JSX 是 JavaScript 的拓展语言(React Blog 写的是 is a syntax extension to JavaScript
),而小程序所采用的 WXML 倒是一个表达能力很是受限的模板语言,咱们不可能完成从一个通用编程语言到模板语言的编译。web
而静态编译类框架为了作到这一点,采起的方式就是限制开发者的写法,这也是为何上面称之为看起来像是 JSX 的模板,这也是为何 taro 对 JSX 的写法作出了诸多限制。npm
这种方案大多声称这些限制并无限制生产力,或者符合最佳实践等等。然而咱们其实都知道这是因为小程序自己的坑形成的,静态编译方案编译的永远都只会是模板语言,而不是 JSX。编程
之因此我说这些限制并不是基于最佳实践,是由于 React 自己对于 JSX 的定位就 并不是模板。小程序
JSX is a syntax extension to JavaScript.微信小程序
在最近 React 团队已经向咱们介绍了 Hooks,指望能够 functional component 不只仅能够是无状态组件,也能够是 useState
的。浏览器
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div>
);
}
复制代码
React 官方博客提到 Classes confuse both people and machines
,咱们也明显能够看到基于 function 的组件明显更为简洁,噪声更小,将来 React 社区的方向更是会逐渐从 class component 过渡到 functional component。
在这种趋势下,把 JSX 当作模板写,且将来永远也不可能支持 functional component 的方案绝非真的基于最佳实践的选择。
在 Remax 中,咱们彻底可使用全新的 Hooks API 来开发组件
由于 Remax 中的 React 就是 React.js,而 JSX 就是 JavaScript 的超集。
上图中使用小程序的原生语法,classname 和 inline style 就只能写成
<view class="weui-navbar__item {{activeIndex == index ? 'weui-bar__item_on' : ''}}">
</view>
<view style="left: {{sliderLeft}}px; transform: translateX({{sliderOffset}}px); -webkit-transform: translateX({{sliderOffset}}px);"></view>
复制代码
而使用 remax 后就能够写成正常的 react:
const innerStyle = {
left: `${sliderLeft}px`,
transform: `translateX(${sliderOffset}px);`,
'-webkit-transform': `translateX(${sliderOffset}px)`,
width: sliderWidth,
};
const itemClassName = classnames({
'weui-navbar__item': true,
'weui-bar__item_on': activeIndex === index,
});
return <View className={itemClassName}> <View style={innerStyle} /> </View> 复制代码
Remax 的实现原理和基于静态编译的方案有所不一样,其核心实际上是从新实现了 ReactDOM 的部分。
众所周知,React 自己的设计就是支持跨端渲染的,render 部分和 React 的核心逻辑是解耦的(甚至不在一个 npm 包里)。主要的 render 有 ReactDOM(浏览器),ReactDOMServer(服务器端)和 ReactNative。
Remax 要作的事情和 ReactNative 要作的事情很是相似,咱们从新接管了 ReactDOM 的 render。
在原有的 React 页面中,React 在完成 Diff 发现须要修改界面时,又 ReactDOM 把改变 Patch 到页面上。
而在小程序中因为咱们不能直接修改页面,则由 React 完成 DIFF 后由 Remax 把修改 Patch 到内存中的虚拟 DOM 上,而后再经过小程序本身的虚拟 DOM 最后把改变同步到页面上。
在这里我把这个过程说得很是简单,但其实是有些坑要填的,主要也都是来自于小程序的限制,后续会有新的文章展开来说。可是这种实现方式使得咱们彻底能够把 React 的代码放在小程序的环境中运行。
工程化很理所固然的用 Webpack 来实现, 除了咱们经常使用的打包等功能外,Webpack 插件也使咱们很容易构建一些咱们须要的东西出来,例如咱们须要在每一个 js 入口除了放一个 js 外还须要添加一个 wxml
文件,就能够经过一个很简单的 Webpack 插件来实现。
function GeneraeWxmlWebpackPlugin() {
const content = `<view>...</view>`;
const apply = (compiler) => {
const emit = (compilation, cb) => {
const {
chunks,
} = compilation;
chunks.forEach((item) => {
compilation.assets[`${item.name}.wxml`] = {
source: () => content,
size: () => content.length,
};
});
cb();
};
if (compiler.hooks) {
const plugin = { name: 'GeneraeWxmlWebpackPlugin' };
compiler.hooks.emit.tapAsync(plugin, emit);
} else {
compiler.plugin('emit', emit);
}
};
return {
apply,
};
}
复制代码
这种方案想实现同一套代码跨到 H5 端显然没有什么问题,至于支付宝小程序目前验证了一下可行性也是可行的。
这个项目主要由几块组成
因为目前整个项目才刚刚起步,暂时还不能用于生产环境,目前的几个主要开发者(和打算参与的)有 @CodeFalling @bramblex @ahonn @simplyy
目前的 DEMO 能够扫码体验:
或者在能够按照 github.com/CodeFalling… 体验本地 DEMO。
若是有人想要参与进来一块儿开发能够联系我,开发相关的细节文档会陆续更新在 github.com/CodeFalling… 。