搜索
您的当前位置:首页正文

Vue实例搭建 - ToDoList

来源:二三娱乐

1. 搭建环境

Nodejs与npm的安装不再叙述(希望大家装上的node版本大于等于6.x,不然还需要加上–harmony标志才可以开启es6),默认读者已经掌握npm安装依赖的方法。先安装npm,然后安装cnpm,这是淘宝源的npm。

我的评论,好像用cnpm install命令安装的文件好像不会自动修改package。

brew install npm
npm install -g cnpm --registry=https://registry.npm.taobao.org

首先全局安装

cnpm i vue-cli -g
vue init webpack demo
cd demo
cnpm install
cnpm run dev

安装一些依赖,这里按照博文里的版本来

cnpm install koa@1.2.4 koa-router@5.4 koa-logger@1.3.0 koa-json@1.1.3 koa-bodyparser@2.3.0

再创建app.js文件,如下。

const app = require('koa')()
  , koa = require('koa-router')()
  , json = require('koa-json')
  , logger = require('koa-logger'); // 引入各种依赖
app.use(require('koa-bodyparser')());
app.use(json());
app.use(logger());
app.use(function* (next){
  let start = new Date;
  yield next;
  let ms = new Date - start;
  console.log('%s %s - %s', this.method, this.url, ms); // 显示执行的时间
});
app.on('error', function(err, ctx){
  console.log('server error', err);
});
app.listen(8889,() => {
  console.log('Koa is listening in 8889');
});
module.exports = app;

新开一个terminal并输入

node app.js

Koa就打开完毕了,在8889端口

2. 前端页面构建

登陆界面

安装element-ui。

cnpm install element-ui@1.1.2

修改src/main.js,这里有坑(主要表现为eslint那行注释不能删),按我的来

import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui' // 引入element-ui
import 'element-ui/lib/theme-default/index.css'
Vue.use(ElementUI) // Vue全局使用

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})

在index.html中加入以下代码为了响应式页面

<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

安装两个stylus依赖

cnpm install stylus@0.54.5 stylus-loader@2.4.0

创建src/components/Login.vue


<template>
  <el-row class="content">
    <el-col :xs="24" :sm="{span: 6,offset: 9}">
      <span class="title">
       欢迎登录 
      </span>
      <el-row>
        <el-input 
          v-model="account" 
          placeholder="账号"
          type="text">
        </el-input>
        <el-input 
          v-model="password" 
          placeholder="密码"
          type="password">
        </el-input>
        <el-button type="primary">登录</el-button>
      </el-row>
    </el-col>
  </el-row>
</template>
<script>
export default {
  data () {
    return {
      account: '',
      password: ''
    }
  }
}
</script>
<style lang="stylus" scoped>
  .el-row.content
    padding 16px
  .title
    font-size 28px
  .el-input
    margin 12px 0
  .el-button
    width 100%
    margin-top 12px    
</style>

修改src/App.vue

<template>
  <div id="app">
// 注意把下面的[]改成<>
    [img src="./assets/logo.png"] 
    <Login></Login> <!--使用Login组件-->
  </div>
