【JavaWeb学习】Vue核心

Vue是一套用于构建用户界面渐进式JavaScript框架,包括Vue核心和模板解析器两部分。
特点:
采用组件化模式,提高代码复用率,让代码更好维护;
声明式编码,让编码人员无需直接操作DOM,提高开发效率;
使用虚拟DOM和优秀的Diff算法,尽量复用DOM节点。
安装:尚硅谷B站Vue教程

1. 初识

  • 引入Vue:<script type="text/javascript" src="../vue/vue.js"></script>
  • 使用步骤:
    准备一个容器 → \rightarrow 创建一个Vue实例对象,传入一个配置对象
    容器里的代码依然符合 htnl 规范,只不过混入了一些特殊的 Vue 语法(比如插值语法);容器里的代码被称为Vue模板
  • 容器和实例是一一对应
  • 真实开发中只有一个Vue实例,并且会配合组件一起使用;
  • {{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
  • 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新。
<!DOCTYPE html>
<html lang="en">
    <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>Vue2</title>
        <link rel="shortcut icon" href="../imgs/kirlant.ico">
        <!-- 引入Vue(开发版) -->
        <script type="text/javascript" src="../vue/vue.js"></script>
    </head>
    <body>     
        <!-- 准备好一个容器 -->
        <div id="root">
            <h1>hello, {{name}}</h1>
        </div>
        <script>
            Vue.config.productionTip = false; // 阻止 vue 在启动时生成生产提示
            // 创建 Vue 实例, 传入的参数只有一个,是配置对象
            new Vue({
                el:'#root', // el 用于指定当前 Vue 为哪个容器服务,其值通常为CSS选择器字符串
                data:{      // data 存储数据, 供 el 指定的容器使用
                    name:"lcy"
                }
            });
        </script>
    </body>
</html>
  • 注意区分js表达式和js代码
    js表达式 一个表达式会产生一个值,可以放在任何一个需要值的地方,如a === b ? true : false
    js代码(语句) 控制代码的走向,如for(){}
  • eldata的另一种写法
    el: 把Vue实例赋值给一个常量对象v,然后用v.$mount()绑定容器;
    data: 函数式写法,需要返回一个对象;这个函数要给Vue实例对象调用,所有不能写成箭头函数(会变成Window调用);学了组件之后必须用函数式嗷。
<body>     
        <!-- 准备好一个容器 -->
        <div id="root">
            el 和 data 的写法二{{name}}
        </div>
        <script>
            Vue.config.productionTip = false; // 阻止 vue 在启动时生成生产提示
            // 创建 Vue 实例, 传入的参数只有一个,是配置对象
            const v = new Vue({
                // el:'#root',
                data(){
                    return {
                        name:"嗷"
                    }
                }
            });
            v.$mount('#root');
        </script>
    </body>
  • MVVM模型
    M(Model )模型: data中的数据;
    V(View) 视图: 模板代码;
    VM(ViewModel)视图模型: Vue实例。
    data中所有的属性,最后都能出现在vm上;
    vm上所有的属性及Vue原型上的所有属性,在Vue模板中都可以直接使用。

2. 模板语法

2.1. 插值语法

功能 解析标签体的内容;
语法 {{xxx}}xxx是js表达式,可以直接读取到data中的所有属性。

2.2. 指令语法

功能 解析标签,包括标签属性、标签体内容、绑定事件等;
举例 v-bind:href="xxx"(可简写为:href="xxx"),xxx是js表达式;
备注 Vue中有很多指令,形式都是v-???:;只有v-bind:可以简写成:

<!DOCTYPE html>
<html lang="en">
    <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>Vue2</title>
        <link rel="shortcut icon" href="../imgs/kirlant.ico">
        <link rel="stylesheet" href="./learn.css">
        <script type="text/javascript" src="../vue/vue.js"></script>
    </head>
    <body>     
        <!-- 准备好一个容器 -->
        <div id="root">
            <h1>插值语法</h1>
            <h3>hello, {{name}}</h3>
            <hr/>
            <h1>指令语法</h1>
            <!-- v-bind 绑定 -->
            <a v-bind:href="url_a1">{{school.name}} 指令语法</a><br>
            <a :href="url_a2">bilibili !</a><br>
            <hr/>
        </div>
        <script>
            Vue.config.productionTip = false; // 阻止 vue 在启动时生成生产提示
            // 创建 Vue 实例, 传入的参数只有一个,是配置对象
            new Vue({
                el:'#root', // el 用于指定当前 Vue 为哪个容器服务,其值通常为CSS选择器字符串
                data:{      // data 存储数据, 供 el 指定的容器使用
                    name:"lcy",
                    school:{
                        name:"尚硅谷",
                        url_a1:"https://www.bilibili.com/video/BV1Zy4y1K7SH?p=7&spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=2ba3a6b645cd54d53aa937c0d558fa0a",
                        url_a2:"https://www.bilibili.com/"
                    }
                    
                }
            });
        </script>
        <!-- <script type="text/javascript" src="./learn.js"></script> -->
    </body>
</html>

3. 数据绑定

3.1. 单向数据绑定

v-bind: 数据只能从data流向页面;

3.2. 双向数据绑定

v-model: 数据不仅能从data流向页面,也能从页面流向data;
只能应用在表单类元素之中(如 input、select),也就是有输入功能的标签中,这些标签都有value属性;
v-model:value 可以简写为v-model,因为v-model默认收集value值;

<body>     
        <!-- 准备好一个容器 -->
        <div id="root">
            单向数据绑定:<input type="text" title="single" v-bind:value="name_single"><br>
            双向数据绑定:<input type="text" title="duplexing" v-model:value="name_duplexing"><br>
        </div>
        <script>
            Vue.config.productionTip = false; // 阻止 vue 在启动时生成生产提示
            // 创建 Vue 实例, 传入的参数只有一个,是配置对象
            new Vue({
                el:'#root',
                data:{
                    name_single:"single bind",
                    name_duplexing:"duplexing bind"
                }
            });
        </script>
    </body>

4. 数据代理

4.1. Object.defineProperty()方法

配置:
enumerable 属性是否可枚举,默认false
writable:属性是否可修改,默认false
configurable:属性是否可删除,默认false
… …
get():当有人读取设置的属性时,get()函数就会被调用,返回值就是属性的值;
set():当有人修改设置的属性时,set()函数就会被调用,返回值是修改后的值;

<script>
    let number = 16;
    let person = {
        name:"yyt",
        sex:"female"
    };
    Object.defineProperty(person,'age',{
        get(){
            return number;
        },
        set(value){
            number = value;
            console.log("被改了!变成了",value,"呜呜呜");
        }
    });
    console.log(person);
</script>
<!-- Object.defineProperty() 把 number 和 person.age联系起来了。 -->

4.2. Vue中的数据代理

通过vm对象代理data对象中属性的操作(读 / 写),更加方便地操作data中的数据。
基本原理:
通过Object.defineProperty()data对象中的所有属性添加到vm → \rightarrow 分别为每一个添加到vm上的属性指定一个getter/setter → \rightarrow getter/setter内部操作(读 / 写)data中对应的属性。
在这里插入图片描述

5. 事件处理

5.1. 事件的基本使用

  • 使用 v-on:xxx@xxx 绑定事件,其中xxx是事件名;
  • 事件的回调函数需要配置在methods对象中,最终会出现在vm上;
  • method中的函数不能用箭头函数,否则其this将不再是vm或组件实例对象,而是window
  • method中配置的函数都是被Vue管理的函数;
  • @click="demo"@click=demo($event)效果一致,后者可以在自身基础上传参;
<body>     
        <!-- 准备好一个容器 -->
        <div id="root">
            <h2>welcome to wazard world</h2>
            <button @click="show_tips">tips</button>
            <button @click="show_myNum($event, '0007')">show my number</button>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false;
            const vm = new Vue({
                el:"#root",
                data:{                   
                    
                },
                methods:{
                    show_tips(event){
                        alert("APPRENTICE, WELCOME TO SAVAGE CAVE!");
                    },
                    show_myNum(event, number){
                        console.log(number);
                    }
                }
            });
        </script>
    </body>

5.2. 事件修饰符

  • prevent 阻止默认事件(常用)
  • stop 阻止事件冒泡(常用)
  • once 事件只触发一次(常用)
  • capture 使用事件的捕获模式
  • self 只有event.target是当前操作的元素时才能触发事件
  • passive 事件的默认行为立即执行,无需等待事件回调执行完毕 累了不想抄了,看视频吧12:00

修饰符可以连续写,比如@click.prevent.stop

<body>     
        <!-- 准备好一个容器 -->
        <div id="root">
            <h2>welcome to {{name}}</h2>
            <!-- .prevent 让页面不在自动跳转 -->
            <a href="https://www.bilibili.com" @click.prevent="show_tips">tips</a>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false;
            const vm = new Vue({
                el:"#root",
                data:{                   
                    name:"bilibili"
                },
                methods:{
                    show_tips(event){
                        alert("welcome to bilibili!");
                    }
                }
            });
        </script>
    </body>

5.3. 键盘事件

  • Vue中常用的按键别名
    回车 → \rightarrow enter
    删除 → \rightarrow delete(捕获 delete 和 backspace 两个按键)
    退出 → \rightarrow esc
    空格 → \rightarrow space
    换行 → \rightarrow tab 把焦点从当前元素上切走,所以不要用keyup,要用keydown
    → \rightarrow up
    → \rightarrow down
    → \rightarrow left
    → \rightarrow right
  • Vue未提供别名的按键,可以使用按键的原始key值进行绑定,但要注意转为kebab-case(短横线命名);
  • 系统修饰键:ctrl、alt、shift、meta(home键)
    配合keyup使用:按下修饰键的同时再按下其他键,随后释放其他键,事件才被触发;
    配合keydown使用:正常触发事件。
  • 可以使用keyCode指定具体按键(不推荐)
  • 定制按键别名:Vue.config.keyCodes.自定义键名 = 键码
<body>     
    <!-- 准备好一个容器 -->
    <div id="root">
        <div id="title"><h2>welcome to {{name}}</h2></div>
        <input type="text" placeholder="按下Enter提示输入" @keyup.enter="pressEnter">
        <input type="text" placeholder="按下CapsLk提示输入" @keyup.caps-lock="pressCL">
        <input type="text" placeholder="按下Tab提示输入" @keydown.tab="pressTab">
        <input type="text" placeholder="按下ctrl提示输入" @keyup.ctrl="pressCtrl">
        <input type="text" placeholder="按下y提示输入" @keyup.yyt="pressY">
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        Vue.config.keyCodes.yyt = 89;
        const vm = new Vue({
            el:"#root",
            data:{                   
                name:"bilibili"
            },
            methods:{
                pressEnter(event){
                    alert(event.target.value);
                },
                pressCL(event){
                    alert(event.target.value + ', 成功了!');
                },
                pressTab(event){
                    alert(event.target.value + ', 好奇怪哦。');
                },
                pressCtrl(event){
                    alert(event.target.value + ', ctrl!');
                },
                pressY(event){
                    alert(event.target.value + ', 我的y嗷');
                }
            }
        });
    </script>
</body>

6. 计算属性与监视

6.1. 计算属性 computed

用已经存在的属性计算出一个全新的属性。computed 的优势在于它拥有缓存机制 ( 复用 ),效率更高,调试方便;而 mathods 每用到一次方法就会进行一次调用。

  • 要用的属性不存在,需要通过已有的属性 (由vm管理的属性) 计算得到;
  • computed对象 中定义计算属性;
  • 在页面中使用 {{方法名}} 来显示计算结果。
  • 原理 底层借助了Object.defineproperty方法提供的gettersetter
    get()函数在初次读取计算属性计算时所依赖的属性发生改变时被调用;
    set()函数在计算属性被修改会引起依赖的属性发生改变时被调用。

在这里插入图片描述
插值语法举例

<body>     
    <div id="root">
        <div id="title"><h2>welcome to bilibili</h2></div>
        <div class="name">姓:<input type="text" title="firstName" v-model="firstName"></div>
        <div class="name">名:<input type="text" title="lastName" v-model="lastName"></div>
        <div class="name">全名:{{firstName}} · {{lastName}}</div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{                   
                firstName:"Saunders",
                lastName:"Igulo"
            }
        });
    </script>
