Skip to content
On this page

基础篇 5-用 Taro 实现一个简单的 Todo 项目


用 Taro 实现一个简单的 Todo 项目

按前面章节的介绍操作,想必已经安装好了 Taro 了,现在我们来实现一个简单的 Todo 项目进行实战。

创建项目

Todo 项目包含两个简单的功能:

  • 输入事项:点击「添加」按钮增加一个事项;
  • 删除事项:点击事项列表里事项后面的「删除」按钮将删除该事项。

首先进入你想存放本项目的目录里,使用 Taro 创建一个 Todolist 项目,即输入命令taro init todoList(Todolist 为项目名称,你也可指定其他名称),过程如下图:

taro init todoList

创建项目成功后,我们先来看看当前的项目结构。

2018-09-03-10-28-11

src 文件夹存放我们开发时的文件,包括一个根目录的全局配置文件 app.js,全局样式 app.scss,入口 HTML 文件 index.htmlindex.html 里内置了 rem 转换代码(这个不需要关注)。而 src 目录 pages 下则对应的是项目的每一个页面,这里默认只有一个 index 页面,每一个页面下又包含 JS 文件和样式文件两个文件,这里即 index.jsindex.scss

如果需要创建组件,我们可以在 src 创建 component 目录来统一存放组件。

了解了目录以后,接下来找到在 src 目录的 pages 下的 index.js (我们前面了解到这个就是默认的页面)。

import Taro, { Component } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import './index.scss'

export default class Index extends Component {

  config = {
    navigationBarTitleText: '首页'
  }

  componentWillMount () { }

  componentDidMount () { }

  componentWillUnmount () { }

  componentDidShow () { }

  componentDidHide () { }

  render () {
    return (
      <View className='index'>
        <Text>Hello world!</Text>
      </View>
    )
  }
}


文件开始第 1 行默认引入 Taro 的 Component 组件,它主要用来做组件化,我们这里暂时不用了解它;接着第 2 行是引入要用到的 ViewText, 我们下文要用到的 Input 组件也会是在这里引入。接下来第 3 行是引入样式对应的样式文件。

在我们正式开始写代码前先看看默认页面的效果吧。可以选择小程序的方式在微信开发者工具里查看(第 3 章《微信小程序开发入门与技术选型》里有相关的介绍)或 H5 的方式在浏览器里查看。在命令行进行项目构建:

# 小程序构建
$ taro build --type weapp --watch
# H5构建
$ taro build --type h5 --watch

得到小程序的效果:

接下来我们就正式开始编写 Todolist 了,我们把事项列表存在 state 里且给它默认几个事项,最后把它渲染出来。

// ...
export default class Index extends Component {
  config = {
    navigationBarTitleText: '首页'
  }

  constructor (props) {
    super (props)
    this.state = {
      // 创建一个初始的 Todolist
      list: [
        'get up',
        'coding',
        'sleep',
      ],
      inputVal: ''
    }
  }
  
  render () {
    let { list, inputVal } = this.state

    return (
      <View className='index'>
        <View className='list_wrap'>
          <Text>Todo list</Text>
          {
            list.map((item, index) => {
              return <View>
                <Text>{index + 1}.{item}</Text>
              </View>
            })
          }
        </View>
      </View>
    )
  }
}

要想实现添加事项的功能,我们需要引入 Input 组件来输入事项,当我们输入的时候触发 onInput 事件,把输入的值保存在 this.inputVal;同时使用 Text 组件把「添加」按钮展示出来,点击的时候触发 onClick 事件,当点击发生时,我们可以查看 this.inputVal 是否为空,如果不为空则可以添加到事项列表里,即在 list 列表里添加事项且更新 state 。具体的代码如下:

// 这里多引入了 Input 组件
import { View, Text, Input } from '@tarojs/components'

export default class Index extends Component {

  // ... 生命周期函数,暂时不需要关注
  // 添加按钮 onClick 时,添加事项,然后更新 list
  addItem () {
    let { list } = this.state
    const inputVal = this.inputVal
    // 如果输入框的值为空,则返回,否则添加到事项列表里
    if (inputVal == '') return
    else {
      list.push(inputVal)
    }
    this.setState({
      list,
      inputVal: ''
    })
  }

  // 输入框 onInput 的时候,它的值暂存起来
  inputHandler (e) {
    this.inputVal = e.target.value
  }

  render () {
    let { list, inputVal } = this.state

    return (
      <View className='index'>
        <Input className='input' type='text' value={inputVal} onInput={this.inputHandler.bind(this)} />
        <Text className='add' onClick={this.addItem.bind(this)}>添加</Text>
        <View className='list_wrap'>
          <Text>Todo list</Text>
          {
            list.map((item, index) => {
              return <View>
                <Text>{index + 1}.{item}</Text>
              </View>
            })
          }
        </View>
      </View>
    )
  }
}

