vue2/3学习(2)

160 阅读7分钟

指令 v-for

  1. 作用:基于数据循环,多次渲染整个元素 --> 数组、对象、数字...

屏幕截图 2024-01-22 083825.png 2. 遍历数组语法:v-for="(item,index) in 数组"
item 每一项 , index 下标
 省略 indexv-for = "item in 数组"

如:数组有4项,则循环四次 屏幕截图 2024-01-22 084313.png

<div id="app">
 <h1>水果铺</h1>
   <ul>
     <li v-for="(item,index) in list">
       {{item}} - {{index}}
     </li>
   </ul>
<!-- 如果只需要显示item,那么可简写成下面这样 -->
   <ul>
     <li v-for="item in list">
       {{item}} 
     </li>
   </ul>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>

<script>
 const app=new Vue({
   el:'#app',
   data:{
     list:['西瓜','草莓','哈密瓜','香蕉']   
   } 
 })
</script>

案例——小黑的书架

屏幕截图 2024-01-22 090118.png 由于vue是响应式布局,所以只需想办法通过删除数组中的数据则可同步实现页面响应删除

  • 删除列表对应项的两种思路

    • 根据index下标删

    • 根据id删(优先)

      1. filter(根据条件,保留满足条件的对应项,得到一个新数组,且不会改变原数组
      2. 将得到的新数组再赋值给原数组 this.book_list=this.book_list.filter(item => item.id!==id)
<div id="book">
  <h1>小黑的书架</h1>
    <ul>
      <li v-for="item in book_list" :key="item.id">
        <span>{{item.name}}</span>
        <span>{{item.author}}</span>
        <button @click="del(item.id)" >删除</button>
        <!-- 必须是item.id,而不是直接写id -->
      </li>
    </ul>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>

<script>
  const book=new Vue({
    el:'#book',
    data:{
      book_list:[
        {id:1,name:'《红楼梦》',author:'曹雪芹'},
        {id:2,name:'《西游记》',author:'吴承恩'},
        {id:3,name:'《水浒传》',author:'施耐庵'},
        {id:4,name:'《三国演义》',author:'罗贯中'}
      ]   
    } ,
    methods:{
      del(id){
        this.book_list=this.book_list.filter(item => item.id!==id)
      }
    }
  })
</script>

指令 v-for的 key

  1. 语法:key属性 = "唯一标识"
  2. 作用:给列表项添加唯一标识。便于 Vue 进行列表项的正确排序复用
  3. 注意点:
    • key 的值只能是字符串或数字
    • key 的值必须具有唯一性
    • 推荐使用 id 作为 key(唯一),不推荐使用 index 作为 key(会变化,不对应) 屏幕截图 2024-01-22 093322.png

不加 key 的话: v-for 的默认行为会尝试原地修改元素(就地复用)
如图:列表有4项,点击删除第一项,会因为默认行为只留下前3项,删除最后一项,并且把所有的文字内容都替换到前3个 li 身上。效果为:最后一个 li 被删除了,文字正确了,但第一项的背景颜色依旧存在

屏幕截图 2024-01-22 092144.png 屏幕截图 2024-01-22 092645.png

指令 v-model

  1. 作用:给表单元素使用,双向数据绑定 --> 可以快速获取设置表单元素内容
    • 数据变化 --> 视图自动更新
    • 视图变化 --> 数据自动更新
  2. 语法: v-model = '变量' 屏幕截图 2024-01-22 161603.png
<div id="app">
  账户:<input type="text" v-model="username"><br><br>
  密码: <input type="password" v-model="password"><br><br>
  <button @click="log">登录</button>
  <button @click="reset">重置</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>

<script>
  const app=new Vue({
    el:'#app',
    data:{
      username:'',
      password:''
    } ,
    methods:{
      log(){
        console.log(this.username,this.password)
      },
      reset(){
        this.username=''
        this.password=''
      }
    }
  })
</script>

案例——小黑记事本

屏幕截图 2024-01-22 162526.png

渲染和删除

  1. 运用 v-forkey{{ }}插值表达式
  2. 运用 v-on调用传参 和 filter过滤
<!-- 主体区域 -->
<section id="app">

  <!-- 输入框 -->
  <header class="header">
    <h1>小黑记事本</h1>
    <input placeholder="请输入任务" class="new-todo" />
  </header>
  
  <!-- 列表区域 -->
  <section class="main">
    <ul class="todo-list">
      <li class="todo" v-for="(item,index) in list" :key="item.id">
        <div class="view">
          <span class="index">{{ index + 1 }}</span> <label>{{ item.name }}</label>
          <!--因为 index 是从0开始的,所以 +1 -->
          <button class="destroy" @click="del(item.id)"></button>
        </div>
      </li>
    </ul>
  </section>
  
</section>

<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      todoName:'',
      list:[
        {id:1,name:'跑步锻炼20分钟'},
        {id:2,name:'复习数组语法'}
      ]
    },
    methods:{
      del(id){
        this.list = this.list.filter(item => item.id !== id)
      }
    }
  })
