Taro微信小程序开发

一.什么是taro?

Taro 是一套遵循 React 语法规范的 多端开发 解决方案。通过一套react的代码,就可以分别编译出微信小程序、H5、支付宝小程序。。。
我根据taro redux模板创建了一套自己写的H5种子项目,大家可以一起学习,项目地址:https://github.com/WangxinsHub/taro-seed

二.如何安装taro?

1.首先全局安装taro命令行:

$ npm install -g @tarojs/cli

$ yarn global add @tarojs/cli

2.创建taro种子项目

$ taro init myApp


创建redux模板

3.编译taro,在创建的种子项目中,package文件如下:


"scripts": {

"build:weapp": "taro build --type weapp",//打包小程序

"build:h5": "taro build --type h5", //打包H5

"dev:weapp": "npm run build:weapp -- --watch",//编译小程序

"dev:h5": "npm run build:h5 -- --watch",//编译H5

},

三、开发前注意

小程序工具:

需要设置关闭 ES6 转 ES5 功能,开启可能报错

需要设置关闭上传代码时样式自动补全,开启可能报错

需要设置关闭代码压缩上传,开启可能报错

四、项目说明

taro推荐目录.png

我开发中的目录.png

1.dist是编译(dev/build)结果目录

2.config配置目录

index.js(默认配置)

const path  = require('path')
const config = {
  projectName: 'Taro-time-bus',
  date: '2019-1-8',
  designWidth: 750,//设计稿以 iPhone6 750px 作为设计尺寸标准。
  //目前 Taro 支持 750、 640 、 828 三种尺寸设计稿,他们的换算规则如下:
  deviceRatio: {
    '640': 2.34 / 2,
    '750': 1,
    '828': 1.81 / 2
  },
  // 项目源码目录
  sourceRoot: 'src',
  // 项目产出目录
  outputRoot: 'dist',
  // 通用插件配置
  plugins: {
    //plugins 用来设置一些各个端通用的编译过程配置,例如 babel 配置,JS/CSS 压缩配置等。
    babel: {
      sourceMap: true,
      presets: ['env'],
      plugins: ['transform-decorators-legacy', 'transform-class-properties', 'transform-object-rest-spread']
    }
    /*
    //设置打包过程中的 JS 代码压缩
    uglify: {
      enable: true,
      config: {
        // 配置项同 https://github.com/mishoo/UglifyJS2#minify-options
      }
    },
    //设置打包过程中的 CSS 代码压缩
    csso: {
      enable: true,
      config: {
        // 配置项同 https://github.com/css/csso#minifysource-options
      }
    }*/
  },
  // 全局变量设置
  defineConstants: {
    context:{
      iconPath:'xxx'
    }
  },
  alias: {
    '@components': path.resolve(__dirname,'../src/components'),
    '@icons': path.resolve(__dirname,'../src/icons'),
    '@src': path.resolve(__dirname,'../src/'),
    '@utils': path.resolve(__dirname, '..', 'src/utils')
  },
  weapp: {
    //小程序编译过程的相关配置。
    compile: {
      compressTemplate: true,//决定小程序打包时是否需要压缩 wxml
    },
    module: {
      postcss: {
        autoprefixer: {
          enable: true,
          config: {
            browsers: [
              'last 3 versions',
              'Android >= 4.1',
              'ios >= 8'
            ]
          }
        },
        pxtransform: {
          enable: true,
          config: {
            onePxTransform: true, //设置 1px 是否需要被转换
            unitPrecision: 5,//REM 单位允许的小数位。
            selectorBlackList: [],//黑名单里的选择器将会被忽略。
            replace: true,//直接替换而不是追加一条进行覆盖。
            mediaQuery: false,//允许媒体查询里的 px 单位转换
            minPixelValue: 0//设置一个可被转换的最小 px 值
          }
        },
        url: {
          enable: true,
          config: {
            limit: 10240 // 设定转换尺寸上限
          }
        },
        cssModules: {
          enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
          config: {
            namingPattern: 'module', // 转换模式,取值为 global/module
            generateScopedName: '[name]__[local]___[hash:base64:5]'
          }
        }
      }
    }
  },
  h5: {
    devServer: {
      port: 10086
    },
    publicPath: '/', //设置输出解析文件的目录。
    staticDirectory: 'static',//h5 编译后的静态文件目录。
    esnextModules: ['taro-ui'],//配置需要额外的编译的源码模块,比如taro-ui:
    miniCssExtractPluginOption: {
      filename: 'css/[name]/[hash].css',
      chunkFilename: 'css/[name]/[hash].css'
    },
    module: {
      postcss: {
        autoprefixer: {
          enable: true,
          config: {
            browsers: [
              'last 3 versions',
              'Android >= 4.1',
              'ios >= 8'
            ]
          }
        },
        cssModules: {
          enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
          config: {
            namingPattern: 'module', // 转换模式,取值为 global/module
            generateScopedName: '[name]__[local]___[hash:base64:5]'
          }
        }
      }
    }
  }
}