</body>

methods举例:只要data中的数据发生改变,Vue就会重新解析模板。

<body>     
    <div id="root">
        <div id="title"><h2>welcome to yyt's test</h2></div>
        <div class="name">姓:<input type="text" title="firstName" v-model="firstName"></div>
        <div class="name">名:<input type="text" title="lastName" v-model="lastName"></div>
        <div class="name">全名:{{fullName()}}</div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{                   
                firstName:"Saunders",
                lastName:"Igulo"
            },
            methods:{
                fullName(){
                    return this.firstName+' · '+this.lastName;
                }
            }
        });
    </script>
</body>

计算属性举例

<body>     
    <div id="root">
        <div id="title"><h2>welcome to yyt's test</h2></div>
        <div class="name">姓:<input type="text" title="firstName" v-model="firstName"></div>
        <div class="name">名:<input type="text" title="lastName" v-model="lastName"></div>
        <div class="name">全名:{{fullName}}</div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{                   
                firstName:"Saunders",
                lastName:"Igulo"
            },
            computed:{
                fullName:{
                    // 当有人读取 fullName 时,get() 就会被调用,且返回值为 fullName 的值
                    get(){
                        return this.firstName+' · '+this.lastName;
                    },
                    // 当 fullName 被修改时,会调用set()                     
                    set(value){
                        splitName = value.split(' ');
                        this.firstName = splitName[0];
                        this.lastName = splitName[2];
                    }
                }
            }
        });
    </script>
