vue2学习笔记——Vue基础知识

4,995 阅读17分钟

Vue2基础

创建一个Vue组件

new Vue({
    //选择一个HTML标签作为容器
    el:"CSS选择器"//或者使用DOM选择器document.getElementById(id)
    date:{
        property01:xxxxx,
        property02:xxxxx
    }
})

一个容器只能被一个Vue实例接管,一对多和多对一的情况下,第二个都不会生效

模板语法

插值语法:HTML标签体中

{{插值}},在HTML代码中可以写:JS表达式(一个表达式会生成一个值,可以放在任何一个需要值的地方)

对象多层嵌套使用 . 运算符

指令语法:HTML用于解析标签(标签属性,标签体内容,绑定事件.....)

v-bind:herf="url",此时在HTML标签属性中,引号内的url被当作JS表达式执行

或者简写为 :herf="url"此时与v-bind类似

数据绑定

单向数据绑定:v-bind ----> 只会从Vue实例影响到组件,后续组件改变不会影响Vue实例数据

双向数据绑定:v-model ----> 组件和Vue实例的数据双向绑定,任何一个数据改变都会改变另一个数据

v-model属性只能用在表单类元素上,即该HTML元素具有value属性

v-model:value = “xxxx”可以简写为 v-model=“xxxxx”

EL与Date的两种写法

EL

el:"#APP" 或者使用 (new Vue()).$mount("#APP")

Date

对象式写法:

date:{property01:xxxxx , property02:xxxxx}

函数式写法:不能写成箭头函数(箭头函数没有自己的this)

data: function(){

return{ property:"xxxxxx" }

}

Vue所管理的函数都不能使用箭头函数

new Vue({
    //选择一个HTML标签作为容器
    el:"CSS选择器"//或者使用DOM选择器document.getElementById(id)
    date:{
        property01:xxxxx,
        property02:xxxxx
    }
})
//另一种写法
let vm = new Vue({
        date:{
        property01:xxxxx,
        property02:xxxxx
    }
})
vm.$mount("CSS选择器")
//Date的函数式写法
let vm = new Vue({
        //ES5写法
        date:function(){
            return{
                property01:xxxxx,
                property02:xxxxx
            }
        }
        //ES6语法
        date(){
           return{
                property01:xxxxx,
                property02:xxxxx
           }
        }
})
vm.$mount("CSS选择器")

Object.defineProperty

Object.defineProperty(targetObject  ,  "propertyName"  ,  {
    value:value  ,  //propertyName对应的属性值
    enumeralbe: true, //可枚举性
    writable : true//可以被修改
    configurable: true  //可以被删除
    get(){
        //当读取该属性时,get方法被调用,且get的返回值就是这个属性的值
        return value;
    }
    set(value){
        //当修改该属性时,set会被调用,且会收到修改的值
    }
})

Vue的数据双向绑定原理

1.实现一个监听器 Observer ,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;

监听器 Observer 的实现,主要是指让数据对象变得“可观测”,即每次数据读或写时,我们能感知到数据被读取了或数据被改写了。要使数据变得“可观测”,Vue 2.0 源码中用到 Object.defineProperty() 来劫持各个数据属性的 setter / getterObject.defineProperty 方法

获取数据 key => getter => data.key

修改数据 key => setter => data.key

/**
  * 循环遍历数据对象的每个属性
  */
function observable(obj) {
    if (!obj || typeof obj !== 'object') {
        return;
    }
    let keys = Object.keys(obj);
    keys.forEach((key) => {
        defineReactive(obj, key, obj[key])
    })
    return obj;
}
/**
 * 将对象的属性用 Object.defineProperty() 进行设置
 */
function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
        get() {
            console.log(`${key}属性被读取了...`);
            return val;
        },
        set(newVal) {
            console.log(`${key}属性被修改了...`);
            val = newVal;
        }
    })
}
​
/*
通过以上方法封装,我们可以直接定义 person:
*/let person = observable({
    name: 'tom',
    age: 15
});

2、实现一个订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理;

function Dep () {
    this.subs = [];
}
Dep.prototype = {
    addSub: function(sub) {
        this.subs.push(sub);
    },
    notify: function() {
        this.subs.forEach(function(sub) {
            sub.update();
        });
    }
};
Dep.target = null;

向定义对象代码中植入订阅器

defineReactive: function(data, key, val) {
    var dep = new Dep();
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function getter () {
            if (Dep.target) {
                dep.addSub(Dep.target);
            }
            return val;
        },
        set: function setter (newVal) {
            if (newVal === val) {
                return;
            }
            val = newVal;
            dep.notify();
        }
    });
}

从代码上看,我们设计了一个订阅器 Dep 类,该类里面定义了一些属性和方法,这里需要特别注意的是它有一个静态属性 Dep.target,这是一个全局唯一 的Watcher,因为在同一时间只能有一个全局的 Watcher 被计算,另外它的自身属性 subs 也是 Watcher 的数组。

3、实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图;

function Watcher(vm, exp, cb) {
    this.vm = vm;
    this.exp = exp;
    this.cb = cb;
    this.value = this.get();  // 将自己添加到订阅器的操作
}
​
Watcher.prototype = {
    update: function() {
        this.run();
    },
    run: function() {
        var value = this.vm.data[this.exp];
        var oldVal = this.value;
        if (value !== oldVal) {
            this.value = value;
            this.cb.call(this.vm, value, oldVal);
        }
    },
    get: function() {
        Dep.target = this; // 全局变量 订阅者 赋值
        var value = this.vm.data[this.exp]  // 强制执行监听器里的get函数
        Dep.target = null; // 全局变量 订阅者 释放
        return value;
    }
};

