1 | # weact 用JSX快速开发小程序
|
2 |
|
3 | [![travis-ci](https://travis-ci.org/haojy/weact.svg?branch=master)](https://travis-ci.org/haojy/weact)
|
4 | [![Code coverage](https://codecov.io/gh/haojy/weact/branch/master/graph/badge.svg)](https://codecov.io/github/haojy/weact?branch=master")
|
5 | [![Dependence](https://david-dm.org/haojy/weact/status.svg)](https://david-dm.org/haojy/weact)
|
6 | [![License](https://img.shields.io/npm/l/weact-cli.svg)](https://github.com/haojy/weact/blob/master/LICENSE)
|
7 |
|
8 |
|
9 | weact实现了用JSX和ES6/7来开发小程序,你可以在一个jsx文件中编写页面或组件,并把关联的JSX代码和引用包编译成小程序代码,然后在*小程序开发者工具*中调试代码。因为使用了JSX和ES标准语法,你可以轻松地把已有的JSX代码重构成小程序,当然你也可以使用喜欢的语法高亮,语法检查器等工具。支持
|
10 |
|
11 | * JSX,ES6/7标准语法
|
12 | * 单文件开发小程序模块
|
13 | * 引用NPM包
|
14 | * 自动添加组件关系
|
15 | * 在jsx文件中编写小程序样式WXSS
|
16 |
|
17 | ## 快速上手
|
18 |
|
19 | - [ 安装 ](#安装)
|
20 | - [ JSX小程序 ](#JSX小程序)
|
21 | - [ 生成小程序 ](#生成小程序)
|
22 | - [ 从JSX到WXML ](#从JSX到WXML)
|
23 | - [ App.jsx ](#App.jsx)
|
24 | - [ Page.jsx ](#Page.jsx)
|
25 | - [ 模版==函数式Component ](#模版==函数式Component)
|
26 | - [ 组件 ](#组件)
|
27 | - [ 引用模块 ](#引用模块)
|
28 | - [ 命令行用法 ](#命令行用法)
|
29 |
|
30 |
|
31 | ### 安装
|
32 | ---
|
33 |
|
34 | 在项目里安装*weact-cli*,
|
35 | ```bash
|
36 | npm install -D weact-cli
|
37 | npx weact
|
38 | ```
|
39 |
|
40 | ### JSX小程序
|
41 | ---
|
42 |
|
43 | 让我们开始写一个Hello world的小程序,只需要两个文件:app.jsx,index.jsx, 通常页面代码会被放在`./pages`目录下,
|
44 | ```
|
45 | src/
|
46 | ├── app.jsx
|
47 | └── pages
|
48 | └── index.jsx
|
49 | ```
|
50 |
|
51 | weact只把app.jsx作为目标文件,也就是说所有需要的页面需要在app.jsx中被import进来。在这个例子中,只有一个页面index可以import。我们还需要继承`weact.App`来声明小程序的app, 这里只export一个空的类,[ App.jsx ](#App.jsx)会详细说明怎么定义app,
|
52 | ```javascript
|
53 | // src/app.jsx
|
54 | import { App } from 'weact'
|
55 | import './pages/index.jsx' // app应用的页面,需要import
|
56 |
|
57 | export default class extends App {
|
58 | }
|
59 | ```
|
60 |
|
61 | 和app一样,所有页面要继承`weact.Page`,并被export才可以。与应用不同的是,页面有`render()`方法来定义显示部分,在render方法里返回JSX标签,在语法上这和React Component的render相同。weact会根据render方法里返回的标签,自动编译出WXML文件,[ 从JSX到WXML ](#从JSX到WXML)会说明如何用JSX写出符合WXML定义的标签。`weact.WXSS`利用ES6字符串模版的能力,可以在jsx中声明符合WXSS语法的样式,这样样式就会被weact编译成对应的WXSS文件。
|
62 |
|
63 | ```javascript
|
64 | // src/pages/index.jsx
|
65 | import { Page, WXSS } from 'weact'
|
66 |
|
67 | WXSS`
|
68 | .hi {
|
69 | color: blue;
|
70 | }
|
71 | `
|
72 | export default class extends Page {
|
73 | render() {
|
74 | return (
|
75 | <view class="hi">
|
76 | Hello World!
|
77 | </view>
|
78 | )
|
79 | }
|
80 | }
|
81 | ```
|
82 |
|
83 | 这样,一个基于JSX的小程序就完成了。
|
84 | > 在[ 实战JSX开发小程序 ](https://github.com/haojy/weact-startup) 中有更多的例子可以参考。
|
85 |
|
86 | ### 生成小程序
|
87 | ---
|
88 |
|
89 | 上面的代码都存放在`./src`目录下,然后执行
|
90 | ```bash
|
91 | npx weact ./src # 等同于 weact ./src/app.jsx ./dist
|
92 | ```
|
93 |
|
94 | 在当前目录下会生成`./dist`目录,里面全是根据jsx文件编译出的小程序代码,
|
95 | ```
|
96 | dist/
|
97 | ├── app.js
|
98 | ├── app.json
|
99 | └── pages
|
100 | └── index
|
101 | ├── index.js
|
102 | ├── index.wxml
|
103 | └── index.wxss
|
104 | ```
|
105 | 在*微信开发者工具*添加项目,项目目录设置成`./dist`, 然后就可以在模拟器中看到运行结果了。
|
106 |
|
107 | ### 从JSX到WXML
|
108 | ---
|
109 |
|
110 | weact可以在语法上把JSX编译成WXML,下面列表给出两种语言的在语法上的对应关系。
|
111 |
|
112 | 语法 | JSX | WXML
|
113 | ----|-----|-----
|
114 | 数据绑定 | `<view>{message}</view>` | `<view> {{ message }} </view>`
|
115 | 属性 | ``<view id={`${prefix}-item}`>hi</view>`` | `<view id="{{prefix}}-item">hi</view>`
|
116 | 关键字 false | `<view checked={false}>hi</view>` | `<view checked="{{false}}">hi</view>`
|
117 | 关键字 | `<view checked>hi</view>` | `<view checked="{{true}}">hi</view>`
|
118 | 三元运算 | `<view hidden={flag ? true: false}>hi</view>` | `<view hidden="{{flag ? true : false}}">hi</view>`
|
119 | 算数运算 | `<view>{a + b} + {c} + d</view>` | `<view>{{a + b}} + {{c}} + d</view>`
|
120 | 逻辑判断 | `<view if={length > 5}>hi</view>` | `<view wx:if="{{length > 5}}">hi</view>`
|
121 | 字符串运算 | `<view>{"hello " + name}</view>` | `<view>{{"hello " + name}}</view>`
|
122 | 数组 | `<view for={[zero, 1, 2, 3, 4]}>`<br/> `{item}`<br/>`</view>` | `<view wx:for="{{[zero, 1, 2, 3, 4]}}">`<br/> `{{item}}`<br/>` </view>`
|
123 | 对象 | `<view data={{ foo: 0, bar: 1 }}>hi</view>` | `<view data="{{ foo: 0, bar: 1 }}">hi</view>`
|
124 | 数据访问 | `<view>{object.key} {array[0]}</view>` | `<view>{{object.key}} {{array[0]}}</view>`
|
125 | for 循环 | `<view for={array} key="message">`<br/> `{index}:{item.message}`<br/>`</view>` | `<view wx:for="{{array}}" wx:key="message">`<br/> `{{index}}:{{item.message}}`<br/>`</view>`
|
126 | if 条件 | `<view if={condition}>hi</view>` | `<view wx:if="{{condition}}">hi</view>`
|
127 | if else | `<view if={x > 5}>1</view>`<br/>`<view elif={x > 2}>2</view>`<br/>`<view else>3</view>` | `<view wx:if="{{x > 5}}">1</view>`<br/>`<view wx:elif="{{x > 2}}">2</view>`<br/>`<view wx:else>3</view>`
|
128 | block 条件 | `<block if={true}>`<br/> `<view> 1 </view>`<br/>` </block>` | `<block wx:if="{{true}}">`<br/> `<view> 1 </view>`<br/>` </block>`
|
129 | block 循环 | `<block for={[1, 2, 3]}>`<br/> `<view>{index}:{item}</view>`<br/>`</block>` | `<block wx:for="{{[1, 2, 3]}}">`<br/> `<view>{{index}}:{{item}}</view>`<br/>`</block>`
|
130 | 事件处理 | `<button bindtap={handleTap}>Next</button>`| `<button bindtap="handleTap">Next</button>`
|
131 | onXXX == bindxxx | `<button onTap={this.handleTap}>Next</button>` | `<button bindtap="handleTap">Next</button>`
|
132 |
|
133 | ### App.jsx
|
134 | ---
|
135 |
|
136 | 小程序在json文件中进行全局配置,用JSX把这些配置写成App的类属性,对比参考[ 小程序配置 ](https://mp.weixin.qq.com/debug/wxadoc/dev/framework/config.html)。同样,App的[ 生命周期函数,自定义公共变量,自定义公共函数等属性 ](https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/app.html)都可以写成类属性。weact把app.jsx编译成对应的app.json,app.js, app.wxss。
|
137 |
|
138 | ```javascript
|
139 | export default class extends App {
|
140 |
|
141 | debug = true
|
142 |
|
143 | window = {
|
144 | navigationBarTitleText: '你好,小程序',
|
145 | navigationBarTextStyle: 'black',
|
146 | navigationBarBackgroundColor: '#f4f5f6',
|
147 | backgroundColor: '#f4f5f6',
|
148 | }
|
149 |
|
150 | tabBar = {
|
151 | color: '#333333',
|
152 | backgroundColor: '#ffffff',
|
153 | list: [
|
154 | {
|
155 | pagePath: 'pages/index/index', // 编译后js路径
|
156 | text: '🏠',
|
157 | },
|
158 | {
|
159 | pagePath: 'pages/page1/page1',
|
160 | text: 'Page 1',
|
161 | },
|
162 | ],
|
163 | }
|
164 |
|
165 | myData = '自定义公共变量',
|
166 |
|
167 | hello() { return '自定义公共函数' }
|
168 |
|
169 | // 生命周期函数
|
170 | onLaunch() { console.log('app: hello world') }
|
171 | onShow() { console.log('app: yes, I am') }
|
172 | onHide() { console.log('app: just minutes') }
|
173 | onError() { console.log('app: woops') }
|
174 | }
|
175 | ```
|
176 |
|
177 | ### Page.jsx
|
178 | ---
|
179 |
|
180 | 类似App.jsx,页面的[ 生命周期函数和其他属性 ](https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/page.html)也写成Page的类属性。除此之外,
|
181 | - Page的`render()`函数定义页面显示,
|
182 | - 标签使用参考[小程序基础组件](https://mp.weixin.qq.com/debug/wxadoc/dev/component/)
|
183 | - 组件的事件处理函数在Page中直接定义类函数
|
184 |
|
185 | ```javascript
|
186 | export default class extends Page {
|
187 |
|
188 | data = {
|
189 | // 页面数据
|
190 | }
|
191 |
|
192 | myData = '自定义公共变量',
|
193 |
|
194 | handleTap() { console.log('自定义公共函数') }
|
195 |
|
196 | // 生命周期函数
|
197 | onLoad() { console.log('page index: loading...') }
|
198 | onShow() { console.log('page index: yes, I am') }
|
199 | onReady() { console.log('page index: I am ready now') }
|
200 | onHide() { console.log('page index: just minutes') }
|
201 | onUnload() { console.log('page index: bye...') }
|
202 | onReachBottom() { console.log('page index: we get to the most bottom') }
|
203 | onPullDownRefresh() { console.log('page index: pull down') }
|
204 | onPageScroll() { console.log('page index: scrolling...') }
|
205 | onShareAppMessage() { console.log('page index: share this') }
|
206 |
|
207 | render() {
|
208 | return (
|
209 | <view>
|
210 | Hello World!
|
211 | <button onTap={this.handleTap}>下一页</button>
|
212 | <navigator url="/pages/page1/page1">跳转到Page 1</navigator>
|
213 | <navigator url="/pages/page2/page2">跳转到Page 2</navigator>
|
214 | </view>
|
215 | )
|
216 | }
|
217 | }
|
218 |
|
219 | ```
|
220 |
|
221 |
|
222 | ### 模版==函数式Component
|
223 | ---
|
224 |
|
225 | 小程序的模版可以理解成,没有状态的函数式Component。weact会把返回JSX标签的函数编译成模版,使用这类组件时,只要确保import的名字和定义的一样就可以。
|
226 |
|
227 | ```javascript
|
228 | // src/components/flex.jsx
|
229 | export default function flex({
|
230 | direction,
|
231 | }) {
|
232 | return (
|
233 | <view>
|
234 | <view class="section">
|
235 | <view>flex-direction: ${direction}</view>
|
236 | <view style={`display:flex;flex-direction:${direction};`}>
|
237 | <view class="flex-item bc_green">1</view>
|
238 | <view class="flex-item bc_red">2</view>
|
239 | <view class="flex-item bc_blue">3</view>
|
240 | </view>
|
241 | </view>
|
242 | </view>
|
243 | )
|
244 | }
|
245 | ```
|
246 |
|
247 | ```javascript
|
248 |
|
249 | // src/pages/index.jsx
|
250 | import { Page } from 'weact'
|
251 | import flex from '../components/flex.jsx'
|
252 |
|
253 |
|
254 | export default class extends Page {
|
255 | render() {
|
256 | return (
|
257 | <view>
|
258 | <flex direction="row" />
|
259 | <flex direction="column" />
|
260 | </view>
|
261 | )
|
262 | }
|
263 | }
|
264 | ```
|
265 |
|
266 | ### 组件
|
267 | ---
|
268 |
|
269 | 用weact自定组件更类似写*react Component*,像Page一样显示声明继承Compnent类就可以。组件的属性可以用`propTypes`和`defaultProps`来定义,分别对应着`properties[...].type`和`properties[...].value`。属性类型由`weact.PropTypes`定义如下
|
270 |
|
271 | PropTypes | 小程序属性类型
|
272 | ----------|-------------
|
273 | *string* | *String*
|
274 | *number* | *Number*
|
275 | *bool* | *Boolean*
|
276 | *object* | *Object*
|
277 | *array* | *Array*
|
278 |
|
279 | 在下面的例子里*a*,*b*就是组件属性。如果你了解*react*,你会比较熟悉这种定义Component的方式。
|
280 | 另外,自定义的方法和事件响应函数可以直接定义为类属性,weact在编译时把这些函数放在`methods`属性里。
|
281 |
|
282 | ```javascript
|
283 | import { Component, PropTypes } from 'weact'
|
284 | export default class extends Component {
|
285 | static propTypes = {
|
286 | a: PropTypes.string
|
287 | b: PropTypes.bool
|
288 | }
|
289 | static defaultProps = {
|
290 | a: 'world',
|
291 | b: true,
|
292 | }
|
293 | state = {
|
294 | open: true,
|
295 | x: 'hi',
|
296 | item: {
|
297 | index: 0,
|
298 | time: '2016-09-15'
|
299 | }
|
300 | }
|
301 |
|
302 | render() {
|
303 | const { a, b } = this.props
|
304 | const { open } = this.state
|
305 | return (
|
306 | <view>
|
307 | <view x={b} y="str">hi {a} </view>
|
308 | <view for={[1, 2, 3]} > </view>
|
309 | <view for={array} for-index="i" for-item="node"> </view>
|
310 | </view>
|
311 | )
|
312 | }
|
313 | }
|
314 | ```
|
315 |
|
316 | #### 组件关系
|
317 |
|
318 | weact会根据父子组件的引用关系,自动编译出`relations`的定义。来看看下面的例子,父组件parent引用了子组件child。
|
319 |
|
320 | ```javascript
|
321 | // ./parent.jsx
|
322 | import { Component, PropTypes } from 'weact'
|
323 | import child from './child.jsx'
|
324 | export default class extends Component {
|
325 | render() {
|
326 | return (
|
327 | <view>
|
328 | 父级组件
|
329 | <child />
|
330 | </view>
|
331 | )
|
332 | }
|
333 | }
|
334 | ```
|
335 |
|
336 | ```javascript
|
337 | // ./child.jsx
|
338 | import { Component, PropTypes } from 'weact'
|
339 | export default class extends Component {
|
340 | render() {
|
341 | return (
|
342 | <view>
|
343 | 子级组件
|
344 | </view>
|
345 | )
|
346 |
|
347 | }
|
348 | }
|
349 | ```
|
350 |
|
351 | weact编译后在各自的js文件里自动生成关系定义,而不用手动定义。
|
352 | ```javascript
|
353 | // ./parent.js
|
354 | relations: {
|
355 | "../child/child": {
|
356 | type: "child"
|
357 | }
|
358 | },
|
359 | ```
|
360 | ```javascript
|
361 | // ./child.js
|
362 | relations: {
|
363 | "../parent/parent": {
|
364 | type: "parent"
|
365 | }
|
366 | },
|
367 | ```
|
368 | ### 引用模块
|
369 | ---
|
370 |
|
371 | 虽然小程序暂不支持直接引入NPM包,但支持类CommonJS的模块引用。weact在语法上实现ES模块间引用, 用*babel-plugin-transform-modules-commonjs*解析成CommonJS的包;NPM包也会被拷贝到*modules*目录下。*weact*模块并没有代码存在,暂时只为了符合语法。
|
372 |
|
373 | import方式 | JS/JSX | 小程序
|
374 | ----------|--------|-------
|
375 | 模块间 | `import reducer from './reducer'` | `var _reducer = require("./reducer.js");`
|
376 | NPM包 | `import redux from 'redux'` | `var _redux = require("modules/redux.js");`
|
377 | 引用Page | `import './pages/index.jsx'` | *app.json* `{"pages":["pages/index/index"]}`
|
378 | 引用Component | `import Component from '../components/Component.jsx'` | *\*.json*: `{"usingComponents":{"Component":"../../components/Component/Component"}}`
|
379 | 引用Template | `import MsgItem from './MsgItem.jsx'` | *wxml* `<import src="../MsgItem.wxml" />`
|
380 |
|
381 | > 引用的NPM包需用npm或yarn安装
|
382 |
|
383 | ### 命令行用法
|
384 |
|
385 | 使用: weact [options] <source> <target>
|
386 |
|
387 | - source 源码目录路径或app.jsx文件路径
|
388 | - target 代码生成路径=./dist
|
389 |
|
390 | options:
|
391 | * -v, --version 显示版本
|
392 | * -h, --help 显示当前内容
|
393 | * -w, --watch Watch源码变化,自动更新代码
|
394 |
|
395 | __例子__:
|
396 |
|
397 | - 在当前路径./dist目录下生成代码
|
398 |
|
399 | `weact examples/01.hello.world/`
|
400 |
|
401 | - 指定代码生成路径
|
402 |
|
403 | `weact examples/01.hello.world/ ./your_distribution`
|
404 |
|
405 | - watch模式, 根据源码改动,自动更新生成代码
|
406 |
|
407 | `weact -w examples/01.hello.world/` |
\ | No newline at end of file |