</body>

计算属性简写 确定该属性只读不改时使用,以属性名为函数名,代替get()函数。

computed:{
    fullName(){
        return this.firstName+' · '+this.lastName;
    }
}
#root{
    width: 400px;
    text-align: center;
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    align-items: center;
    #title{
        width: 100%;
    }
    .name{
        width: 240px;
        margin-top: 10px;
        text-align: left;
        input{
        width: 200px;
        }
    }
}

练习:天气切换
在这里插入图片描述

<body>     
    <div id="root">
        <div id="title"><h2>welcome to yyt's test</h2></div>
        <div class="content">
            <div class="weather">今日天气:{{weather}}</div>
            <div class="weather"><button @click="changeWeather">切换天气</button></div>
        </div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{                   
                isHot:true
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot;
                }
            },
            computed:{
                weather(){
                    return this.isHot ? '热呜呜呜':'凉爽嗷';
                }
            }
        });
    </script>
</body>
#root{
    width: 400px;
    text-align: center;
    #title{
        width: 100%;
        border: 1px solid #aaa;
    }
    .content{
        width: 100%;
        height: 200px;
        border: 1px solid #aaa;
        
        display: flex;
        flex-wrap: wrap;
        flex-direction: column;
        align-items: center;
        .weather{
            width: 300px;
            margin-top: 15px;
            text-align: left;
        }
    }   
}

