Taro + dva 使用小結(jié)(搭建配置過程)

最近寫一個微信小程序的項(xiàng)目乙帮,由于是協(xié)同開發(fā),前期的搭建工作由另一個妹子完成极景,現(xiàn)在項(xiàng)目階段一完成了察净,為了備忘回顧,做一個階段性小結(jié)盼樟。
在寫小程序之前經(jīng)過對比最后采用了京東凹凸實(shí)驗(yàn)室開發(fā)的類react框架Taro氢卡,用框架的好處就不多說了,比直接寫原生小程序方便太多晨缴。數(shù)據(jù)管理采用的是封裝了reduxdva框架译秦,如果沒有學(xué)過的同學(xué)可以去看看文檔。先聲明篇幅比較長击碗,如果你需要筑悴,還請看完,相信一定有幫助延都,不想看的同學(xué)文末放了GitHub地址雷猪,自己去下睛竣。

附上文檔鏈接:

taro文檔:https://nervjs.github.io/taro/docs/README.html
dva文檔:https://dvajs.com/guide/

1.基礎(chǔ)步驟

// 全局安裝taro (cnpm為淘寶鏡像)
 cnpm install -g @tarojs/cli
// 創(chuàng)建項(xiàng)目
taro init taro-demo

如下配置(推薦為項(xiàng)目配上ts):

項(xiàng)目創(chuàng)建簡單配置

安裝與 react-redux API 幾乎一致的包 @tarojs/redux
cnpm install --save redux @tarojs/redux @tarojs/redux-h5 redux-thunk redux-logger
安裝dva
cnpm install --save dva-core dva-loading
  • dva-core:封裝了 redux 和 redux-saga的一個插件
  • dva-loading:管理頁面的loading狀態(tài)

2.整理項(xiàng)目文件

刪除
  • 刪除./src/page文件夾下的index文件夾
添加
  • ./src文件夾下添加如下文件夾(根據(jù)自己實(shí)際情況和項(xiàng)目需求進(jìn)行配置晰房,只羅列一些必要的):
    assets :靜態(tài)資源,如images射沟、scss殊者、iconfont...
    components :編寫共用組件
    config :項(xiàng)目配置文件
    models :dva插件model函數(shù)引用或者共用的js
    types :公共typescript類型申明
    utils :封裝的插件

3.編寫插件(主要且常用的)

1.在./src/config下創(chuàng)建index.ts,添加項(xiàng)目配置信息验夯,例如:
/** 
 * 線上環(huán)境
 * 為了方便測試猖吴,使用的是聚合數(shù)據(jù)免費(fèi)接口
 * 網(wǎng)址:https://www.juhe.cn/
 */
export const ONLINEHOST = 'http://api.juheapi.com'

/** 
 * 測試環(huán)境
 */
export const QAHOST = 'http://xxx.cn'

/** 
 * 線上mock
 */
export const MOCKHOST = 'http://xxx/mock'

/** 
 * 是否mock
 */
export const ISMOCK = false

/**
 * 當(dāng)前的host  ONLINEHOST | QAHOST | MOCKHOST
 */
export const MAINHOST = ONLINEHOST

/**
 * 全局的分享信息 不用每一個都去寫
 */
export const SHAREINFO = {
  'title': '分享標(biāo)題',
  'path': '路徑',
  'imageUrl': '圖片'
}

2.在./src/utils下創(chuàng)建dva.ts,配置dva挥转,內(nèi)容如下:
import { create } from 'dva-core';
import { createLogger } from 'redux-logger';
import createLoading from 'dva-loading';

let app
let store
let dispatch
let registered

function createApp(opt) {
  // redux日志
  opt.onAction = [createLogger()]
  app = create(opt)
  app.use(createLoading({}))

  if (!registered) opt.models.forEach(model => app.model(model))
  registered = true
  app.start()

  store = app._store
  app.getStore = () => store
  app.use({
    onError(err) {
      console.log(err)
    },
  })

  dispatch = store.dispatch

  app.dispatch = dispatch
  return app
}

export default {
  createApp,
  getDispatch() {
    return app.dispatch
  }
}

3.在./src/config下創(chuàng)建requestConfig.ts海蔽,統(tǒng)一配置請求接口共屈,內(nèi)容如下:
/** 
 * 請求的公共參數(shù)
 */