订阅者 Watcher 分析如下:

订阅者 Watcher 是一个 类,在它的构造函数中,定义了一些属性:

  • vm: 一个 Vue 的实例对象;
  • exp:node 节点的 v-model 等指令的属性值 或者插值符号中的属性。如 v-model="name"exp 就是name;
  • cb:Watcher 绑定的更新函数;

当我们去实例化一个渲染 watcher 的时候,首先进入 watcher 的构造函数逻辑,就会执行它的 this.get() 方法,进入 get 函数,首先会执行:

Dep.target = this;  // 将自己赋值为全局的订阅者
复制代码

实际上就是把 Dep.target 赋值为当前的渲染 watcher ,接着又执行了:

let value = this.vm.data[this.exp]  // 强制执行监听器里的get函数
复制代码

在这个过程中会对 vm 上的数据访问,其实就是为了触发数据对象的 getter

每个对象值的 getter 都持有一个 dep,在触发 getter 的时候会调用 dep.depend() 方法,也就会执行this.addSub(Dep.target),即把当前的 watcher 订阅到这个数据持有的 depwatchers 中,这个目的是为后续数据变化时候能通知到哪些 watchers 做准备。

这样实际上已经完成了一个依赖收集的过程。那么到这里就结束了吗?其实并没有,完成依赖收集后,还需要把 Dep.target 恢复成上一个状态,即:

Dep.target = null;  // 释放自己
复制代码

update() 函数是用来当数据发生变化时调用 Watcher 自身的更新函数进行更新的操作。先通过 let value = this.vm.data[this.exp]; 获取到最新的数据,然后将其与之前 get() 获得的旧数据进行比较,如果不一样,则调用更新函数 cb 进行更新。

至此,简单的订阅者 Watcher 设计完毕。

4、实现一个解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化。

通过监听器 Observer 订阅器 Dep 和订阅者 Watcher 的实现,其实就已经实现了一个双向数据绑定的例子,但是整个过程都没有去解析 dom 节点,而是直接固定某个节点进行替换数据的,所以接下来需要实现一个解析器 Compile 来做解析和绑定工作。解析器 Compile 实现步骤:

compileText: function(node, exp) {
    var self = this;
    var initText = this.vm[exp]; // 获取属性值
    this.updateText(node, initText); // dom 更新节点文本值
    // 将这个指令初始化为一个订阅者,后续 exp 改变时,就会触发这个更新回调,从而更新视图
    new Watcher(this.vm, exp, function (value) { 
        self.updateText(node, value);
    });
}

事件处理

v-on:click:function 单击事件 @click:function 简写形式

function函数需要在Vue实例的methods对象中保存

<!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>Document</title>
</head>
<script src="vue.js"></script>
<body>
    <div id="app">
        <h1>{{message}}</h1>
        <!-- v-on:click:function  单击事件  -->
        <button v-on:click = "showInfo01">点我弹窗</button>
        <!-- v-on:click:function  单击事件 的简写形式 -->
        <!-- v-on:click:function  可加入括号向函数传参 -->
        <button @click = "showInfo02(value,$event)">点我弹窗</button>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                message:"Hello VUE!!"
            },
            methods: {//function函数需要在Vue实例的methods对象中保存
                //未传参默认参数未event
                showInfo01(event){
                    
                    alert("Hello")
                    //该函数的this为app对象
                    //如果是箭头函数写法,this是全局对象
                },
                //可以接收参数
                showInfo02(value,event){
                    alert("Hello")
                    //该函数的this为app对象
                    //如果是箭头函数写法,this是全局对象
                }
            },
        });
    </script>
</body>
</html>

事件修饰符

事件修饰符可以连写,靠前的修饰符先执行

.prevent 阻止默认事件

.stop 阻止事件冒泡

.once 事件只触发一次

.capture 使用事件捕获模式

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

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

示例:

event.preventDefault()阻止事件默认行为

@click.prevent = "showInfo02(value,$event)" 也可以组织默认行为

<!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>Document</title>
</head>
<script src="vue.js"></script>
<body>
    <div id="app">
        <h1>{{message}}</h1>
        <!-- v-on:click:function  单击事件  -->
        <button v-on:click = "showInfo01">点我弹窗</button>
        <!-- v-on:click:function  单击事件 的简写形式 -->
        <!-- v-on:click:function  可加入括号向函数传参 -->
        <button @click = "showInfo02(value,$event)">点我弹窗</button>
        <a href="http://127.0.0.1" @click.prevent = "showInfo02">点我不会跳转</a>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                message:"Hello VUE!!"
            },
            methods: {//function函数需要在Vue实例的methods对象中保存
                //未传参默认参数未event
                showInfo01(event){
                    event.preventDefault()  //阻止事件默认行为
                    event.target  //触发事件的对象
                    alert("Hello")
                    //该函数的this为app对象
                    //如果是箭头函数写法,this是全局对象
                },
                //可以接收参数
                showInfo02(value,event){
                    alert("Hello")
                    //该函数的this为app对象
                    //如果是箭头函数写法,this是全局对象
                }
            },
        });
    </script>
</body>
</html>

键盘事件

按键别名也可以连续写,代表组合键

@keyup 松开按键触发

@keydown 按下按键触发

@keydown.enter 按下回车触发,enter未按键别名

Vue中常见按键别名

