Vue学习笔记
- 一、创建Vue3.0工程
- 二、常用 Composition API
- 三、其它 Composition API
- 四、Composition API 的优势
- 五、新的组件
- 六、其他
声明
该文章为转载多方文章加一些自己的理解所成所成
转自百度网盘,链接链接:https://pan.baidu.com/s/1HC_TRtyt2ncC1zMRNCnlSw?pwd=1234
提取码:1234
1、初识vue
初识vue:
1.要用vue首先得创建一个vue实例,还要绑定一个
2.root容器里的代码依然符合html规范,只不过混入了一些特殊的语法;
3.root容器中的代码被称为Vue模板
4.Vue实例和容器是一一对应的;
5真实开发中只有一个Vue实例并且会配合着组件一起使用;
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;7.一旦data中的数据发生改变,那么模板中用到该数据的地方也会自动更新;
1.1.第一个vue程序
学习到了vue里vue实例和容器的绑定以及对vue-devtools的使用
代码如下(示例):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h1>你好,{{name}}</h1>
</div>
<script>
Vue.config.productionTip=false
//创建vue实例
const x =new Vue({
el:"#root" , //el用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data:{ //data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
name:'vue',
age:13
}
})
</script>
</body>
</html>
2.一个容器只能对应一个实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div class="root">
<h1>你好,{{name}}</h1>
</div>
<div class="root">
<h1>你好,{{name}}</h1>
</div>
<script>
Vue.config.productionTip=false
//创建vue实例
const x =new Vue({
el:".root" , //el用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data:{ //data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
name:'vue',
age:13
}
})
</script>
</body>
</html>
3.模板语法
vue模板语法有2大类:
1.插值语法:
功能:用于解析标签体内容。
写法:{ixxx}}, xxx是js表达式,且可以直接读取到data中的所有属性。
2.指令语法:
功能:用于解析标签
(包括:标签属性、标签体内容、绑定事件…) .
举例:v-bind: href=“xxx”或简写为:href=” xxx",xxx同样要写js表达式,
且可以直接读取到data中的所有属性。
备注:Vue中有很多的指令,且形式都是: v-???,此处我们只是拿v-bind举个例
代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
<h1>插值语法</h1>
<h3>你好,{{name.toUpperCase()}}</h3>
<hr>
<h1>指令语法</h1>
<a v-bind:href="school.url.toUpperCase()">点我去{{school.name}}学习!</a>
<a :href="school.url">点我去学习!</a>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip=false
new Vue({
el:'#root',
data:{
name:'jack',
school:{
name:"csdn",
url:'https://www.csdn.net/',
}
}
})
</script>
</html>
4.数据绑定
这里是引用
Vue中有2种数据绑定的方式:
1.单向绑定(v-bind):数据只能从data流向页面。
2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
备注:
1.双向绑定一般都应用在表单类元素上(如: input、select等)
2.v-model:value可以简写为v-model,因为v-model默认收集的就是value值
4.el和data的两种写法
真相只在代码之中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div class="root">
<h1>你好,{{name}}</h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip=false //阻止vue 在启动时生成生产提示
const v = new Vue({
// el:"#root" , el d的第一种写法
data:{
name:'vue'
}
})
console.log(v)
v.$mount('#root') //el的第二种写法
//data的两种写法
new Vue({
el:'#root',
//data的第一种写法,对象式
/*
data:{
name:'尚硅谷'
}
*/
//data的第二种写法:函数式
data(){
console.log('@@@',this) //此处的this
return{
name:'vue'
}
}
})
</script>
</html>
5.MVVM
MVVM模型
1。M:模型(Model) : data中的数据
2.V:视图(View):模板代码
3.VM:视图模型(ViewModel):Vue实例
观察发现:
1.data中所有的属性,最后都出现在了vm身上。
2.vm身上所有的属性及 Vue原型上所有属性,在Vue模板中都可以直接使用。
6.数据代理
6.1.Object.defineProperty方法
这一小节主要将了Object.defineProperty方法的用法,其中有许多使实用的属性值需要学习
代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>回顾Object.defineproperty方法</title>
<script src="vue.js"></script>
</head>
<body>
<script>
let address='阿大多数'
let person={
name:'崔浩然',
age:18,
sex:'男'
}
Object.defineProperty(person,'address', {
value: "山西省大同市",
enumerable: true, //控制该属性是否可被枚举
writable: true, //控制属性是否可以被修改
configurable: true, //控制属性是否可以被删除
//当有人读取person的age属性时,get函数就可以被调用,且返回值就是age的值
get: function () { //简写为get(){}
return address
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log("有人修改了age属性,且值是:",address)
}
})
console.log(Object.keys(person)) //Object.keys方法:参数:要返回其枚举自身属性的对象 返回值:一个表示给定对象的所有可枚举属性的字符串数组
for(let key in person){
console.log("@",person[key])
}
</script>
</body>
</html>
6.2 数据代理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>何为数据代理</title>
</head>
<body>
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)-->
<script type="text/javascript">
let obj={x:100}
let obj2={y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x=value
}
})
</script>
</body>
</html>
6.3Vue中的数据代理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip=false
const vm = new Vue({
el:'#root',
data:{
name:'齐大',
address:'齐齐哈尔市建华区'
}
})
</script>
</html>
7.事件处理
7.1事件的基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<button v-on:click="showInfo">点我1</button>
<button @click="showInfo2(66,$event)">点我2</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip =false //阻止vue在启动的时候生成提示
new Vue({
el:'#root',
data:{
name:'齐大'
},
methods:{
showInfo(event){
alert('同学你好')
console.log(event.target.innerText)
console.log(this) //此处的this是vm
},
showInfo2(number,a){
console.log(number,a)
}
}
})
</script>
</html>
7.2 事件修饰符
Vue中的事件修饰符:
1.prevent:阻止默认事件(常用);
2.stop:阻止事件冒泡(常用):
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
5.self:只有event.target是当前操作的元素是才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="vue.js"></script>
<style>
* {
margin-top: 20px;
}
.demo1 {
height: 50px;
background-color: red;
}
.box1 {
padding: 5px;
background-color: skyblue;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list{
width:200px;
height: 20px;
background-color: darkblue;
overflow: auto;
}
li{
height: 100px;
}
</style>
</head>
<body>
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- 阻止默认事件(常用)-->
<a href="https://www.bilibili.com/" @click.prevent="showInfo">dianwo</a>
<!-- 阻止事件冒泡-->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">dinawo2</button>
</div>
<!-- 事件只触发一次(常用)-->
<button @click.once="showInfo">dinawo3.once</button>
<!-- 使用事件的捕获模式-->
<div class="box1" @click="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
<!-- 只有event.target是当前操作的元素时才触发事件-->
<div class="demo1" @click="showInfo5">
<button @click="showInfo5"> 点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕-->
<!-- @scroll是滚动滚动条触发事件,wheel是鼠标滚轮滚动触发事件,让他们的默认行为立即执行则@scroll.passive-->
<ul @scroll="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止vue在启动的时候生成提示
new Vue({
el: '#root',
data: {
name: '齐大'
},
methods: {
showInfo(event) {
event.preventDefault()
alert('同学你好')
},
showMsg(msg){
alert('alshdad')
},
showInfo5(event) {
console.log(event.target)
},
demo() {
console.log('A')
}
}
})
</script>
</html>
7.3键盘修饰符
1.Vue中常用的按键别名:
回车=>enter
删除=>delete(捕获"删除”和退格”键)退出=>esc
空格=>space
换行=>tab(特殊,必须配合keydown使用)
上=> up
下 =>down
左=>left
右=>right
2.Vue未提供别名的按键,可以使用按健原始的key值去绑定,但注意要转为kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctr1、alt、shift、meta
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐)
5.Vue.config.keyCodes.自定义键名=键码,可以去定制按键别名
8.姓名案例
8.1插值语法实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>姓名案例_插值语法实现</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h1>{{name}}</h1>
<input type="text" v-model="firstname"> <br> <br>
<input type="text" v-model="lastname"> <br> <br>
全名: <span> {{firstname+lastname}}</span>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
firstname: 'cui',
lastname:'haoran'
},
methods:{
showInfo(e){
console.log(e.target.value)
}
}
})
</script>
</body>
</html>
8.2methods实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>姓名案例_插值语法实现</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<input type="text" v-model="firstname"> <br> <br>
<input type="text" v-model="lastname"> <br> <br>
全名: <span> {{fullName()}}</span>
<button @click="fullName()">点我</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
firstname: 'cui',
lastname:'haoran'
},
methods:{
fullName(){
return this.firstname+this.lastname
}
}
})
</script>
</body>
</html>
8.3计算属性
计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理:底层借助了objcet.defineproperty方法提供的getter和setter。3.get函数什么时候执行?
(1).初次读取时会执行一次。|
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>姓名案例_计算属性实现</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<input type="text" v-model="firstname"> <br> <br>
<input type="text" v-model="lastname"> <br> <br>
测试 <input type="text" v-model="x"> <br> <br>
全名: <span> {{fullName()}}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
firstname: 'cui',
lastname:'haoran'
},
computed:{
fullname:{
get(){
return this.firstname+'-'+this.lastname
},
set(value){
console.log('set',value)
const arr= value.split('-')
this.firstname=arr[0]
this.lastname=arr[1]
}
}
}
})
</script>
</body>
</html>
9监视属性
监视属性watch:
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视!!
3.监视的两种写法:
(1).new Vue时传入watch配置
(2).通过vm. $watch监视
9.1天气案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>天气案例</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="change">切换天气</button>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
isHot:true
},
computed:{
info(){
return this.isHot?'炎热':"凉爽"
}
},
methods:{
change(){
this.isHot=!this.isHot
}
}
})
</script>
</html>
9.2handler
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>天气案例_监视属性</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="change">切换天气</button>
</div>
</body>
<script>
Vue.config.productionTip = false
const vm=new Vue({
el: "#root",
data: {
isHot:true
},
computed:{
info(){
return this.isHot?'炎热':"凉爽"
}
},
methods:{
change(){
this.isHot=!this.isHot
}
},
watch:{
isHot:{
// immediate:true,//初始化时让handler调用一下
//handler什么时候调用?当ishot发生改变时
handler(newValue,oldValue){
console.log("isHot被修改了",newValue,oldValue)
}
}
}
})
vm.$watch('isHot',{
immediate:true,//初始化时让handler调用一下
//handler什么时候调用?当ishot发生改变时
handler(newValue,oldValue){
console.log("isHot被修改了",newValue,oldValue)
}
})
</script>
</html>
9.3深度属性
deep:true //监视多级结构中所有的变化,可以在对numbers监视的情况下,只要改变其中的值就可以检测到numbers的变化
9.4watch和computed的区别
计算属性computed不能开启异步任务去维护数据
computed和lwatch之间的区别:
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作。两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象
2所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
10绑定样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<!-- 绑定css样式--字符串写法,适用于:样式的类名不确定,需要动态指定-->
<div class="basic" :class="a" @click="changeMood">{{name}}</div>
<!-- 绑定css样式--数组写法,要绑定的样式个数不确定,名字也不确定-->
<div class="basic" :class="classArr" >{{name}}</div>
<!-- 绑定css样式--对象写法,适用于:要绑定的样式个数确定,名字也确定,单要动态觉得用不用-->
<div class="basic" :class="classObj" >{{name}}</div>
<div class="basic" :style="styleObj">{{name}}</div>
<!-- 同时使用俩个样式-->
<div class="basic" :style="[styleObj,styleObj2]">{{name}}</div>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: '崔浩然',
a:'normal',
classArr:['a','b','c'],
classObj:{
asdhl:false,
asdad:false
},
fsize:20,
styleObj:{
fontSize:'40px',
color:'red',
},
styleObj2:{
backgroundColor:'blue'
}
},
methods:{
// changeMood(){
// console.log('阿达代理商')
// document.getElementById('demo').className='basic happy'
// },
changeMood(){
const arr = ['happy','sad','normal']
const index=Math.floor(Math.random()*3)
this.a=arr[index]
},
}
})
</script>
</html>
11.条件渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>条件渲染</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<!-- v-show-->
<h1 v-show="false">{{name}}</h1>
<!-- v-if-->
<h1 v-if="false">{{n}}</h1>
<button @click="n++">点我n加一</button>
<div v-if="n===1">阿萨达</div>
<div v-else-if="n===2">阿的萨达</div>
<div v-else-if="n===3">add</div>
<div v-else-="n===3">啊大大</div>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: '崔浩然',
n:1
},
methods:{
showInfo(e){
console.log(e.target.value)
}
}
})
</script>
</html>
12.列表渲染
12.1基本列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表渲染</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<!-- <li v-for="person in persons" :key="p.id">{{person}}</li>-->
<li v-for="(p,index) in persons" :key="p.id">{{p.name}}---{{p.age}}</li>
</ul>
<h2>汽车嘻嘻</h2>
<ul>
<li v-for="(value,key) of car" :key="key">{{key}}---{{value}}</li>
</ul>
<h2>遍历字符串</h2>
<ul>
<li v-for="(value,key) in str" :key="key">{{key}}---{{value}}</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: '崔浩然',
persons: [
{id: '1', name: '张三', age: 18},
{id: '2', name: '三大', age: 12},
{id: '3', name: '啊大', age: 13}
],
car: {
name: '奥迪a6',
price: '57w',
color: 'white'
},
str:'hello'
}
})
</script>
</html>
12.2key的原理
面试题:react、 vue中的key有什么作用?(key的内部原理)
1。虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
1.若虚拟DOM中内容没变,直接使用之前的真实DOM !
2.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM.
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。
3。用index作为key可能会引发的问题:
(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作;会产生没有必要的真实DOM更新==>界面效果没问题,但效率低。
(2)如果结构中还包含输入类的DOM:会产生错误DOM更新==>界面有问题。
4。开发中如何选择key?:
(1)最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
(2)t加果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用干渲染列表用于展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表渲染</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="p.key">{{p.name}}---{{p.age}}
<input type="text"></li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: '崔浩然',
persons: [
{id: '1', name: '张三', age: 18},
{id: '2', name: '三大', age: 12},
{id: '3', name: '啊大', age: 13}
]
},
methods:{
add(){
const p={id:'4',name:'老刘',age:23}
this.persons.unshift(p)
}
}
})
</script>
</html>
12.3列表过滤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表过滤</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyword">
<ul>
<li v-for="(p,index) of filPersons" :key="p.key">{{p.name}}---{{p.age}}--{{p.sex}}
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
keyword: "",
persons: [
{id: '1', name: '张三', age: 18, sex: '男'},
{id: '2', name: '三大', age: 12, sex: '男'},
{id: '3', name: '啊大', age: 13, sex: '男'},
{id: '4', name: '阿萨达', age: 13, sex: '男'},
{id: '5', name: '梵蒂冈', age: 13, sex: '男'}
],
// filPersons: [] 用watch实现时启用
},
//用watch是实现
// watch: {
// keyword: {//这里通过immediate让监视属性在页面加载时执行一次,再通过indexof("")的结果为全部使初始页面展示全部人的信息
// immediate:true,
// handler(val) {
// this.filPersons = this.persons.filter((p) => {
// return p.name.indexOf(val) !== -1
// })
//
// }
// }
// },
//
//用computed实现
computed:{
filPersons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyword)!== -1})
}
}
})
</script>
</html>
12.4列表排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表排序</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyword">
<button @click="sortType=2"> 年龄升序</button>
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=0">原顺序</button>
<ul>
<li v-for="(p,index) of filPersons" :key="p.id">{{p.name}}---{{p.age}}--{{p.sex}}
<input type="text">
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
keyword: "",
sortType:0, //0原序,1降序,2升序
persons: [
{id: '1', name: '张三', age: 18, sex: '男'},
{id: '2', name: '三大', age: 12, sex: '男'},
{id: '3', name: '啊大', age: 13, sex: '男'},
{id: '4', name: '阿萨达', age: 13, sex: '男'},
{id: '5', name: '梵蒂冈', age: 13, sex: '男'}
],
},
computed:{
filPersons(){
const arr = this.persons.filter((p)=>{
return p.name.indexOf(this.keyword)!== -1})
//判断一下是否需要排序
if(this.sortType){
arr.sort((p1,p2)=>{
return this.sortType === 1 ? p2.age-p1.age:p1.age-p2.age
})
}
return arr
}
}
})
</script>
</html>
12.5更新时的一个问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>更新时的</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<button @click="updateSan">更新信息张三</button>
<ul>
<li v-for="(p,index) of persons" :key="p.id">{{p.name}}---{{p.age}}--{{p.sex}} </li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
keyword: "",
sortType:0, //0原序,1降序,2升序
persons: [
{id: '1', name: '张三', age: 18, sex: '男'},
{id: '2', name: '三大', age: 12, sex: '男'},
{id: '3', name: '啊大', age: 13, sex: '男'},
{id: '4', name: '阿萨达', age: 13, sex: '男'},
{id: '5', name: '梵蒂冈', age: 13, sex: '男'}
]
},
methods:{
updateSan(){
this.person[0].name='马老师'//奏效
this.person[0].age=50//奏效
this.person[0].name='马老师'//奏效
this.persons[0]={id: '1', name: '张四', age: 18, sex: '男'}
}
}
})
</script>
</html>
12.6 Vue检测数据改变的原理_对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue监测数据改变的原理</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>学校名称{{name}}</h2>
<h2>学校地址{{address}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: "#root",
data: {
name:"宗棂",
address:"山西"
},
methods: {
updateSan() {
this.person[0].name = '马老师'//奏效
this.person[0].age = 50//奏效
this.person[0].name = '马老师'//奏效
this.persons[0] = {id: '1', name: '张四', age: 18, sex: '男'}
}
}
})
</script>
</html>
12.7模拟一个数据监测
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据监测</title>
</head>
<body>
<div id="root">
<h2>学校名称{{name}}</h2>
<h2>学校地址{{address}}</h2>
</div>
</body>
<script type="text/javascript">
let data={
name:"大会上的",
address:"大师等级"
}
//创建一个监视的实例对象,用于监测data中属性的变化
const obs=new Observer(data)
console.log(obs)
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>
</html>
12.8Vue中set的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据监测</title>
</head>
<body>
<div id="root">
<h2>学校名称{{name}}</h2>
<h2>学校地址{{address}}</h2>
<hr>
<h2>学生姓名:{{student.name}}</h2>
<h2>学生年龄:真实{{student.age.rage}},对外:{{{student.age.sage}}</h2>
<h2>朋友们</h2>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: "#root",
data: {
name:"宗棂",
address:"山西",
student:{
name:'tom',
age:{
rage:20,
sage:19
},
friends:[
{name:'安顺',age:32},
{name:'三大',age:43},
]
}
},
methods: {
updateSan() {
this.person[0].name = '马老师'//奏效
this.person[0].age = 50//奏效
this.person[0].name = '马老师'//奏效
this.persons[0] = {id: '1', name: '张四', age: 18, sex: '男'}
}
}
})
</script>
</html>
12.9Vue监测数据时改变的原理_数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据监测改变原理</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h2>学校信息</h2>
<h2>学校名称:{{school.name}}</h2>
<h2>学校地址{{school.address}}</h2>
<hr/>
<h1>学生信息</h1>
<button @click="updateSan">添加一个性别属性,默认值是男</button>
<h2>姓名:{{student.name}}</h2>
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<h2>年龄:真实{{student.age.rage}},对外:{{student.age.sage}}</h2>
<h2>爱好们</h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul><ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}--{{p .age}}--{{p.sex}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: "#root",
data: {
school:{
name:'齐大',
address:'黑龙江'
},
student:{
name:'崔浩然',
age:{
rage:40,
sage:29
},
friends:[
{name:"tom",age:23},
{name:"mary",age:18},
],
hobby:["抽烟","喝酒","烫头"],
},
persons: [
{id: '1', name: '张三', age: 18, sex: '男'},
{id: '2', name: '三大', age: 12, sex: '男'},
{id: '3', name: '啊大', age: 13, sex: '男'},
{id: '4', name: '阿萨达', age: 13, sex: '男'},
{id: '5', name: '梵蒂冈', age: 13, sex: '男'}
]
},
methods: {
updateSan() {
//0表示从第0个开始,1表示删除并插入一个,后面要插入的值
this.persons.splice(0, 1, {id: '1', name: "马老师", age: 50, sex: "男"})
}
}
})
</script>
</html>
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).重新解析模板,进而更新页面。在Vue修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort(),reverse()
(2)Vue.set()或vm. s e t ( ) 特 别 注 意 : V u e . s e t ( ) 和 v m . set() 特别注意:Vue.set()和 vm. set()特别注意:Vue.set()和vm.set()不能给vm或 vm的根数据对象添加属性!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据监测改变原理</title>
<script src="vue.js"></script>
</head>
<body>
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄增加一岁</button>
<button @click="addsex">添加一个性别属性,默认值是男</button>
<button @click="stundent.sex='未知'">修改性别</button>
<button @click="addfriend">在列表首位添加一个朋友</button>
<button @click="updateFirstfriendname">修改死一个朋友的名字为:张三</button>
<button @click="addHobby">添加一个爱好</button>
<button @click="updateHobby">修改第一个爱好为:开车</button>
<h2>姓名:{{student.name}}</h2>
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<h2>年龄:{{student.age}}</h2>
<h2>爱好们</h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul><ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}--{{p .age}}--{{p.sex}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: "#root",
data: {
student:{
name:'崔浩然',
age:23,
friends:[
{name:"tom",age:23},
{name:"mary",age:18},
],
hobby:["抽烟","喝酒","烫头"],
}
},
methods: {
addsex() {
//添加性别方法一
// Vue.set(this.student,'sex','男')
//第二种方法
this.$set(this.student,"sex","男")
},
addfriend(){
this.student.friends.unshift({name:"jack",age:34})
},
updateFirstfriendname(){
this.student.friends[0].name="张三"
},
addHobby(){
this.student.hobby.push("学习")
},
updateHobby(){
// this.studnet.hobby.splice(0,1,'开车') //方法一
// Vue.set(this.student.hobby,0,'开车') //方法二
this.$set(this.student.hobby,0,'开车') //方法三
}
}
})
</script>
</html>
13 表单提交
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue.js"></script>
</head>
<!--收集表单数据:-->
<!--若:<input type="text"/>,则v-model收集的是value值。用户输入的就是value值。若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。若:<input type="checkbox" />-->
<!--1.没有配置input的value属性,那么收集的就是checked(勾选or未勾选,是布尔值)2.配置input的value属性:-->
<!--(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or未勾选,是布尔值)(2)v-model的初始值是数组,那么收集的的就是value组成的数组-->
<!--备注:v-model的三个修饰符:-->
<!--lazy:失去焦点再收集数据-->
<!--number:输入字符串转为有效的数字trim:输入首尾空格过滤-->
<body>
<div id="root">
<h1>提交表单</h1>
<form @submit.prevent="demo">
账号: <input type="text" v-model.trim="account"><br><br> //trim可以去掉输入框前后的空格
密码: <input type="password" v-model="password"><br><br>
年龄: <input type="number" v-model.number="age"> <br> <br>
性别:
男: <input type="radio" name="sex" v-model="sex" value="男">
女: <input type="radio" name="sex" v-model="sex" value="女"><br><br>
爱好:
学习: <input type="checkbox" name="hobby" v-model="hobby" value="学习">
打游戏: <input type="checkbox" name="hobby" v-model="hobby" value="打游戏">
吃饭: <input type="checkbox" name="hobby" v-model="hobby" value="吃饭"><br><br>
所属校区:
<select v-model="city">
<option value="请选择校区">请选择校区</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="天津">天津</option>
</select>
<br><br>
其他信息:
<textarea v-model.lazy="other"></textarea> //lazy可以实现输入框在市区焦点的时候才收集
<br><br>
<input type="checkbox" v-model="agree">阅读并接受 <a href="https://www.baidu.com/">用户协议</a>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
account:'',
password:'',
age:"",
sex:'',
hobby:[],
city:"请选择校区",
other:'',
agree:''
},
methods:{
demo(){
console.log(JSON.stringify(this._data))
}
}
})
</script>
</html>
14过滤器
过滤器: 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。语法:
1.注册过滤器:Vue.filter(name,callback)或new Vue{filters:{}
2.使用过滤器:{{ xxx│过滤器名}}或v-bind:属性="xxx|过滤器名"备注:
- 过滤器也可以接收额外参数、多个过滤器也可以串联
- 并没有改变原本的数据,是产生新的对应的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>过滤器</title>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript" src="js/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h1>显示格式化后的时间</h1>
<!-- 计算属性实现-->
<h3>现在是:{{fmtTime}}</h3>
<!-- methods实现-->
<h3>现在是:{{getFmtTime()}}</h3>
<!-- 过滤器实现-->
<h3>现在是:{{time | timeFormater | mySlice}} </h3>
</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:1649243285810 //时间戳
},
methods:{
getFmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
computed:{
fmtTime(){
return dayjs(this.time).format("YYYY-MM-DD HH:mm:ss")
}
},
filters:{
timeFormater(value){
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
},
mySlice(value){
return value.slice(0,4)
}
}
})
</script>
</html>
15内置指令
我们学过的指令:
v-bind :单向绑定解析表达式,可简写为 :xxx
v-model :双向数据绑定
v-for :遍历数组/对象/字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-show:条件渲染(动态控制节点是否展示)
v-text指令:
1.作用:向其所在的节点中渲染文N内容。
2.与插值语法的区别: v-text会替换掉节点中的内容,{{xx}}则不会。
15.1 v_text指令
<!DOCTYPE html>
<html lang="en">
<head>
<!--
我们学过的指令:
v-bind :单向绑定解析表达式,可简写为 :xxx
v-model :双向数据绑定
v-for :遍历数组/对象/字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-show:条件渲染(动态控制节点是否展示)
v-text指令:
1.作用:向其所在的节点中渲染文N内容。
2.与插值语法的区别: v-text会替换掉节点中的内容,{{xx}}则不会。-->
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>你好,{{name}}</h1>
<h1 v-text="name">你好,</h1>
<h1 v-text="str"></h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: '崔浩然',
str:'<h3>你好啊</h3>'
},
methods:{
showInfo(e){
console.log(e.target.value)
}
}
})
</script>
</html>
15.2 v-html指令
v-html指令:
1.作用:向指定节点中渲染包含html结构的内容
2.与插值语法的区别:
(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
(2).v-html可以识别html结构。
3.严重注意:v-html有安全性问题!!!
(1)·在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-html指令</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<!--v-html指令:
1.作用:向指定节点中渲染包含html结构的内容
2.与插值语法的区别:
(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
(2).v-html可以识别html结构。
3.严重注意:v-html有安全性问题!!!!
(1)·在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
-->
<body>
<div id="root">
<h1>你好,{{name}}</h1>
<h1 v-html="name">你好,</h1>
<h1 v-html="str"></h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: '崔浩然',
str: '<h3>你好啊</h3>'
}
})
</script>
</html>
15.3 v-cloak指令
v-cloak指令(没有值):
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
- 使用css配合v-cloak可以解决网速慢时页面展示出{{xxXx]}的问题。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-cloak指令</title>
<style>
[v-cloak]{
display:none;
}
</style>
</head>
<body>
<div id="root">
<h1 v-cloak>你好,{{name}}</h1>
</div>
<script type="text/javascript" src="js/vue.js" ></script>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: '崔浩然',
str:'<h3>你好啊</h3>'
}
})
</script>
</html>
15.4 v-once指令
v-once指令:
- v-once所在节点在初次动态渲染后,就视为静态内容了。
- 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-once指令</title>
<style>
[v-cloak]{
display:none;
}
</style>
</head>
<body>
<div id="root">
<h2 v-once> 初始的n值是{{n}}</h2>
<h2> 当前的n值是{{n}}</h2>
<button @click="n++">n加一</button>
</div>
<script type="text/javascript" src="js/vue.js" ></script>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
n:1
}
})
</script>
</html>
15.5v-pre指令
v-pre指令:
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-once指令</title>
<style>
[v-cloak]{
display:none;
}
</style>
</head>
<body>
<div id="root">
<h2 v-pre>VUe简单</h2>
<h2 v-once> 初始的n值是{{n}}</h2>
<h2> 当前的n值是{{n}}</h2>
<button @click="n++">n加一</button>
</div>
<script type="text/javascript" src="js/vue.js" ></script>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
n:1
}
})
</script>
</html>
16自定义指令
<!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>自定义指令</title>
<script src="js/vue.js"></script>
</head>
<body>
<!--
需求:
1.定义一个v-big指令,和v-text功能相似,但是会把绑定的数值放大10倍。
2.定义一个v-fbind指令,和v-bind功能类似,但是可以让其绑定的input元素默认获取焦点。
-->
<!--
自定义指令总结:
1.定义语法:
1.局部指令:
new Vue({
diretives:{指令名:配置对象}
})
或者
new Vue({
diretives(){}
})
2.全局指令
Vue.diretive(指令名,配置对象) 或者 Vue.diretive(指令名,回调函数)
2.配置对象中常用的3个回调:
1.bind:指令与元素成功绑定时调用。
2.inserted:指令所在元素被插入页面时调用。
3.update:指令所在模板结构被重新解析时调用。
3.备注:
1.指令定义时不加v-,但是使用时加v-。
2.指令名如果是多个单词,要是用kebab-case命名方式,不要使用camelCase
-->
<div id="root">
<h2>当前的n值:<span v-text='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>
<script>
//定义全局指令与全局函数:
//全局函数
Vue.directive('big',function(element,binding){
element.innerText = binding .value * 10
console.log(binding)
})
//全局指令
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:{
n:1
},
directives:{
//big何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
// big(element,binding){
// element.innerText = binding.value * 10
// console.log(binding)
// },
// fbind:{
// //指令与元素成功绑定时(一上来)
// bind(element,binding){
// element.value = binding.value
// },
// //指令所在元素被插入元素时
// inserted(element,binding){
// element.focus()
// },
// //指令所在模板被重新解析时
// update(element,binding){
// element.value = binding.value
// }
// }
}
})
</script>
</body>
</html>
17生命周期
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关健时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm或组件实例对象。
17.1引出生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>引出生命周期</title>
</head>
<body>
<div id="root">
<h2 :style="{opacity}">VUe简单</h2>
</div>
<script type="text/javascript" src="js/vue.js"></script>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: "#root",
data: {
opacity: 1
},
methods:{
// change(){
// setInterval(()=>{
// this.opacity-=0.01
// if(this.opacity<=0)
// this.opacity=1
// },16)
// }
},
//vue完成模板的解析并把真实的DOM元素放入页面后调用mounted
mounted(){
setInterval(()=>{
this.opacity-=0.01
if(this.opacity<=0)
this.opacity=1
},16)
}
})
// setInterval(()=>{
// vm.opacity-=0.01
// if(vm.opacity<=0)
// vm.opacity=1
// },16)
</script>
</html>
17.2分析生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分析生命周期</title>
</head>
<body>
<div id="root">
<h2>当前n值为{{n}}</h2>
<button @click="add">n++</button>
<button @click="bye">点我销毁vm</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: "#root",
data: {
n: 1
},
methods: {
add() {
this.n++
},
bye(){
this.$destroy()
}
},
beforeCreate() {
console.log(' beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log(' beforeMount')
},
mounted() {
console.log(' mounted')
},
beforeUpdate() {
console.log(' beforeUpdate ')
},
updated(){
console.log('updated')
// debugger
},
beforeDestroy(){
console.log('beforeDestroy')
},
destroyed(){
console.log('destroyed')
}
})
</script>
</html>
17.3总结生命周期
18非单文件组件
Vue中使用组件的三大步骤:
1.定义组件(创建组件)
2.注册组件
3.使用组件(写组件标签)
1.如何定义一个组件
1.使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但是也有一点区别:
区别如下:
1.el不要写,why?最终所有的组件都需要经过一个vm管理,由vm中的el决定服务哪个容器。
2.data必须写成函数,why?避免组件被重复使用,数据存在引用关系。
备注:使用template可以配置组件结构
2.如何注册组件?
1.局部注册:靠new Vue()的时候传入components选项
2.全局注册:靠Vue.component('组件名',组件)
3.编写组件标签
<school></school>
18.1基本使用
<!DOCTYPE html>
<html lang="en">
<head>
-->
<meta charset="UTF-8">
<title>非单文件组件</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 第三部编写组件标签-->
<hello></hello>
<school></school>
<student></student>
</div>
<div id="root2"><school></school></div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//第一步:创建school组件
const school = Vue.extend({
// el: "#root", //一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
template: `
<div>
<h1>学校名称:{{ schoolName }}</h1>
<h1>学校地址:{{ address }}</h1>
<hr>
</div>
`,
data() {
return {
schoolName: '齐大',
address: '齐齐哈尔',
}
}
})
jad
const student = Vue.extend({
template: `
<div>
<h1>学生名称:{{ studentName }}</h1>
<h1>学生年龄:{{ age }}</h1>
<hr>
</div>
`,
data() {
return {
studentName: '崔浩然',
age: 21
}
}
})
const hello=Vue.extend({
template:`
<div>你好{{name}}</div>`,
data(){
return{
name:'Tom'
}
}
})
Vue.component("hello",hello)
new Vue({
el: '#root',
//第二步:注册组件(局部注册)
components: {
school: school,
student: student
}
})
new Vue({
el: '#root2',
//第二步:注册组件(局部注册)
components: {
school: school,
student: student
}
})
function data() {
return {
a: 1,
b: 2
}
}
const x1 = data()
const x2 = data()
// new Vue({
// el: "#root",
// data: {
// schoolName: '齐大',
// address: '齐齐哈尔',
// studentName: '崔浩然',
// age: 21
// },
// methods: {
// showInfo(e) {
// console.log(e.target.value)
// }
// }
// })
</script>
</html>
18.2几个注意点
几个注意点:
1.关于组件名:
一个单词组成:
第一种写法:首字母小写,school。
第二种写法:首字母大写,School。
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):需要Vue脚手架支持。
备注:
1.组件名尽可能回避HTML中已有的元素名称,例如,H2,h2都不行。
2.可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关羽组件标签:
1.第一种写法:<school></school>
2.第二种写法:<school/>
备注:不适用脚手架时,<school/>会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options) 可以简写为: const school = options
<!DOCTYPE html>
<html lang="en">
<head>
-->
<meta charset="UTF-8">
<title>非单文件组件</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h1> {{msg}}</h1>
<school1></school1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//第一步:创建school组件
const school = Vue.extend({
template: `
<div>
<h1>学校名称:{{ schoolName }}</h1>
<h1>学校地址:{{ address }}</h1>
<hr>
</div>
`,
data() {
return {
schoolName: '齐大',
address: '齐齐哈尔',
}
}
})
new Vue({
el:'#root',
data:{
msg:'欢迎学习vue'
},
components:{
school1:school
}
})
</script>
</html>
18.3组件嵌套
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件的嵌套</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h1> {{msg}}</h1>
<school1></school1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const student = Vue.extend({
template: `
<div>
<h1>学生名称:{{ studentName }}</h1>
<h1>学生年龄:{{ age }}</h1>
<hr>
</div>
`,
data() {
return {
studentName: '崔浩然',
age: 21
}
}
})
const school = Vue.extend({
template: `
<div>
<h1>学校名称:{{ schoolName }}</h1>
<h1>学校地址:{{ address }}</h1>
<hr>
<student></student>
</div>
`,
data() {
return {
schoolName: '齐大',
address: '齐齐哈尔',
}
},
components:{
student
}
})
new Vue({
el:'#root',
data:{
msg:'欢迎学习vue'
},
components:{
school1:school
}
})
</script>
</html>
18.4VueComponents
关于VueComponent:
- school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
- 我们只需要写成或者,Vue解析时会帮我们创建school组件的实例对象。 即Vue帮助我们执行的:new VueComponent(options)。
- 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent。
- 关于this的指向:
1.在组件配置中:
data函数,methods中的函数,watch中的函数,computed中的函数 它们的this均是{VueComponent实例对象}。
2.new Vue()配置中:
data函数,methods中的函数,watch中的函数,computed中的函数,它们的this均是{Vue实例对象} - VueComponent的实例对象,以后简称vc(也可以称为:组件实例对象)
Vue的实例对象,以后简称vm
18.5 一个重要的内置关系
VueComponent.prototype.proto === Vue.prototype
Vue和VueComponent的关系
<!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>一个重要的内置关系</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--
1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
2.为什么要有这个关系:让组件实例对象(vc)可以访问到Vue原型上的属性,方法。
-->
<div id="root"></div>
<script>
//定义一个构造函数
function Demo(){
this.a = 1
this.b = 2
}
//创建一个Demo实例对象
const d = new Demo()
console.log(Demo.prototype)//显式原型属性
console.log(Demo.__proto__)//隐式原型属性
new Vue({
el:'#root',
})
</script>
</body>
</html>
19单文件组件
app.vue
app.vue:它是所有组件的父组件
<template>
<School></School>
<Student></Student>
</template>
<script>
import School from "./School.vue";
import Student from "./Student.vue";
export default {
name: "App",
components:{
School,
Student
}
}
</script>
<style scoped>
</style>
main.js
它是整个vue程序的入口
import App from "./App.vue";
new Vue({
el: "root",
template: `<App></App>`,
components: {
App
},
})
其他子组件
school组件
<template>
//组件结构
<div class="demo">
<h1>学校名称:{{ schoolName }}</h1>
<h1>学校地址:{{ address }}</h1>
<button @click="showName">点我提示学校名</button>
<hr>
</div>
</template>
<script>
export default {
name: "School",
data() {
return {
schoolName: '齐大',
address: '齐齐哈尔'
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
}
</script>
<style scoped>
.demo{
background:cadetblue;
}
</style>
student组件
<template>
<div>
<h1>学生名称:{{ name }}</h1>
<h1>学生年龄:{{ age }}</h1>
<hr>
</div>
</template>
<script>
export default {
name: "Student",
data(){
return {
name:"崔浩然",
age:21
}
}
}
</script>
<style scoped>
</style>
html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>单文件组件</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
20脚手架的使用
20.1初始化脚手架
20.1.1说明
- Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)
- 最新的版本是 4.x
- 文档:Vue CLI
20.1.2具体步骤
- 如果下载缓慢请配置 npm 淘宝镜像:npm config set registry http://registry.npm.taobao.org
- 全局安装@vue/cli:npm install -g @vue/cli
- 切换到你要创建项目的目录,然后使用命令创建项目:vue create xxxx
- 选择使用vue的版本
- 启动项目:npm run serve
- 暂停项目:Ctrl+C
查看vue的配置需要在控制台输入vue inspect > output.js
20.1.3脚手架的结构
脚手架结构文件:
.文件目录
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ └── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
└── package-lock.json: 包版本控制文件
关于不同版本的vue:
-vue.js与vue.runtime.xxx.js的区别:
(1).vue.js是完整版的vue,包含:核心功能+模板解析器
(2).vue.runtime.xxx.js是运行版的vue,只包含:核心功能,没有模板解析器.
-因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用
render函数接收到的createElement函数去指定具体内容
20.2ref 与 props
=====ref:
1.作用:用于给节点打标识
2.读取方式:this.$refs.xxxxxx
====props
1.作用:用于父组件给子组件传递数据
2.读取方式一: 只指定名称
props: [‘name’, ‘age’, ‘setName’]
3.读取方式二: 指定名称和类型
props: {
name: String, age: Number,
setNmae: Function
}
4.读取方式三: 指定名称/类型/必要性/默认值
props: {
name: {type: String, required: true, default:xxx},
}
20.3混入
1.Vue 插件是一个包含install 方法的对象
2.通过install 方法给Vue 或Vue 实例添加方法, 定义全局指令等
20.4插件
1.Vue 插件是一个包含install 方法的对象
2.通过install 方法给Vue 或Vue 实例添加方法, 定义全局指令等
20.5Todo-list 案例
组件化编码流程(通用)
1.实现静态组件:抽取组件,使用组件实现静态页面效果
2.展示动态数据:
2.1.数据的类型、名称是什么?
2.2.数据保存在哪个组件? 3.交互——从绑定事件监听开始
20.6Vue 中的自定义事件
绑定事件监听
触发事件
20.7全局事件总线
20.7.1理解
1.Vue 原型对象上包含事件处理的方法
1)$on(eventName, listener): 绑定自定义事件监听
2)$emit(eventName, data): 分发自定义事件
3)$off(eventName): 解绑自定义事件监听
4)$once(eventName, listener): 绑定事件监听, 但只能处理一次
2.所有组件实例对象的原型对象的原型对象就是Vue 的原型对象
1)所有组件对象都能看到Vue 原型对象上的属性和方法
2)Vue.prototype.
b
u
s
=
n
e
w
V
u
e
(
)
,
所
有
的
组
件
对
象
都
能
看
到
bus = new Vue(), 所有的组件对象都能看到
bus=newVue(),所有的组件对象都能看到bus 这个属性对象
3.全局事件总线
1)包含事件处理相关方法的对象(只有一个)
2)所有的组件都可以得到
3.7.2指定事件总线对象
3.7.3绑定事件
3.7.4分发事件
3.7.5解绑事件
3.8消息订阅与发布
3.8.1理解
1.这种方式的思想与全局事件总线很相似
2.它包含以下操作:
(1)订阅消息 --对应绑定事件监听
(2)发布消息 --分发事件
(3)取消消息订阅 --解绑事件监听
3.需要引入一个消息订阅与发布的第三方实现库: PubSubJS
3.8.2使用PubSubJS
1.在线文档: https://github.com/mroderick/PubSubJS
2.下载: npm install -S pubsub-js
3.相关语法
(1)import PubSub from ‘pubsub-js’ // 引 入
(2)PubSub.subscribe(‘msgName’, functon(msgName, data){ })
(3)PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用
(4)PubSub.unsubscribe(token): 取消消息的订阅
3.9过度与动画
3.9.1效果
3.9.2vue 动画的理解
1.操作css 的trasition 或animation
2.vue 会给目标元素添加/移除特定的class
3.过渡的相关类名:
1.xxx-enter-active: 指定显示的transition
2.xxx-leave-active: 指定隐藏的transition
3.xxx-enter/xxx-leave-to: 指定隐藏时的样式
3.9.3基本过渡动画的编码
1.在目标元素外包裹
2.定义class 样式
a)指定过渡样式: transition
b)指定隐藏时的样式: opacity/其它
第 4 章:Vue 中的 ajax
4.1解决开发环境 Ajax 跨域问题
使用代理服务器
4.2github 用户搜索案例
4.2.1效果
4.2.2接口地址
https://api.github.com/search/users?q=xxx
4.3vue 项目中常用的 2 个 Ajax 库
4.3.1axios
通用的Ajax 请求库, 官方推荐,使用广泛
4.3.2vue-resource
vue 插件库, vue1.x 使用广泛,官方已不维护。
4.4slot 插槽
4.4.1效果
效果一(不使用插槽):
效果二(默认插槽):
效果三(具名插槽):
效果三(作用域插槽):
4.4.1理解
父组件向子组件传递带数据的标签,当一个组件有不确定的结构时, 就需要使用slot 技术,注意:插槽内容是在父组件中编译后, 再传递给子组件的。
4.4.2分类
1.默认插槽
2.命名插槽
3.作用域插槽
第 5 章:vuex
5.1理解 vuex
5.1.1vuex 是什么
1.概念:专门在Vue 中实现集中式状态(数据)管理的一个Vue 插件,对vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
2.Github 地址: https://github.com/vuejs/vuex
5.1.2什么时候使用 Vuex
1.多个组件依赖于同一状态
2.来自不同组件的行为需要变更同一状态
5.1.3案例
5.1.4Vuex 工作原理图
5.2vuex 核心概念和 API
5.2.1state
1.vuex 管理的状态对象
2.它应该是唯一的
3.示例代码:
5.2.2actions
1.值为一个对象,包含多个响应用户动作的回调函数
2.通过commit( )来触发mutation 中函数的调用, 间接更新state
3.如何触发actions 中的回调?
在组件中使用: $store.dispatch(‘对应的 action 回调名’) 触发
4.可以包含异步代码(定时器, ajax 等等)
5.示例代码:
5.2.3mutations
1.值是一个对象,包含多个直接更新state 的方法
2.谁能调用mutations 中的方法?如何调用?
在action 中使用:commit(‘对应的 mutations 方法名’) 触发
3.mutations 中方法的特点:不能写异步代码、只能单纯的操作state
4.示例代码:
5.2.4getters
1.值为一个对象,包含多个用于返回数据的函数
2.如何使用?—— $store.getters.xxx
3.示例代码:
5.2.5modules
1.包含多个module
2.一个module 是一个store 的配置对象
3.与一个组件(包含有共享数据)对应
第 6 章:vue-router
6.1相关理解
6.1.1vue-router 的理解
vue 的一个插件库,专门用来实现SPA 应用
6.1.2对SPA 应用的理解
1.单页Web 应用(single page web application,SPA)。
2.整个应用只有一个完整的页面。
3.点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
4.数据需要通过ajax 请求获取。
6.1.3路由的理解
1.什么是路由?
1.一个路由就是一组映射关系(key - value)
2.key 为路径, value 可能是function 或component
2.路由分类
1.后端路由:
1)理解:value 是function, 用于处理客户端提交的请求。
2)工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。
2.前端路由:
1)理解:value 是component,用于展示页面内容。
2)工作过程:当浏览器的路径改变时, 对应的组件就会显示。
6.2基本路由
6.2.1效果
6.2.2总结: 编写使用路由的 3 步
1.定义路由组件
2.注册路由
3.使用路由
6.3嵌套(多级)路由
6.4路由传参
6.5编程式路由导航
相关 API:
1.this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
2.this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
3.this.$router.back(): 请求(返回)上一个记录路由
4.this.$router.go(-1): 请求(返回)上一个记录路由
5.this.$router.go(1): 请求下一个记录路由
第 7 章:Vue UI 组件库
7.1移动端常用UI 组件库
1.Vant https://youzan.github.io/vant
2.Cube UI https://didi.github.io/cube-ui
3.Mint UI http://mint-ui.github.io
7.2PC 端常用UI 组件库
1.Element UI https://element.eleme.cn
2.IView UI https://www.iviewui.com
Vue3快速上手
![](https://user-images.githubusercontent.com/499550/93624428-53932780-f9ae-11ea-8d16-af949e16a09f.png)
1.Vue3简介
- 2020年9月18日,Vue.js发布3.0版本,代号:One Piece(海贼王)
- 耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者
- github上的tags地址:https://github.com/vuejs/vue-next/releases/tag/v3.0.0
2.Vue3带来了什么
1.性能的提升
-
打包大小减少41%
-
初次渲染快55%, 更新渲染快133%
-
内存减少54%
…
2.源码的升级
-
使用Proxy代替defineProperty实现响应式
-
重写虚拟DOM的实现和Tree-Shaking
…
3.拥抱TypeScript
- Vue3可以更好的支持TypeScript
4.新的特性
-
Composition API(组合API)
- setup配置
- ref与reactive
- watch与watchEffect
- provide与inject
- …
-
新的内置组件
- Fragment
- Teleport
- Suspense
-
其他改变
- 新的生命周期钩子
- data 选项应始终被声明为一个函数
- 移除keyCode支持作为 v-on 的修饰符
- …
一、创建Vue3.0工程
1.使用 vue-cli 创建
官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve
2.使用 vite 创建
官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite
vite官网:https://vitejs.cn
- 什么是vite?—— 新一代前端构建工具。
- 优势如下:
- 开发环境中,无需打包操作,可快速的冷启动。
- 轻量快速的热重载(HMR)。
- 真正的按需编译,不再等待整个应用编译完成。
- 传统构建 与 vite构建对比图
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
二、常用 Composition API
官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html
1.拉开序幕的setup
- 理解:Vue3.0中一个新的配置项,值为一个函数。
- setup是所有Composition API(组合API)“ 表演的舞台 ”。
- 组件中所用到的:数据、方法等等,均要配置在setup中。
- setup函数的两种返回值:
- 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
- 若返回一个渲染函数:则可以自定义渲染内容。(了解)
- 注意点:
- 尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
- 但在setup中不能访问到Vue2.x配置(data、methos、computed…)。
- 如果有重名, setup优先。
- setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
- 尽量不要与Vue2.x配置混用
2.ref函数
- 作用: 定义一个响应式的数据
- 语法:
const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
- JS中操作数据:
xxx.value
- 模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
- 备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的。 - 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive
函数。
3.reactive函数
- 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用
ref
函数) - 语法:
const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象) - reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
4.Vue3.0中的响应式原理
vue2.x的响应式
-
实现原理:
-
对象类型:通过
Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。 -
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', { get () {}, set () {} })
-
-
存在问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
Vue3.0的响应式
-
实现原理:
-
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
-
通过Reflect(反射): 对源对象的属性进行操作。
-
MDN文档中描述的Proxy与Reflect:
-
Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
-
Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
new Proxy(data, { // 拦截读取属性值 get (target, prop) { return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 拦截删除属性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) } }) proxy.name = 'tom'
-
-
5.reactive对比ref
- 从定义数据角度对比:
- ref用来定义:基本类型数据。
- reactive用来定义:对象(或数组)类型数据。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive
转为代理对象。
- 从原理角度对比:
- ref通过
Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。 - reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- ref通过
- 从使用角度对比:
- ref定义的数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
。 - reactive定义的数据:操作数据与读取数据:均不需要
.value
。
- ref定义的数据:操作数据需要
6.setup的两个注意点
-
setup执行的时机
- 在beforeCreate之前执行一次,this是undefined。
-
setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 - slots: 收到的插槽内容, 相当于
this.$slots
。 - emit: 分发自定义事件的函数, 相当于
this.$emit
。
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
7.计算属性与监视
1.computed函数
-
与Vue2.x中computed配置功能一致
-
写法
import {computed} from 'vue' setup(){ ... //计算属性——简写 let fullName = computed(()=>{ return person.firstName + '-' + person.lastName }) //计算属性——完整 let fullName = computed({ get(){ return person.firstName + '-' + person.lastName }, set(value){ const nameArr = value.split('-') person.firstName = nameArr[0] person.lastName = nameArr[1] } }) }
2.watch函数
-
与Vue2.x中watch配置功能一致
-
两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
//情况一:监视ref定义的响应式数据 watch(sum,(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue) },{immediate:true}) //情况二:监视多个ref定义的响应式数据 watch([sum,msg],(newValue,oldValue)=>{ console.log('sum或msg变化了',newValue,oldValue) }) /* 情况三:监视reactive定义的响应式数据 若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!! 若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 */ watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{immediate:true,deep:false}) //此处的deep配置不再奏效 //情况四:监视reactive定义的响应式数据中的某个属性 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true}) //情况五:监视reactive定义的响应式数据中的某些属性 watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true}) //特殊情况 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
3.watchEffect函数
-
watch的套路是:既要指明监视的属性,也要指明监视的回调。
-
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。 watchEffect(()=>{ const x1 = sum.value const x2 = person.age console.log('watchEffect配置的回调执行了') })
8.生命周期
![lifecycle_2](https://cn.vuejs.org/images/lifecycle.png)
1
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy
改名为beforeUnmount
destroyed
改名为unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
9.自定义hook函数
-
什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
-
类似于vue2.x中的mixin。
-
自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
10.toRef
-
作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
-
语法:
const name = toRef(person,'name')
-
应用: 要将响应式对象中的某个属性单独提供给外部使用时。
-
扩展:
toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
三、其它 Composition API
1.shallowReactive 与 shallowRef
-
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
-
shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
-
什么时候使用?
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
2.readonly 与 shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
3.toRaw 与 markRaw
- toRaw:
- 作用:将一个由
reactive
生成的响应式对象转为普通对象。 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- 作用:将一个由
- markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
4.customRef
-
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
-
实现防抖效果:
<template> <input type="text" v-model="keyword"> <h3>{{keyword}}</h3> </template> <script> import {ref,customRef} from 'vue' export default { name:'Demo', setup(){ // let keyword = ref('hello') //使用Vue准备好的内置ref //自定义一个myRef function myRef(value,delay){ let timer //通过customRef去实现自定义 return customRef((track,trigger)=>{ return{ get(){ track() //告诉Vue这个value值是需要被“追踪”的 return value }, set(newValue){ clearTimeout(timer) timer = setTimeout(()=>{ value = newValue trigger() //告诉Vue去更新界面 },delay) } } }) } let keyword = myRef('hello',500) //使用程序员自定义的ref return { keyword } } } </script>
5.provide 与 inject
![](https://v3.cn.vuejs.org/images/components_provide.png)
-
作用:实现祖与后代组件间通信
-
套路:父组件有一个
provide
选项来提供数据,后代组件有一个inject
选项来开始使用这些数据 -
具体写法:
-
祖组件中:
setup(){ ...... let car = reactive({name:'奔驰',price:'40万'}) provide('car',car) ...... }
-
后代组件中:
setup(props,context){ ...... const car = inject('car') return {car} ...... }
-
6.响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由
reactive
创建的响应式代理 - isReadonly: 检查一个对象是否是由
readonly
创建的只读代理 - isProxy: 检查一个对象是否是由
reactive
或者readonly
方法创建的代理
四、Composition API 的优势
1.Options API 存在的问题
使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
2.Composition API 的优势
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。
五、新的组件
1.Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
2.Teleport
-
什么是Teleport?——
Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。<teleport to="移动位置"> <div v-if="isShow" class="mask"> <div class="dialog"> <h3>我是一个弹窗</h3> <button @click="isShow = false">关闭弹窗</button> </div> </div> </teleport>
3.Suspense
-
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
-
使用步骤:
-
异步引入组件
import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
-
使用
Suspense
包裹组件,并配置好default
与fallback
<template> <div class="app"> <h3>我是App组件</h3> <Suspense> <template v-slot:default> <Child/> </template> <template v-slot:fallback> <h3>加载中.....</h3> </template> </Suspense> </div> </template>
-
六、其他
1.全局API的转移
-
Vue 2.x 有许多全局 API 和配置。
-
例如:注册全局组件、注册全局指令等。
//注册全局组件 Vue.component('MyButton', { data: () => ({ count: 0 }), template: '<button @click="count++">Clicked {{ count }} times.</button>' }) //注册全局指令 Vue.directive('focus', { inserted: el => el.focus() }
-
-
Vue3.0中对这些API做出了调整:
-
将全局的API,即:
Vue.xxx
调整到应用实例(app
)上2.x 全局 API( Vue
)3.x 实例 API ( app
)Vue.config.xxxx app.config.xxxx Vue.config.productionTip 移除 Vue.component app.component Vue.directive app.directive Vue.mixin app.mixin Vue.use app.use Vue.prototype app.config.globalProperties
-
2.其他改变
-
data选项应始终被声明为一个函数。
-
过度类名的更改:
-
Vue2.x写法
.v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; }
-
Vue3.x写法
.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }
-
-
移除keyCode作为 v-on 的修饰符,同时也不再支持
config.keyCodes
-
移除
v-on.native
修饰符-
父组件中绑定事件
<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
-
子组件中声明自定义事件
<script> export default { emits: ['close'] } </script>
-
-
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
-
…