module.exports = function (merge) {
  if (process.env.NODE_ENV === 'development') {
    return merge({}, config, require('./dev'))
  }
  return merge({}, config, require('./prod'))
}

dev.js

module.exports = {
  env: {
    NODE_ENV: '"development"',
    API_HOSTNAME:JSON.stringify('https://appdev.ibuscloud.com'),//test环境地址
  },
  defineConstants: {
  },
  weapp: {
  },
  h5: {}
}

3.入口文件为 app.js

import '@tarojs/async-await'
import Taro, { Component } from '@tarojs/taro'
import { Provider } from '@tarojs/redux'
import 'taro-ui/dist/style/index.scss' // 全局引入一次即可

import Index from './pages/index'

import configStore from './store'

import './app.less'

// 如果需要在 h5 环境中开启 React Devtools
// 取消以下注释:
// if (process.env.NODE_ENV !== 'production' && process.env.TARO_ENV === 'h5')  {
//   require('nerv-devtools')
// }

const store = configStore()

class App extends Component {

  config = {
    pages: [
      'pages/index/index',
      'pages/search/index',
      'pages/lineDetail/index',
      'pages/page2/index'
    ],
    "permission": {
      "scope.userLocation": {
        "desc": "你的位置信息将用于小程序位置接口的效果展示"
      }
    },
    window: {
      backgroundTextStyle: 'light',
      navigationBarBackgroundColor: '#fff',
      navigationBarTitleText: 'WeChat',
      navigationBarTextStyle: 'black'
    }
  }

  componentDidMount () {}

  componentDidShow () {
    //获取用户位置,TODO:H5
    Taro.getLocation().then(data=>{
      console.log(data)
      const {latitude,longitude} = data ;
      Taro.setStorageSync('userLat', latitude);
      Taro.setStorageSync('userLng', longitude);
    })
  }

  componentDidHide () {}

  componentCatchError () {}

  componentDidCatchError () {}

  // 在 App 类中的 render() 函数没有实际作用
  // 请勿修改此函数
  render () {
    return (
      <Provider store={store}>
        <Index />
      </Provider>
    )
  }
}

Taro.render(<App />, document.getElementById('app'))

1️⃣其中config主要参考微信小程序的全局配置而来,在编译成小程序时,这一部分配置将会被抽离成 app.json,而编译成其他端,亦会有其他作用。
页面路由在此配置,页面背景色导航栏等也在此设置
2️⃣app.js的生命周期、页面与组件的生命周期:
而且由于入口文件继承自 Component 组件基类,它同样拥有组件生命周期,但因为入口文件的特殊性,他的生命周期并不完整,如下

image.png

3️⃣普通页面生命周期与component一致
image.png

小程序专有的方法:(H5中暂不支持)
image.png

4️⃣组件Taro 的组件同样是继承自 Component 组件基类,与页面类似,组件也必须包含一个 render 函数,返回 JSX 代码。 比页面多了一个componentWillReceiveProps。

注意 : 组件的 constructor 与 render 提前调用,所以componentWillMount这个生命周期有一个滞后性,不可以直接在render中用路由得来的数据

render () {
  // 在 willMount 之前无法拿到路由参数
  const abc = this.$router.params.abc
  return <Custom adc={abc} />
}

// 正确写法
componentWillMount () {
  const abc = this.$router.params.abc
  this.setState({
    abc
  })
}
render () {
  // 增加一个兼容判断
  return this.state.abc && <Custom adc={abc} />
}