export const commonParame = {}

/**
 * 請求映射文件
 */
export const requestConfig = {
  loginUrl: '/api/user/wechat-auth', // 微信登錄接口
}
5.在./src/utils下創(chuàng)建tips.ts,整合封裝微信原生彈窗党窜,內(nèi)容如下:
import Taro from '@tarojs/taro'
/**
 * 提示與加載工具類
 */
export default class Tips {
  static isLoading = false

  /**
   * 信息提示
   */
  static toast(title: string, onHide?: () => void) {
    Taro.showToast({
      title: title,
      icon: 'none',
      mask: true,
      duration: 1500
    });
    // 隱藏結(jié)束回調(diào)
    if (onHide) {
      setTimeout(() => {
        onHide();
      }, 500);
    }
  }
  /**
 * 彈出加載提示
 */
  static loading(title = '加載中', force = false) {
    if (this.isLoading && !force) {
      return
    }
    this.isLoading = true
    if (Taro.showLoading) {
      Taro.showLoading({
        title: title,
        mask: true
      })
    } else {
      Taro.showNavigationBarLoading()
    }
  }

  /**
   * 加載完畢
   */
  static loaded() {
    let duration = 0
    if (this.isLoading) {
      this.isLoading = false
      if (Taro.hideLoading) {
        Taro.hideLoading()
      } else {
        Taro.hideNavigationBarLoading()
      }
      duration = 500
    }
    // 隱藏動畫大約500ms拗引,避免后面直接toast時的顯示bug
    return new Promise(resolve => setTimeout(resolve, duration))
  }

  /**
   * 彈出提示框
   */
  static success(title, duration = 1500) {
    Taro.showToast({
      title: title,
      icon: 'success',
      mask: true,
      duration: duration
    });
    if (duration > 0) {
      return new Promise(resolve => setTimeout(resolve, duration));
    }
  }
}

5.在./src/utils下創(chuàng)建common.ts,共用函數(shù)幌衣,內(nèi)容如下:
/** 時間格式的轉(zhuǎn)換 */
export const formatTime = time => {
 `${pad(time.getHours())}:${pad(time.getMinutes())}:${pad(time.getSeconds())}.${pad(time.getMilliseconds(), 3)}`
}

export var globalData: any = {} // 全局公共變量

6.在./src/utils下創(chuàng)建logger.ts矾削,封裝log函數(shù),內(nèi)容如下:
import {
  formatTime
} from './common'

const defaults = {
  level: 'log',
  logger: console,
  logErrors: true,
  colors: {
    title: 'inherit',
    req: '#9E9E9E',
    res: '#4CAF50',
    error: '#F20404',
  }
}

function printBuffer(logEntry, options) {
  const {
    logger,
    colors
  } = options;

  let {
    title,
    started,
    req,
    res
  } = logEntry

  // Message
  const headerCSS = ['color: gray; font-weight: lighter;']
  const styles = s => `color: ${s}; font-weight: bold`

  // render
  logger.group(`%c ${title} @${formatTime(started)}`, ...headerCSS)
  logger.log('%c req', styles(colors.req), req)
  logger.log('%c res', styles(colors.res), res)
  logger.groupEnd()
}

interface LogEntry {
  started?: object // 觸發(fā)時間
}

function createLogger(options: LogEntry = {}) {
  const loggerOptions = Object.assign({}, defaults, options)
  const logEntry = options
  logEntry.started = new Date()
  printBuffer(logEntry, Object.assign({}, loggerOptions))
}

export {
  defaults,
  createLogger,
}

7.在./src/utils下創(chuàng)建request.ts豁护,封裝http請求哼凯,內(nèi)容如下:
import Taro, { Component } from '@tarojs/taro'
import {
  ISMOCK,
  MAINHOST
} from '../config'
import {
  commonParame,
  requestConfig
} from '../config/requestConfig'
import Tips from './tips'

// import { createLogger } from './logger'

declare type Methods = "GET" | "OPTIONS" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE" | "CONNECT";
declare type Headers = { [key: string]: string };
declare type Datas = { method: Methods;[key: string]: any; };
interface Options {
  url: string;
  host?: string;
  method?: Methods;
  data?: Datas;
  header?: Headers;
}