</script>

添加

  1. 通过 v-model 绑定输入框 --> 实时获取表单元素内容
  2. 点击按钮进行新增,往数组最前面加 unshift
<!-- 主体区域 -->
<section id="app">

  <!-- 输入框 -->
  <header class="header">
    <h1>小黑记事本</h1>
    <input v-model="todoName" placeholder="请输入任务" class="new-todo" />
    <button @click="add" class="add">添加任务</button>
  </header>
  
  <!-- 列表区域 -->
  <section class="main">
    <ul class="todo-list">
      <li class="todo" v-for="(item,index) in list" :key="item.id">
        <div class="view">
          <span class="index">{{ index + 1 }}</span> <label>{{ item.name }}</label>
          <button class="destroy" @click="del(item.id)"></button>
        </div>
      </li>
    </ul>
  </section>
  
</section>

<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      todoName:'',
      list:[
        {id:1,name:'跑步锻炼20分钟'},
        {id:2,name:'复习数组语法'}
      ]
    },
    methods:{
      del(id){
        this.list = this.list.filter(item => item.id !== id)
      },
      add(){ 
        if(this.todoName.trim()===''){  //trim指在处理表单输入时自动去除输入字符串两端的空白字符
          alert('请输入任务名称')
          return 
        } 
        this.list.unshift({
          id: +new Date(),   //时间戳,在这里临时当一下 id
          name: this.todoName 
        }) 
        this.todoName=''  //添加完之后输入框内自动清空
      }
    }
  })
</script>

底部统计和清空

<!-- 主体区域 -->
<section id="app">

  <!-- 输入框 -->
  <header class="header">
    <h1>小黑记事本</h1>
    <input v-model="todoName" placeholder="请输入任务" class="new-todo" />
    <button @click="add" class="add">添加任务</button>
  </header>
  
  <!-- 列表区域 -->
  <section class="main">
    <ul class="todo-list">
      <li class="todo" v-for="(item,index) in list" :key="item.id">
        <div class="view">
          <span class="index">{{ index + 1 }}</span> <label>{{ item.name }}</label>
          <button class="destroy" @click="del(item.id)"></button>
        </div>
      </li>
    </ul>
  </section>
  
  <!-- 统计和清空 -->
  <!-- 如果没有任务了,就用v-show把底部隐藏掉 --> 
  <footer class="footer" v-show="list.length>0" >
    <!-- 统计 --> 
    <span class="todo-count">合 计:<strong> {{list.length}}</strong></span>
    <!-- 清空 --> 
    <button @click="cut" class="clear-completed"> 清空任务 </button> 
  </footer>

</section>

<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      todoName:'',
      list:[
        {id:1,name:'跑步锻炼20分钟'},
        {id:2,name:'复习数组语法'}
      ]
    },
    methods:{
      del(id){
        this.list = this.list.filter(item => item.id !== id)
      },
      add(){ 
        if(this.todoName.trim()===''){  //trim指在处理表单输入时自动去除输入字符串两端的空白字符
          alert('请输入任务名称')
          return 
        } 
        this.list.unshift({
          id: +new Date(),   //时间戳,在这里临时当一下 id
          name: this.todoName 
        }) 
        this.todoName=''  //添加完之后输入框内自动清空
      },
      cut(){ 
        this.list=[]
      }
    }
  })
</script>

指令的修饰符

