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 / getter,Object.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 订阅到这个数据持有的 dep 的 watchers 中,这个目的是为后续数据变化时候能通知到哪些 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,随后渲染到到页面。
-
用index作为key可能会引发的问题:
-
若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
- 开发中如何选择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监视数据的原理:
-
vue会监视data中所有层次的数据。
-
如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
- 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(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 = false
new 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 或 组件实例对象。
生命周期图示
vm.$destroy() 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器(自定义事件,若事件属于DOM原生事件无法解绑)。
触发 beforeDestroy 和 destroyed 的钩子。
生命周期概括
常用的生命周期钩子:
-
mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
-
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 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
单文件组件
.vue文件的组成
<template>
<div>
<!-- 页面模板 -->
</div>
</template>
<script>
export default {
name:'VueComponentName',
data(){},
methods: {},
computed:{},
components:{},
}
// 此处为JS模块对象
</script>
<style>
/* 此处为CSS样式 */
</style>
声明:该笔记为尚硅谷Vue教程学习笔记,并参考官方文档以及网上一些文章后的学习记录,如有侵权请联系作者删除。