.enter 回车键

.delect 删除(Delect键和Backspace键)

.esc Esc键

.tab 换行键

.space 空格键

.up 上箭头键

.down 下箭头键

.left 左箭头键

.rigtht 右箭头键

没有别名的按键,使用按键原始的Key绑定,event.key可以获取原始Key值,event.keyCode可以获取对应的编码,但是切换大小写键的key未CapsLock需要用.caps-lock来进行绑定

tab键会使元素失去焦点,keyup无法捕获,应该使用keydown

系统修饰键:ctrl、alt、shift、meta(windows系统为win键)

配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才会被触发

配合keydown使用: 正常触发事件

也可以使用keyCode区指定按键(该方法不推荐使用,再JS中已被废弃)

计算属性

Vue中计算属性在Computed中进行配置

1.定义:要用的属性不存在,要通过已有属性计算得来。

2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。

3.get函数什么时候执行?

(1).初次读取时会执行一次。

(2).当依赖的数据发生改变时会被再次调用。

4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。

5.备注:

1.计算属性最终会出现在vm上,直接读取使用即可。

2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。

以下为使用插值语法和methods实现的代码和使用计算属性的对比代码

<!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>Document</title>
</head>
<script src="vue.js"></script>
<body>
    <!-- 使用插值语法 -->
    <div id="app">
        姓:<input type="text" name="firstName" id="firstName"  v-model:value="firstName">  <br>
        名:<input type="text" name="secondName" id="secondName" v-model:value="lastName"> <br>
        姓名: <span>
            {{firstName}} - {{lastName}}
        </span>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                firstName:"Han",
                lastName: "Shirllyuan"
            },
        });
    </script>
​
    <!-- 使用methods -->
    <div id="app1">
        姓:<input type="text" name="firstName" id="firstName"  v-model:value="firstName">  <br>
        名:<input type="text" name="secondName" id="secondName" v-model:value="lastName"> <br>
        姓名: <span>
            {{fullName()}}
        </span>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app1 = new Vue({
            el:"#app1",                                           
            data:{
                firstName:"Han",
                lastName: "Shirllyuan"
            },
            methods: {
                fullName(){
                    return this.firstName + "-" + this.lastName
                }
            },
        });
    </script>
​
    <!-- 使用计算属性的方法 -->
<div id="app2">
        姓:<input type="text" name="firstName" id="firstName"  v-model:value="firstName">  <br>
        名:<input type="text" name="secondName" id="secondName" v-model:value="lastName"> <br>
        姓名: <span>
                {{fullName}}
        </span>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app2 = new Vue({
            el:"#app2",                                           
            data:{
                firstName:"Han",
                lastName: "Shirllyuan"
            },
            computed:{
                fullName:{
            //1.get有什么作用? 当有人读取fullName时,get就会被调用,且返回值为fullName的值
            //2.get什么时候被调用 初次读取fullName时  fullName的依赖的数据被改变时
            //当依赖的数据未改变时,多次读取fullName,get方法只调用一次
                    get(){
                        console.log("Getter被调用了")
                        //此处的this为app的Vue对象
                        return this.firstName + "-" + this.lastName
                    },
                    //set什么时候被调用?  当fullName被修改时
                    set(value){
                        
                    }
                }
            }
        });
    </script>
</body>
</html>

计算属性的简写

var app = new Vue({
            el:"#app",                                           
            data:{
                firstName:"Han",
                lastName: "Shirllyuan"
            },
            computed:{
                   fullName(){
                        console.log("Getter被调用了")
                        return this.firstName + "-" + this.lastName
                    }
                }
            }
        });

监视属性

<!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>Document</title>
</head>
<script src="vue.js"></script>
<body>
    <div id="app">
        <h2>今天天气很{{info}}</h2>
        <!-- 可以直接在事件的属性内写语句,读取的变量为挂载该组件的Vue对象内的变量,其他变量无法读取 -->
        <!-- <button @click.prevent="isHot = !isHot">切换天气</button> -->
        <!-- 此时报错,仅在Vue对象内查找是否存在属性内的值或方法 -->
        <!-- <button @click.prevent="alert()">切换天气</button> -->
        <button @click.prevent="change">切换天气</button>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                isHot: true,
            },
            methods: {
                change(){
                    this.isHot = !this.isHot
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热" : "凉爽"
                }
            }
        });
    </script>
</body>
</html>

监视属性watch

1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作

2.监视的属性必须存在,才能进行监视!!

3.监视的两种写法:

(1).new Vue时传入watch配置

(2).通过vm.$watch监视

<!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>Document</title>
</head>
<script src="vue.js"></script>
<body>
    <div id="app">
        <h2>今天天气很{{info}}</h2>
        <button @click.prevent="change">切换天气</button>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                isHot: true,
            },
            methods: {
                change(){
                    this.isHot = !this.isHot
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热" : "凉爽"
                }
            },
            //监视属性可以监视,data内的属性也可以监视computed的属性
            watch:{
                // isHot:{
                //     //当为true时,初始化时会调用handler
                //     immediate:true,
                //     //handler什么时候调用? 当isHot发生改变时
                //     //调用时会传入新的值和旧的值
                //     handler(newValue,oldValue){
                //         console.log(newValue + "---" + oldValue)
                //     }
                // },
                info:{
                    //当为true时,初始化时会调用handler
                    immediate:true,
                    //handler什么时候调用? 当isHot发生改变时
                    //调用时会传入新的值和旧的值
                    handler(newValue,oldValue){
                        console.log(newValue + "---" + oldValue)
                    }
                },
            }
        });
        //另一种实现监视的方式
        app.$watch("isHot",{
                    //当为true时,初始化时会调用handler
                    immediate:true,
                    //handler什么时候调用? 当isHot发生改变时
                    //调用时会传入新的值和旧的值
                    handler(newValue,oldValue){
                        console.log(newValue + "---" + oldValue)
                    }
                })
    </script>