export class Request {
  //登陸的promise
  static loginReadyPromise: Promise<any> = Promise.resolve()
  // 正在登陸
  static isLogining: boolean = false
  // 導(dǎo)出的api對象
  static apiLists: { [key: string]: () => any; } = {}
  // token
  static token: string = ''

  // constructor(setting) {

  // }
  /**
   * @static 處理options
   * @param {Options | string} opts
   * @param {Datas} data
   * @returns {Options}
   * @memberof Request
   */
  static conbineOptions(opts, data: Datas, method: Methods): Options {
    typeof opts === 'string' && (opts = { url: opts })
    return {
      data: { ...conmomPrams, ...opts.data, ...data },
      method: opts.method || data.method || method || 'GET',
      url: `${opts.host || MAINHOST}${opts.url}`
    }
  }

  static getToken() {
    !this.token && (this.token = Taro.getStorageSync('token'))
    return this.token
  }

  /**
   * 
   * @static request請求 基于 Taro.request
   * @param {Options} opts 
   */
  static async request(opts: Options) {
    // token不存在
    // if (!this.getToken()) { await this.login() }

    // token存在
    // let options = Object.assign(opts, { header: { 'token': this.getToken() } })

    //  Taro.request 請求
    const res = await Taro.request(opts)

    // 是否mock
    if (ISMOCK) { return res.data }

    // 登陸失效 
    if (res.data.code === 99999) { await this.login(); return this.request(opts) }

    // 請求成功
    // if (res.data && res.data.code === 0 || res.data.succ === 0) { return res.data }
    if (res.data) { return res.data }

    // 請求錯誤
    const d = { ...res.data, err: (res.data && res.data.msg) || `網(wǎng)絡(luò)錯誤~` }
    Tips.toast(d.err);
    throw new Error(d.err)
  }

  /**
   * 
   * @static 登陸
   * @returns  promise 
   * @memberof Request
   */
  static login() {
    if (!this.isLogining) { this.loginReadyPromise = this.onLogining() }
    return this.loginReadyPromise
  }

  /**
   *
   * @static 登陸的具體方法
   * @returns
   * @memberof Request
   */
  static onLogining() {
    this.isLogining = true
    return new Promise(async (resolve, reject) => {
      // 獲取code
      const { code } = await Taro.login()

      // 請求登錄
      const { data } = await Taro.request({
        url: `${MAINHOST}${requestConfig.loginUrl}`,
        data: { code: code }
      })

      if (data.code !== 0 || !data.data || !data.data.token) {
        reject()
        return
      }

      Taro.setStorageSync('token', data.data.token)
      this.isLogining = false
      resolve()
    })
  }

  /**
   *
   * @static  創(chuàng)建請求函數(shù)
   * @param {(Options | string)} opts
   * @returns
   * @memberof Request
   */
  static creatRequests(opts: Options | string): () => {} {
    return async (data = {}, method: Methods = "GET") => {
      const _opts = this.conbineOptions(opts, data, method)
      const res = await this.request(_opts)
      // createLogger({ title: 'request', req: _opts, res: res })
      return res
    }
  }

  /**
   *
   * @static 拋出整個項(xiàng)目的api方法
   * @returns
   * @memberof Request
   */
  static getApiList(requestConfig) {
    if (!Object.keys(requestConfig).length) return {}

    Object.keys(requestConfig).forEach((key) => {
      this.apiLists[key] = this.creatRequests(requestConfig[key])
    })

    return this.apiLists
  }
}

// 導(dǎo)出
const Api = Request.getApiList(requestConfig)
Component.prototype.$api = Api
export default Api as any

Tip:這時候tslint會報這樣的錯:類型“Component<any, any>”上不存在屬性“$api”。楚里,因?yàn)槲覀儧]有添加聲明断部,我們可以這樣解決,在./src目錄下創(chuàng)建app-shim.d.ts腻豌,內(nèi)容如下:

/**
 *
 * @static 添加taro等自定義類型
 * @interface Component
 */
import Taro, { Component } from '@tarojs/taro'

// 在Component上定義自定義方法類型
declare module '@tarojs/taro' {
  interface Component {
    $api: any
  }
}