6.2. 监视属性 watch

  • 当监视的属性发生变化时,回调函数自动被调用,进行相关操作;
  • 属性必须存在才能被监视;
  • 计算属性也可以被监视;
  • 两种添加监视的写法:new Vue()时传入watch配置;vm实例化后通过$watch()监视。

以上面的切换天气为例,在vm的配置对象中添加watch属性,监视isHot的变化:

watch:{
    isHot:{
        // immediate:true 初始化时调用一次 handler, 默认值是 false
        handler(newValue, oldValue){
            console.log('isHot 被改了嘤嘤嘤', oldValue+' --> '+newValue);
        }
    }
}

方法二:在vm实例对象初始化完成后,使用$绑定监视

const vm = new Vue({......});
vm.$watch('isHot', {
    handler(newValue, oldValue){
        console.log('isHot被改了嘤嘤嘤', oldValue+' --> '+newValue);
    }
});

简写

watch:{
    isHot(newValue, oldValue){
        console.log('isHot 被改了嘤嘤嘤', oldValue+' --> '+newValue);
    },
}
vm.$watch('isHot', function(newValue, oldValue){
    console.log('isHot 被改了嘤嘤嘤', oldValue+' --> '+newValue);
})
  • 深度监视
    Vue中的watch默认不监视对象内部值的改变(只能监视一层),配置了deep:true之后则可以监视对象内部值的改变(监视好多层了嗷);
    Vue自身可以监测对象内部值的改变,只是它的watch默认不可以嗷;
    使用watch时要根据数据的具体结构确定是否要采用深度监视。
<body>     
    <div id="root">
        <div id="title"><h2>welcome to yyt's test</h2></div>
        <div class="content">
            <div>
                <button @click="numbers.a++;">a++</button>
                <button @click="numbers.a--;">a--</button>
                 a的值是 {{numbers.a}}
            </div>
            <div>
                <button @click="numbers.b++;">b++</button>
                <button @click="numbers.b--;">b--</button>
                 b的值是 {{numbers.b}}
            </div>
        </div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{                   
                numbers:{
                    a:1,
                    b:1
                }
            },
            watch:{
                // 监视多级结构中所有属性的变化
                numbers:{
                    deep:true, 
                    handler(newValue, oldValue){
                        console.log('numbers被改了嘤嘤嘤');
                    }
                }
            }
        });
    </script>
</body>

6.3. watch 和 conputed 对比

  • computed能完成的功能,watch都能完成;
  • watch能完成的功能,computed不一定能完成,比如watch可以实现异步计算,而computed不可以;

用watch写6.1节的全名计算:

<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el:"#root",
        data:{                   
            firstName:"Saunders",
            lastName:"Igulo",
            fullName:""
        },
        watch:{
            firstName:{
                immediate: true,
                handler(newValue){
                // 箭头函数的this指向当前作用域,所以此时定时器里的箭头函数的this指向的是vm
                    setTimeout(()=>{this.fullName = newValue+' · '+this.lastName;}, 1000);                            
                }
            },
            lastName:{
                immediate: true,
                handler(newValue){
                    this.fullName = this.firstName+' · '+newValue;
                }
            }
        }
    });
</script>

被Vue管理的函数要写成普通函数,这样this才能指向vm或者组件实例对象;
不被Vue管理的函数(比如定时器、Ajax、promise的回调函数等)最好写成箭头函数,这样才能让this指向vm或者组件实例对象。

#root{
    width: 400px;
    border: 1px solid #aaa;
    padding-bottom: 10px;

    text-align: center;
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    align-items: center;
    #title{
        width: 100%;
        border-bottom: 1px solid #aaa;
    }
    .name{
        width: 240px;
        margin-top: 10px;
        text-align: left;
        input{
        width: 200px;
        }
    }
}

6.4. class 与 style 绑定

  • 绑定class样式:class="vm.data中对应于样式的属性名"
写法适用场景
字符串样式的类名不确定,需要动态指定
数组要绑定的样式个数不确定,名字也不确定
对象要绑定的样式个数确定,名字也确定,但不确定用不用
<body>     
    <div id="root">
        <!-- 字符串写法 -->
        <div class="basic" :class="mood" @click="changeMood">{{name}}</div>
        <!-- 数组写法 -->
        <div class="basic" :class="classArr">{{name}}</div>
        <!-- 对象写法 -->
        <div class="basic" :class="classObj">{{name}}</div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:'#root',
            data:{
                name:'yyt',
                mood:'normal',
                classArr:['yyt1', 'yyt2', 'yyt3'],
                classObj:{
                    yyt1:false,
                    yyt2:true,
                    yyt3:true
                }
            },
            methods: {
                changeMood(){
                    // 随机改变心情
                    let allMoods = ['happy','sad','normal'];
                    this.mood = allMoods[Math.floor(Math.random()*3)];
                }
            },
        });
    </script>
