vuejs进修笔记:制造pokemon记录器(一周目)

花了两周,看了好久的文档和案例,照样要实践一下,因而做了这个demo,设想就如许看吧,我的设想程度至心不好T^T,一周目标demo是静态的,二周目再搭建数据层。

项目堆栈:https://github.com/gknpezgssb…

项目简介

鉴于近来的pokemon大热,此次的demo也挑选了pokemon主题的(实在我是想选守望屁股来着的)。

主体

《vuejs进修笔记:制造pokemon记录器(一周目)》

项目构造:

《vuejs进修笔记:制造pokemon记录器(一周目)》

Demo:

  1. 主显现地区(这里由vue-router掌握)可举行三个界面的切换;

  2. 功用地区(由logo按钮,搜刮栏,pokemon列表,精灵捕获纪录四个部份构成)

各部份功用

《vuejs进修笔记:制造pokemon记录器(一周目)》
点击捕获按钮,主界面切换,能够纪录pokemon信息,点击捕获胜利后列表会自动更新(谅解科幻迷的我 (/≥▽≤/))

《vuejs进修笔记:制造pokemon记录器(一周目)》
能够在搜刮栏顶用你给精灵起的名字寻觅你的pokemon

《vuejs进修笔记:制造pokemon记录器(一周目)》
点击详细精灵会显现他的信息

一周目制造

脚手架

vue-cli是一款脚手架东西,运用他能帮你自动天生模版,免除冗杂的设置事情,这里我选用了webpack的模版。

起首装置vue-cli:

npm install -g vue-cli

以后进入项目目次,装置依靠的包:

vue init webpack vue-time-tracker
cd vue-time-tracker
npm install

此时,实行 npm run dev,在地址栏输入localhost:8080,假如胜利看到下图就申明你已搭好框架了

《vuejs进修笔记:制造pokemon记录器(一周目)》

main.js 与 App.vue 设置

起首将 index.html 改写成

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>yourpokemon</title>
  </head>
  <body>
    <div id="app">
        
    </div>
    <!-- built files will be auto injected -->
  </body>
</html>

装置vue-router


npm install vue-router --save

在main.js到场以下代码:

import Vue from 'vue'  
import App from './App'  
import Home from './components/Home'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter()
router.map({
  '/home': {
    component: Home
  }
})
router.redirect({
  '*': '/home'
})

router.start(App, '#app')

这里临时先援用Home组件。

我们援用了vue与vue-router,而且经由过程map设置了路由,如许在任何标签(不限于a)上运用v-link都能完成路由变化。

<template>
  <div id="pokemon">
   <div class="game-boy">
    <div class="gb-main">
     <router-view ></router-view>
   </div>
   <asidebar :items="monsters"></asidebar> 
 </div>
</div>
</template>

如许App.vue中的内容会替代掉到index里的div,这里左部的主题地区用vue-router来掌握,右边的功用地区自定义组件asidebar。

接下来我们在App.vue中增加

import Asidebar from './components/Asidebar.vue'
export default {
  components: {
    'asidebar': Asidebar
  },
  data () {
    let monstersData = [
      {
        name: '艾萨克·阿西莫夫',
        type: 'thunder',
        species: '皮卡丘',
        height: '0.4m',
        weight: '6.0kg ',
        sex: 'male'
      },
      {
        name: '亚瑟·克拉克',
        type: 'grass',
        species: '妙蛙种子',
        height: '0.7m',
        weight: '6.9kg ',
        sex: 'female'
      }
    ]
    return {
      monsters: monstersData  
    }
  },
  events: {
  }
}

这里援用组件Asidebar,捏造两组数据,经由过程v-bind举行父子组件通讯,详见运用props通报数据

末了往App.vue增加款式

*{
  margin: 0;
  padding: 0;
  box-sizing:border-box;
}
#pokemon{
  width: 100vw;
  height: 100vh;
  background-image: url(./assets/bg.jpg);
  background-position: center center;
  display: -webkit-flex;
  display: -moz-flex;
  display: -ms-flex;
  display: -o-flex;
  display: flex;
  justify-content: center;
  -ms-align-items: center;
  align-items: center;
}
.gb-main{
  width: 500px;
  border: 20px solid  #444;
  float: left;
  height: 100%;
}
.game-boy{
  box-shadow: 0 0 15px #333;
  width: 800px;
  height: 450px;
  background-color: #fff;
  border: 25px solid  #fc0;
  border-radius: 18px 18px 13px 13px; 
}