//聲明
declare var require: any
declare var dispach: any

這時候應(yīng)該不報錯了家坎。

8.在./src/config下創(chuàng)建taroConfig.ts,封裝taro小程序的一些方法吝梅,內(nèi)容如下:
/**
 * 進(jìn)行taro的處理 
 * 1.方法的改寫
 * 2.utils的掛載
 * 
 */
import Taro, { Component } from "@tarojs/taro";
import { SHAREINFO } from '../config/index'

/**
 * navigateTo 超過8次之后 強(qiáng)行進(jìn)行redirectTo 否則會造成頁面卡死
 * 
 */
const nav = Taro.navigateTo
Taro.navigateTo = (data) => {
  if (Taro.getCurrentPages().length > 8) {
    return Taro.redirectTo(data)
  }
  return nav(data)
}


/**
 * Component掛載分享方法
 */
Component.prototype.onShareAppMessage = function () {
  return SHAREINFO
}

4.編寫node命令快速創(chuàng)建pagecomponent

先來看一張圖虱疏,就明白為什么需要編寫這樣一個命令了

文件示意圖

當(dāng)你每次需要創(chuàng)建一個頁面的時候需要不斷的創(chuàng)建,這樣太麻煩了苏携,而且容易出錯做瞪,所以寫個node命令快速生成如圖中index文件夾下的5個文件,一條命令的事情右冻,下面上代碼:
首先在根目錄下創(chuàng)建scripts文件夾装蓬,在該文件夾下添加如下文件:

  1. 添加./scripts/template.js,內(nèi)容如下:
/**
 * pages頁面快速生成腳本 
 * 用法:npm run tep `文件名`
 */

const fs = require('fs');

const dirName = process.argv[2];
const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1);
if (!dirName) {
    console.log('文件夾名稱不能為空纱扭!');
    console.log('示例:npm run tep test');
    process.exit(0);
}

//頁面模板
const indexTep = `
import Taro, { Component, Config } from '@tarojs/taro'
import { View } from '@tarojs/components'
// import { connect } from '@tarojs/redux'
// import Api from '../../utils/request'
// import Tips from '../../utils/tips'
import { ${capPirName}Props, ${capPirName}State } from './${dirName}.interface'
import './${dirName}.scss'
// import { } from '../../components'

// @connect(({ ${dirName} }) => ({
//     ...${dirName},
// }))

class ${capPirName} extends Component<${capPirName}Props,${capPirName}State > {
  config:Config = {
    navigationBarTitleText: '標(biāo)題'
  }
  constructor(props: ${capPirName}Props) {
    super(props)
    this.state = {}
  }

  componentDidMount() {
    
  }

  render() {
    return (
      <View className='${dirName}-wrap'>
          
      </View>
    )
  }
}

export default ${capPirName}
`

// scss文件模版
const scssTep = `
${dirName}-wrap {
    width: 100%;
    min-height: 100vh;
}
`

// config 接口地址配置模板
const configTep = `
export default {
  test: '/wechat/perfect-info', //xxx接口
}
`
// 接口請求模板
const serviceTep = `
import Api from '../../utils/request'

export const testApi = data => Api.test(
  data
)
`

//model模板

const modelTep = `
// import Taro from '@tarojs/taro';
import * as ${dirName}Api from './service';

export default {
  namespace: '${dirName}',
  state: {
  },

  effects: {},

  reducers: {}

}
`

const interfaceTep = `
/**
 * ${dirName}.state 參數(shù)類型
 *
 * @export
 * @interface ${capPirName}State
 */
export interface ${capPirName}State {}

/**
 * ${dirName}.props 參數(shù)類型
 *
 * @export
 * @interface ${capPirName}Props
 */
export interface ${capPirName}Props {}
`

fs.mkdirSync(`./src/pages/${dirName}`); // mkdir $1
process.chdir(`./src/pages/${dirName}`); // cd $1

fs.writeFileSync(`${dirName}.tsx`, indexTep); //tsx
fs.writeFileSync(`${dirName}.scss`, scssTep); // scss
fs.writeFileSync('config.ts', configTep); // config
fs.writeFileSync('service.ts', serviceTep); // service
fs.writeFileSync('model.ts', modelTep); // model
fs.writeFileSync(`${dirName}.interface.ts`, interfaceTep); // interface
process.exit(0);
  1. 添加./scripts/component.js牍帚,內(nèi)容如下:
/**
 * pages頁面快速生成腳本 
 * 用法:npm run com `文件名`
 */

const fs = require('fs');

const dirName = process.argv[2];
const capPirName = dirName.substring(0,1).toUpperCase() + dirName.substring(1);
if (!dirName) {
  console.log('文件夾名稱不能為空!');
  console.log('示例:npm run com test');
  process.exit(0);
}

//頁面模板
const indexTep = `import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { ${capPirName}Props, ${capPirName}State } from './${dirName}.interface'
import './${dirName}.scss'

class ${capPirName} extends Component<${capPirName}Props,${capPirName}State > {
  constructor(props: ${capPirName}Props) {
    super(props)
    this.state = {}
  }
  static options = {
    addGlobalClass: true
  }
  static defaultProps:${capPirName}Props = {}

  render() {
    return (
      <View className='fx-${dirName}-wrap'>

      </View>
    )
  }
}

export default ${capPirName}
`

// scss文件模版
const scssTep = `
${dirName}-wrap {
    width: 100%;
 }
`

const interfaceTep = `
/**
 * ${dirName}.state 參數(shù)類型
 *
 * @export
 * @interface ${capPirName}State
 */
export interface ${capPirName}State {}

/**
 * ${dirName}.props 參數(shù)類型
 *
 * @export
 * @interface ${capPirName}Props
 */
export interface ${capPirName}Props {}
`

fs.mkdirSync(`./src/components/${dirName}`); // mkdir $1
process.chdir(`./src/components/${dirName}`); // cd $1

fs.writeFileSync(`${dirName}.tsx`, indexTep); //tsx
fs.writeFileSync(`${dirName}.scss`, scssTep); // scss
fs.writeFileSync(`${dirName}.interface.ts`, interfaceTep); // interface

Tip:最后也是重點(diǎn)乳蛾,記得在根目錄的package.jsonscripts里加上如下內(nèi)容:

"scripts": {
  ...
  "tep": "node scripts/template",
  "com": "node scripts/component"
}

5.編寫業(yè)務(wù)代碼

上面4個步驟基本已經(jīng)配置完了暗赶,接下去進(jìn)入正題,可以愉快的擼代碼了肃叶。
運(yùn)行我們上面寫的快速生成腳本蹂随,在命令行里輸入:

cnpm run tep index

ok,這時候tslint應(yīng)該不報找不到index的錯了因惭,可以看到我們page文件夾下生成了一個index的文件夾岳锁,里面包含config.tsindex.interface.ts蹦魔、index.scss激率、index.tsx咳燕、model.tsservice.ts

1.改寫./src/app.tsx

首先先下載taro的@tarojs/async-await乒躺,在命令行輸入如下:

cnpm i --save @tarojs/async-await

下載完了之后迟郎,按照如下改寫app.tsx

import Taro, { Component, Config } from "@tarojs/taro";
import "@tarojs/async-await";
import { Provider } from "@tarojs/redux";
import "./utils/request";
import Index from "./pages/index";
import dva from './utils/dva'
import models from './models'
import './app.scss'
import { globalData } from "./utils/common";

const dvaApp = dva.createApp({
  initialState: {},
  models: models,
});

const store = dvaApp.getStore();

class App extends Component {
  config: Config = {
    pages: [
      'pages/index/index'
    ],
    window: {
      backgroundTextStyle: 'light',
      navigationBarBackgroundColor: '#fff',
      navigationBarTitleText: 'WeChat',
      navigationBarTextStyle: 'black'
    }
  }

  /**
   *
   *  1.小程序打開的參數(shù) globalData.extraData.xx
   *  2.從二維碼進(jìn)入的參數(shù) globalData.extraData.xx
   *  3.獲取小程序的設(shè)備信息 globalData.systemInfo
   * @memberof App
   */
  async componentDidMount() {
    // 獲取參數(shù)
    const referrerInfo = this.$router.params.referrerInfo;
    const query = this.$router.params.query;
    !globalData.extraData && (globalData.extraData = {});
    if (referrerInfo && referrerInfo.extraData) {
      globalData.extraData = referrerInfo.extraData;
    }
    if (query) {
      globalData.extraData = {
        ...globalData.extraData,
        ...query
      };
    }

    // 獲取設(shè)備信息
    const sys = await Taro.getSystemInfo();
    sys && (globalData.systemInfo = sys);
  }