</body>
</html>

深度监视

在watch中“object.property”:{ handler(){ ..... }}可以监视object中property的属性变化

在watch中object:{deep : true} 可以监视object内的属性的变化

深度监视:

(1).Vue中的watch默认不监测对象内部值的改变(一层)。

(2).配置deep:true可以监测对象内部值改变(多层)。

备注:

(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!

(2).使用watch时根据数据的具体结构,决定是否采用深度监视。

<!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>Document</title>
</head>
<script src="vue.js"></script>
<body>
    <div id="app">
        <h2>今天天气很{{info}}</h2>
        <button @click.prevent="change">切换天气</button>
        <hr>
        <h3>a的值是:{{numbers.a}}</h3>
        <button @click.prevent="numbers.a++">点我让a++</button>
        <h3>b的值是:{{numbers.b}}</h3>
        <button @click.prevent="numbers.b++">点我让b++</button>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                isHot: true,
                numbers:{
                    a:1,
                    b:2,
                }
            },
            methods: {
                change(){
                    this.isHot = !this.isHot
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热" : "凉爽"
                }
            },
            //监视属性可以监视,data内的属性也可以监视computed的属性
            watch:{
                isHot:{
                    //当为true时,初始化时会调用handler
                    // immediate:true,
                    //handler什么时候调用? 当isHot发生改变时
                    //调用时会传入新的值和旧的值
                    handler(newValue,oldValue){
                        console.log(newValue + "---" + oldValue)
                    }
                },
                //监视data内的numbers内的a
                //此处必须用字符串的形式
                // "numbers.a":{
                //     handler(){
                //         console.log("a改变了")
                //     }
                // }
                numbers:{
                    //deep配置项可以监视对象内数据的改变
                    deep:true,
                    handler(){
                        console.log("numbers改变了")
                    }
                }
            }
        });
​
    </script>
</body>
</html>

深度监视的简写

const vm = new Vue({
            el:'#root',
            data:{
                isHot:true,
            },
            computed:{
                info(){
                    return this.isHot ? '炎热' : '凉爽'
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
            watch:{
                //正常写法
                /* isHot:{
                    // immediate:true, //初始化时让handler调用一下
                    // deep:true,//深度监视
                    handler(newValue,oldValue){
                        console.log('isHot被修改了',newValue,oldValue)
                    }
                }, */
                //简写
                /* isHot(newValue,oldValue){
                    console.log('isHot被修改了',newValue,oldValue,this)
                } */
            }
        })
​
        //正常写法
        /* vm.$watch('isHot',{
            immediate:true, //初始化时让handler调用一下
            deep:true,//深度监视
            handler(newValue,oldValue){
                console.log('isHot被修改了',newValue,oldValue)
            }
        }) */
​
        //简写
        /* vm.$watch('isHot',(newValue,oldValue)=>{
            console.log('isHot被修改了',newValue,oldValue,this)
        }) */
​

computed和watch之间的区别

1.computed能完成的功能,watch都可以完成。

2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。

两个重要的小原则:

1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。

2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数), 最好写成箭头函数,

这样this的指向才是vm 或 组件实例对象。

class与style绑定

class绑定

写法:class="xxx" xxx可以是字符串、对象、数组。

字符串写法适用于:类名不确定,要动态获取。

对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。

数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

    <body><div id="root">
        <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
            <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/><!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
            <div class="basic" :class="classArr">{{name}}</div> <br/><br/><!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
            <div class="basic" :class="classObj">{{name}}</div> <br/><br/>
        </div>
    </body><script type="text/javascript">
        Vue.config.productionTip = false
        
        const vm = new Vue({
            el:'#root',
            data:{
                name:'Hsiaoyuan',
                mood:'normal',
                classArr:['class1','class2','class3'],
                classObj:{
                    atguigu1:false,
                    atguigu2:false,
                },
            },
            methods: {
                changeMood(){
                    const arr = ['happy','sad','normal']
                    const index = Math.floor(Math.random()*3)
                    this.mood = arr[index]
                }
            },
        })
    </script>

style绑定

:style="{fontSize: xxx}"其中xxx是动态值。

:style="[a,b]"其中a、b是样式对象。

<body>
            <!-- 绑定style样式--对象写法 -->
            <div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
            <!-- 绑定style样式--数组写法 -->
            <div class="basic" :style="styleArr">{{name}}</div>
        </div>
    </body><script type="text/javascript">
        Vue.config.productionTip = false
        
        const vm = new Vue({
            el:'#root',
            data:{
                name:'Hsiaoyuan',
                styleObj:{
                    fontSize: '40px',
                    color:'red',
                },
                styleObj2:{
                    backgroundColor:'orange'
                },
                styleArr:[
                    {
                        fontSize: '40px',
                        color:'blue',
                    },
                    {
                        backgroundColor:'gray'
                    }
                ]
            },
            methods: {
                changeMood(){
                    const arr = ['happy','sad','normal']
                    const index = Math.floor(Math.random()*3)
                    this.mood = arr[index]
                }
            },
        })
    </script>
    

条件渲染

1.v-if:当为False时会将节点删除

写法:

(1).v-if="表达式"

(2).v-else-if="表达式"

中间若被其他元素打断v-else-if结构则无法生效

类似于if -else-if语句当前面的v-if为false时触发下一个v-else-if

(3).v-else="表达式"

中间若被其他元素打断v-else-if结构,或v-if与v-else中间有其他节点则无法生效

当这一组的v-if与v-else-if都不成立,则当前节点的状态为v-if="ture"

适用于:切换频率较低的场景。

特点:不展示的DOM元素直接被移除。

注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。

2.v-show:当为False时会将节点隐藏 Display:none

写法:v-show="表达式"

适用于:切换频率较高的场景。

特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。

<!--  template标签不会影响HTML结构,vue渲染时,会将其该容器去掉,直接暴漏内部的子节点  -->
<!--  template标签只能和v-if使用,与v-show使用时无法生效  -->
<template v-if="true">
                <h2>AAAAAA</h2>
                <h2>BBBBBB</h2>
                <h2>CCCCCC</h2>
</template>

列表渲染

v-for指令

1.用于展示列表数据

2.语法:v-for="(item, index) in xxx" :key="yyy"

3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

 <div id="app">
        <ul>
            <!-- in都可以用of代替,of也都可以用in代替 -->
            <!-- 遍历数组 -->
            <li v-for="(person,index) in persons" :key="person.id">
                {{person.name}} ---  {{person.age}}----{{index}}
            </li>
            <!-- 遍历对象 -->
            <li v-for="(value,key) of Hsiaoyuan" :key="key">
                {{value}} ---  {{key}}
            </li>
            <!-- 遍历字符串 -->
            <li v-for="(byte,index) in str" :key="index">
                {{byte}} ---  {{index}}
            </li>
            <!-- 遍历指定次数 -->
            <li v-for="(number,index) in 10" :key="index+1000">
                {{number}} ---  {{index}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                persons:[
                    {id:"001",name:"Shirllyuan",age:21},
                    {id:"002",name:"Hsiaoyuan",age:20},
                    {id:"003",name:"Yuanzihan",age:19},
                ],
                Hsiaoyuan:{
                    name:"Hsiaoyuan",
                    age:21,
                    otherName:"Shirllyuan",
                },
                str:"Hsiaoyuan"
            },
        });