捣腾 Sidebar.vue

起首增加html:

<template>
    <div class="gb-aside">
        <div class="gb-logo" v-link="'/home'"></div>
        <input type="text" class="gb-search" placeholder="寻觅你的梦可宝" v-model="search">
        <ol class="gb-list">
            <li class="monster" v-for="item in items | search" v-link="'/detail'" @click="show(item)">
                <div class="m-img" :class="item.type">
                </div>
                <h2 class="m-name">{{item.species}}:<span>{{item.name}}</span></h2>
            </li>
        </ol>
        <button v-link="'/new'" class="gb-catch">捕获</button>
    </div>
</template>

这里设置了v-link使

  • logo按钮能接见home页面

  • li标签能接见detail页面

  • button按钮能接见new页面

  • 能够看到搜刮栏绑定了v-model,经由过程他连系li上的search函数即可完成列表的搜刮

在li上的click事宜,绑定了show函数,他完成了一个事宜派发,通报到App.vue后再由其举行播送。js代码以下:

export default {
  data () {
    return {search: ''}
  },
  props: ['items'],
  filters: {
    search (name) {
      return name.filter(item => item.name.indexOf(this.search) > -1)
    }
  },
  methods: {
    show (item) {
      let index = this.items.indexOf(item)
      this.$dispatch('findMonster', index)
    }
  }
    }

css代码以下:

.gb-aside{
    position: relative;
    float: right;
    width: 250px;
    background-color: #FC0;
    height: 100%;
    padding-left: 25px;
}
.gb-list{
    list-style: none;
    background-color: #fff;
    width: 100%;
    margin-top: 20px;
    margin-bottom: 20px;
    height: 200px;
    overflow-y: scroll;
    overflow-x:hidden; 
}
.monster{
    cursor: pointer;
    width: 100%;
    height: 40px;
    border-bottom: 1px solid  #dadada;
    position: relative;
    padding-left: 43px;
    position: relative;
}
.m-name{
    width: 150px;
    line-height: 40px;
    font-size: 12px;
    color: #666;
    white-space: nowrap;
    overflow: hidden;
    -ms-text-overflow: ellipsis;
    text-overflow: ellipsis;
}
.m-name span{
    font-size: 14px;
    color: #333;
}
.m-img.grass{
    background-image: url(../assets/grass.png);
}
.m-img.thunder{
    background-image: url(../assets/thunder.png);
}
.m-img.water{
    background-image: url(../assets/water.png);
}
.m-img.fire{
    background-image: url(../assets/fire.png);
}
.m-img.normal{
    background-image: url(../assets/normal.png);
}
.m-img.chonoryoku{
    background-image: url(../assets/chonoryoku.png);
}
.m-img.wrestle{
    background-image: url(../assets/wrestle.png);
}
.m-img{
    background-position: center center;
    -webkit-background-size: 23px 23px;
    background-size: 23px 23px;
    background-repeat: no-repeat;
    width: 30px;
    height: 40px;
    position: absolute;
    left: 5px;
    top: 0;
}
.m-img img{
    width: 25px;
    height: 25px;
}
.gb-logo{
    height: 60px;
    background-image: url(../assets/logo.png);
    background-repeat: no-repeat;
    background-position: center top;
    -webkit-background-size: auto 20px;
    background-size: auto 40px;
    cursor: pointer;
}
.gb-search{
    width: 100%;
    display: block;
    border: 0px;
    height: 40px;
    padding-left: 1em;
    font-size: 18px;
    color: #333;
    /*margin-top: 60px;*/
}
.gb-catch{
    background-color: #fff;
    cursor: pointer;
    position: absolute;
    bottom: 10px;
    left: 25px;
    right: 0;
    margin: 0px auto;
    border: 0;
    width: 150px;
    height: 50px;
    border-radius: 25px;
    padding-left: 70px;
    font-size: 18px;
    line-height: 50px;
    color: #666;
    text-align: left;
}
.gb-catch::before{
    background-image: url(../assets/ball.png);
    background-position: center center;
    background-repeat: no-repeat;
    -webkit-background-size:auto 40px ;
    background-size:auto 40px ;
    content:"";
    position: absolute;
    left: 3px;
    top: 0;
    width: 50px;
    height: 50px;
    animation-name: catch-pokemon;
    animation-duration:2s;
    animation-timing-function:linear;
    animation-iteration-count:infinite;
    animation-direction:normal;
}
@keyframes catch-pokemon {
    from { 
        transform:rotate(0deg);
    }
    to {  
        transform:rotate(360deg);
    }
}

