day2 – vue起步
Vue安装
目前有种使用方式:
1 CDN引入
开发环境版本:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
生产环境版本:
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
开发人员的话,还是建议使用第一个
2 下载引入
直接去vue的官网找这个,然后点击链接另存为即可下载vue.js文件,然后放到自己的项目JS文件夹中,在需要使用的页面引入即可
3 npm安装
这个我还没有学到脚手架,学了以后在回来说说
Vue初体验
我是采用通过下载vue.js,然后导入页面的方式。
<div id="app">
<h1>{{price}}</h1> //Mustache语法,响应式
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({//Vue实例,参数是一个对象
el:"#app",//el挂载元素选择器 String、HtmlElement
data:{ //代理数据,data可以是一个对象,也可以是一个函数
price:'haha'
}
methods:{}//定义方法的地方,参数是一个对象
})
</script>
mustache语法:中译(小胡须),可能以长得像八字胡而得名。这个有专门的mustache语法,通过Mustache.rander函数对其进行渲染。
Vue的MVVM模型
MVVM(Model-view-ViewModel)。
Model:是指代表真实状态内容的领域模型(面向对象),或者代表内容的数据访问层(以数据为中心)
View:即图形化界面
ViewModel:可以理解为一个值转换器,从模型中转换数据对象,以方便管理和呈现数据对象,并且负责把view对数据的修改同步回model。
MVVM模型:将用户的图形化界面的开发和业务逻辑(数据模型)分离开来,利用数据绑定的优势和数据绑定的框架尽可能是的使逻辑操作从视图层消失。
MVVM缺点:在非常大的应用程序中数据绑定会导致相当大的内存消耗。
Vue虽然没有完全遵循MVVM模型,但是也收到了他的启发,因此我们经常在vue的代码中可以看到 vm 这个变量。
Vue的生命周期函数
定义生命周期函数:
mounted(){} 或者 mounted:function(){}
注意:千万不能写成箭头函数: mounted:()=>{},因为在箭头函数中的this对象是默认为window对象的,而我们大多数时候只需要使用的是当前实例化的vue对象,所有的生命周期钩子是自动绑定this上下文到实例中。
1 beforeCreate: 在实例初始化后,数据观测和event/watchr之前被调用
2 create: 在实例创建之后被调用,已经完成数据观测、属性和方法的运算、watch\event事件的回调。但是没有挂载
3 beforeMounted: 在挂载之前被调用:相关的render首次被调用,在渲染期间是不调用这个的
4 mounted:实例被挂载后调用,这是el被创建的新vm. e l 替 换 了 , m o u n t e d 不 能 保 证 所 有 的 子 组 件 被 挂 载 , 如 果 要 等 所 有 的 子 组 件 比 挂 载 完 成 之 后 再 调 用 , 可 以 使 用 v m . el替换了,mounted不能保证所有的子组件被挂载,如果要等所有的子组件比挂载完成之后再调用,可以使用 vm. el替换了,mounted不能保证所有的子组件被挂载,如果要等所有的子组件比挂载完成之后再调用,可以使用vm.nextTick(function(){})
5 beforeUpdated:数据发生更新之前,在数据更新的时候,会生成新的虚拟dom,然后和就旧的dom进行比较,然后同通过render函数进行渲染,这里需要注意的是只有和模板绑定的数据发生改变的时候才会进行更新dom,否则不会(为了提高代码效率,因为没有用到的数据,没有必要进行响应式处理)。在比较之前 调用钩子函数。
6 updated:数据更新,导致虚拟dom重新渲染和打补丁,在这之后会调用改钩子函数,同理 update不会保证所有的子组件一起被重绘,如果需要等到整个视图重绘完毕,可以在里面加入$nextTick回调
7 actived:在keep-alive被激活的时候调用
8 deactived:在keep-alive失效的时候调用
9 beforeDestory:在实例销毁之前调用
10 destoryed:实例销毁之后调用
11 errorCaptured:捕捉子组件的异常
下面来列一个小例子,检测生命周期:
<div id="app">
<h1>{{price}}</h1>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
price:'4.5元'
},
beforeCreate(){
console.log("------------beforeCreate------------");
console.log(this.$el);
console.log(this.price);
this.changePrice();
},
created(){
console.log("------------created------------");
console.log(this.$el);
console.log(this.price);
this.changePrice();
},
beforeMount(){
console.log("------------beforeMount------------");
console.log(this.$el);
console.log(this.price);
this.changePrice();
},
mounted(){
console.log("------------beforeCreate------------");
console.log(this.$el);
console.log(this.price);
this.changePrice();
},
beforeUpdate(){
console.log("------------beforeUpdate------------");
console.log(this.$el);
console.log(this.price);
this.changePrice();
},
updated(){
console.log("------------updated------------");
console.log(this.$el);
console.log(this.price);
this.changePrice();
},
beforeDestory(){
console.log("------------beforeDestory------------");
console.log(this.$el);
console.log(this.price);
},
destoryed(){
console.log("------------destoryed------------");
console.log(this.$el);
console.log(this.price);
this.changePrice();
},
watch:{
price(){
console.log("----------监听到价格的改变---------");
}
},
methods:{
changePrice(){
this.price = '10';
console.log("------调用changePrice的方法------");
}
}
});
app.price = '5.5元';
</script>
Vue深度响应式原理
上面学了Vue的生命周期,但是对很多专业术语都存在一些了解不完全,所以又去看了看vue的深度响应式原理,加深理解,不过现在还没有阅读源码,所以只能进行很简单的理解。
在说到响应式原理之前,先说个Vue的局限吧,不支持IE8及一下的浏览器版本,这是由于vue使用Object.defineProperty方法对属性进行getter和setter操作的,而 Object.defineProperty是ES5无法shim的特性。
vue生命周期中有几个步骤,有一个是initDate,在初始化数据时,会便利data中所有属性,并使用Object.defineProperty对其进行getter和setter操作,方便对其进行追踪,只有在模板上被使用的数据属性才会被记录为依赖。当依赖的setter触发时,会通知water,从而使他关联的组件重新渲染。
然后有几个值得注意的地方:
由于vue无法检测到对象属性的添加和删除,所以只有在data对象中存在才会使响应式的,如果有特殊需求,可以是使用$(set)方法。
整个渲染流程具体要点如下:
1 当某个属性被利用的时候,就会触发getter函数,water就会将这个属性作为依赖监听下来
2 当渲染完成的时候,每一个被用到的属性都在watcher中记录了下来
3 当数据发生改变的时候,会触发setter去通知数据对象发生的值改变
4 此时会通知对应的组件,其数据依赖发生改变,需要重新渲染
5 对应的组件再次调用渲染函数,生成虚拟DOM,实现更新
插值语法
1 mustache语法:
除了能绑定数值键值以外,还可以绑定 单个表达式:
<div id="app">
<h1>{{price*10}}</h1>
<h1>{{price > 10 ? 30 : 9}}</h1>
</div>
2 常用指令:
v-html :容易被xss攻击
v-text
v-once:只渲染一次,后面的渲染自动跳过
v-pre:跳过编译过程
v-cloak: 在关联实例编译结束后执行 ,在vue解析之前存在这个属性,vue解析之后,这个属性自动消除
绑定参数
1 v-bind绑定基础属性:
<img v-bind:src="imgurl">//绑定单个属性
<img :src="imgurl">//v-bind的语法糖为 ‘:’
<div class="content-box" :class="{'big-box':isBig}"></div> //动态绑定类对象
<div class="content-box" :class="{'big-box':isBig > 10 ? true : false}"></div> //带有判断的类动态绑定
<div class="content-box" :class="['big-box','back-color']"></div>//动态绑定类数组
<div class="content-box" :style="{width:10+'px',height:300+'px'}"></div>//动态绑定样式对象,传数字的花,直接解析,也可以变量
2 v-on 监听dom
<button v-on:click="changePrice"></button>
<button @click="changePrice"></button>//v-on语法糖为@
这里补充一下关于v-on的参数传递:
<button v-on:click.stop="havenotProperty">不带参数例子</button>
<button v-on:click.stop="haveProperty">带参数例子</button>
<button v-on:click.stop="haveProperty()">带参数括号例子</button>
<button v-on:click.stop="haveProperty($event)">带参数括号例子</button>
havenotProperty(){
},
haveProperty(val){
console.log(val);
},
不带参数的函数在调用的时候,而可以省略括号
带参数的函数,如果省略括号,默认传的是vue生成的event对象作为参数,如果带括号的但是不传参数,则显示undefined,还有一种情况就是将event对象作为参数传递,则直接使用$event
3 绑定动态属性或者监听动态事件
此处有一个注意事项:就是属性名不能有 大写字母,驼峰也不行,中间的横线会被解析为减号,不过看官网就是直接这样attributeName命名的,可能是因为直接引入vue.js的一些局限
<a v-bind:[imgurl]="attributeName">hahah </a>
4 修饰符
.stop 阻止事件冒泡
.pervent 组织默认事件
.native 监听组件的原生事件
<div v-on:click.stop="changePrice"></div>
计算属性
1.计算属性 vs methods
当我们需要对参数进行一些运算在展示出来的时候可以选择计算属性,虽然也可以在mustache的括号中写表达式,但是只能写单行表达式,不能做复杂的运算,所以选择计算表达式更方便。
但是大家可能有个疑问,为什么不用方法呢?都可以进行运算,然后直接返回值,下面的代码告诉你为什么不用方法:
<div>总价:{{totalPrice}}</div>
<div>总价:{{totalPrice}}</div>
<div>总价:{{totalPrice}}</div>//调用计算属性
<div>总价:{{getTotalPrice}}</div>//调用方法
<div>总价:{{getTotalPrice}}</div>
<div>总价:{{getTotalPrice}}</div>
computed:{
totalPrice(){ //计算数组累加,这里用到了一个js的reduce方法
console.log("---计算属性----");
return this.books.reduce(function(pre,book){
return pre+book.price*book.num;
},0)
},
methods:{
getTotalPrice(){
console.log("---方法----");
return this.books.reduce(function(pre,book){
// console.log(pre);
return pre+book.price*book.num;
},0)
}
}
运行结果:
同时调用计算属性和方法三次,但是在计算属性真正只执行了一次,而方法执行了三次,这次因为计算属性是基于他们的响应式依赖进行缓存的,只有当相关依赖的值进行改变的时候,计算属性才会重新进行计算,所以在多次求totalPrice的值的时候,直接返回之前计算的数据,而不同用重新进行计算。
2.计算属性的getter和setter
计算属性是同时存在get和set方法的,但是很多时候都用不到set,所以默认为只有get,不过在需要的时候,还是可以定义set的
computed:{
totalPrice:{
get(){
console.log("-------执行getter--------")
return this.books.reduce(function(pre,book){
return pre+book.price*book.num;
},0)
},
set(val){
console.log("-------执行setter--------"+val)
}
}
},
条件渲染
<template v-if="changeWay">
<label for="telephone">电话号码</label>
<input id="telephone" v-model="telephone">
</template>
<template v-else>
<label for="email">邮箱</label>
<input id="email" v-model="telephone">
</template>
<button @click="changeWays">邮箱登陆</button>
在这里说一下v-if和v-show的区别:
v-if是直接不渲染,页面没有这个元素,而v-show是display:none
比较下:v-if有更高的切换开销,而v-show在初始时渲染开销较大,因此如果需要非常频繁的切换的话,可以使用v-show
另外上面这个例子存在一个问题,这个问题想必大家在开发过程中也须到过很多次,如果使用同一个表单组件很多次,那么数据不会自动清空
这是由于为了更高效的渲染元素,通常会复用一些已有元素,比如现在的input标签,由于v-if是直接控制不渲染,那个在切换的时候,会在虚拟DOM树中找到之前的电话号码用过的input标签,直接将id和placeholder进行改变,其他继续复用,所以已经输入的数据没有被清空。
这种情况在有些地方也挺实用的。但是如果我们不想复用的话,vue也有一个解决方法,那就是增加一个 key 属性,每次切换的时候都会重新渲染。
<template v-if="changeWay">
<label for="telephone">电话号码</label>
<input id="telephone" v-model="telephone" key="telephone">
</template>
<template v-else>
<label for="email">邮箱</label>
<input id="email" v-model="telephone" key="telephone">
</template>
<button @click="changeWays">邮箱登陆</button>
这样就是全新的渲染了,不过label依然会复用,因为lable,没有key属性
虚拟DOM
既然上面的条件渲染说到了,虚拟DOM,那就去学习一下虚拟DOM的相关知识
1 渲染流程
在了解虚拟dom之前,我们先来看看vue的一个渲染过程
先对模板进行编译,编译成一个个渲染函数,函数被调用的时候,就会返回一个虚拟的DOM树,然后将虚拟DOM树交给一个patch函数,把虚拟DOM施加到真正的DOM上去。
在这个 过程中,vue有自身的响应系统来监测在渲染过程中所依赖的数据来源,侦测到数据源后就可以感知到数据的变动,当数据发生改变时,需要重新进行渲染,重新渲染会生成一个新的虚拟DOM,然后和原来的虚拟DOM进行比较后,再将需要的改变施加到真实DOM上。
2 为什么要有虚拟DOM
因为Javascript在现在的浏览器引擎中运算的非常的快,而DOM是比较缓慢的,当调用DOM原生的API的时候,浏览器需要在Javascript引擎的语境下去接触原生的DOM的实现,这个过程性能消耗比较高,所以我们应该尽量把操作放在计算中去实现,保证最后计算出来需要接触DOM的操作是最少的。
今日份学习笔记,在底层原理上学习不足,后期在慢慢整改
如果有错误或者表达不当的地方,希望大家批评指正。