博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
微信小程序模块化开发实践
阅读量:6295 次
发布时间:2019-06-22

本文共 6986 字,大约阅读时间需要 23 分钟。

前言: 省略...

准备

  • 了解微信小程序是什么?

  • 了解应用状态管理方案: , 也是的具体实现

  • 了解Javascript打包工具:

  • 了解ES6/7代码转译(transcompile)工具: , 原理大致是借助语法分析工具(Esprima之类的), 将代码解析成抽象语法树, 再"重写"成最终的代码.

  • Javascript测试工具: jest, mocha等等, 请根据需要选择.

TL;DR;

微信小程序目前版本的API实现需要兼顾方方面面, 所以仍然使用callback写法, 众所周知的Callback-Hell是传统js语法上的历史问题, 但毕竟称手的工具是开发效率的源泉. 因此笔者对当前版本的微信小程序API做了简单的封装 .

同时, 微信小程序框架本身专注于交互和UI的实现, 并未提供内置的状态管理, 如果众多的异步操作都直接在App或者Page中一一实现, 相信写起来会是一场噩梦, 而且不易于测试, 笔者又因此针对微信小程序实现了一个基于Redux方案的状态管理模块, 用以方便的在小程序中实现应用状态管理 .

特别地, 微信小程序构建(编译)时不支持从App scope之外require文件, npm在此就不好用了. 所以, 我们需要实时build依赖到应用本地, 在微信小程序中引用本地的modules, 对于这种构建场景, 笔者认为webpack算是最方便的方案. 大家都说COPY到本地是最最最方便的方式~~

安装工具和依赖模块

下载微信小程序开发者工具

开发者工具是用nwjs模拟的环境, 实际在微信中是JavascriptCore环境, 不过不用担心, 只是两个不同的vm, 本质是一样的.

nwjs可能存在一些, 写代码的时候注意一下就好.

下载

用npm命令开始一个微信小程序项目

mkdir myappcd myappnpm 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文件, 若需要打包其他诸如css, image等资源, 请读者自行研究. 实际上, 微信小程序包有1MB的上限.

// webpack.config.jsvar 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做范例了.

// 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, 希望你会感到很舒服~~.

在这个范例(myapp)中, 我们目标是去查询 github/octokit 的开源项目, 并显示在小程序中.

建议不了解Redux的读者先去快速了解一下(2 hours)

myapp模块

  • 定义store: /es6/store.js

这里只是简单的范例, 实际中会有比较复杂的store shape, 需要引入更多的middleware来处理动作和状态的变化.

// /es6/store.jsimport {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 actions时发生的状态变化的function, 参数总是为(state, action)

// /es6/reducers.jsimport { 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

Action通常是个Plain Object, 总是被Store dispatch, 描述了"发生了什么, 结果是什么"的逻辑

// /es6/actions.jsimport {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.jsimport {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.jsapp.json

// /app.jsApp({  // 方便起见, 这里不做任何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// 引入编译过的modulesimport {  weapp,  connect,  bindActionCreators,  store,  actions} from '../../lib/app'// 标准Page定义Objectconst config = {  data: {    projects: [] //for init-render  },  onReady(){    // 哪里来的 loadProjects? 往下看    this.loadProjects('octokit')  },  onStateChange(nextState){    this.setData({projects: nextState})  }}// connect store with pageconst 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

{
{project.name}}

后记

范例代码未实际运行, 仅用以表示开发步骤, 我会尽快把这个范例实现完整, 放到github上.

最后, 谢谢您耐心阅读至此!

参考

转载地址:http://rtvta.baihongyu.com/

你可能感兴趣的文章
在Android源码树中添加userspace I2C读写工具(i2c-util)
查看>>
Nginx upstream的几种分配方式
查看>>
《互联网运营智慧》第7章“简单cdn”正式版下载
查看>>
如何解决SQL Server 2008 R2中“阻止保存要求重新创建表的更改”的问题!
查看>>
基于Xcode原型驱动的iOS应用设计
查看>>
SOA标准之----SCA架构思想
查看>>
9.VMware View 4.6安装与部署-connection server(View Replica Server)
查看>>
项目管理和产品管理绉议
查看>>
一步一步SharePoint 2007之三十七:在SharePoint中实现Workflow(3)——运行Workflow
查看>>
后端码农谈前端(CSS篇)第三课:选择器
查看>>
Rafy 领域实体框架设计 - 重构 ORM 中的 Sql 生成
查看>>
编程之基础:数据类型(二)
查看>>
倒排索引PForDelta压缩算法——基本假设和霍夫曼压缩同
查看>>
java基础--相等
查看>>
记一次网站服务器搬迁实录
查看>>
Sql server restore script(还原数据库正确的步骤)
查看>>
探秘重编译(Recompilations)(1/2)
查看>>
Lucene.Net 的“System.IndexOutOfRangeException: 索引超出了数组界限”错误
查看>>
Android杂谈--layout的横竖屏处理
查看>>
升级Windows Phone Developer Tools Beta
查看>>