​
    </script>

在template上使用v-for

类似于 v-if,你也可以利用带有 v-for<template> 来循环渲染一段包含多个元素的内容。

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

key的原理

1.虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,

随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

2.对比规则:

(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:

①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!

②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

(2).旧虚拟DOM中未找到与新虚拟DOM相同的key

创建新的真实DOM,随后渲染到到页面。

  1. 用index作为key可能会引发的问题:

  2. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:

会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

  1. 如果结构中还包含输入类的DOM:

会产生错误DOM更新 ==> 界面有问题。

  1. 开发中如何选择key:

1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。

2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,

使用index作为key是没有问题的。

3.不使用key默认用index作为key

列表过滤

使用watch实现

 <div id="app">
        <ul>
            <input type="text" placeholder="请输入姓名:"  v-model="inputByUser">
            <li v-for="(person,index) in filterPersons" :key="person.id">
                {{person.name}} ---  {{person.age}}----{{person.sex}}
            </li>
​
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                inputByUser:"",
                persons:[
                    {id:"001",name:"韩子源",age:21,sex:"男"},
                    {id:"002",name:"袁子涵",age:20,sex:"男"},
                    {id:"003",name:"憨小源",age:19,sex:"男"},
                    {id:"004",name:"韩晓园",age:18,sex:"男"},
                ],
                filterPersons:[],
            },
            watch:{
                inputByUser:{
                    immediate:true,
                    handler(value){
                        this.filterPersons = this.persons.filter((p) => {
                            return p.name.indexOf(value) !== -1
                        }) 
                }}
            }
        });
​
    </script>

使用computed

    <div id="app">
        <ul>
            <input type="text" placeholder="请输入姓名:"  v-model="inputByUser">
            <li v-for="(person,index) in filterPersons" :key="person.id">
                {{person.name}} ---  {{person.age}}----{{person.sex}}
            </li>
​
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                inputByUser:"",
                persons:[
                    {id:"001",name:"韩子源",age:21,sex:"男"},
                    {id:"002",name:"袁子涵",age:20,sex:"男"},
                    {id:"003",name:"憨小源",age:19,sex:"男"},
                    {id:"004",name:"韩晓园",age:18,sex:"男"},
                ],
            },
            computed:{
                filterPersons(){
                    return this.persons.filter(
                        (p) => p.name.indexOf(this.inputByUser) !== -1);
                }
            }
            
        });
​
    </script>

列表排序

    <div id="app">
        <ul>
            <input type="text" placeholder="请输入姓名:"  v-model="inputByUser">
            <li v-for="(person,index) in filterPersons" :key="person.id">
                {{person.name}} ---  {{person.age}}----{{person.sex}}
            </li>
            <button @click.prevent="sortType = 2">升序排列</button>
            <button @click.prevent="sortType = 1">降序排列</button>
            <button @click.prevent="sortType = 0">原序排列</button>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