  componentDidShow() { }

  componentDidHide() { }

  componentDidCatchError() { }

  // 在 App 類中的 render() 函數(shù)沒有實(shí)際作用
  // 請勿修改此函數(shù)
  render() {
    return (
      <Provider store={store}>
        <Index />
      </Provider>
    )
  }
}

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

發(fā)現(xiàn)tslint找不到模塊“./models”。這樣的錯聪蘸,不要急宪肖,我們在./models文件夾下創(chuàng)建index.ts這樣一個文件夾,內(nèi)容如下:

import index from '../pages/index/model';// index 頁面的model

// 這里記得export的是數(shù)組健爬,不是對象
export default [
  index
]

可以發(fā)現(xiàn)控乾,tslint已經(jīng)不報錯了。

2.改寫./src/pages/index/config.ts
export default {
  getLists: '/japi/toh', // 獲取歷史上的今天接口
}
3.在./src/config/requestConfig.ts引入上面配置的接口娜遵,如下修改:
import index from '../pages/index/config' // index接口
/** 
 * 請求的公共參數(shù)
 */
export const commonParame = {}

/**
 * 請求映射文件
 */
export const requestConfig = {
  loginUrl: '/api/user/wechat-auth', // 微信登錄接口
  ...index
}
4.改寫./src/pages/index/service.ts蜕衡,如下:
import Api from '../../utils/request'

export const getLists = (data) => {
  return Api.getLists(data)
}
5.改寫./src/pages/index/index.interface.ts,如下:

/**
 * index.state 參數(shù)類型
 *
 * @export
 * @interface IndexState
 */
export interface IndexState {
  month: number
  day: number
}

/**
 * index.props 參數(shù)類型
 *
 * @export
 * @interface IndexProps
 */
export interface IndexProps {
  dispatch?: any,
  data?: Array<DataInterface>
}

export interface DataInterface {
  day: number
  des: string
  lunar: string
  month: number
  pic: string
  title: string
  year: number
  _id: string
}
6.改寫./src/pages/index/model.ts设拟,如下:
// import Taro from '@tarojs/taro';
import * as indexApi from './service';

export default {
  namespace: 'index',
  state: {
    data: [],
    key: '72eed010c976e448971655b56fc2324e',
    v: '1.0'
  },

  effects: {
    * getLists({ payload }, { select, call, put }) {
      const { key, v } = yield select(state => state.index)
      const { error, result } = yield call(indexApi.getLists, {
        key,
        v,
        ...payload
      })
      if (!error) {
        yield put({
          type: 'updateState',
          payload: {
            data: result
          }
        })
      }
    }
  },

  reducers: {
    updateState(state, { payload: data }) {
      return { ...state, ...data }
    }
  }

}
7.改寫./src/pages/index/index.tsx慨仿,如下:

import Taro, { Component, Config } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import { connect } from '@tarojs/redux'
// import Api from '../../utils/request'
// import Tips from '../../utils/tips'
import { IndexProps, IndexState } from './index.interface'
import './index.scss'
// import {  } from '../../components'

@connect(({ index }) => ({
  ...index,
}))

class Index extends Component<IndexProps, IndexState> {
  config: Config = {
    navigationBarTitleText: 'Taro + dva demo'
  }
  constructor(props: IndexProps) {
    super(props)
    this.state = {
      month: 0,
      day: 0
    }
  }

  // 獲取今日數(shù)據(jù)
  async getData(month: number, day: number) {
    await this.props.dispatch({
      type: 'index/getLists',
      payload: {
        month: month,
        day: day
      }
    })
  }

  // 獲取系統(tǒng)當(dāng)前時間并請求參數(shù)
  getDate() {
    const myDate = new Date()
    const m = myDate.getMonth() + 1
    const d = myDate.getDate()
    this.setState({
      month: m,
      day: d
    })
    this.getData(m, d)
  }