</body>
.basic{
    width: 200px;
    height: 50px;
    border: 1px solid #aaa;
    padding: 20px;
}
.happy{
    background-color: rgb(206, 172, 232);
}
.sad{
    background-color: rgb(191, 255, 178);
}
.normal{
    background-color: antiquewhite;
}
.yyt1{
    border-radius: 10px;
}
.yyt2{
    font-size: 35px;
    text-align: center;
}
.yyt3{
    background-color: rgb(197, 201, 204);
}
<div class="basic" :style="styleObj">{{name}}</div>
... ... 
data:{
    name:'yyt',
    styleObj:{
        fontSize: '20px'
    }
}

7. 渲染

7.1. 条件渲染

  • v-ifv-else-ifv-else
    可以用templete标签包裹使用同一组v-if的元素,不会破坏结构;
    适用于切换频率较低的场景;
    不展示的DOM元素直接被移除;
    v-ifv-else-ifv-else一起使用时,结构不能被打断。
  • v-show
    适用于切换频率较高的场景;
    不展示的DOM元素不被移除,只是用样式隐藏掉。

使用v-if时元素可能无法获取到,而使用v-show一定可以获取到。

<!DOCTYPE html>
<html lang="en">
    <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>Vue2</title>
        <link rel="shortcut icon" href="../imgs/kirlant.ico">
        <link rel="stylesheet" href="./learn.css">
        <script type="text/javascript" src="../vue/vue.js"></script>
        <style>
            #root{
                width: 300px;
                border: 1px solid #aaa;
                line-height: 50px;
                text-align: center;
            }
        </style>
    </head>
    <body>     
        <div id="root" @click="change">
            <div v-show="ifShow">welcome to {{name}}'s 1st test</div>
            <div v-if="showIdx == 0">welcome to {{name}}'s 2nd test</div>
            <div v-else-if="ifShow == 1">welcome to {{name}}'s 3rd test</div>
            <div v-else>welcome to {{name}}'s 4th test</div>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false;
            const vm = new Vue({
                el:'#root',
                data:{
                    name:'yyt',
                    ifShow:true,
                    showIdx:2
                    
                },
                methods: {
                    change(){
                        this.ifShow = !this.ifShow;
                        this.showIdx = (this.showIdx+1)%3;
                    }
                }
            });
        </script>
    </body>
</html>

7.2. 列表渲染 v-for

7.2.1 基本用法

用于展示列表数据;
语法:v-for="(item, index) in xxx" :key="index"
可以遍历数组对象、字符串、指定次数(后两者用得少)。

<!DOCTYPE html>
<html lang="en">
    <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>Vue2</title>
        <link rel="shortcut icon" href="../imgs/kirlant.ico">
        <link rel="stylesheet" href="./learn.css">
        <script type="text/javascript" src="../vue/vue.js"></script>
        <style>
            #root{
                width: 300px;
                border: 1px solid #aaa;
                padding: 10px 0;
                text-align: center;
                display: flex;
                flex-wrap: wrap;
                flex-direction: column;
                align-content: center;
            }
            ul{
                width: 80%;    
                text-align: left;
                border-top: 1px solid #aaa;
                border-bottom: 1px solid #aaa;
            }
        </style>
    </head>
    <body>     
        <div id="root">
            <div>Welcome to yyt's list</div>
            <!-- 遍历数组 -->
            <ul><li v-for="p in persons" :key="p.id">{{p.name}} - {{p.age}}</li></ul>
                <!-- <li v-for="(p,idx) in persons" :key="idx">{{p.name}} - {{p.age}}</li> -->
            
            <!-- 遍历对象 -->
            <ul><li v-for="(val,k) of wizards" :key="k">{{k}} :  {{val}}</li></ul>
            <!-- 遍历字符串 -->
            <ul><li v-for="(val,k) of yyt" :key="k">[{{k}}]  {{val}}</li></ul>
            <!-- 遍历指定次数-->
            <ul><li v-for="(val,k) of 5" :key="k">idx:{{k}} - val:{{val}}</li></ul>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false;
            const vm = new Vue({
                el:'#root',
                data:{
                    persons:[
                        {id:'001',name:'yyt',age:16},
                        {id:'002',name:'lyl',age:18},
                        {id:'003',name:'lcy',age:20}
                    ],
                    wizards:{
                        name:'santos',
                        age:207,
                        orgnization:'Savage cave'
                    },
                    yyt:"yyt"
                }
            });
        </script>
    </body>
</html>
7.2.2. key的作用与原理
  • 虚拟DOM中key的作用
    key 是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM旧虚拟DOM的差异比较。
  • 对比规则
    旧虚拟DOM中找到了与新虚拟DOM相同的key:
    \qquad 虚拟DOM中内容没变 → \rightarrow 直接使用之前的真实DOM;
    \qquad 虚拟DOM中内容改变 → \rightarrow 生成新的真实DOM,然后替换之前的真实DOM;
    旧虚拟DOM中未找到与新虚拟DOM相同的key:
    \qquad 创建新的真实DOM,然后渲染到页面。
  • 用index作为key可能会引发的问题
    若对数据进行逆序添加、逆序删除等破坏顺序的操作:
    \qquad 会产生没有必要的真实DOM更新,界面效果没问题,但效率低;
    如果结构中包含输入类的DOM:
    \qquad 会产生错误DOM更新,界面有问题。
  • 开发中如何选择key
    最好用每条数据的唯一标识作为key,如id、手机号、身份证号、学号等唯一值;
    如果不存在破坏顺序的操作,仅用于渲染列表用于展示,那么用index也没什么问题。

