74. vue之es6_列表渲染_数据双向绑定_事件处理

1. es6

1.1 key的引号可以省略

  // 1 对象定义--原始方法
    var person = {
        'name': 'avril',
        'age': 19
    }
 // es6中key的引号可以省略
    var person2 = {
        name: 'avril',
        age: 19

1.2 修改对象中的值

[1] 修改对象中不可变类型的值时,对象的值不会发生改变

<script>
    var name = 'kylian'
    var age = 19
    var person = {
        name,  /*将变量放到对象中*/
        age
    }
    name = 'haaland'
    /*consol.log运行代码直接在前端的控制台打印*/
    console.log(name)
    console.log(person)
</script>

name为不可变类型修改为了haaland,但是对象中的person未发生修改

image

[2] 修改对象中可变类型的值时,对象的值会随之发生改变

<script>
    var name = 'kylian'
    var age = 19
    var player = {
        name:"neymar",
        hobby:"soccer"
    }
    var person = {
        name,  /*将变量放到对象中*/
        age,
        player  /*将对象放到对象中*/
    }
    name = 'haaland'
    player.hobby='swim'
    /*consol.log运行代码直接在前端的控制台打印*/
    console.log(name)
    console.log(person)
</script>

person对象中的player对象为可变类型,修改player的值person的值随之改变

image

1.3 对象中放函数

方式一

<script>
    var name = 'kylian'
    var age = 19
    var showName = function () {
        console.log(this.name)
    }
    var person = {
        name,
        age,
        showName
    }
    showName()  /*this指代window对象*/
    person.showName()  /*调用函数*/  this指代person对象
</script>

image

直接调用showName()这个函数里面的this指代window对象

通过person对象调用showName(),showName()里面的this指代person对象

方式二:对象中的函数不需要写function

<script>
    var name = 'avril'
    var age = 20
    var person = {
        name,
        age,
        showName() {
            console.log(this.name)
        }
    }
    person.showName()
</script>

2. 指令系统

2.1 列表渲染v-for

[1] 概念

列表渲染指令:v-for,基于数组 / 对象循环渲染一组 DOM 元素。

(1) v-for循环数组

语法:v-for="(item, index) in arr"

  item:数组每一项
  index:索引(可选)

推荐使用 of替代 in,更贴近 JS 迭代语法:v-for="(item, index) of list"

(2) v-for循环对象

v-for="(value, key, index) in obj"

  value:属性值
  key:属性名
  index:索引(可选)

[2] 代码

bootstrap官网--V5--进入中文文档--下载--下载bootstrap生产文件

bootstrap V5不依赖jquery

(1) v-for循环数组

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>购物车</title>
    <script src="vue2/vue.js"></script>
    <link rel="stylesheet" href="bootstrap/bootstrap.css">
    <script src="bootstrap/bootstrap.js"></script>
</head>
<body>
<div id="d1">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 offset-md-3">
            <h1 class="text-center">购物车</h1>
            <hr>
            <table class="table table-striped">  <!--复制tables的striped rows-->
                <thead>
                <tr>
                    <th scope="col">商品id</th>
                    <th scope="col">商品名</th>
                    <th scope="col">商品价格</th>
                    <th scope="col">商品数量</th>
                </tr>
                </thead>
                <tbody>
                <tr v-for="item in goods">  <!--循环表体的每一行-->
                    <th scope="row">{{item.id}}</th>  <!--插值语法-->
                    <td>{{item.name}}</td>
                    <td>{{item.price}}</td>
                    <td>{{item.count}}</td>
                </tr>
                </tbody>
            </table>
            </div>

        </div>
    </div>

</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            goods:[
                {id:1001, name:'作业本', price:'2', count:6},
                {id:1002, name:'书包', price:'60', count:1},
                {id:1003, name:'图书', price:'15', count:6},
                {id:1004, name:'笔', price:'2', count:10},
            ]
        },
    })