  componentDidMount() {
    this.getDate()
  }

  render() {
    const { month, day } = this.state
    const { data } = this.props
    return (
      <View className='fx-index-wrap'>
        <View className='index-topbar'>
          <Text>{`${month}月${day}日`}</Text>
          <View>歷史上的今天都發(fā)生了這些大事</View>
        </View>
        <View className='index-list'>
          {
            data && data.map((item, index) => {
              return <View className='index-li' key={index}>
                <View className='index-bg' style={`background-image: url(${item.pic})`}></View>
                <View className='index-info'>
                  <View className='index-title'>{item.title}</View>
                  <View className='index-des'>{item.des}</View>
                </View>
              </View>
            })
          }
        </View>
      </View>
    )
  }
}

export default Index

5.接下來寫樣式./src/pages/index/index.scss,如下:
.index {
  &-wrap {
    width: 100%;
    min-height: 100vh;
  }

  &-topbar {
    padding: 10rpx 50rpx;
    text-align: center;
    font-weight: bold;

    Text {
      color: rgb(248, 122, 3);
      font-size: 40rpx;
    }

    View {
      color: #333;
      font-size: 30rpx;
    }
  }

  &-list {
    padding: 50rpx;
  }

  &-li {
    box-shadow: 0 4rpx 20rpx rgba($color: #000000, $alpha: 0.1);
    margin-bottom: 50rpx;
    border-radius: 8rpx;
    overflow: hidden;
  }

  &-bg {
    width: 100%;
    height: 300rpx;
    background-repeat: no-repeat;
    background-size: contain;
    background-position: center center;
    background-color: #f5f5f5;
  }

  &-info {
    padding: 15rpx;
  }

  &-title {
    font-size: 30rpx;
    font-weight: bold;
    margin-bottom: 10rpx;
  }

  &-des {
    font-size: 26rpx;
    color: #666;
  }
}

這時候基本結(jié)束了纳胧,在命令行運(yùn)行:

cnpm run dev:weapp

如下顯示镰吆,說明編譯成功(tip:以后記得先編譯,我是之前寫好了的跑慕,不然很有可能一堆報錯万皿,那時候估計(jì)你會絕望的)

編譯成功顯示圖

最后的最后,打開微信開發(fā)者工具核行,選擇微信小程序牢硅,選擇taro-demo文件夾下編譯成功的dist,appid就用微信提供給你測試的芝雪,名字隨便輸入一個减余,點(diǎn)擊確定,之前步驟都沒問題的話惩系,最后顯示的結(jié)果如下圖:
小程序界面圖

最后位岔,恭喜你,配置完了蛆挫,可以滿足基本開發(fā)和需求了赃承,如果有什么錯誤還望指出妙黍。

-------------------------- END -----------------------

示例GitHub地址:
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悴侵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拭嫁,更是在濱河造成了極大的恐慌可免,老刑警劉巖抓于,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異浇借,居然都是意外死亡捉撮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門妇垢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巾遭,“玉大人,你說我怎么就攤上這事闯估∽粕幔” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵涨薪,是天一觀的道長骑素。 經(jīng)常有香客問我,道長刚夺,這世上最難降的妖魔是什么献丑? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮侠姑,結(jié)果婚禮上创橄,老公的妹妹穿的比我還像新娘。我一直安慰自己莽红,他們只是感情好筐摘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著船老,像睡著了一般咖熟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柳畔,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天馍管,我揣著相機(jī)與錄音,去河邊找鬼薪韩。 笑死确沸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俘陷。 我是一名探鬼主播罗捎,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拉盾!你這毒婦竟也來了桨菜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎倒得,沒想到半個月后泻红,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霞掺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年禀崖,在試婚紗的時候發(fā)現(xiàn)自己被綠了矾飞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖狈究,靈堂內(nèi)的尸體忽然破棺而出耸袜,到底是詐尸還是另有隱情秽浇,我是刑警寧澤盛末,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站矿卑,受9級特大地震影響喉恋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜母廷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一轻黑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琴昆,春花似錦氓鄙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至舷暮,卻和暖如春态罪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背下面。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工复颈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沥割。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓耗啦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親机杜。 傳聞我的和親對象是個殘疾皇子帜讲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353