vue2.0-脚手架-todolist案例

一 vue脚手架2.0

安装

npm install  vue-cli -g

查阅一下脚手架可支持的模板

 vue list
 可以查到 template-name

使用脚手架生成项目(以下命令得运行在项目的包裹目录下)

vue init webpack projectName

启动项目

1 进入到项目目录
2 启动开发环境:npm run dev  或者  npm start(开发环境)
3 启动生产环境:npm run bulid(生产打包)
4 自动启动chrom:在dev中加入: --open chrom

vue2.0脚手架的问题

没有办法让vue的开发者脱离对webpack的学习

不会webpack就不能学vue

比如项目中药使用eslint的强制约束,我们需要改动config目录下的showEslintErrorsInOverlay这个配置,而且在不同的webpack环境中 eslint的版本不一样 配置也会不一样!

我们急需vue推出一个脚手架 让vue的开发者远离webpack的配置!!! @vue/cli!!
vue.js完整版
new Vue({
        el:"#app",
        template: "<v-damu></v-damu>",
        components:{
            "v-damu":{
                template:"<strong>v-damu</strong>"
            }
        }
    })
// vue.runtime.js压缩版运行时的写法	
new Vue({
        el:"#app",
        render(h){return h("v-damu")},
        components:{
            "v-damu":{
                render(h){return h("strong","v-damu")},
            }
        }
    })
// h: 创建dom节点(参数是节点名字符串)  也 直接创建组件!!!!(参数是组件对象)
    new Vue({
        el:"#app",
        render(h){
            return h({
                name:"v-damu",
                render(h){return h("strong","v-damu")},
            })
        },
    })

二 webpack找包的机制

https://webpack.docschina.org/concepts/module-resolution

import App from "./App";
    经过vue-loader的处理 我们拿到的就是app组件的配置对象
import Vue from "vue";
    webpack找到的到底是一个什么样的文件?
        webpack import的规则?
            import Vue from "vue";

配别名的情况: "vue"是一个别名!!
直接找别名指定的文件

沒有配别名的情况: "vue"是一个包!!
1. 根据resolve.modules这个配置去指定的目录中查找vue这个包
2. 找包的描述文件package.json
3. 根据resolve.mainFields 去package.json找对应的字段
4. 如果以上步骤没有找到对应的js文件 则按照resolve.mainFiles字段指定的文件名 去找对应的文件
5. 文件的扩展名 由resolve.extensions来决定
6. 如果以上步骤都没有成功  那么webpack就找不到对应的包!

vue-loader .vue文件

三 配别名:webpack.base.conf.js

 function resolve (dir) {
      return path.join(__dirname, '..', dir)
    }
    resolve: {
        alias: {
          "components":resolve('src/components')
        }
    }

scoped使用:css作用域

四 todolist案例

将静态页面拆分成静态组件

① 注意子组件的引入

② 静态资源一定要放在stasic中 或者 将静态资源作为模块引入;建议放在stasic,通过index.html的标签中引入

初始化静态页面

app.vue