v-for遍历时,默认会把遍历时的index作为key的值。10:30

在这里插入图片描述
在这里插入图片描述

<div id="root">
    <div>Welcome to yyt's list</div>
    <ul>
        <li v-for="p in persons" :key="p.id">
        <!-- 
            <li v-for="(p, idx) in persons" :key="idx">
            <li v-for="p in persons">
            这两种情况中,后面的 input 框都会出现错位
        -->
        {{p.name}} - {{p.age}}
        <input type="text" title="info"></li>
    </ul>
    <button type="button" @click.once="add">add a person</button>
</div>

7.3. 列表过滤 列表排序

<!DOCTYPE html>
<html lang="en">
    <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>Vue2</title>
        <link rel="shortcut icon" href="../imgs/kirlant.ico">
        <script type="text/javascript" src="../vue/vue.js"></script>
        <style>
            #root{
                width: 500px;
                border: 1px solid #aaa;
                padding: 10px 0;
                text-align: center;
                display: flex;
                flex-wrap: wrap;
                flex-direction: column;
                align-content: center;
            }
            ul{
                width: 80%;    
                text-align: center;
                list-style: none;
                border-top: 1px solid #aaa;
                border-bottom: 1px solid #aaa;
                margin: 0;
            }
            .gap{
                margin: 5px 0;                
            }
        </style>
    </head>
    <body>     
        <div id="root">
            <div class="gap">Welcome to yyt's list</div>
            <div class="gap">
                <input type="text" title="serch" placeholder="请输入姓名" v-model="keyWord">
                <button @click="sortStyle = 1;">年龄升序</button>
                <button @click="sortStyle = 2;">年龄降序</button>
                <button @click="sortStyle = 0;">原顺序</button>
            </div>
            
            <ul>
                <li v-for="p in filPersons" :key="p.id">{{p.name}} - {{p.age}} - {{p.sex}}
            </ul>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false;
            const vm = new Vue({
                el:'#root',
                data:{
                    keyWord:"",
                    persons:[
                        {id:'001',name:'马冬梅',age:19,sex:'女'},
                        {id:'002',name:'周冬雨',age:21,sex:'女'},
                        {id:'003',name:'周杰伦',age:20,sex:'男'},
                        {id:'004',name:'温兆伦',age:22,sex:'男'}
                    ],
                    sortStyle:0 // 0 原顺序; 1 升序; 2 降序
                },
                computed:{
                    filPersons(){
                        // 过滤后的数组
                        const arr = this.persons.filter((p)=>{
                            return p.name.indexOf(this.keyWord) !== -1;
                        });
                        // 是否需要排序
                        if(this.sortStyle){
                            arr.sort((a,b)=>{
                                return this.sortStyle == 1 ? (a.age-b.age):(b.age-a.age);
                            });
                        };
                        return arr;
                    },
                    
                }
            });
        </script>
    </body>
</html>

7.4. Vue监测数据的原理

Vue会监视data中所有层次的数据。

  • 对象
    通过setter实现监视,且要在 new Vue 时就传入要监视的数据。
    对象中后追加的属性,Vue默认不做响应式处理;
    如需给后添加的属性做响应式,需要使用Vue.set(target, propertyName/index, value)vm.$set(target, propertyName/index, value)
  • 数组
    通过包裹数组更新元素的方法实现,本质上做了两件事:调用原生对应的方法对数组进行更新 → \rightarrow 重新解析模板,更新页面。
    在Vue中修改数组的某个元素一定要用以下方法:push(), pop(), shift(), unshift(), splice(), sort(), reverse()Vue.set() 或 vm.$set()

Vue.set() 和 vm.$set() 不能给 vm 或 vm的根数据对象 添加属性。

8. 收集表单数据

v-model配合不同的输入框有不同的使用技巧:

  • <input type="text">
    v-model收集的是value值,用户输入的就是value值;
  • <input type="radio">
    v-model收集的是value值,需要给标签配置value值;
  • <input type="checkbox">
    未配置value属性时,v-model收集的是checked值(勾选true,未勾选false);
    配置value属性时,收集的是value组成的数组

v-model的三个修饰符:

  • lazy 失去焦点时收集数据;
  • number 输入字符串转为有效数字;
  • trim 对输入字符串的首尾进行空格过滤。
