Vue学习(3)

目录

使用vue-cli创建项目

碰到的问题

打包发布项目

案例_评论

组件间通信

props通信

案例_todoList

存储数据


使用vue-cli创建项目

通过指令:npm run serve运行

main.js代码:

// 入口js:创建Vue实例
import Vue from 'vue/dist/vue.esm.js'
import App from './App.vue'

new Vue({
  el:'#app',
  components:{
    App
  },
  template: '<App/>'
})

HelloWorld.vue(组件vue)代码:

<template>
  <div>
    <p>{{msg}}</p>
  </div>
</template>

<script>
export default {  //配置对象(与Vue一致)
    data(){   //data必须写函数
      return {
        msg:'hello!',
      }
    }
}
</script>

<style>
  p{
    color: aqua;
  }
</style>

App.vue代码:

<template>
  <div>
    <img src="./assets/logo.png" alt="logo">
    <!-- 3.使用组件标签 -->
    <hello/>
  </div>
</template>

<script>
//1.引入组件
import hello from './components/HelloWorld.vue'
export default {
  //2.映射组件标签
  components:{
    hello
  }
}
</script>

<style>
  img{
    width: 200px;
  }
</style>

index.html代码:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>vue</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

碰到的问题

运行脚手架时报错:

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

解决方案:

在引用vue时直接写成如下写法

import Vue from 'vue/dist/vue.esm.js'

打包发布项目

通过指令:npm run build来打包项目


案例_评论

App.vue代码:

<template>
  <div id="app">
    <div>
      <header class="site-header jumbotron">
        <div class="container">
          <div class="row">
            <div class="col-xs-12">
              <h1>请发表对Vue的评论</h1>
            </div>
          </div>
        </div>
      </header>
      <div class="container">
        <Add :addComment='addComment'/>
        <List :comments="comments" :delComment='delComment'/>
      </div>
    </div>
  </div>
</template>

<script>
import Add from "./components/Add.vue";
import List from "./components/List.vue";
export default {
  data() {
    return {
      comments: [ //数据在哪个组件,更新数据的行为(方法)就应该定义在哪个组件
        { name: "Bob", content: "Vue还不错" },
        { name: "Mike", content: "Vue很简单" },
        { name: "Chen", content: "Vue很难" },
      ],
    };
  },
  methods:{
    addComment(comment){
      this.comments.unshift(comment);
    },
    delComment(index){
      this.comments.splice(index,1);
    }
  },
  components: {
    Add,
    List,
  },
};
</script>

<style>
</style>

List.vue代码:

<template>
  <div class="col-md-8">
    <h3 class="reply">评论回复:</h3>
    <h2 v-show="comments.length==0">暂无评论,点击左侧添加评论!!!</h2>
    <ul class="list-group">
      <Item
        v-for="(comment, index) in comments"
        :key="index"
        :comment='comment'
        :delComment='delComment'
        :index='index'
      />
    </ul>
  </div>
</template>

<script>
import Item from "./Item.vue";
export default {
  //声明接收属性:这个属性就会成为组件对象的属性
  props: ["comments","delComment"], //这种方式只指定了属性名
  components: {
    Item,
  },
};
</script>

<style>
.reply {
  margin-top: 0px;
}

li {
  transition: 0.5s;
  overflow: hidden;
}

.handle {
  width: 40px;
  border: 1px solid #ccc;
  background: #fff;
  position: absolute;
  right: 10px;
  top: 1px;
  text-align: center;
}

.handle a {
  display: block;
  text-decoration: none;
}

.list-group-item .centence {
  padding: 0px 50px;
}

.user {
  font-size: 22px;
}
</style>

Add.vue代码:

<template>
  <div class="col-md-4">
    <form class="form-horizontal">
      <div class="form-group">
        <label>用户名</label>
        <input
          type="text"
          class="form-control"
          placeholder="用户名"
          v-model="name"
        >
      </div>
      <div class="form-group">
        <label>评论内容</label>
        <textarea
          class="form-control"
          rows="6"
          placeholder="评论内容"
          v-model="content"
        ></textarea>
      </div>
      <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
          <button
            type="button"
            class="btn btn-default pull-right"
            @click="add"
          >提交</button>
        </div>
      </div>
    </form>
  </div>
</template>

<script>
export default {
    props:{
        addComment:{    //指定属性名/属性值的类型/必要性
            type:Function,
            required:true
        }
    },
    data(){
        return{
            name:'',
            content:''
        }
    },
    methods:{
        add(){
            //1.检查输入的合法性
            const name=this.name.trim();
            const content=this.content.trim();
            if(!name || !content){
                alert('姓名或内容不能为空');
                return;
            }
            //2.根据输入的数据封装成一个comment对象
            const comment={name,content};
            //3.添加到comments中
            this.addComment(comment);
            //4.清除输入
            this.name='';this.content='';
        }
    }
};
</script>

<style>
</style>

Item.vue代码:

<template>
  <div>
    <li class="list-group-item">
      <div class="handle">
        <a href="javascript:;" @click="del">删除</a>
      </div>
      <p class="user"><span>{{comment.name}}</span><span>说:</span></p>
      <p class="centence">{{comment.content}}</p>
    </li>
  </div>
</template>

<script>
export default {
  props: {
    //指定属性名和属性值的类型
    comment: Object,
    delComment:Function,
    index:Number
  },
  methods:{
      del(){
          const {comment,index,delComment}=this;
          if(window.confirm('确定删除'+comment.name+'的评论吗?')){
              delComment(index);
          }
      }
  }
};
</script>

<style>
</style>

组件间通信

通过在标签处添加属性可实现将data中的数据传入到对应的组件中,然后在相应的组件的js中通过props声明接受属性。

组件间通信的基本原则:

  1. 不要在子组件中直接修改父组件的状态数据
  2. 数据在哪,更新数据的行为(方法)就定义在哪

props通信

使用组件标签:

<my-component name='tom' :age='3' :set-name='setName'></my-component>

定义Mycomment时:

  • 在组件内声明所有的props
  • 方式一:只指定名称
props: ['name', 'age', 'setName']
  • 方式二:指定名称和类型
props: {
    name: String, 
    age: Number, 
    setNmae: Function
}
  • 方式三:指定名称/类型/必要性/默认值
props: {
    name: {type: String, required: true, default:xxx},
}

注意:

  1. 此方式用于父组件向子组件传递数据
  2. 所有标签属性都会成为组件对象的属性,模板页面可以直接引用
  3. 如果需要向非直接子后代传递数据必须逐层传递
  4. 兄弟组件也不能直接props通信,必须借助父组件才可以

案例_todoList

App.vue代码:

<template>
  <div class="todo-container">
    <div class="todo-wrap">
      <todoHeader :addTodo="addTodo"/>
      <todoList :todos='todos' :delTodo='delTodo'/>
      <todoFooter :todos='todos' :delCom='delCom' :selectAll='selectAll'/>
    </div>
  </div>
</template>

<script>
import todoHeader from './components/todoHeader.vue'
import todoList from './components/todoList.vue'
import todoFooter from './components/todoFooter.vue'

export default {
  data(){
    return {
      //从localStroage读取todos
      todos:JSON.parse(window.localStorage.getItem('todos_key')||'[]')
    }
  },
  watch:{ //监视
    todos:{
      deep:true, //深度监视
      handler:function(newValue){
        //将todo最新值的JSON数据,保存到localStroage
        window.localStorage.setItem('todos_key',JSON.stringify(newValue));
      }
    }
  },
  components:{
    todoHeader,todoList,todoFooter
  },
  methods:{
    addTodo(todo){
      this.todos.unshift(todo);
    },
    delTodo(index){
      this.todos.splice(index,1);
    },
    delCom(){
      this.todos=this.todos.filter(todo=>!todo.complete);
    },
    selectAll(check){
      this.todos.forEach(todo=>todo.complete=check);
    }
  }
};
</script>