<todo-list  :list="list"></todo-list>
data(){
   return {
      list:[
      {id:0,checked:true,name:"张三"},
      {id:1,checked:true,name:"李四"},
      {id:2,checked:true,name:"王五"},
      {id:3,checked:true,name:"赵六"}
    ]
   }

在list中通过props进行接参 因为list为数组,所以需要进循环遍历

<todo-item v-for="item in list" :key="item.id" :item="item"></todo-item>
props:{
      list:Array  //prop限制为数组类型
  },

将静态数据放入到item中,可以在页面中呈现出来,通过props来接收值。

<span>{{item.name}}</span>
 props:{
    item:Object  //prop限制为对象类型 因为{id:0,checked:true,name:"张三"}
  },

添加todo数据

header .vue 进行事件的双向绑定,在输入框中输入内容,当点击回车的时候增加数据

	<input type="text" placeholder="请输入你的任务名称,按回车键确认"
        v-model.trim="name" @keydown.13="addTask"/>
  let id = 4  
  data(){
    return {
      name:""
    }
  },
  // 当按回车的时候会触发事件,增加数据
  methods:{
    addTask() {
      // 1 获取用户输入,构建一个任务对象,塞到list中
      let name = this.name;
      let id = id
      let task = {
        id:id++,
        checked:false,
        name
      }
      // 2 实现list到app的子向父的数据传递
      this.$emit("addTask",task)
      // 3 清空输入框
      this.name = ""
    }
  }

app.vue

 methods:{
     // 输入框中增加的是来自数组list中的数据,在前面增加数据
    addTask(task) {
      this.list.unshift(task)
    },
 }

子组件被选中传递数据

v-model=“item.checked” 脏数据 直接修改了子组件的内容,当list中数据较多的时候后期代码不易维护,因此用总线机制进行非父子间的传递,手动进行选中和改变

item.vue

<input type="checkbox" :checked="item.checked" @change="changeFn(item.id,$event)"/>
methods:{
      changeFn(id,ev) {
          console.log(ev.target)   //<input data-v-79668b5a type ="checkbox">
          //todo-item 将数据传给app(非父子)--总线机制
          let checked = ev.target.checked
          this.$bus.$emit("checked",id,checked)  //将id,checked进行发布,在app中进行订阅查收
          
      }
  }

或者通过计算属性进行数据的传送和接收

 computed:{
      checked:{
          //得到并返回被选中的值
          get() {return this.item.checked},  
          //将得到的被选中的值进行弹射发布
          set(checked) {
              this.$bus.$emit("checked",this.item.id,checked) 
          }
      }
  }

在main.js入口文件中定义总线机制

Vue.protype.$bus = new Vue()

在app.vue中进行数据的订阅查收

 methods:{  // 接收了$emit弹射的id,checked,根据id修改list中的数据
 	checked(id,checked) {
       this.list.forEach((item)=> {
         if(item.id === id) {
           item.checked = checked
         }
       })
     }
 }
 //  订阅查收在生命周期的mounted中进行 接收了$emit弹射的id,checked
  mounted(){
    this.$bus.$on("checked",this.checked)
  },

注意:

① v-model.lazy不能和input.change一起使用,不能跟js认识的数据类型,一定跟类似data这种管理的数据

② v-model脏数据:

v-model=“非本组件的数据”,因为v-model是双向绑定,跟非本组件的数据语法上市没有问题的,违背了数据传递的规则:子组件直接修改了父组件的数据

解决:将v-model读取值和修改值得能力使用其他方案代替。

  • v-model读取值能力,可使用v-bind:value=“非本组件的数据”

  • v-model读取值能力,可使用v-model=“计算属性” 计算属性的get方法返回非组件的值来代替

  • v-model修改值能力,可以使用change事件+子向父数据传递来代替

  • v-model修改值能力,计算属性set方法+子向父数据传递来代替

删除todo数据

item:去除button中的display:none,加hover

li:hover {
  background-color: pink;
}
li:hover > button {
  display: block
}

非父子传递:总线机制 item

<button class="btn btn-danger" @click="delTask(item.id)">删除</button>
methods:{
 	     // 删除item中的内容
      	deleteTask(id) {
        this.$bus.$emit("deleteTask",id)
      }
 }

app.vue列表删除,在mounted中注册on事件,根据id删除list中数据

mounted(){
    // item上的列表功能
    this.$bus.$on("checked",this.checked)
    // item上的列表删除功能
    this.$bus.$on("delTask",this.delTask)
  },
 methods:{
 	delTask(id) {
       this.list = this.list.filter(item=> item.id !== id)
     }
 }

需求扩展

item.vue 加–每个li给伪元素,用class和数据做绑定

.done {
  position: relative;
}
.done:after {
  content:'';
  display: block;
  height:3px;
  background-color:red;
  width: 80%;

  position: absolute;
  left: -55px;
  right:0;
  top: 0;
  bottom: 0;
  margin: auto;
}
<li :class="{done:checked}"></li>

完成footer功能

footer样式调整

	<span>
        <span>
          <strong>已完成</strong>
          <strong style="font-size:20px;color:green">{{doneNum}}</strong>
        </span> 
        <span>/</span>
        <span>
          <strong>全部</strong>
          <strong style="font-size:20px;color:red">{{list.length}}</strong>
        </span>
    </span>

已完成:传list 用props接收,已完成用计算属性(有依赖关系,且复杂)

<todo-footer :list="list"></todo-footer>
 computed:{
    // 已完成数量
    doneNum() {
     // 基础写法
      // let num = 0;
      // this.list.forEach(item => {
      //   if(item.checked)  {
      //     num++
      //   }
      // })
      // return num

      // 累加器reduce
      return this.list.reduce((adder,item) =>{
        // 简易写法
        return item.checked ? adder+1 : adder
        // if(item.checked) {
        //   return adder+1
        // } else {
        //   return adder
        // }
      },0)
    }
  }
  }