​
        var app = new Vue({
            el:"#app",                                           
            data:{
                inputByUser:"",
                sortType:0,
                persons:[
                    {id:"001",name:"韩子源",age:21,sex:"男"},
                    {id:"002",name:"袁子涵",age:20,sex:"男"},
                    {id:"003",name:"憨小源",age:19,sex:"男"},
                    {id:"004",name:"韩晓园",age:18,sex:"男"},
                ],
            },
            computed:{
                filterPersons(){
                    const arr =  this.persons.filter(
                        (p) => p.name.indexOf(this.inputByUser) !== -1
                    );
                    if(this.sortType){
                        arr.sort(
             (p1,p2) => this.sortType === 1? p2.age - p1.age : p1.age - p2.age
                        )
                    }
                    return arr;
                }
            }
            
        });
​
    </script>

Vue数据监视的原理

Vue监视数据的原理:

  1. vue会监视data中所有层次的数据。

  2. 如何监测对象中的数据?

通过setter实现监视,且要在new Vue时就传入要监测的数据。

(1).对象中后追加的属性,Vue默认不做响应式处理

(2).如需给后添加的属性做响应式,请使用如下API:

Vue.set(target,propertyName/index,value) 或

vm.$set(target,propertyName/index,value)

  1. 如何监测数组中的数据?

通过包裹数组更新元素的方法实现,本质就是做了两件事:

(1).调用原生对应的方法对数组进行更新。

(2).重新解析模板,进而更新页面。

4.在Vue修改数组中的某个元素一定要用如下方法:

1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()

2.Vue.set() 或 vm.$set()

特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!

模拟数据检测

    <script type="text/javascript" >let data = {
                name:'Shirllyuan',
                age:20,
            }
​
            //创建一个监视的实例对象,用于监视data中属性的变化
            const obs = new Observer(data)      
            console.log(obs)    
​
            //准备一个vm实例对象
            let vm = {}
            vm._data = data = obs
​
            function Observer(obj){
                //汇总对象中所有的属性形成一个数组
                const keys = Object.keys(obj)
                //遍历
                keys.forEach((k)=>{
                    Object.defineProperty(this,k,{
                        get(){
                            return obj[k]
                        },
                        set(val){
                console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)
                            obj[k] = val
                        }
                    })
                })
            } 
        </script>

收集表单数据

若:,则v-model收集的是value值,用户输入的就是value值。

若:,则v-model收集的是value值,且要给标签配置value值。

若:

1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)

2.配置input的value属性:

(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)

(2)v-model的初始值是数组,那么收集的的就是value组成的数组

备注:v-model的三个修饰符:

lazy:失去焦点再收集数据

number:输入字符串转为有效的数字

trim:输入首尾空格过滤

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>收集表单数据</title>
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <div id="root">
            <form @submit.prevent="demo">
                账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
                密码:<input type="password" v-model="userInfo.password"> <br/><br/>
                年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
                性别:
                男<input type="radio" name="sex" v-model="userInfo.sex" value="male"><input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>
                爱好:
                学习<input type="checkbox" v-model="userInfo.hobby" value="study">
                打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
                吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
                <br/><br/>
                所属校区
                <select v-model="userInfo.city">
                    <option value="">请选择校区</option>
                    <option value="Harbin">哈尔滨</option>
                    <option value="weihai">威海</option>
                    <option value="shenzhen">深圳</option>
                </select>
                <br/><br/>
                其他信息:
                <textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
                <input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.hitwh.edu.cn">《用户协议》</a>
                <button>提交</button>
            </form>
        </div>
    </body><script type="text/javascript">
        Vue.config.productionTip = falsenew Vue({
            el:'#root',
            data:{
                userInfo:{
                    account:'',
                    password:'',
                    age:18,
                    sex:'female',
                    hobby:[],
                    city:'beijing',
                    other:'',
                    agree:''
                }
            },
            methods: {
                demo(){
                    console.log(JSON.stringify(this.userInfo))
                }
            }
        })
    </script>
</html>

过滤器

定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

语法:

1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}

2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"

备注:

1.过滤器也可以接收额外参数、多个过滤器也可以串联

2.并没有改变原本的数据, 是产生新的对应的数据

        <div id="root">
            <h2>显示格式化后的时间</h2>
            <!-- 计算属性实现 -->
            <h3>现在是:{{fmtTime}}</h3>
            <!-- methods实现 -->
            <h3>现在是:{{getFmtTime()}}</h3>
            <!-- 过滤器实现 -->
            <h3>现在是:{{time | timeFormater}}</h3>
            <!-- 过滤器实现(传参) -->
            <h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
            <h3 :x="msg | mySlice">尚硅谷</h3>
        </div><div id="root2">
            <h2>{{msg | mySlice}}</h2>
        </div>
    </body><script type="text/javascript">
        Vue.config.productionTip = false
        //全局过滤器
        Vue.filter('mySlice',function(value){
            return value.slice(0,4)
        })
        
        new Vue({
            el:'#root',
            data:{
                time:1621561377603, //时间戳
                msg:'你好,Shirllyuan'
            },
            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:{
                timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
                    // console.log('@',value)
                    return dayjs(value).format(str)
                }
            }
        })
​
        new Vue({
            el:'#root2',
            data:{
                msg:'hello,Shirllyuan!'
            }
        })
    </script>

内置指令

指令回顾

v-bind : 单向绑定解析表达式, 可简写为 :xxx

v-model : 双向数据绑定

v-for : 遍历数组/对象/字符串

v-on : 绑定事件监听, 可简写为@

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

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

v-show : 条件渲染 (动态控制节点是否展示)

v-text

1.作用:向其所在的节点中渲染文本内容。

2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。