我门还需对App.vue增加事宜

events: {
    findMonster (index) {
      let monster = this.monsters[index]
      this.$broadcast('showMonster', monster)
    }
  }

这里接受到事宜派发的App.vue又举行了一次事宜播送完成了子组件的通讯,固然另有更好的体式格局这里地道是为了运用dispatch和broadcast.( ̄▽ ̄”)

关于这块详见vue api broadcastdispatch

主显现区

我们在main.js中先援用别的两个组件

import New from './components/NewMonster'
import Detail from './components/Detail'

//而且修正路由设置

router.map({
  '/home': {
    component: Home
  },
  '/new': {
    component: New
  },
  '/detail': {
    component: Detail
  }
})

NewMonster 组件

<template>
  <div class="detail">
    <div class="d-info">
      <label for="">姓名:</label>
      <input type="text" v-model="monster.name">
      <label for="">属性:</label>
      <select name="" id="" v-model="monster.type">
        <option class="water" selected>water</option>
        <option class="fire" >fire</option>
        <option class="grass" >grass</option>
        <option class="thunder" >thunder</option>
        <option class="chonoryoku" >chonoryoku</option>
        <option class="wrestle" >wrestle</option>
        <option class="normal" >normal</option>
      </select>
    </div>
    <div class="d-info">
      <label for="">性别:</label>
      <select name="" id="" v-model="monster.sex">
        <option selected>male</option>
        <option >female</option>
      </select>
      <label for="">品种:</label>
      <input type="text" v-model="monster.species">
    </div>
    <div class="d-info">
      <label for="">身高:</label>
      <input type="text" v-model="monster.height">
      <label for="">体重:</label>
      <input type="text" v-model="monster.weight">
    </div>
    <button class="d-catch" @click="save()">捕获胜利</button> 
  </div>
</template>

