微信小程序开发提升效率
http://www.ifanr.com/minapp/790017
微信小程序的 API 实现需要兼顾方方面面,所以仍然使用 callback 写法。
众所周知,Callback-Hell(回调地狱)是传统 JS 语法上的历史问题。但毕竟称手的工具是开发效率的源泉,因此笔者对当前版本的微信小程序 API 做了简单的封装——weapp。
同时,微信小程序框架本身专注于交互和 UI 的实现,并未提供内置的状态管理。如果众多的异步操作都直接在 App
或 Page
中一一实现,相信开发起来会很困难,而且不易于测试。
因此,我又因此针对微信小程序实现了一个基于 Redux 方案的状态管理模块,用以方便的在小程序中实现应用状态管理 redux-weapp。
特别地,微信小程序构建(编译)时不支持从 App scope 之外 require 文件,npm 在此就不好用了。
所以,我们需要实时 build 依赖到应用本地,在微信小程序中引用本地的 modules。
对于这种构建场景,我认为 webpack 算是最方便的方案。
在开始之前,你需要准备
- 从官方文档,了解微信小程序是什么;
- 了解 Redux 应用状态管理方案,同时它也是 Flux 架构的具体实现;
- 了解 JavaScript 打包工具 webpack;
- 了解 ES6/7 代码转译(transcompile)工具 Babel。原理是借助语法分析工具,将代码解析成抽象语法树后「重写」成最终的代码;
- 类似 Jest、Mocha 等 JavaScript 测试工具,可以根据需要选择。
安装工具和依赖模块
下载微信小程序开发者工具
开发者工具是用 NW.js 模拟的环境,在微信中,则是 JavascriptCore 环境。
不过不用担心, 只是两个不同的 VM,本质是一样的。
NW.js 可能存在一些小 bug,写代码的时候注意一下就好。
用 npm 命令开始一个微信小程序项目
mkdir myapp
cd myapp
npm init
开始安装必要的依赖模块
由于除了小程序运行时需要的模块,还有构建所需要的模块。
看起来会比较多,不过不用担心,大多数都是声明性的,不需要你直接调用。
为了方便经验少些的同学理解,我将这些依赖分步安装。
首先是代码转译工具 Babel:
npm install --save-dev babel-cli babel-core babel-loader babel-plugin-add-module-exports babel-polyfill babel-preset-es2015 babel-preset-stage-0
有了上面这些模块,就可以在构建时,将 ES6/7 的代码转译为 ES5 的代码了(其实解释器都只认 ES5)。
接下来,我们安装打包工具 webpack:
npm install webpack --save-dev
我们只需要对代码进行打包,不需要 dev server 和 hot module replace 功能。
因此,我们只需要安装 webpack module 本身即可,无需安装其他扩展和插件。
接下来,我们来安装 Redux:
npm install redux redux-thunk --save-dev
需要注意的是,由于在实际应用中,我们经常会需要异步调用 API 服务器的接口,因此我们还需要 redux-thunk
这个模块,来处理异步行为。
然后安装开发小程序的辅助模块:
npm install xixilive/weapp xixilive/redux-weapp --save-dev
其中,weapp 模块是对微信小程序 API 的 wrapper,提供了更易于使用的 API,redux-weapp 是基于 Redux 对微信小程序进行状态管理。
建立项目目录结构
myapp
|- es6 # 源代码
|- myapp.js # 在app.js文件中require此文件
|- lib # 存放编译之后的js文件
|- pages # 小程序页面定义
|- projects
|- projects.js
|- projects.json
|- projects.wxml
|- projects.wxss
...
|- app.js # 小程序入口文件
|- app.json
|- app.wxss
|- webpack.config.js # webpack配置文件
编写构建脚本
首先得写 webpack.config.js
, 这个是必须的。
由于这个构建是为了本地化微信小程序的依赖,因此我们只处理 JS 文件。若需要打包其他资源,请读者自行研究。
而且,值得注意的是,微信小程序包有 1 MB 的上限。
// webpack.config.js
var path = require('path'), webpack = require('webpack')
var jsLoader = {
test: /\.js$/, // 你也可以用.es6做文件扩展名, 然后在这里定义相应的pattern
loader: 'babel',
query: {
// 代码转译预设, 并不包含ES新特性的polyfill, polyfill需要在具体代码中显示require
presets: ["es2015", "stage-0"]
},
// 指定转译es6目录下的代码
include: path.join(__dirname, 'es6'),
// 指定不转译node_modules下的代码
exclude: path.join(__dirname, 'node_modules')
}
module.exports = {
// sourcemap 选项, 建议开发时包含sourcemap, production版本时去掉(节能减排)
devtool: null,
// 指定es6目录为context目录, 这样在下面的entry, output部分就可以少些几个`../`了
context: path.join(__dirname, 'es6'),
// 定义要打包的文件
// 比如: `{entry: {out: ['./x', './y','./z']}}` 的意思是: 将x,y,z等这些文件打包成一个文件,取名为: out
// 具体请参看webpack文档
entry: {
myapp: './myapp'
},
output: {
// 将打包后的文件输出到lib目录
path: path.join(__dirname, 'lib'),
// 将打包后的文件命名为 myapp, `[name]`可以理解为模板变量
filename: '[name].js',
// module规范为 `umd`, 兼容commonjs和amd, 具体请参看webpack文档
libraryTarget: 'umd'
},
module: {
loaders: [jsLoader]
},
resolve: {
extensions: ['', '.js'],
// 将es6目录指定为加载目录, 这样在require/import时就会自动在这个目录下resolve文件(可以省去不少../)
modulesDirectories: ['es6', 'node_modules']
},
plugins: [
new webpack.NoErrorsPlugin(),
// 通常会需要区分dev和production, 建议定义这个变量
// 编译后会在global中定义`process.env`这个Object
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development')
}
})
]
}
定义 npm 命令
首先是代码测试命令 test
。
由于我喜欢用 Jest,所以这里也用 Jest 做范例。
// package.json
"scripts": {
"pretest": "eslint es6", //推荐进行静态检查
"test": "jest",
...
},
...,
// jest允许在package.json中定义配置
"jest": {
"automock": false,
"bail": true,
"transform": {
".js": "/node_modules/babel-jest" //用babel转译
},
"testPathDirs": [
"/__tests__/"
],
"testRegex": ".test.js$",
"unmockedModulePathPatterns": [
"/node_modules/"
],
"testPathIgnorePatterns": [
"/node_modules/"
]
}
接下来,就是激动人心的 build
命令。成败在此一举
// package.json
"scripts": {
...,
// 带上watch选项, 实时编译修改, 由于小程序开发工具也监视应用文件的修改, 所以es6目录下的js文件修改, 将导致小程序开发工具自动重新加载
"build": "webpack --watch --progress --colors --config webpack.config.js"
},
写小程序代码
到这里,我们总算进入正题了。
借助上述的 weapp 和 redux-weapp,希望你在开发小程序的时候,会感到很舒服。
在这个范例中,我们目标是去查询 GitHub 和 Octokit 的开源项目,并显示在小程序中。
myapp 模块
我们首先定义 store: /es6/store.js
。
这里只是简单的范例,实际中会有比较复杂的 store shape,需要引入更多的 middleware,来处理动作和状态的变化。
// /es6/store.js
import {createStore, applyMiddleware, bindActionCreators} from 'redux'
import thunk from 'redux-thunk'
import reducers from './reducers'
export default function(initState = {}){
return createStore(
reducers,
initState,
applyMiddleware(thunk)
)
}
接下来,我们继续定义 reducers:/es6/reducers.js
。
Reducer 就是处理因 Store dispatch 在执行时,发生的状态变化的函数,参数总是为 (state, action)
。
// /es6/reducers.js
import { combineReducers } from 'redux'
// 处理projects逻辑
const projects = (state = [], action) => {
switch (action.type) {
case 'PROJECTS_LOADED':
return state.concat[action.payload]
//other cases
}
return state
}
// 将多个reducer合并起来
// 这里就可以看出store的结构了, 是不是很 predictable ?
export default combineReducers({
projects
})
还有 actions:/es6/actions.js
,它通常是个 Plain Object,总是被 Store dispatch,描述了「发生了什么,结果是什么」的逻辑。
// /es6/actions.js
import {weapp} from 'weapp'
// 更好的方法是定义一个api module, 来处理网络请求
const http = weapp.Http('https://api.github.com')
// 这是一个异步action, redux-thunk会处理返回值为Function的action(可以编入绕口令大全了~~)
export const loadProjects = (org) => {
return (dispatch) => {
http.get(`/orgs/${org}/repos`).then(response => {
// 让store去广播'PROJECTS_LOADED'这件事情发生了
dispatch({
type: 'PROJECTS_LOADED',
payload: response
})
})
}
}
最后还有 myapp 模块的入口:/es6/myapp.js
。
// /es6/myapp.js
import {bindActionCreators} from 'redux'
import {weapp} from 'weapp'
import connect from 'redux-weapp'
import store from './store'
import actions from './actions'
export {
weapp,
connect,
bindActionCreators,
store,
actions
}
小程序模块
首先是小程序总体逻辑文件:app.js
。
// /app.js
App({
// 方便起见, 这里不做任何life-cycle处理
})
以及 app.json
。
{
"pages": [
"pages/projects/projects"
],
"window": {
"navigationBarTitleText": "Orchid"
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
},
"debug": true
}
还有页面逻辑 projects.js
。在之前,我们也将小程序的启动页面,定义为 projects
了。
// /pages/projects/projects.js
// 引入编译过的modules
import {
weapp,
connect,
bindActionCreators,
store,
actions
} from '../../lib/app'
// 标准Page定义Object
const config = {
data: {
projects: [] //for init-render
},
onReady(){
// 哪里来的 loadProjects? 往下看
this.loadProjects('octokit')
},
onStateChange(nextState){
this.setData({projects: nextState})
}
}
// connect store with page
const page = connect.Page(
store, // required
// 这个页面只关注projects变化
(state) => ({projects: state.projects}),
// 将Action定义与Store.dispatch binding在一起, 这样就是一个可以发起对github API的请求了
(dispatch) => {
return {
loadProjects: bindActionCreators(actions.loadProjects, dispatch)
}
}
)
// 启动被connect过的页面
Page(page(config))
接下来是页面 UI:projects.wxml
。
<scroll-view wx:for="{{projects}}" wx:for-item="project" class="container">
<view>{{project.name}}</view>
</scroll-view>
最后的话
范例代码未实际运行,仅用以表示开发步骤。我会尽快把这个范例实现完整,放到 GitHub 上。
最后,谢谢您耐心阅读至此!
原文地址:https://gist.github.com/xixilive/5bf1cde16f898faff2e652dbd08cf669
weapp 项目地址:https://github.com/xixilive/weapp
微信小程序开发提升效率的更多相关文章
- 5款微信小程序开发工具使用报告,微信官方开发工具还有待提升
微信小程序已经内测有一段时间了,笔者本着好奇加学习的心态写了几个小demo,虽然在MINA框架上并没有遇到太多的坑,但官方开发工具实在不敢恭维. api提示不全,要一个个查api啊,写代码超级慢啊 很 ...
- 微信小程序开发中的二三事之网易云信IMSDK DEMO
本文由作者邹永胜授权网易云社区发布. 简介 为了更好的展示我们即时通讯SDK强悍的能力,网易云信IM SDK微信小程序DEMO的开发就提上了日程.用产品的话说就是: 云信 IM 小程序 SDK 的能力 ...
- 微信小程序开发(一)开发工具推荐VSCode
虽然微信小程序官方开发工具非常优秀,但用的时间久了,会发现一些问题,比如代码编辑区小,自定义能力差,不支持插件,有时还会出现莫名其妙的bug,最不能忍的是编辑器代码提示功能不健全,这对于新手来说,很不 ...
- 微信小程序开发03-这是一个组件
编写组件 基本结构 接上文:微信小程序开发02-小程序基本介绍 我们今天先来实现这个弹出层: 之前这个组件是一个容器类组件,弹出层可设置载入的html结构,然后再设置各种事件即可,这种组件有一个特点: ...
- 微信小程序开发教程 #043 - 在小程序开发中使用 npm
本文介绍了如何在微信小程序开发中使用 npm 中包的功能,大大提高微信小程序的开发效率,同时也是微信小程序系列教程的视频版更新. 微信小程序在发布之初没有对 npm 的支持功能,这也是目前很多前端开发 ...
- 零基础入门微信小程序开发
注:本文来源于:<零基础入门微信小程序开发> 课程介绍 本达人课是一个系列入门教程,目标是从 0 开始带领读者上手实战,课程以微信小程序的核心概念作为主线,介绍配置文件.页面样式文件.Ja ...
- 微信小程序开发的基本流程
微信小程序开发的基本流程 一,微信小程序简介 1,微信小程序简称小程序,张小龙在微信公开课 Pro 上发布的小程序正式上线,时间是2017年1月9日. 2,微信小程序这个词可以分解为“微信”和“小程序 ...
- 微信小程序开发学习资料
作者:初雪链接:https://www.zhihu.com/question/50907897/answer/128494332来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
- 微信小程序开发教程,大多数人都搞错的八个问题
小程序目前被炒得沸沸扬扬,无数媒体和企业借机获取阅读流量. 这再次证明一点,微信想让什么火,真的就能让什么火.这种能力真是全中国再也没有人有了,政府也没有. 但四处传的消息很多是失真的,废话不说,先列 ...
随机推荐
- LCA/在线(倍增)离线(Tarjan)
概念 祖先 公共祖先 最近公共祖先 方法1:暴力爬山法 方法2:倍增 求公共祖先 求俩点的距离 Tarjan 概念 祖先 有根树中,一个节点到根的路径上的所有节点被视为这个点的祖先,包括根和它本身 公 ...
- OpenIddict 登录及详细流程解析
GitHub上实例都是集成了Identity来实现,我这里去掉了相关东西,实现自定义的登录满足自己的结构要求 服务端配置添加数据库服务以及定时任务服务 builder.Services.AddDbCo ...
- 自动化怎么判断anndroid.listview是否包含某个指定的元素或内容
做自动化的时候,定位元素时,会发现整屏的内容都是放在一个列表,在多层嵌套其他控件组成 如柠檬班的题库,里面会有很多分类 那么在做自动化的时候,怎么判断页面有没有包含指定的分类呢? 整体思路: 1.找到 ...
- python连接真机或模拟器
前言: 最近写自动化代码的时候,使用模拟器运行 python + appium代码时,APP闪退了,只能使用真机运行代码了.真机要怎么配置设备的信息呢? 怎么配置设备的信息? 配置是使用真机还是模拟器 ...
- 初识python 之 爬虫:爬取某电影网站信息
注:此代码仅用于个人爱好学习使用,不涉及任何商业行为! 话不多说,直接上代码: 1 #!/user/bin env python 2 # author:Simple-Sir 3 # time:201 ...
- 快速搭建Hadoop-Hive-Zoopkeeper-Sqoop环境进入Sqoop学习环境
原文链接: https://www.toutiao.com/i6771763211927552523/ CDH简单了解 CDH: C:cloudera(公司) D:distribute H:Hadoo ...
- 学习笔记--我的第一个Java程序
我的第一个Java程序 // pubilc 表示公开的 // class 表示定义一个类 // HelloWorld 表示一个类名 public class HelloWorld { // 表示定义一 ...
- 灵雀云新一期DevOps认证培训圆满结束,下期学员招募同步开启
近日,灵雀云最新一期EXIN DevOps认证培训在北京圆满结束,来自某知名运营商领域ISV的近40名学员以百分百的通过率为此次培训画上圆满的句号. 灵雀云是国内首家在DevOps培训领域与EXIN合 ...
- 函数实现将 DataFrame 数据直接划分为测试集训练集
虽然 Scikit-Learn 有可以划分数据集的函数 train_test_split ,但在有些特殊情况我们只希望它将 DataFrame 数据直接划分为 train, test 而不是像 tr ...
- 机器学习|线性回归算法详解 (Python 语言描述)
原文地址 ? 传送门 线性回归 线性回归是一种较为简单,但十分重要的机器学习方法.掌握线性的原理及求解方法,是深入了解线性回归的基本要求.除此之外,线性回归也是监督学习回归部分的基石. 线性回归介绍 ...