<!DOCTYPE html>
<html lang="en">
    <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>Vue2</title>
        <link rel="shortcut icon" href="../imgs/kirlant.ico">
        <script type="text/javascript" src="../vue/vue.js"></script>
        <style>
            #root{
                width: 500px;
                border: 1px solid #aaa;
                padding: 10px 0;
                text-align: left;
                display: flex;
                flex-wrap: wrap;
                flex-direction: column;
                align-content: center;
            }
            .gap{
                margin: 5px 0;                
            }
        </style>
    </head>
    <body>     
        <div id="root">
            <form @submit.prevent="submitAllInfo">
                <!-- for 和 id 将两个标签联系起来,单击‘账号’的时候也能让 input 框获取焦点 -->
                <label for="num">账号:</label><input type="text" id="num" v-model.trim="account"><br/>
                <br/>
                <label for="pwd">密码:</label><input type="password" id="pwd" v-model="password"><br/>
                <br/>
                年龄:
                <input type="number" v-model.number="age"><br/>
                <br/>
                性别: 
                男<input type="radio" value="male" v-model="sex"><input type="radio" value="female" v-model="sex"><br/>
                <br/>
                爱好:
                唱歌<input type="checkbox" value="sing" v-model="hobby"> 
                游泳<input type="checkbox" value="swimming" v-model="hobby"> 
                打麻将<input type="checkbox" value="mahjong" v-model="hobby"><br/>
                <br/>
                本命:
                <select name="animation" v-model="animation" title="lovest animation character">
                    <option value="bf">白凤</option>
                    <option value="ssm">少司命</option>
                    <option value="ks">时崎狂三</option>
                    <option value="hy">四宫辉夜</option>
                    <option value="aer">阿尔托莉雅</option>
                    <option value="sd">塞巴斯蒂安</option>
                </select><br/>
                <br/>
                其他信息:<br/><br/>
                <textarea cols="30" rows="10" v-model.lazy="other_info"></textarea><br/>
                <br/>
                <input type="checkbox" v-model="access">
                阅读并接受 <a href="https://www.bilibili.com">《用户协议》</a><br/>
                <br/>
                <button>提交</button>
            </form>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false;
            const vm = new Vue({
                el:'#root',
                data:{
                   account:'',
                   password:'',
                   age:'16',
                   sex:'female',
                   hobby:[],
                   animation:'ssm',
                   other_info:'',
                   access:false
                },
                methods: {
                    submitAllInfo(){
                        alert(JSON.stringify(this._data));
                    }
                },
            });
        </script>
    </body>
</html>

9. 过滤器

  • 定义
    对要显示的数据进行特定格式化后再显示,适用于一些简单逻辑的处理。
  • 语法
    注册过滤器: Vue.filter(name, callback)new Vue({ filters:{ name(){} } })
    使用过滤器:{{ xxx | 过滤器名 }}v-bind:属性 = " xxx | 过滤器名 "
  • 备注
    过滤器可以接收参数;
    多个过滤器可以串联;
    过滤器没有改变原本的数据,而是产生了新的数据。
<!DOCTYPE html>
<html lang="en">
    <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>Vue2</title>
        <link rel="shortcut icon" href="../imgs/kirlant.ico">
        <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.3/dayjs.min.js"></script>
        <script type="text/javascript" src="../vue/vue.js"></script>
        <style>
            #root{
                width: 500px;
                border: 1px solid #aaa;
                padding: 10px 0;
                text-align: left;
                display: flex;
                flex-wrap: wrap;
                flex-direction: column;
                align-content: center;
            }
            .gap{
                margin: 5px 0;                
            }
        </style>
    </head>
    <body>     
        <div id="root">
            <h2>显示格式化后的时间</h2>
            <!-- 计算属性实现 -->
            <h3>现在是 {{fmtTime}}</h3>
            <!-- methods实现 -->
            <h3>现在是 {{getFmtTime()}}</h3>
            <!-- 过滤器实现:把 time 传给 timeFmt, 用 timeFmt 的返回值替换整个'time | timeFmt' -->
            <h3>现在是 {{time | timeFmt}}</h3>
            <!-- 过滤器实现:传参 -->
            <h3>现在是 {{time | timeFmt('YYYY-MM-DD') | mySlice}}</h3>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false;
            // 注册全局过滤器,必须写在 new Vue() 之前
            Vue.filter('mySlice', function(value){
                return value.slice(0, 4);
            });
            const vm = new Vue({
                el:'#root',
                data:{
                    time:1621561377603
                },
                computed: {
                    fmtTime(){
                        return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
                    }
                },
                methods: {
                    getFmtTime(){
                        return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
                    }
                },
                // 局部过滤器
                filters:{
                    timeFmt(value, str='YYYY-MM-DD HH:mm:ss'){
                        return dayjs(value).format(str);
                    }
                }
            });
        </script>
    </body>
</html>

10. 指令