上面我们完成了添加事项的功能,下面我们开发删除功能。删除功能就是点击事项后的「删除」按钮删除对应的事项,即从事项列表中移除该事项。首先是在展示事项的列表后添加「删除」按钮并且把该事项的索引传入点击事件里,发生点击的时候就可以根据索引删除对应的事件。

export default class Index extends Component {
  config = {
    navigationBarTitleText: '首页'
  }

  // ... 其他代码已略
  // 根据索引删除事项,然后更新 list
  delItem (index) {
    let { list } = this.state
    list.splice(index, 1)
    this.setState({
      list
    })
  }

  render () {
    let { list, inputVal } = this.state

    return (
      <View className='index'>
        <Input className='input' type='text' value={inputVal} onInput={this.inputHandler.bind(this)} />
        <Text className='add' onClick={this.addItem.bind(this)}>添加</Text>
        <View className='list_wrap'>
          <Text>Todo list</Text>
          {
            list.map((item, index) => {
              return <View>
                <Text>{index + 1}.{item}</Text>
                <Text className='del' onClick={this.delItem.bind(this, index)}>删除</Text>
              </View>
            })
          }
        </View>
      </View>
    )
  }
}


到这里我们实现了一个简单的可添加可删除的 Todolist,因为我们之前使用的是 Taro 的 taro build \--type weapp \--watchtaro build \--type h5 \--watch 命令,所以在编写代码的同时可以不停地查看我们编写的效果。我们再补充一些样式,样式写在 index 页面下的样式文件里,这里即 index.scss

.input {
    display: inline-block;
    margin: 20px;
    border: 1px solid #666;
    width: 500px;
    vertical-align: middle;
}
.list_wrap {
    padding: 50px 20px;
}
.list {
    margin: 20px 0;
}
.add,
.del {
    display: inline-block;
    width: 120px;
    height: 60px;
    margin: 0 10px;
    padding: 0 10px;
    color: #333;
    font-size: 22px;
    line-height: 60px;
    text-align: center;
    border-radius: 10px;
    border: 1px solid #C5D9E8;
    box-sizing: border-box;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    justify-content: center;
    vertical-align: middle;
}
.add {
    background-color: #5c89e4;
    color: #fff;
    border: 1px solid #5c89e4;
}
.del {
    background-color: #fff;
    color: #5c89e4;
    border: 1px solid #5c89e4;
    margin-left: 100px;
}

为了方便大家实践,且篇幅不算长,这里把 index.js 全部代码展示一下:


import Taro, { Component } from '@tarojs/taro'
import { View, Text, Input } from '@tarojs/components'
import './index.scss'

export default class Index extends Component {
  config = {
    navigationBarTitleText: '首页'
  }

  constructor (props) {
    super (props)
    this.state = {
      // 创建一个初始的 Todolist
      list: [
        'get up',
        'coding',
        'sleep',
      ],
      inputVal: ''
    }
  }

  // ... 生命周期函数,暂时不需要关注

  addItem () {
    let { list } = this.state
    const inputVal = this.inputVal
    if (inputVal == '') return
    else{
      list.push(inputVal)
    }
    this.setState({
      list,
      inputVal: ''
    })
  }

  delItem (index) {
    let { list } = this.state
    list.splice(index, 1)
    this.setState({
      list
    })
  }

  inputHandler (e) {
    // 不参与渲染的变量可不使用state储存,提高性能
    this.inputVal = e.target.value
  }

  render () {
    let { list, inputVal } = this.state

    return (
      <View className='index'>
        <Input className='input' type='text' value={inputVal} onInput={this.inputHandler.bind(this)} />
        <Text className='add' onClick={this.addItem.bind(this)}>添加</Text>
        <View className='list_wrap'>
          <Text>Todo list</Text>
          {
            list.map((item, index) => {
              return <View className='list'>
                <Text>{index + 1}.{item}</Text>
                <Text className='del' onClick={this.delItem.bind(this, index)}>删除</Text>
              </View>
            })
          }
        </View>
      </View>
    )
  }
}

实际效果

最后来看一眼实现的效果:

todos

小结

本文主要介绍了如何使用 Taro 创建一个应用目录,并快速进行开发,以及目录中的一些配置内容。Taro 给前端同学们带来了全新的编码体验,想必熟悉 React 的同行们都能快速上手,(如果从未使用 React,则可以看本小册的第 2 章《React 核心语法初识》)这里就不再赘述了。

下一章将为大家讲解如何在应用中添加以及使用 Redux 来管理我们的数据。