<div id="root">
            <div>Hello,{{name}}</div>
            <div v-text="name"></div>
            <div v-text="str"></div>
        </div>
    </body><script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
        
        new Vue({
            el:'#root',
            data:{
                name:'Shirllyuan',
                str:'<h3>你好啊!</h3>'
            }
        })
    </script>

v-html

1.作用:向指定节点中渲染包含html结构的内容。

2.与插值语法的区别:

(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。

(2).v-html可以识别html结构。

3.严重注意:v-html有安全性问题!!!!

(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。

(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!

v-cloak

1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。

2.使用css属性选择器配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。

v-once

1.v-once所在节点在初次动态渲染后,就视为静态内容了。

2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

v-pre

1.跳过其所在节点的编译过程。

2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

自定义指令

一、定义语法:

(1).局部指令:

new Vue({ new Vue({

directives:{指令名:配置对象} 或 directives{指令名:回调函数}

}) })

(2).全局指令:

Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)

使用回调函数的形式仅触发bind和update的钩子函数

二、配置对象中常用的3个回调:

(1).bind:指令与元素成功绑定时调用。

(2).inserted:指令所在元素被插入页面时调用。

(3).update:指令所在模板结构被重新解析时调用。

钩子函数参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM。

  • binding:一个对象,包含以下 property:

    name:指令名,不包括 v- 前缀。

    value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。

    oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可 用。无论值是否改变都可用。

    expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1+ 1"。

    arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。

    modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象 为 { foo: true, bar: true }。

  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详

  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

三、备注:

1.指令定义时不加v-,但使用时要加v-;

2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

<body>
        <!-- 
                需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
                需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
                自定义指令总结:
                        一、定义语法:
                                    (1).局部指令:
                                new Vue({                                                                               new Vue({
            directives:{指令名:配置对象}   或           directives{指令名:回调函数}
                                                })                                                                      })
                                    (2).全局指令:
            Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)
​
                        二、配置对象中常用的3个回调:
                                    (1).bind:指令与元素成功绑定时调用。
                                    (2).inserted:指令所在元素被插入页面时调用。
                                    (3).update:指令所在模板结构被重新解析时调用。
​
                        三、备注:
                            1.指令定义时不加v-,但使用时要加v-;
                            2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
        -->
        <!-- 准备好一个容器-->
        <div id="root">
            <h2>{{name}}</h2>
            <h2>当前的n值是:<span v-text="n"></span> </h2>
            <!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> -->
            <h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
            <button @click="n++">点我n+1</button>
            <hr/>
            <input type="text" v-fbind:value="n">
        </div>
    </body>
    
    <script type="text/javascript">
        Vue.config.productionTip = false//定义全局指令
        /* Vue.directive('fbind',{
            //指令与元素成功绑定时(一上来)
            bind(element,binding){
                element.value = binding.value
            },
            //指令所在元素被插入页面时
            inserted(element,binding){
                element.focus()
            },
            //指令所在的模板被重新解析时
            update(element,binding){
                element.value = binding.value
            }
        }) */new Vue({
            el:'#root',
            data:{
                name:'尚硅谷',
                n:1
            },
            directives:{
                //big函数何时会被调用?
                //1.指令与元素成功绑定时(一上来)。
                //2.指令所在的模板被重新解析时。
                /* 'big-number'(element,binding){
                    // console.log('big')
                    element.innerText = binding.value * 10
                }, */
                big(element,binding){
                    console.log('big',this) //注意此处的this是window
                    // console.log('big')
                    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
                    }
                }
            }
        })
        
    </script>

Vue的生命周期

1.又名:生命周期回调函数、生命周期函数、生命周期钩子。

2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。

3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。

4.生命周期函数中的this指向是vm 或 组件实例对象。

生命周期图示

生命周期.png

vm.$destroy() 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器(自定义事件,若事件属于DOM原生事件无法解绑)。

触发 beforeDestroydestroyed 的钩子。

生命周期概括

常用的生命周期钩子:

  1. mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。

  2. beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。该生命周期可以 访问到vm的元素,方法等,但此时无法触发元素更新

关于销毁Vue实例

1.销毁后借助Vue开发者工具看不到任何信息。

2.销毁后自定义事件会失效,但原生DOM事件依然有效。

3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。

其他生命周期钩子函数

在vue-router中详细分析

activated :被 keep-alive 缓存的组件激活时调用。该钩子在服务器端渲染期间不被调用。

deactivated:被 keep-alive 缓存的组件失活时调用。该钩子在服务器端渲染期间不被调用。

errorCaptured:在捕获一个来自后代组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误 的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错 误继续向上传播。

Vue2组件式开发

非单文件组件

基本使用

Vue中使用组件的三大步骤:

一、定义组件(创建组件)

二、注册组件

三、使用组件(写组件标签)

一、如何定义一个组件?

使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样, 但也有点区别;

区别如下:

1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪 个容器。

2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。

备注:使用template可以配置组件结构。

二、如何注册组件?

1.局部注册:靠new Vue的时候传入components选项

2.全局注册:靠Vue.component('组件名',组件)

三、编写组件标签:

<body>
        <!-- 准备好一个容器-->
        <div id="root">
            <hello></hello>
            <hr>
            <h1>{{msg}}</h1>
            <hr>
            <!-- 第三步:编写组件标签 -->
            <school></school>
            <hr>
            <!-- 第三步:编写组件标签 -->
            <student></student>
        </div><div id="root2">
            <hello></hello>
        </div>
    </body><script type="text/javascript">
        Vue.config.productionTip = false//第一步:创建school组件
        const school = Vue.extend({
            template:`
                <div class="demo">
                    <h2>学校名称:{{schoolName}}</h2>
                    <h2>学校地址:{{address}}</h2>
                    <button @click="showName">点我提示学校名</button>  
                </div>
            `,
            // el:'#root', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
            data(){
                return {
                    schoolName:'哈尔滨工业大学威海',
                    address:'山东威海'
                }
            },
            methods: {
                showName(){
                    alert(this.schoolName)
                }
            },
        })
​
        //第一步:创建student组件
        const student = Vue.extend({
            template:`
                <div>
                    <h2>学生姓名:{{studentName}}</h2>
                    <h2>学生年龄:{{age}}</h2>
                </div>
            `,
            data(){
                return {
                    studentName:'张三',
                    age:18
                }
            }
        })
        
        //第一步:创建hello组件
        const hello = Vue.extend({
            template:`
                <div>   
                    <h2>Hello,{{name}}</h2>
                </div>
            `,
            data(){
                return {
                    name:'Tom'
                }
            }
        })
        
        //第二步:全局注册组件
        Vue.component('hello',hello)
​
        //创建vm
        new Vue({
            el:'#root',
            data:{
                msg:'Hello!'
            },
            //第二步:注册组件(局部注册)
            components:{
                school,
                student
            }
        })
​
        new Vue({
            el:'#root2',
        })
    </script>

注意事项

1.关于组件名:

一个单词组成:

第一种写法(首字母小写):school

第二种写法(首字母大写):School

多个单词组成:

第一种写法(kebab-case命名):my-school

第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)

备注:

(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。

(2).可以使用name配置项指定组件在开发者工具中呈现的名字。

2.关于组件标签:

第一种写法:

第二种写法:

备注:不用使用脚手架时,会导致后续组件不能渲染。

3.一个简写方式:

const school = Vue.extend(options) 可简写为:const school = options

组件的嵌套

<body>
        <!-- 准备好一个容器-->
        <div id="root">
            
        </div>
    </body><script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。//定义student组件
        const student = Vue.extend({
            name:'student',
            template:`
                <div>
                    <h2>学生姓名:{{name}}</h2>  
                    <h2>学生年龄:{{age}}</h2>   
                </div>
            `,
            data(){
                return {
                    name:'Shirllyuan',
                    age:18
                }
            }
        })
        
        //定义school组件
        const school = Vue.extend({
            name:'school',
            template:`
                <div>
                    <h2>学校名称:{{name}}</h2>  
                    <h2>学校地址:{{address}}</h2>   
                    <student></student>
                </div>
            `,
            data(){
                return {
                    name:'哈工大威海',
                    address:'威海'
                }
            },
            //注册组件(局部)
            components:{
                student
            }
        })
​
        //定义hello组件
        const hello = Vue.extend({
            template:`<h1>{{msg}}</h1>`,
            data(){
                return {
                    msg:'Welcome to hitwh'
                }
            }
        })
        
        //定义app组件
        const app = Vue.extend({
            template:`
                <div>   
                    <hello></hello>
                    <school></school>
                </div>
            `,
            components:{
                school,
                hello
            }
        })
​
        //创建vm
        new Vue({
            template:'<app></app>',
            el:'#root',
            //注册组件(局部)
            components:{app}
        })
    </script>

VueComponent

关于VueComponent:

1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生 成的。

2.我们只需要写或,Vue解析时会帮我们创建school组件的实例 对象,即Vue帮我们执行的:new VueComponent(options)。

3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!

4.关于this指向:

(1).组件配置中:

data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是 【VueComponent实例对象】。

(2).new Vue(options)配置中:

data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue 实例对象】。

5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。

Vue的实例对象,以后简称vm。

    <body>
        <!-- 准备好一个容器-->
        <div id="root">
            <school></school>
            <hello></hello>
        </div>
    </body><script type="text/javascript">
        Vue.config.productionTip = false
        
        //定义school组件
        const school = Vue.extend({
            name:'school',
            template:`
                <div>
                    <h2>学校名称:{{name}}</h2>  
                    <h2>学校地址:{{address}}</h2>   
                    <button @click="showName">点我提示学校名</button>
                </div>
            `,
            data(){
                return {
                    name:'哈工大威海',
                    address:'威海'
                }
            },
            methods: {
                showName(){
                    console.log('showName',this)
                }
            },
        })
​
        const test = Vue.extend({
            template:`<span>Hitwh</span>`
        })
​
        //定义hello组件
        const hello = Vue.extend({
            template:`
                <div>
                    <h2>{{msg}}</h2>
                    <test></test>   
                </div>
            `,
            data(){
                return {
                    msg:'Hello!'
                }
            },
            components:{test}
        })
​
​
        // console.log('@',school)
        // console.log('#',hello)//创建vm
        const vm = new Vue({
            el:'#root',
            components:{school,hello}
        })
    </script>

Vue实例的构造函数和VueComponent的原型

1.一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype

2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

Vue&VueComponent.jpg

单文件组件

.vue文件的组成

<template>
    <div>
        <!-- 页面模板 -->
    </div>
</template><script>
     export default {
        name:'VueComponentName',
        data(){},
        methods: {},
        computed:{},
        components:{},
    }
    // 此处为JS模块对象
</script><style>
/*  此处为CSS样式 */
</style>

声明:该笔记为尚硅谷Vue教程学习笔记,并参考官方文档以及网上一些文章后的学习记录,如有侵权请联系作者删除。