全选:v-model(计算属性),考虑不选的时候

<input type="checkbox" v-model="checkAll"/>
computed:{
 //全选功能
    checkAll:{
      get(){
        console.log("checkAll ,get")
        //全选按钮选中的逻辑:  代办任务的数量等于已完成的数量 且 他们都不等于0
        return( (this.list.length === this.doneNum) && (this.list.length!==0 && this.doneNum!==0) )
      },
      set(checked){
        // checked: 当前全选按钮最新的状态
        this.$emit("checkAll",checked)
      }
    }
  }
}

app.vue:全选功能 用事件的方法(总线机制也可以)

<todo-footer :list="list"  @checkAll="checkAll"></todo-footer>
methods:{
	//全选功能
      checkAll(checked){
        this.list.forEach(item=>item.checked=checked)
      },
}

footer:全部按钮禁用:input有个属性disabled 样式修改为:not-allow

<input type="checkbox" v-model="checkAll" :disabled="disabled"/>  
input[disabled]{
    cursor: not-allowed;
  }
computed:{
	//全选按钮的禁用
    disabled(){
      //当没有数据的时候,已完成和已选中的等于0不能被全选
      return this.list.length===0 && this.doneNum===0
    }
}

清除已完成任务

footer点击事件 触发app的清除任务

<button class="btn btn-danger" @clear="clear">清除已完成任务</button>
 methods:{
    clear() {
      this.$emit("clear")
    }
  },

app.vue 清除任务:过滤操作

<todo-footer :list="list" @checkAll="checkAll" @clear="clear" ></todo-footer>
methods:{
      //清除已完成任务
      clear(){
        this.list = this.list.filter( item => !item.checked)
      }
}

收尾

全部用总线机制传值,利于维护,且美观

    mounted() {
      //item上的列表选中功能
      this.$bus.$on("checked",this.checked);
      //item上的列表删除功能
      this.$bus.$on("delTask",this.delTask);
      //往list中新增数据
      this.$bus.$on("addTask",this.addTask);
      //全选功能
      this.$bus.$on("checkAll",this.checkAll);
      //清除已完成任务
      this.$bus.$on("clear",this.clear);
    },

本地存储

app.vue:watch监听list到本地:属性名(变量名)

写:将newList存到localstorage中(存json字符串)

watch:{
      //默认是浅监听
      list:{
        handler(newList){
          //将newList存到localStorage中 (存json字符串!)
          localStorage.setItem("todo-list-data",JSON.stringify(newList))
        },
        deep:true //开启深度监听
      }
    }

读:在生命周期的create中 读取数据,并赋值给list

    created() {
      //将储存中的数据读出来 赋值给list
      const list = localStorage.getItem("todo-list-data")
      this.list = list ? JSON.parse(list) : []
    }

header.vue 修改id

  data(){
    let id = localStorage.getItem("todo-list-id")
    id = id?JSON.parse(id) :0
    return {
      name:"",
      id,
    }
 methods:{
 	//4 将id存储起来
      localStorage.setItem("todo-list-id",JSON.stringify(this.id))
 }   

版权声明:本文为weixin_57218747原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。