10.1. 内置指令

  • v-bind 单向绑定解析表达式
  • v-model 双向数据绑定
  • v-for 遍历数组、对象、字符串
  • v-on 绑定事件监听,可简写为@
  • v-if 条件渲染(动态控制节点是否存在)
  • v-else 条件渲染(动态控制节点是否存在)
  • v-show 条件渲染(动态控制节点是否展示)
  • v-text 向所在节点中渲染文本内容,会替换节点中的内容;解析文本,不解析标签嗷。
  • v-html 向指定节点中渲染包含html结构的内容
    与插值语法相比,v-html会替换掉节点中全部的内容,插值语法不会;且v-html可以识别html结构。
    v-html有安全性问题:在网站上动态渲染任意html是非常危险的,容易导致XSS攻击(虽然得不到用户名和密码,但是可以偷走用户的cookie,冒充用户去找服务器);一定要在可以信任的网站上使用v-html,永远不要对用户提交的内容使用。
  • v-clock 没有值,本质是一个特殊的属性,Vue实例创建完毕并接管容器后,会删掉该属性。配合CSS可以解决网速慢时页面展示出{{xxx}}的问题。[v-clock]{ display: none;} // 属性选择器,标签中有v-clock的时候不展示该标签的内容,当Vue接管后删掉v-clock,就可以展示出渲染完毕的内容。
  • v-once 所在的节点在初次动态渲染后,就视为静态内容了,之后数据的改变不会引起v-once所在结构的更新,可用于优化性能。
  • v-pre让Vue跳过所在节点的编译过程,可以利用它跳过没有使用指令语法、插值语法的节点,加快编译。
<!DOCTYPE html>
<html lang="en">
    <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>Vue2</title>
        <link rel="shortcut icon" href="../imgs/kirlant.ico">
        <script type="text/javascript" src="../vue/vue.js"></script>
        <style>
            #root{
                width: 500px;
                border: 2px solid #aaa;
                padding: 10px 0;
                text-align: left;
                display: flex;
                flex-wrap: wrap;
                flex-direction: column;
                align-content: center;
            }
            .title{
                width: 80%;
                font-weight: bold;
                text-align: center;
                border-bottom: solid 1.5px #aaa;
                padding-bottom: 5px;
            }
            .gap{
                padding: 5px 20px;  
                border-top: solid 1px #aaa;              
            }
        </style>
    </head>
    <body>     
        <div id="root">
            <div class="title">Welcome to {{name}}'s Test</div>
            <div class="gap" v-text="str_text"></div>
            <div class="gap" v-html="str_html"></div>
            <div class="gap">
                <p v-once>初始化n值为:{{n}}</p>
                <p>当前n值为:{{n}}</p>
                <button @click="++n;">++n</button>
            </div>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false;

            const vm = new Vue({
                el:'#root',
                data:{
                    name:'yyt',
                    str_text:'<h4> v-text不支持结构解析 <h4/>',
                    str_html:'<h4> v-html支持结构解析 <h4/>',
                    n:7
                }
            });
        </script>
    </body>
</html>

10.2 自定义指令

所有指令的this都是window

  • 定义语法
    局部指令:new Vue({ directives:{ 指令名:配置对象 } })new Vue({ directives:{ 指令名(){} } })
    全局指令:Vue.directive( 指令名, 配置对象 )Vue.directive( 指令名, 回调函数 )
  • 配置对象中常用的三个回调
    bind指令与元素成功绑定时调用;
    inserted指令所在的元素被成功插入页面时调用;
    update指令所在的模板结构被重新解析时调用。
  • 备注
    指令定义时不加v-,使用时才加v-
    指令名如果是多个单词,要使用kebab-case命名方式,不能用camelCase命名。
<div class="gap">
    当前的n值是:<span v-text="n"></span><br>
    放大的n值是:<span v-big="n"></span><br>
    <button @click="++n;">++n</button>
</div>
<div class="gap">
    v-fbind: <input type="text" v-fbind:value="n">
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el:'#root',
        data:{
            n:0
        },
        directives:{
            // 自定义的指令在指令和元素成功绑定时会被调用,指令所在的模板被重新解析时也会被调用。
            big(element, binding){
                element.innerText = binding.value * 10;
            },
            fbind:{
            // 元素和指令绑定时调用
                bind(element, binding){                       
                    element.value = binding.value;
                    
                },
                inserted(element, binding){
                    element.focus();
                },
                update(element, binding){
                    element.value = binding.value;
                    element.focus();
                }
            }
        }
    });
</script>

11. Vue实例生命周期

生命周期 = 生命周期回调函数 = 生命周期函数 = 生命周期钩子
生命周期函数就是Vue在关键时刻帮我们调用的一些特殊名称的函数,它们的名字不可更改,但内容是根据需求编写的。
生命周期函数的this指向的是vm组件实例对象

vm的一生

将要创建 ⟶ \longrightarrow 调用beforeCreate()
创建完毕 ⟶ \longrightarrow 调用created()
将要挂载 ⟶ \longrightarrow 调用beforeMount()
挂载完毕 ⟶ \longrightarrow 调用mounted()
将要更新 ⟶ \longrightarrow 调用beforeupdate()
更新完毕 ⟶ \longrightarrow 调用updated()
将要销毁 ⟶ \longrightarrow 调用beforeDestroy()
销毁完毕 ⟶ \longrightarrow 调用destroyed()
在这里插入图片描述
注:
1). 在 进入 beforeUnmount() 后,也就是开始执行卸载流程后,能够访问数据,也能够调用方法,但是对数据进行的所有操作都不会进行更新了。
2). 销毁后借助Vue开发工具看不到任何信息,自定义事件失效,但原生DOM事件依然有效。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值