由于微信小程序里页面在 onLoad 时才能拿到页面的路由参数,而页面 onLoad 前组件都已经 attached 了。因此页面的 componentWillMount 可能会与预期不太一致。

五、开发中的问题

1.静态资源的引入

1️⃣通过ES6的import引用图片、js、等文件(暂不支持svg),而且不需要安装任何 loader。
2️⃣可以先上传到服务器,然后引用服务器的地址(在less中background用的多一点)

全局原始app.less 只会影响到页面级别的文件,组件的获取不到全局的样式
可以同过@import 让组件获得app.less全局样式

@import "../../app";

2.jsx的支持程度:

<View {...this.props} />
 
<View {...props} />
 
<Custom {...props} />

以上是错误写法

3.组件化 & props &state

1️⃣使用 PropTypes 检查类型

Greeting.propTypes = {
  name: PropTypes.string
};

2️⃣给组件设置 defaultProps

3️⃣组件传递函数属性名以 on 开头

4️⃣当组件传入jsx的时候必须用render开头,在小程序中其实是通过slot插槽来实现的,所以和this.props.children一样, this.props.children && this.props.children、this.props.children[0] 在 Taro 中都是非法的。且组合只能传入单个 JSX 元素,不能传入其它任何类型。当你需要进行一些条件判断或复杂逻辑操作的时候,可以使用一个 Block 元素包裹住,然后在 Block 元素的里面填充其它复杂的逻辑。

state:

5️⃣state this.state 和 props 一定是异步更新的,所以你不能在 setState 马上拿到 state 的值

6️⃣不要在 stateprops 上用同名的字段,因为这些被字段在微信小程序中都会挂在 data
7️⃣尽量避免在 componentDidMount 中调用 this.setState 因为在 componentDidMount 中调用 this.setState 会导致触发更新 (尽量避免,可以componentWillMount 中处理)
不要在 componentWillUpdate/componentDidUpdate/render 中调用 this.setState

5.事件

事件类型参照微信小程序https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html

6.路由

1️⃣传参:

Taro.navigateTo({
  url: '/pages/page/path/name?id=2&type=test'
})

接收:this.$router.params

2️⃣预加载传参
在微信小程序中,从调用 Taro.navigateTo、Taro.redirectTo 或 Taro.switchTab 后,到页面触发 componentWillMount 会有一定延时。因此一些网络请求可以提前到发起跳转前一刻去请求。


image.png

传参:

this.$preload({
  x: 1,
  y: 2
})
Taro.navigateTo({ url: '/pages/B/B' })

(也能够绕过 componentWillMount 延时)

接收componentWillMount () {
  console.log('preload: ', this.$router.preload)
}

7异步编程 以及 接口请求

$ yarn add @tarojs/async-await
import '@tarojs/async-await'
异步dispatch,action.js:

import '@tarojs/async-await'
import {
  ADD,
  MINUS,
  ASYNC,
  ASYNC_BEFORE
} from './action-type';
import Http from '../../api/Server'
import Url from '../../api/url';

export const add = () => {
  return {
    type: ADD
  }
}
export const minus = () => {
  return {
    type: MINUS
  }
}

export const asyncAdd = (params) => {
  // 返回函数,异步dispatch
  return async dispatch => {
    try{
      dispatch({
        type: ASYNC_BEFORE,
      })
      let result = await Http.request('post',Url.lineRecommendQuery,params);
      // 如果不成功,则将不成功的信息打印出来
      if(result){
        if(!result.success) console.error(result.message);
        dispatch({
          type: ASYNC,
          response: result,
        })
      }
    }catch(err){
      console.error(err);
    }
  }
}

Http 请求工具类:api.js

import Taro from '@tarojs/taro'

class Http {
  constructor(){
    const HOSTNAME = process.env.API_HOSTNAME
    this.url={
    
   }
  }
  request(method = 'post', url, params) {
    console.log(process.env.TARO_ENV)
    Taro.showNavigationBarLoading();
    return new Promise((resolve, reject) => {
      Taro.request({
        url,
        method,
        data: params,
        header: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'Accept': '*/*'
        }
      }).then(res => {
        Taro.hideNavigationBarLoading()
        resolve(typeof res.data === 'object' ? res.data : JSON.parse(res.data))
      }, err => {
        Taro.hideNavigationBarLoading()
        reject(err)
      })
    })
  }
}

export default new Http();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容