通过 "." 指明一些指令后缀,不同后缀封装了不同的处理操作 --> 简化代码

  1. 按键修饰符
  • @keyup.enter="" 键盘回车监听 屏幕截图 2024-01-22 175150.png keyup 键盘弹起,加上enter就是回车弹起
  1. v-model 修饰符
  • v-model.trim 去除首尾空格
  • v-model.number 尝试转数字
  1. 事件修饰符 冒泡是默认存在的(点击子盒子,父盒子也会触发)
  • @事件名.stop 阻止冒泡 屏幕截图 2024-01-22 180057.png
  • @事件名.prevent 阻止默认行为

v-bind 操作class

语法::class = "对象/数组" 屏幕截图 2024-01-22 181737.png

<style>
  .pink{
    backgroundcolor:pink;
  }
  .big{
    width:200px;
    height:200px;
  }
</style>

<div class = "box" :class = "{ pink: true, big: true }">哈哈哈</div>
<div class = "box" :class = "['pink','big']">哈哈哈</div>

案例——导航高亮

屏幕截图 2024-01-22 181916.png

<div id="app">
  <ul>
    <li v-for = "(item,index) in list" :key = "item.id" @click = "activeIndex=index">
      <a :class = "{active:index === activeIndex}"  href = "#">{{ item.name }}</a>
    </li>
  </ul>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      activeIndex:0,
      list: [
        { id: 1, name: '京东秒杀' },
        { id: 2, name: '每日特价' },
        { id: 3, name: '品类秒杀' }
      ]
    }
  })

v-bind 操作style

语法: :style = "样式对象"

屏幕截图 2024-01-22 190430.png

<style>
  .box{
    backgroundcolor:pink;
    width:200px;
    height:200px;
  }
</style>

<div class = "box" : style = "{ width: '400px', height: '400px',backgroundColor: 'red' }">哈哈哈</div>

还可以

屏幕截图 2024-01-22 190323.png

v-model 应用于其他表单元素

屏幕截图 2024-01-22 190708.png

  <div id="app">
    <h3>小黑学习网</h3>

    姓名:
    <input type="text" v-model="username">
    <br><br>

    是否单身:
    <input type="checkbox" v-model="isSingle">
    <br><br>

    <!--
      前置理解:
        1.name:给单选框加上 name 属性可以分组——同一组相互会排斥
        2.value:给单选框加上 value 属性,用于提交给后台的数据
      结合 Vue 使用 —— v-model
    -->
    性别:
    <input v-model="gender" type="radio" name="gender" value="1"><input v-model="gender" type="radio" name="gender" value="2"><!--
      前置理解:
        1.option 需要设置 value 值提供给后台
        2.select 的 value 值,关联了选中的 option 的 value 值
      结合 Vue 使用 —— v-model
    -->
    所在城市:
    <select v-modle="cityId">
      <option value="101">北京</option>
      <option value="102">上海</option>
      <option value="103">南京</option>
      <option value="104">成都</option>
    </select>
    <br><br>

    自我描述:
    <textarea v-model="desc"></textarea>

    <button>立即注册</button>

  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: '',
        isSingle: false,
        gender: "1",
        cityId: '102',
        desc: ""
      }
    })
  </script>

计算属性

概念:基于现有的数据,计算出来的新属性依赖的数据变化,自动重新计算 屏幕截图 2024-01-22 204019.png

  <div id="app">
    <h3>小黑的礼物清单</h3>
    <table>
      <tr>
        <th>名字</th>
        <th>数量</th>
      </tr>
      <tr v-for="(item,index) in list" :key="item.id">
        <td>{{ item.name }}</td>
        <td>{{ item.num }}个</td>
      </tr>
    </table>

    <!--目标:统计求和,求得礼物总数-->
    <p>礼物总数:{{ totalCount }}个</p>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        //现有的数据
        list: [
          { id: 1, name: '篮球', num: 1 },
          { id: 2, name: '玩具', num: 2 },
          { id: 3, name: '铅笔', num: 5 },
        ]
      },
      computed: {
        totalCount() {
          //基于现有的数据,编写求值逻辑
          //计算属性函数内部,可以直接通过 this 访问到 app 实例

          //需求:对 this.list 数组里面的 num 进行求和 : reduce
          let total = this.list.reduce((sum, item) => sum + item.num, 0)
          return total
        }
      }
    })
  </script>

computed 计算属性 Vs 方法 methods

computed 计算属性