</template>
<script>
import Login from './components/Login' // 引入Login组件
export default {
  name: 'app',
  components: {
    Login // 注册组件
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

todolist 界面

还是在src/components目录下,写一个叫做TodoList.vue的文件。

<template>
  <el-row class="content">
    <el-col :xs="{span:20,offset:2}" :sm="{span:8,offset:8}">
      <span>
        欢迎:{{name}}!你的待办事项是:
      </span>
      <el-input placeholder="请输入待办事项" v-model="todos" @keyup.enter.native="addTodos"></el-input>
      <el-tabs v-model="activeName">
        <el-tab-pane label="待办事项" name="first">
          <el-col :xs="24">
            <template v-if="!Done"> <!--v-if和v-for不能同时在一个元素内使用,因为Vue总会先执行v-for-->
              <template v-for="(item, index) in list">
                <div class="todo-list" v-if="item.status == false">
                  <span class="item">
                    {{ index + 1 }}. {{ item.content }}
                  </span>
                  <span class="pull-right">
                    <el-button size="small" type="primary" @click="finished(index)">完成</el-button>
                    <el-button size="small" :plain="true" type="danger" @click="remove(index)">删除</el-button>
                  </span>
                </div>
              </template> 
            </template>
            <div v-else-if="Done">
              暂无待办事项
            </div>
          </el-col>
        </el-tab-pane>
        <el-tab-pane label="已完成事项" name="second">
          <template v-if="count > 0">
            <template v-for="(item, index) in list">
              <div class="todo-list" v-if="item.status == true">
                <span class="item finished">
                  {{ index + 1 }}. {{ item.content }}
                </span>
                <span class="pull-right">
                  <el-button size="small" type="primary" @click="restore(index)">还原</el-button>
                </span>
              </div>
            </template> 
          </template>
          <div v-else>
            暂无已完成事项
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-col>
  </el-row>
</template>
<script>
export default {
  data () {
    return {
      name: 'Molunerfinn',
      todos: '',
      activeName: 'first',
      list:[],
      count: 0
    };
  },
  computed: { // 计算属性用于计算是否已经完成了所有任务
    Done(){
      let count = 0;
      let length = this.list.length;
      for(let i in this.list){
        this.list[i].status == true ? count += 1 : '';
      }
      this.count = count;
      if(count == length || length == 0){
        return true
      }else{
        return false
      }
    }
  },
  methods: {
    addTodos() {
      if(this.todos == '')
        return
      let obj = {
        status: false,
        content: this.todos
      }
      this.list.push(obj);
      this.todos = '';
    },
    finished(index) {
      this.$set(this.list[index],'status',true) // 通过set的方法让数组的变动能够让Vue检测到
      this.$message({
        type: 'success',
        message: '任务完成'
      })
    },
    remove(index) {
      this.list.splice(index,1);
      this.$message({
        type: 'info',
        message: '任务删除'
      })
    },
    restore(index) {
      this.$set(this.list[index],'status',false)
      this.$message({
        type: 'info',
        message: '任务还原'
      })
    }
  }
};
</script>
<style lang="stylus" scoped>
  .el-input
    margin 20px auto
  .todo-list
    width 100%
    margin-top 8px
    padding-bottom 8px
    border-bottom 1px solid #eee
    overflow hidden
    text-align left
    .item
      font-size 20px
      &.finished
        text-decoration line-through
        color #ddd
  .pull-right
    float right
</style>

写完TodoList之后,我们需要将它和vue-router配合起来,从而使这个单页应用能够进行页面跳转。

页面路由

由于不采用服务端渲染,所以页面路由走的是前端路由。安装一下vue-router:cnpm install vue-router@2.1.1.

这一部分需要改写main.js 和 App.vue,引入路由router。一个坑是eslint真的很严格,为了避免严格的错误,在/build/webpack-base.conf.js中删除以下部分:

{ 
    test: /\.(js|vue)$/,
    loader: 'eslint-loader',
    enforce: 'pre',
    include: [resolve('src'), resolve('test')],
    options: {
      formatter: require('eslint-friendly-formatter')
    }
 },

然后记得重新cnpm run dev!! 这样就可以解除严格的eslint啦。

具体改后的代码如下,main.js挂载路由:

import Vue from 'vue'
import App from './App'
// import router from './router'
import ElementUI from 'element-ui' // 引入element-ui
import 'element-ui/lib/theme-default/index.css'
import VueRouter from 'vue-router'

Vue.use(ElementUI) // Vue全局使用
Vue.use(VueRouter)

import Login from './components/Login'
import TodoList from './components/TodoList'
const router = new VueRouter({
  mode: 'history', // 开启HTML5的history模式,可以让地址栏的url长得跟正常页面跳转的url一样。(不过还需要后端配合,讲Koa的时候会说)
  base: __dirname,
  routes: [
    {
      path: '/',  // 默认首页打开是登录页
      component: Login // 注册login
    },
    {
      path: '/todolist',
      component: TodoList // 注册todolist
    },
    {
      path: '*',
      redirect: '/' // 输入其他不存在的地址自动跳回首页
    }
  ]
})

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router: router,
  template: '<App/>',
  components: { App }
})

App.vue把路由视图放到页面上:

<template>
  <div id="app">
    ![](./assets/logo.png)
    <router-view></router-view> <!-- 原本的Login换成了router-view 这就是路由视图渲染的目标元素-->
  </div>
</template>

<script>
export default {
  name: 'app',// 不需要再引入`Login`\`TodoList`组件了,因为在路由里已经注册了
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

这个时候你如果在地址栏后加上/todolist那么就会跳转到TodoList页面啦。

下面改写一下Login.vue,就可以从login跳转到todolist了。

<!-- Login.vue -->
······
<!-- 给input增加键盘事件,当输入完密码回车也执行loginToDo方法 -->
<el-input 
  v-model="password" 
  placeholder="密码"
  type="password"
  @keyup.enter.native="loginToDo">
</el-input>
<!-- 增加一个click方法 loginToDo -->
<el-button type="primary" @click="loginToDo">登录</el-button>
······
<script>
export default {
  data () {
    return {
      account: '',
      password: ''
    };
  },
  methods: {
    loginToDo() {
      this.$router.push('/todolist') // 编程式路由,通过push方法,改变路由。
    }
  }
};
</script>

实际上我们是单页应用,只是在应用内进行页面跳转而已,没有向后端额外请求。

3. 后端环境搭建

Top