</script>
</html>

image

(2) 点击按钮加载购物车数据,否则显示为空

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>购物车</title>
    <script src="vue2/vue.js"></script>
    <link rel="stylesheet" href="bootstrap/bootstrap.css">
    <script src="bootstrap/bootstrap.js"></script>
</head>
<body>
<div id="d1">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 offset-md-3">
                <h1 class="text-center">购物车</h1>
                <div class="d-flex justify-content-center">  <!--div与class的作用是使按钮居中-->
                    <button class="btn btn-success" @click="handleLoad">加载购物车</button>
                    <button class="btn btn-info" @click="handleClear" style="margin-left: 20px">清空购物车</button>
                </div>
                <hr>
                <p v-if="goods.length==0">购物车是空的</p>  <!--v-if的情况-->
                <!--v-else的情况-->
                <table class="table table-striped" v-else>  <!--复制tables的striped rows-->
                    <thead>
                    <tr>
                        <th scope="col">商品id</th>
                        <th scope="col">商品名</th>
                        <th scope="col">商品价格</th>
                        <th scope="col">商品数量</th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr v-for="item in goods">  <!--循环表体的每一行-->
                        <th scope="row">{{item.id}}</th>  <!--插值语法-->
                        <td>{{item.name}}</td>
                        <td>{{item.price}}</td>
                        <td>{{item.count}}</td>
                    </tr>
                    </tbody>
                </table>
            </div>

        </div>
    </div>

</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            goods: []
        },
        methods: {
            handleLoad() {
                this.goods = [
                    {id: 1001, name: '作业本', price: '2', count: 6},
                    {id: 1002, name: '书包', price: '60', count: 1},
                    {id: 1003, name: '图书', price: '15', count: 6},
                    {id: 1004, name: '笔', price: '2', count: 10},
                ]
            },
            handleClear() {
                this.goods = []
            }
        }
    })
</script>
</html>

image

点击加载按钮

image

点击加载之后再点击清空按钮

image

(2) v-for循环字符串

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <p v-for="item in x">{{item}}</p>
</div>

</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
          x: 'hello world'
        },
    })
</script>
</html>

image

(3) v-for循环数字

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <p v-for="item in 10">{{item}}</p>
</div>

</body>
<script> 
    /*循环数字也必须加上script*/
    var a = new Vue({
        el: '#d1',
    })
</script>
</html>

image

(4) v-for循环对象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <p v-for="item in obj">{{item}}</p>
</div>

</body>
<script>
   
    var a = new Vue({
        el: '#d1',
        data: {
            obj:{name: 'avril', age: 19, hobby: 'sing'}
        }
    })
</script>
</html>

前端打印的是value值

image

同时循环k、v

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <p v-for="(v, k) in obj">key值为:{{k}},value值为:{{v}}</p>
</div>

</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            obj: {name: 'avril', age: 19, hobby: 'sing'}
        }
    })
</script>
</html>

image

[3] key的作用(重点)

(1) 概念

:key必须绑定唯一标识,不要直接用 index 当 key(存在隐患)

作用:Vue 虚拟 DOM diff 算法,通过 key 识别节点,Vue diff 在更新列表时,默认就地复用 DOM
风险:数组增删、打乱顺序时,index 作为 key 会造成状态错位(输入框内容错乱等)
最佳实践:使用业务唯一 id(id、uuid)

如果没有 key,或者 key 用 index,数组顺序变化时 Vue 不会移动 DOM,只会复用已有 DOM 修改内容,导致元素内部状态错乱。

diff算法:vue中使用的是虚拟DOM,会和原生的DOM进行比较,然后进行数据的更新,提高数据的刷新速度

在v-for循环数组、对象时,建议在控件/组件/标签写1个key属性,属性值唯一
页面更新之后,会加速DOM的替换(渲染)
:key="变量"

(2) 代码

不写key或用index作为key会出现状态错乱

