zoukankan      html  css  js  c++  java
  • vue组件化编程应用2

    写几个小案例来理解vue的组件化编程思想,下面是一个demo.

    效果图示:
    在这里插入图片描述
    需求:
    header部输入任务,进行添加,在list中显示;
    list中每个item项,鼠标移入时,背景变灰并显示delete按钮.点击按钮可删除该项;鼠标移出时,恢复原样;
    footer部: 1.根据列表勾选状态及数量显示已完成数和全部数; 2.当已完成数为0时,不显示清除已完成任务按钮; 3.当已完成数等于全部数量时,全选框勾选. 4.当list列表为空时,全选框不勾选; 5.当主动勾选全选时,所有项都勾选,去勾选时所有项去勾选.

    项目目录:
    在这里插入图片描述
    代码:

    1.全局样式

    base.css

    /*base*/
    body {
      background: #fff;
    }
    
    .btn {
      display: inline-block;
      padding: 4px 12px;
      margin-bottom: 0;
      font-size: 14px;
      line-height: 20px;
      text-align: center;
      vertical-align: middle;
      cursor: pointer;
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
      border-radius: 4px;
    }
    
    .btn-danger {
      color: #fff;
      background-color: #da4f49;
      border: 1px solid #bd362f;
    }
    
    .btn-danger:hover {
      color: #fff;
      background-color: #bd362f;
    }
    
    .btn:focus {
      outline: none;
    }
    
    

    2.storageUtil.js

    /**
     * 使用localStorage存储数据的工具模块
     * 1.函数
     * 2.模块
     * 需要向外暴露一个功能还是多个功能
     */
    
    const PROJECTS_KEY = 'project_key'
    
    export default {
      saveProjects(val) {
        window.localStorage.setItem(PROJECTS_KEY, JSON.stringify(val))
    
      },
    
      readProjects() {
        return JSON.parse(window.localStorage.getItem(PROJECTS_KEY) || '[]');
      }
    }
    
    

    3.index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <title>vue_demo</title>
    <!--    <link rel="stylesheet" href="./static/css/bootstrap.css">-->
      </head>
      <body>
        <div id="app"></div>
        <!-- built files will be auto injected -->
      </body>
    </html>
    
    

    4.main.js

    import Vue from 'vue'
    import App from './App'
    
    import './base.css' // 全局引入base.css文件
    
    new Vue({
      el: '#app',
      components: { App },
      template: '<App/>'
    })
    
    

    5.App.vue

    <template>
      <div class="todo-container">
        <div class="todo-wrap">
          <!-- 传递函数属性(传递性)和绑定事件监听(用于父子组件) -->
          <Header @addProject="addProject"/>  <!-- 给标签对象绑定addProject事件监听 -->
          <List :projects="projects" :deleteProject="deleteProject"/>
          <todo-footer :projects="projects" :deleteAllComplete="deleteAllComplete"
            :setSelect="setSelect"/>
        </div>
      </div>
    </template>
    
    <script>
      import Header from './components/Header'
      import List from './components/List'
      import TodoFooter from './components/Footer'
      import storageUtil from './utils/storageUtil'
    
      export default {
        name: 'App',
        data () {
          return {  // 从localStorage中读取数据
            // projects: JSON.parse(window.localStorage.getItem('project_key') || '[]')
            projects: storageUtil.readProjects()
            /*[
              {
                content: '吃饭',
                complete: false,
              },
              {
                content: '睡觉',
                complete: true,
              },
              {
                content: 'coding',
                complete: false,
              },
            ],*/
          }
        },
        methods: {
          addProject (project) {  //添加project
            this.projects.push(project)
          },
          deleteProject (index) { //根据index删除project
            this.projects.splice(index,1)
          },
    
          deleteAllComplete () {  //删除所有完成的任务
            //将project中complete为true的过滤掉,留下false为一个新的数组,重新赋值
            this.projects = this.projects.filter(project => !project.complete)
          },
          setSelect (isAllCheck) {  //根据全选框值设置多选框是否勾选
            this.projects.forEach(project => project.complete = isAllCheck)
          }
        },
        watch: {  // 监视
          projects: {
            deep: true, // 深度监视
            /*handler: function (val) {
              // 将projects最新的值的json数据保存到localStorage
              // window.localStorage.setItem('project_key', JSON.stringify(val))
              storageUtil.saveProjects(val)
            }*/
            handler: storageUtil.saveProjects
          }
        },
        components: { Header, List, TodoFooter},
    
      }
    </script>
    
    <style>
      /*app*/
      .todo-container {
         600px;
        margin: 0 auto;
      }
      .todo-container .todo-wrap {
        padding: 10px;
        border: 1px solid #ddd;
        border-radius: 5px;
      }
    </style>
    
    

    6.Header.vue

    <template>
      <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认"
          @keyup.enter="addItem" v-model="content"/>
      </div>
    </template>
    
    <script>
        export default {
          props: {
            // addProject: Function
          },
          data () {
            return {
              content: '',
            }
          },
          methods: {
            addItem () {
              // 1.检查输入的合法性
              const content = this.content.trim()
              if (!content) { // content默认不为空字符串,可不写 === ''
                alert("不能为空")
                return
              }
              // 2.根据输入生成一个project对象
              const project = {
                content: this.content,
                complete: false
              }
              // 3.将对象添加到projects中
              // this.addProject(project)
              // 触发自定义事件:addProject; 只能用在父子组件,不能跨代通信
              this.$emit('addProject',project)
              // 4.清空输入
              this.content = ''
            }
          }
        }
    </script>
    
    <style>
      /*header*/
      .todo-header input {
         560px;
        height: 28px;
        font-size: 14px;
        border: 1px solid #ccc;
        border-radius: 4px;
        padding: 4px 7px;
      }
    
      .todo-header input:focus {
        outline: none;
        border-color: rgba(82, 168, 236, 0.8);
        box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
      }
    
    </style>
    
    

    7.List.vue

    <template>
      <ul class="todo-main">
        <Item v-for="(project,index) in projects" :key="index" :project="project"
          :index="index" :deleteProject="deleteProject"/>
      </ul>
    </template>
    
    <script>
        import Item from "../components/Item";
        export default {
          name: 'List',
          components: {Item},
          props: {
            projects: Array,
            deleteProject: Function
          }
        }
    </script>
    
    <style>
      /*main*/
      .todo-main {
        margin-left: 0px;
        border: 1px solid #ddd;
        border-radius: 2px;
        padding: 0px;
      }
    
      .todo-empty {
        height: 40px;
        line-height: 40px;
        border: 1px solid #ddd;
        border-radius: 2px;
        padding-left: 5px;
        margin-top: 10px;
      }
    </style>
    
    

    8.Item.vue

    <template>
    <!-- onmouseover="" onmouseout="" -->
      <li @mouseenter="handleEnter(true)" @mouseleave="handleEnter(false)" :style="{background: bgColor}">
        <label>
          <input type="checkbox" v-model="project.complete"/>
          <span>{{project.content}}</span>
        </label>
        <button class="btn btn-danger" v-show="isShow" @click="deleteItem">删除</button>
      </li>
    </template>
    
    <script>
        export default {
          name: 'Item',
          props: {
            project: Object,
            index: Number,
            deleteProject: Function
          },
          data () {
            return {
              bgColor: 'white',
              isShow: false
            }
          },
          methods: {
            handleEnter (isEnter) {
              if (isEnter) {
                this.bgColor = '#aaaaaa'
                this.isShow = true
              }else {
                this.bgColor = 'white'
                this.isShow = false
              }
            },
            deleteItem () {
              const {deleteProject, project, index} = this
              if (window.confirm(`确认删除${project.content}吗`)) {
                deleteProject(index)
              }
            }
          }
        }
    </script>
    
    <style>
      /*item*/
      li {
        list-style: none;
        height: 36px;
        line-height: 36px;
        padding: 0 5px;
        border-bottom: 1px solid #ddd;
      }
    
      li label {
        float: left;
        cursor: pointer;
      }
    
      li label li input {
        vertical-align: middle;
        margin-right: 6px;
        position: relative;
        top: -1px;
      }
    
      li button {
        float: right;
        display: none;
        margin-top: 3px;
      }
    
      li:before {
        content: initial;
      }
    
      li:last-child {
        border-bottom: none;
      }
    </style>
    
    

    9.Footer.vue

    <template>
      <div class="todo-footer">
        <label>
          <input type="checkbox" v-model="isAllCheck"/>
        </label>
        <span>
              <span>已完成{{completeSize}}</span> / 全部{{projects.length}}
            </span>
        <button class="btn btn-danger" v-show="completeSize" @click="deleteAllComplete">清除已完成任务</button>
      </div>
    </template>
    
    <script>
        export default {
          name: 'Footer',
          props: {
            projects: Array,  //project数组
            deleteAllComplete: Function,  //清除已完成任务
            setSelect: Function //根据全选框来设置其他所有多选框
          },
          computed: {
            completeSize () {
              // 遍历projects,初始化preTotal为0,若project.complete为true则加1,返回最终preTotal值
              return this.projects.reduce((preTotal,project) => preTotal+(project.complete?1:0), 0)
            },
            isAllCheck: {
              get(){  //获取多选框的值,当已完成数等于全部数量且已完成数大于0时,勾选(默认>0,可以不写)
                return this.completeSize === this.projects.length && this.completeSize
              },
              set(value){ //监视多选框的值,value是当前多选框最新的值
                this.setSelect(value)
              }
            }
          }
        }
    </script>
    
    <style>
      /*footer*/
      .todo-footer {
        height: 40px;
        line-height: 40px;
        padding-left: 6px;
        margin-top: 5px;
      }
    
      .todo-footer label {
        display: inline-block;
        margin-right: 20px;
        cursor: pointer;
      }
    
      .todo-footer label input {
        position: relative;
        top: -1px;
        vertical-align: middle;
        margin-right: 5px;
      }
    
      .todo-footer button {
        float: right;
        margin-top: 5px;
      }
    </style>
    
    

    扩展说明

    1. 案例中多处使用箭头函数,模板字符串,函数默认参数,扩展运算符,对象初始化简写等ES6新特性写法,可阅读 es6入门
    2. 关于vue的使用可参考vue官方教程和API: https://cn.vuejs.org/v2/guide/
    3. 若数字大于0(val>0),大于0可不写,默认大于0; 字符串为空时( !str),默认不为空字符串
    4. 关于Footer.vue中使用的reduce()函数: 实现数组的累加操作
      array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
      reduce函数可接受一个function函数以及一个initialValue(传入的初始值)的初始值。
      其中function函数接受四个参数分别为total(每次计算的返回值),currentValue(当前数组的元素值),currentIndex(当前数组元素的索引值),arr(当前数组元素)
  • 相关阅读:
    strcmp()比较函数和strcasecmp()和strnatcmp()
    substr()函数
    改变字符串中的字母大小写
    explode()与相反函数 implode() 和join()
    PHP nl2br() 函数
    PHP trim() 函数
    PHP的count(数组)和strlen(字符串)的内部实现
    变量处理函数库
    php中定义数组的方法
    80端口的烦恼:[3]清除NT Kernel占用80端口
  • 原文地址:https://www.cnblogs.com/itzlg/p/11879709.html
Copyright © 2011-2022 走看看