<style>
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

todoHeader.vue代码:

<template>
  <div class="todo-header">
    <input
      type="text"
      placeholder="请输入你的任务名称,按回车键确认"
      @keyup.enter="add"
      v-model="title"
    />
  </div>
</template>

<script>
export default {
  props:{
    addTodo:Function
  },
  data(){
    return {
      title:''
    }
  },
  methods:{
    add(){
      //1.检查输入的合法性
        const title=this.title.trim();
        if(!title){
          alert("请输入!");
          retrun;
        }
      //2.根据输入生成一个todo对象
        const todo={
          title,
          complete:false
        };
      //3.添加到todos
        this.addTodo(todo);
      //4.清除输入
        this.title='';
    }
  }
};
</script>

<style>
.todo-header input {
  width: 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>

todoList.vue代码:

<template>
  <ul class="todo-main">
    <todoItem v-for="(todo, index) in todos" :key="index" :todo='todo' :index='index' :delTodo='delTodo'/>
  </ul>
</template>

<script>
import todoItem from './todoItem.vue'

export default {
  props:{
    todos:Array,
    delTodo:Function
  },
  components:{
    todoItem
  }
};
</script>

<style>
.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>

todoItem.vue代码:

<template>
    <li @mouseenter="show(true)" @mouseleave="show(false)" :style="{background:bgColor}">
      <label>
        <input type="checkbox" v-model="todo.complete"/>
        <span>{{todo.title}}</span>
      </label>
      <button
        class="btn btn-danger"
        v-show="isShow"
        @click="del"
      >删除</button>
    </li>
</template>

<script>
export default {
    props:{
        todo:Object,
        index:Number,
        delTodo:Function
    },
    data(){
      return {
        bgColor:'white',
        isShow:false,
      }
    },
    methods:{
      show(isEnter){
        if(isEnter){
          this.bgColor='gray';
          this.isShow=true;
        }
        else {
          this.bgColor='white';
          this.isShow=false;
        }
      },
      del(){
        const {todo,index,delTodo}=this;
        if(window.confirm('确认删除'+todo.title+'吗?')){
          delTodo(index);
        }
      }
    }
}
</script>

<style>
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>

todoFooter.vue代码:

<template>
  <div class="todo-footer">
    <label>
      <input type="checkbox" v-model="isCheck"/>
    </label>
    <span>
      <span>已完成{{completeCount}}</span> / 全部{{todos.length}}
    </span>
    <button class="btn btn-danger" @click="delCom()" v-show="completeCount">清除已完成任务</button>
  </div>
</template>

<script>
export default {
  props:{
      todos:Array,
      delCom:Function,
      selectAll:Function
  },
  computed:{
    completeCount(){
      var count=0;
      const {todos}=this;
      for(var i=0;i<todos.length;i++){
        if(todos[i].complete==true) count++;
      }
      return count;
    },
    isCheck:{
      get(){
        return this.completeCount==this.todos.length && this.todos.length>0;
      },
      set(value){
        this.selectAll(value);
      }
    }
  }
};
</script>

<style>
.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>

存储数据

通过对todos数组进行深度监视,在localStroage中读写数据:

  • localStroage读取todos

  data(){
    return {
      //从localStroage读取todos
      todos:JSON.parse(window.localStorage.getItem('todos_key')||'[]')
    }
  }
  • todo最新值的JSON数据,保存到localStroage

  watch:{ //监视
    todos:{
      deep:true, //深度监视
      handler:function(newValue){
        //将todo最新值的JSON数据,保存到localStroage
        window.localStorage.setItem('todos_key',JSON.stringify(newValue));
      }
    }
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值