使用业务唯一 id 作为 key(正确写法)

<div v-for="item in list" :key="item.id">
  {{ item.name }}
  <input type="text">
</div> 

[4] 数组的检测与更新

Vue 能够监听数组函数触发前端响应式更新

可以检测到的函数:

  push:最后位置添加
  pop:最后位置删除
  shift:第一个位置删除
  unshift:第一个位置添加
  splice:切片
  sort:排序
  reverse:反转

不能检测到的函数:

  filter:过滤
  concat:追加另一个数组
  slice
  map

原因:

  作者重写了相关函数(只重写了函数的一部分,还有另一部分没有重写)

数组或对象的值改了,但是页面没有变化:
  使用Vue.set(对象,对象key,对象value)
  使用Vue.set(数组,数组的下标,数组value)

解决方法:

// 方法1:通过 索引值 更新数组(数据会更新,但是页面不会发生改变)
vm.arrayList[0]
"Alan"
vm.arrayList[0]='Darker'
"Darker"

// 方法2:通过 Vue.set(对象, index/key, value) 更新数组(数据会更新,页面也会发生改变)
Vue.set(vm.arrayList, 0, 'Darker')

2.2 表单元素数据双向绑定

[1] 概念

v-model 是语法糖,用于表单元素双向数据绑定,本质是 :value + @input 的简写。

双向绑定:数据改变 → 视图更新;视图输入修改 → 同步更新数据。

v-model作用于输入型标签:

  1. input 各类输入框,text、password、radio、checkbox 等
  2. textarea 多行文本域
  3. select 下拉选择框

[2] 代码

(1) 属性指定单向绑定数据

属性指令给标签绑定值

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <p>用户名:<input type="text" :value="username"></p>  <!--通过属性指令获取data中的值-->
    <p>密码:<input type="password"></p>
    <p><input type="submit" value="登录" @click="handleSubmit"></p>
</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            username: 'avril',  /*通过属性指令传值给input标签*/
            password: ''
        },
        methods:{
            handleSubmit(){
                console.log(this.username)
            }
        }
    })
</script>
</html>

image

变量改变,页面随之改变

image

页面发生变化,变量不会随之改变

数据是单向绑定

image

(2) v-model进行数据双向绑定 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <p>用户名:<input type="text" v-model="username"></p>  <!--v-model与data中的数据双向绑定-->
    <p>密码:<input type="password"></p>
    <p><input type="submit" value="登录" @click="handleSubmit"></p>
</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            username: 'avril',  /*传值给input标签*/
            password: ''
        },
        methods:{
            handleSubmit(){
                console.log(this.username)
            }
        }
    })
</script>
</html>

 变量改变,页面随之改变

image

image

页面改变,变量随之改变

数据是双向绑定

image

2.3 事件处理

[1] 概念

input标签的事件:

  input  输入框输入内容,触发的事件  每输入一个字符就立刻触发

  change  输入框发生变化,触发的事件  只有内容变更 + 失焦(文本框)才触发

  blur  输入框失去焦点,触发的事件

  focus  获得焦点,触发的事件

[2] 代码

(1) input事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <input type="text" v-model="aa" @input="handleInput">  <!--v-model实现输入框与data中的aa双向绑定,并且给标签绑定input事件-->
    <br>
    {{aa}}  <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            aa: '',  /*存储双向绑定的数据,供v-model和插值语法使用*/
        },
        methods: {
            handleInput() {
                console.log("检测到内容输入")
            }
        }
    })
</script>
</html>

输入框每输入一个字母,都会触发一次input事件

image

(2) change事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <input type="text" v-model="aa" @change="handleChange">  <!--v-model实现输入框与data中的aa双向绑定,并且给标签绑定Change事件-->
    <br>
    {{aa}}  <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            aa: '',  /*存储双向绑定的数据,供v-model和插值语法使用*/
        },
        methods: {
            handleChange() {
                console.log("检测到内容变化")
            }
        }
    })
