微信小顺序模块化开辟实践

媒介: 省略…

预备

  • 相识微信小顺序是什么? 微信小顺序官方文档

  • 相识运用状况治理计划: Redux, 也是Flux架构的详细完成

  • 相识Javascript打包东西: webpack

  • 相识ES6/7代码转译(transcompile)东西: Babel, 道理大抵是借助语法分析东西(Esprima之类的), 将代码剖析成笼统语法树, 再”重写”成终究的代码.

  • Javascript测试东西: jest, mocha等等, 请根据须要挑选.

TL;DR;

微信小顺序现在版本的API完成须要统筹各个方面, 所以依然运用callback写法, 尽人皆知的Callback-Hell是传统js语法上的历史问题, 但毕竟称手的东西是开辟效力的源泉. 因而笔者对当前版本的微信小顺序API做了简朴的封装 weapp.

同时, 微信小顺序框架自身专注于交互和UI的完成, 并未供应内置的状况治理, 假如浩瀚的异步操纵都直接在App或许Page中逐一完成, 置信写起来会是一场恶梦, 而且不易于测试, 笔者又因而针对微信小顺序完成了一个基于Redux计划的状况治理模块, 用以轻易的在小顺序中完成运用状况治理 redux-weapp.

特别地, 微信小顺序构建(编译)时不支持从App scope以外require文件, npm在此就不好用了. 所以, 我们须要及时build依靠到运用当地, 在微信小顺序中援用当地的modules, 关于这类构建场景, 笔者以为webpack算是最轻易的计划. 人人都说COPY到当地是最最最轻易的体式格局~~

装置东西和依靠模块

下载微信小顺序开辟者东西

开辟者东西是用nwjs模仿的环境, 现实在微信中是JavascriptCore环境, 不过不必忧郁, 只是两个差别的vm, 实质是一样的.

nwjs能够存在一些小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这个模块来处置惩罚 异步action.

装置开辟小顺序的辅佐模块

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.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": "<rootDir>/node_modules/babel-jest" //用babel转译
  },
  "testPathDirs": [
    "<rootDir>/__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) Getting started with Redux – from egghead

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 actions时发作的状况变化的function, 参数老是为(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

Action通常是个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.jsapp.json

// /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上.

末了, 谢谢您耐烦浏览至此!

参考

    原文作者:87654367
    原文地址: https://segmentfault.com/a/1190000007998447
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