<script>
export default {
  data () {
    return {
      monster: {
        name: '',
        type: '',
        species: '',
        height: '',
        weight: '',
        sex: ''
      }
    }
  },
  methods: {
    save () {
      if (this.monster.name && this.monster.species) {
        let monster = this.monster
        this.$dispatch('catch', monster)
        this.monster = {}
      } else {
        window.alert('请填入完全精灵信息')
      }
    }
  }
}
</script>
<style scoped>
.detail {
  overflow: hidden;
  position: relative;
  width: 100%;
  height: 100%;
  background-image: url(../assets/sightseeing.png);
  background-repeat: no-repeat;
  -webkit-background-size: 130% 100%;
  background-size: 130% 100%;
  background-position: center bottom;
  padding-top: 30px;
}
.d-info{
  width: 100%;
  height: 35px;
  position: relative;
  margin-top: 30px;
  margin-bottom: 30px;
  text-align: center;
}
.d-catch{
  width: 120px;
  height: 35px;
  border: 3px solid  #666;
  display: block;
  margin: 0 auto;
  background-color: #fff;
  border-radius: 10px;
  cursor: pointer;  
}
option{
   height: 25px;
  line-height: 25px;
  display: block;
  font-size: 14px;
  background-repeat: no-repeat;
  background-position: right center;
  -webkit-background-size: 20px 20px;
  background-size: 20px 20px;
  padding-left: 10px;
}
option.water{
  background-image: url(../assets/water.png);
}
option.fire{
  background-image: url(../assets/fire.png);
}
option.grass{
  background-image: url(../assets/grass.png);
}
option.thunder{
  background-image: url(../assets/thunder.png);
}
option.normal{
  background-image: url(../assets/normal.png);
}
option.wrestle{
  background-image: url(../assets/wrestle.png);
}
option.chonoryoku{
  background-image: url(../assets/chonoryoku.png);
}
.detail label{
  width: 80px;
  text-align: right;
  height: 35px;
  line-height: 35px;
  font-size: 14px;
  color: #666;
  display: inline-block;
  background-repeat: no-repeat;
  background-position: center left;
  -webkit-background-size: auto 35px;
  background-size: auto 35px;
}
.d-info input,.d-info select{
  display: inline-block;
  width: 120px;
  height: 100%;
  border: 1px solid  #999;
  font-size: 14px;
  color: #444;
  padding-left: 10px;
  background-color: #fff;
}
.d-info:nth-of-type(1) label:nth-of-type(1){
background-image: url(../assets/Eevee.png);
}
.d-info:nth-of-type(1) label:nth-of-type(2){
background-image: url(../assets/Flareon.png);
}
.d-info:nth-of-type(2) label:nth-of-type(1){
background-image: url(../assets/Jolteon.png);
}
.d-info:nth-of-type(2) label:nth-of-type(2){
background-image: url(../assets/Vaporeon.png);
}
.d-info:nth-of-type(3) label:nth-of-type(1){
background-image: url(../assets/Espeon.png);
}
.d-info:nth-of-type(3) label:nth-of-type(2){
background-image: url(../assets/Umbreon.png);
}
</style>

经由过程save要领派发一个事宜,用来保留新捕获的精灵;

关于App需增加events

events: {
    catch (monster) {
      this.monsters.push(monster)
    },
    findMonster (index) {
      let monster = this.monsters[index]
      this.$broadcast('showMonster', monster)
    }
  }

而且设置名字与品种未填写时没法提交。

Detail 组件

<template>
  <div class="detail" :class="monster.type">
    <h1 class="name"><span>{{monster.name}}</span></h1>
    <h1 class="species"><span>{{monster.species}}</span></h1>
    <h1 class="height"><span>{{monster.height}}</span></h1>
    <h1 class="weight"><span>{{monster.weight}}</span></h1>
    <h1 class="sex"><span>{{monster.sex}}</span></h1>
  </div>
</template>

<script>
export default {
  data () {
    return {
      monster: {
        name: '??',
        species: '??',
        height: '??',
        weight: '??',
        sex: '??',
        type: ''
      }
    }
  },
  events: {
    showMonster (info) {
      this.monster.name = info.name
      this.monster.species = info.species
      this.monster.height = info.height === '' ? '??' : info.height
      this.monster.weight = info.weight === '' ? '??' : info.weight
      this.monster.sex = info.sex
      this.monster.type = info.type
    }
  }
}
</script>

App.vue播送的时候被Detail收到从而完成右边列表点击左边内容切换的结果。

Home 组件

这是一个地道静态的组件,代码也很简短:

<template>
  <div class="cover">
    <h2>{{msg}}</h2>
  </div>
</template>

<script>
export default {
  data () {
    return {
      msg: '打造你的梦可宝图鉴!'
    }
  }
}
</script>
<style scoped>
h2 {
  color: #4E3A13;
  text-align: center;
  line-height: 3.5em;
  font-size: 25px;
  position: absolute;
  bottom: 0px;
  width: 100%;
}
.cover{
    position: relative;
    width: 100%;
    height: 100%;
    background-image: url(../assets/cover.jpg);
    background-position: center center;
    -webkit-background-size: 500px auto;
    background-size: 500px auto;
}

</style>

总结

  • 这只是一个很粗拙的试手Demo,有许多代码须要优化,会在二周目举行,二周目也会搭建实在的数据层;

  • demo真的没有什么现实的用途╮(╯_╰)╭ ,然则vue倒是有用且细腻的模范

    原文作者:master_yoda
    原文地址: https://segmentfault.com/a/1190000006066080
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