</script>
</html>

只输入内容并且文本框聚焦时,不会触发change事件

image

当内容变化且文本框失去焦点时,会触发change事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <input type="text" v-model="aa" @change="handleChange">  <!--v-model实现输入框与data中的aa双向绑定,并且给标签绑定Change事件-->
    <br>
    {{aa}}  <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            aa: '',  /*存储双向绑定的数据,供v-model和插值语法使用*/
        },
        methods: {
            handleChange() {
                console.log("检测到内容变化")
            }
        }
    })
</script>
</html>

image

(3) blur事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <input type="text" v-model="aa" @blur="handleBlur">  <!--v-model实现输入框与data中的aa双向绑定;给标签绑定blur事件-->
    <br>
    {{aa}}  <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            aa: '',  /*存储双向绑定的数据,供v-model和插值语法使用*/
        },
        methods: {
            handleBlur() {
                console.log("检测到失去焦点")
            }
        }
    })
</script>
</html>

只要从聚焦到失焦,就会触发blur事件

image

(4) focus事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <input type="text" v-model="aa" @focus="handleFocus">  <!--v-model实现输入框与data中的aa双向绑定;给标签绑定focus事件-->
    <br>
    {{aa}}  <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            aa: '',  /*存储双向绑定的数据,供v-model和插值语法使用*/
        },
        methods: {
            handleFocus() {
                console.log("检测到聚焦")
            }
        }
    })
</script>
</html>

只要从失焦到聚焦,就会触发focus事件

image

2.3 练习前补充

[1] 数组的筛选方法

匿名函数返回true,就保留这个字符串;返回false就不保留。

    var a=['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf']
    var res=a.filter(function (item){
        if (item.length >=3){
            return true
        }else {
            return false
        }
    })
    console.log(res)

[2] indexOf判断字符串的包含

在目标字符串中查找字符串,返回首次匹配的起始索引;找不到返回 -1。

    var a = 'avril'
    var b = 'avril is beautiful'
    if (b.indexOf(a) >= 0) {  /*只要大于等于0就代表包含*/
        console.log('在')
    } else {
        console.log('不在')
    }

[3] _this

_this 不是 Vue 内置关键字,是开发者自定义变量,Vue 官方没有定义 _this。
作用:缓存当前组件实例 this,用于回调函数内部丢失 this 指向的场景,Vue2 选项式 API 使用最多,Vue3 setup 语法糖一般不用。

外层函数是 methods 的方法(普通 function):外层直接用 this,this 指向 Vue 实例。
里层函数如果是 普通 function:this 不再指向 Vue 实例,需要提前保存 var _this = this。
里层函数如果是 箭头函数:不需要 _this,直接继承外层的 this。

只区分里层函数是普通 function 还是箭头函数,无需关注是不是嵌套。

示例1 :里层普通 function → 需要 _this

methods: {
  outer() {
    // 外层:methods函数,this = Vue实例
    console.log(this.name)

    var _this = this // 在外层先存好Vue实例

    function inner() {
      // 里层普通function,this不是vue实例
      console.log(_this.name) // 要用保存好的_this
    }
    inner()
  }
}

示例 2:里层箭头函数 → 不需要 _this

methods: {
  outer() {
    console.log(this.name)

    const inner = () => {
      // 箭头函数不创建独立this,继承outer里的this
      console.log(this.name)
    }
    inner()
  }
}

示例 3:异步回调嵌套(最常用场景)

methods: {
  fetch() {
    // 外层 this → vue实例
    var _this = this

    setTimeout(function () {
      // 第一层普通function回调,this丢失
      _this.msg = '第一层'

      setTimeout(function () {
        // 第二层,依然使用同一个_this
        _this.msg = '第二层'
      }, 100)
    },100)
  }
}

改成箭头函数版本,直接省去 _this:

