美文网首页微信小程序开发前端小圈子让前端飞
微信小程序及h5,基于taro,zoro最佳实践探索

微信小程序及h5,基于taro,zoro最佳实践探索

作者: FaureWu | 来源:发表于2018-11-18 20:15 被阅读48次

    这段时间一直忙于公司业务中,不可自拔,好在通过零零星星的点滴时间,慢慢的还是完成了脚手架搭建。微信小程序发展到现在,已经不再像以前只是简简单单的应用,业务愈发的臃肿了起来,同时也催生出了许多框架,mpvue,wepy以及后起之秀taro等。

    搭建这次脚手架的目的主要是为了满足后期小程序快速开发的需求,首先看看该脚手架搭建的简单的todo演示应用效果


    打包生成的微信小程序演示.gif
    打包生成的h5应用演示.gif

    最初选择taro,主要的原因是习惯于react开发方式,在taro还没发布正式版之前,上一个项目选择了wepy作为开发框架,深陷苦扰,我们不得不游走切换于wepy语法及原生小程序组件语法之间

    特性

    • 简化redux的引入和配置,只需简单几步即可快速开发
    • 简易的环境配置,支持配置多环境开发
    • 对于业务错误进行了全局捕获,即可统一提示,又可定制性处理
    • 引入资源自动上传阿里云oss服务器,并自动替换成最终服务器路径
    • 封装request,支持restful api

    快速开始

    我们使用yarn工具代替npm进行依赖管理,没有安装yarn的,请先安装,实在不想安装,可以用npm代替

    绑定oss配置信息

    首先我们克隆脚手架到本地服务器

    $ git clone git@github.com:FaureWu/ztaro.git
    

    安装依赖包

    $ cd ./ztaro
    $ yarn # 如果没有安装yarn 则可以使用npm install
    

    接下来我们需要配置阿里云oss服务器,打开文件./ztaro/config/config.js

    module.exports = {
      // 阿里云oss插件配置
      oss: {
        dev: {
          accessKeyId: '************',
          accessKeySecret: '***************',
          endpoint: 'https://************.aliyuncs.com',
          region: '*************',
          bucket: '*********',
        },
        prod: {
          accessKeyId: '************',
          accessKeySecret: '***************',
          endpoint: 'https://************.aliyuncs.com',
          region: '*************',
          bucket: '*********',
        },
        path: 'src/assets/',
        prefix: '@oss',
        formats: ['png', 'jpeg', 'jpg', 'svg'],
      },
    }
    

    可以看到oss配置项,该配置项支持区分编译环境BUILD_ENV,dev为开发环境下的配置,prod为线上环境的配置,要是没有区分,那就都配置成一样的嘛,其中accessKeyId,accessKeySecret,endpoint,region,bucket都是阿里云oss基本配置,这里就不多说了,说一下其他参数吧

    • path 这个路径用于指定需要上传到阿里云oss资源的搜索路径,这个路径下的资源并不会所有的全部都上传到阿里云,只会上传在代码中使用的并且以prefix开头的资源
    • prefix 需要上传到阿里云oss的前缀,也类似path的路径别名
    • formats 需要上传资源格式

    通过以上配置之后我们便可以在js代码中

    以下代码仅用于展示如何使用,需要把资源放入上面配置的path路径下

    <Image src="@oss/logo.jpeg" /> // 可以这样用
    

    或者

     const activeHomeIcon = '@oss/home-active.png' // 或者这样用
    
    class App extends Component {
      config: {
         tarbar: {
            list: [
              {
                pagePath: 'pages/home/home',
                iconPath: '@oss/home.png', // 还可以这样用
                selectedIconPath: activeHomeIcon,
                text: '首页',
              },
            ],
         },
      },
    }
    

    或者在样式文件中

    .app {
      background: url('@oss/logo.jpeg')
    }
    

    又或者在json文件中

    {
      "logo": "@oss/logo.jpeg"
    }
    

    让微信小程序跑起来

    执行如下命令,该命令会做三件事

    • 编译taro语法为微信小程序语法,并启动监听文件修改
    • 启动本地mock服务器
    • 启动gulp任务,上传图片资源,并监听文件修改
    $ yarn mock:weapp
    

    等待命令执行完成,会在根目录下生成dist目录,打开微信开发者工具,选择编译后dist预览最终效果

    编写todo应用

    当我们准备开始编写一个功能前,我们希望能数据驱动开发,所以首先我们编写模拟api请求,该脚手架主要是采用express搭建一个简易的node api服务器,通过faker进行模拟数据,如果习惯其他生成模拟数据的库,可以替换faker

    编写获取todo列表的接口,新建文件ztaro/mock/todos.js

    const faker = require('faker')
    
    function createTodos(number) {
      const todos = []
      for (let i = 0; i < number; i += 1) {
        todos.push({
          id: faker.random.uuid(),
          text: faker.random.words(10),
        })
      }
    
      return todos
    }
    
    let todos = createTodos(faker.random.number({ min: 3, max: 6 }))
    
    function getTodos(req, res) {
      res.status(200).json({
        code: 'success',
        message: '获取待办列表成功',
        data: todos,
      })
    }
    
    module.exports = {
      'GET /v1/todos': getTodos,
    }
    

    编写getTodos的request请求,新建/ztaro/src/requests/todos.js

    import request from '../utils/request'
    
    export function getTodos() {
      return request({
        url: '/v1/todos',
      })
    }
    

    编写用于todos model,在ztaro/src/models/todos.js

    import { getTodos } from '../requests/todos'
    
    export default {
      namespace: 'todos',
      state: {
        lists: [],
      },
      // 这里的配置用于扩展model,model之前共用逻辑
      // common mixins定义于ztaro/src/mixins/common.js
      // 主要提供共用的update action
      mixins: ['common'],
      effects: {
        async getTodos(action, { put }) {
          const { data } = await getTodos()
          // 这里的update是由于上面引入了common mixins
          // 如果没有引入打开下方的reducers注释
          put({ type: 'update', payload: { lists: data } })
        },
      },
      // reducers: {
      //  update({ payload }, state) {
      //    return { ...state, ...payload }
      //  },
      // },
      },
    

    将todos model注入到应用中,引入到ztaro/src/models/index.js

    import todos from './todos'
    
    export default [todos]
    

    该文件会被引入到到app.js中

    数据准备已经完成,我们可以开始编写界面,ztaro/src/pages/todos/*

    import Taro, { Component } from '@tarojs/taro'
    import { View } from '@tarojs/components'
    import { connect } from '@tarojs/redux'
    import { dispatcher } from '@opcjs/zoro'
    
    import ComponentSpin from '../../components/spin/spin'
    
    import './todos.scss'
    
    // 从redux中绑定数据到界面
    @connect(({ todos }) => ({
      todos: todos.lists,
    }))
    class PageTodos extends Component {
      config = {
        navigationBarTitleText: '待办事项',
      }
    
      state = {
        loading: false,
      }
    
      componentWillMount() {
        this.showLoading()
        dispatcher.todos
          .getTodos()
          // 获取成功之后执行.then
          .then(this.hideLoading)
          // 获取失败之后执行.catch
          .catch(this.hideLoading)
      }
    
      showLoading = () => this.setState({ loading: true })
    
      hideLoading = () => this.setState({ loading: false })
    
      render() {
        const { todos } = this.props
        const { value, loading } = this.state
    
        return (
          <View className="todos">
            <ComponentSpin loading={loading} />
            <View className="logo" />
            {todos.map(todo => (
              <View className="todo" key={todo.id}>
                <Text>{todo.text}</Text>
              </View>
            ))}
          </View>
        )
      }
    }
    
    export default PageTodos
    

    最后我们需要编写我们的样式

    .todos {
      position: relative;
      counter-reset: count;
    
      .logo {
        display: block;
        height: 160px;
        // 这里以@oss前坠开头,因此编译时会被上传至阿里云oss,并替换成最终路径
        background-image: url("@oss/logo.jpeg");
        background-size: auto 160px;
        background-repeat: no-repeat;
        background-origin: center;
      }
    
      .todo {
        font-size: 28px;
        word-break: break-all;
        counter-increment: count;
        padding: 10px;
        display: flex;
        flex-direction: row;
        justify-content: flex-start;
        align-items: flex-start;
    
        &::before {
          content: counter(count);
          display: inline-block;
          border: 2px solid #e9e9e9;
          padding: 0 10px;
          margin: 0 10px 0 0;
          border-radius: 10px;
        }
      }
    }
    
    

    全局错误提示

    在zoro框架中,可以注册一个全局错误函数onError,该函数注册于ztaro/src/app.js

    import zoro from '@opcjs/zoro'
    
    const app = zoro({
      onError(error) {
        if (error.message) {
          Taro.showToast({
            icon: 'none',
            title: error.message,
            duration: 2000,
          })
        }
      },
    })
    

    zoro框架会对于每一个effect外层做了try/catch,捕获在执行effect过程中抛出的一切错误,并回调到onError函数里

    那我们如何捕获异步请求的错误呢,我们可以移步ztaro/src/utils/request.js

    export default function request(options) {
      const { url } = options
      return Taro.request(
        resolveParams({
          ...options,
          url: `${CONFIG.SERVER}${url}`,
          mode: 'cors',
          header: {
            'content-type': 'application/json',
            ...options.header,
          },
        }),
      )
      .then(checkHttpStatus)
      .then(checkSuccess)
      .catch(throwError)
    }
    

    封装的request函数中有checkHttpStatus,checkSuccess两个函数,我们分别看下它们做了什么

    function checkHttpStatus(response) {
      // 当http状态在200到300之间时,说明请求是成功的
      // 我们只需返回响应的数据即可
      if (response.statusCode >= 200 && response.statusCode < 300) {
        return response.data
      }
      
      // 当状态出现错误时,我们需要抛出相关信息
      // 这样错误被一层层往上抛出,最终被zoro捕获,回调onError函数
      const message =
        HTTP_ERROR[response.statusCode] || `ERROR CODE: ${response.statusCode}`
      const error = new Error(message)
      error.response = response
      throw error
    }
    
    function checkSuccess(data) {
      // 当数据是个字符串,或者是ArrayBuffer时,我们认为业务是成功的
      // 返回数据即可
      if (typeof data === 'string' && data instanceof ArrayBuffer) {
        return data
      }
    
      // 当业务响应数据中的code值返回SUCCESS时,我们认为业务是成功的
      // 这里主要根据与后端约定好的格式,你可以根据实际情况进行更改
      if (
        typeof data.code === 'string' &&
        data.code.toLocaleUpperCase() === 'SUCCESS'
      ) {
        return data
      }
      
      // 当业务出现错误时,我们依旧需要获取后台抛出的错误,
      // 像上一层抛出错误,最终被zoro捕获,回调onError
      const error = new Error(data.message)
      error.data = data
      throw error
    }
    

    这样处理过后,当后台接口报错,或者业务错误时,就会看到弹出toast错误提示了,无需额外的处理

    那当我们想要屏蔽某些不那么重要的接口,错误提示,我们该怎么办呢?就那getTodos来举例,我们只需修改它

    async getTodos(action, { put }) {
      try {
        const { data } = await getTodos()
        put({ type: 'update', payload: { lists: data } })
      } catch (error) {
        // 这里仅仅是为了可以知道该接口是否错误
        return { isError: true, error }
      }
    }
    

    这样即使是getTodos接口抛出错误也不会触发全局错误提示了

    接下来我们可以自定义该接口的错误逻辑了

    dispatcher.todos.getTodos().then((data = {}) => {
      if (data.isError) {
        // 执行一些相关的错误
      }
    })
    

    假如我们想要在dispatcher.todos.getTodos()的.then函数中获取到某些数据,我们又该如何呢?依旧那getTodos举例

    async getTodos(action, { put }) {
      const { data } = await getTodos()
      put({ type: 'update', payload: { lists: data } })
      return data
    }
    

    然后我们在使用的时候

    dispatcher.todos.getTodos().then(data => console.log(data))
    

    与服务器进行接口联调

    在进行接口联调之前,我们首先需要配置开发环境下的api地址,打开./ztaro/config/config.js

    module.exports = {
      server: {
        // 修改下面这一行为你的开发环境下的api服务器
        dev: 'https://devapiserver',
      },
    }
    

    配置完成后执行如下命令

    yarn dev:weapp
    

    这个所有的api便会指向开发环境下的api服务器地址了

    打包测试包

    执行如下命令即可

    yarn build:weapp-dev
    

    打包线上包

    首先配置生产环境下的api地址,打开./ztaro/config/config.js

    module.exports = {
      server: {
        // 修改下面这一行为你的生产环境下的api服务器
        prod: 'https://devapiserver',
      },
    }
    

    配置完成后执行

    yarn build:weapp
    

    以上教程仅列出了微信小程序端,h5端也基本是一致的,只需执行的命令中,将weapp替换成h5即可

    其他更详细的使用方式,请查看对应github仓库

    欢迎star,欢迎加我咨询相关问题

    相关文章

      网友评论

        本文标题:微信小程序及h5,基于taro,zoro最佳实践探索

        本文链接:https://www.haomeiwen.com/subject/vxgvfqtx.html