作用:封装了一段对于数据的处理,求得一个结果
语法:

  • 写在 computed 配置项中
  • 作为属性,直接使用 —— this.计算属性 {{ 计算属性 }}

缓存特性(提升性能):
  计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算并再次缓存。

方法 methods

作用:给实例提供一个方法,调用以处理业务逻辑
语法

  • 写在 methods 配置中
  • 作为方法,需要调用 —— this.方法名(){{ 方法名() }}    @事件名 = "方法名"

计算属性的完整写法

计算属性默认的简写,只能读取访问,不能“修改”
如果要“修改” --> 需要写计算属性的完整写法

  computed:{
    计算属性名:{
      get(){  //当计算属性被获取求值时,执行 get (有缓存,优先读缓存)
        一段代码逻辑(计算逻辑)
        return 结果
      },
      set(修改的值){  //当计算属性被修改赋值时,执行 set ,修改的值会传递给 set 方法的形参
        一段代码逻辑(修改逻辑)
      }
    }
  }

案例:改名

<div id="app">
    姓:<input type="text" v-model="firstName"> +
    名:<input type="text" v-model="lastName"> =
    <span>{{ fullName }}</span>

    <button>改名</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        firstName: '刘',
        lastName: '贝',
      },
      methods: {
        changeName() {
          this.fullName = '刘备'
        }
      },
      computed: {
        fullName: {
          get() {
            return this.firstName + this.lastName
          },
          set(value) {
            this.firstName = value.slice(0, 1)
            this.lastName = value.slice(1)
          }
        }
      }
    })
  </script>

案例——成绩

屏幕截图 2024-01-22 223405.png

<div id="app" class="score-case">
    <div class="table">
      <table>
        <thead>
          <tr>
            <th>编号</th>
            <th>科目</th>
            <th>成绩</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody v-if="list.length>0">
          <tr v-for = "(item,index) in list" :key = "item.id">
            <td>{{ index+1 }}</td>  <!--为了保证连续,编号用 index ,并且 +1 -->
            <td>{{ item.subject }}</td>
            <!--需求:不及格的成绩要标红,当成绩小于 60 时,加上 red 类-->
            <td :class="{red:item.score < 60}">{{ item.score }}</td>
            <td><a href="#" @click.prevent="del(item.id)">删除</a></td>
          </tr>
        </tbody>
        
        <!--有数据时就渲染上面的结构,无数据时就渲染下面的结构-->
        <tbody v-else>
          <tr>
            <td colspan="5">
              <span class="none">暂无数据</span>
            </td>
          </tr>
        </tbody>

        <tfoot>
          <tr>
            <td colspan="5">
              <span>总分:{{ totalCount }}</span>
              <span style="margin-left: 50px">平均分:{{ average }}</span>
            </td>
          </tr>
        </tfoot>
      </table>
    </div>
    <div class="form">
      <div class="form-item">
        <div class="label">科目:</div>
        <div class="input">
          <input type="text" placeholder="请输入科目" v-model.trim="subject" />
        </div>
      </div>
      <div class="form-item">
        <div class="label">分数:</div>
        <div class="input">
          <input type="text" placeholder="请输入分数" v-model.number="score" />
        </div>
      </div>
      <div class="form-item"> 
        <div class="label"></div>
        <div class="input">
          <button class="submit" @click="add">添加</button>
        </div>
      </div>
    </div>
  </div> 
  
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
    const app = new Vue({
      el: '#app',
      data: {
        list: [
          { id: 1, subject: '语文', score: 86 },
          { id: 7, subject: '数学', score: 95 },
          { id: 12, subject: '英语', score: 59 },
        ],
        subject: '',
        score: ''
      },
      methods: {
        del(id) {
          this.list = this.list.filter(item => item.id != id)
        },
        add() {
          if (!this.subject) {
            alert("请输入科目")
            return
          }
          if (typeof this.score !== 'number') {
            alert("请输入正确的成绩")
          }
          this.list.unshift({
            id: +new Date(),
            subject: this.subject,
            score: this.score
          })
          this.subject = ''
          this.score = ''
        }
      },
      computed: {
        totalCount() {
          return this.list.reduce((sum, item) => sum + item.score, 0)
        },
        average() {
          if (this.list.length === 0) {
            return 0
          }
          return (this.totalCount / this.list.length).toFixed(2)
        }
      }
    })
  </script>