methods: {
  fetch() {
    setTimeout(() => {
      this.msg = '第一层'
      setTimeout(() => {
        this.msg = '第二层'
      },100)
    },100)
  }
}

2.4 练习

根据输入框输入的字符串来匹配包含该字符串的字符串

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
    <!--v-model实现输入框与data中的msg双向绑定,msg为输入的值;给标签绑定focus事件,输入框聚焦再展示数据;-->
    <!--绑定input事件,根据输入的数据动态筛选-->
    <input type="text" v-model="msg" @focus="handleFocus" @input="handleInput">
    <ul>
        <li v-for="item in info_revise">{{item}}</li>  <!--遍历修改后的数据-->
    </ul>

</div>
</body>
<script>
    var a = new Vue({
        el: '#d1',
        data: {
            msg: '',  /*存储双向绑定的数据,供v-model使用*/
            /*初始值为空,聚焦再添加数据*/
            info: ['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf'],  /*保存原始数据,使得输入字符串在退格时也能读取全部数据*/
            info_revise: [],  /*保存修改后的数据*/
        },
        methods: {
            handleFocus() {  /*聚焦才添加数据*/
                /*如果不加if判断则每次搜索at筛选出两个字符串之后,重新聚焦会展示列表所有的字符串*/
                /*只有在输入框为空时才展示所有数据,不为空则不作处理*/
                if (this.info_revise.length==0){
                    this.info_revise = ['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf']
                }
            },
            handleInput() {  /*根据输入的msg值动态的去info中筛选包含msg的字符串*/
                var _this = this  /*外层函数的里层普通函数需要_this*/
                /*根据所有字符串去筛选输入的字符串*/
                this.info_revise = this.info.filter(function (i) {  /*i是每次遍历取得的字符串*/
                    if (i.indexOf(_this.msg)>=0) {
                        return true  /*包含输入的字符串则保留*/
                    } else {
                        return false
                    }
                })
            }
        }
    })
</script>
</html>

输入'at',触发input事件运行
  拿着输入的msg:"at",去info(包含所有值)中筛选
  保留包含"at"的所有字符串,赋值给info_revise
退格时,触发input事件运行
  拿着输入的msg:"...",去info(包含所有值)中筛选
  保留包含"at"的所有字符串,赋值给info_revise

搜索

image

退格

image

箭头函数改写

<script>
    var a = new Vue({
        el: '#d1',
        data: {
            msg: '',  /*存储双向绑定的数据,供v-model使用*/
            /*初始值为空,聚焦再添加数据*/
            info: ['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf'],  /*保存原始数据,使得输入字符串在退格时也能读取全部数据*/
            info_revise: [],  /*保存修改后的数据*/
        },
        methods: {
            handleFocus() {  /*聚焦才添加数据*/
                /*如果不加if判断则每次搜索at筛选出两个字符串之后,重新聚焦会展示列表所有的字符串*/
                /*只有在输入框为空时才展示所有数据,不为空则不作处理*/
                if (this.info_revise.length==0){
                    this.info_revise = ['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf']
                }
            },
            handleInput() {  /*根据输入的msg值动态的去info中筛选包含msg的字符串*/
                var _this = this  /*外层函数的里层普通函数需要_this*/
                /*根据所有字符串去筛选输入的字符串*/
                // this.info_revise = this.info.filter(function (i) {  /*i是每次遍历取得的字符串*/
                //     if (i.indexOf(_this.msg)>=0) {
                //         return true  /*包含输入的字符串则保留*/
                //     } else {
                //         return false
                //     }
                // })

                // 箭头函数一个参数有返回值:return 箭头之后的值
                // 表达式>=0时return true,否则return false
                this.info_revise = this.info.filter(i => i.indexOf(_this.msg)>=0)
            }
        }
    })
</script>

 

posted @ 2026-11-03 20:01  pythondjango  阅读(1)  评论(0)    收藏  举报