vue基础
vue 是一个MVVM框架,model-view-viewModel,是一个MVC的前端版本
viewModel是一个控制器的角色,在他里面实现逻辑运算等,这样就可以把Model层和View层高度分离
View:视图,用于封装和展示数据以及效果,其实就是html结构
Model层:数据模型,用于封装数据以及数据运行的业务逻辑
----使用vue的方法
1.通过script 引入vue文件
2.可以通过vue-cli 创建vue项目,进行开发
----解构出来一个创建应用的方法
const {createApp}=Vue;
----创建应用
createApp({
// 设置数据
// data函数中存放当前页面中所有的数据,如果需要声明数据,必须声明在data中
// data是一个方法,返回值是一个对象,data必须有返回值,否则报错
data(){
return{
str:'创建应用'
}
}
}).mount('#app');
.mount(‘#app’); 把应用挂载到 id=app 的div上
插值表达式 {{ }}
{{ }} :插值表达式,在他的里面可以执行vue的变量,表达式,也可以执行函数
插值表达式实际上是一个js的域,在插值表达式中可以执行简单的js语句,并且把执行结果返回到插值表达式所在dom结构的位置上
----插值表达式的缺点:初次加载的时候文本闪烁
解决方法
1.把vue文件引入到head中,不建议使用
2.使用指令 v-cloak 来隐藏数据没有加载完成的dom结构,这样可以解决页面开始时的闪烁问题。该指令的运行方式是,首先隐藏没有编译成功的dom结构,等到dom编译成功之后,会自动把隐藏的方式去掉
<p v-cloak>{{ 1+1 }}</p>
{{}}插值表达式不能直接绑定属性的值
也可以使用指令 v-html 、v-text 绑定数据
属性绑定
在vue中,可以使用 v-bind来绑定属性
语法是:
v-bind:属性名=‘属性值/变量名’
简写方式
:属性名=‘属性值/变量名’
绑定style和class
----绑定style
<h2 :style="{ color:'#04be02',fontSize:'30px'}">{{str}}</h2>
//以下:style="style" 引号中的style需要在data中声明
<div :style="style"></div>
<div :style="'font-size:20px;color:#f00;'">人之初性本善</div>
<div :style="[ {color:'#04be02'},{fontSize:'30px'} ]">多行不义必自毙</div>
----绑定class
<div class="abc">柳宗元</div>
<div :class="'abc aa'">曾巩</div>
<!-- 使用数组形式绑定class -->
<div :class="['abc','aa']">韩愈</div>
<!-- 使用对象形式绑定class,属性名为class名,属性的值是一个bol值,如果bol值为true,则绑定该class名,否则不绑定 -->
<div :class="{'abc':3>4,'aa':3<4}">苏轼</div>
总结:style和class的绑定都支持数组形式和对象形式
事件绑定
绑定事件使用
v-on:事件名=“表达式/方法名”
方法名的参数问题,如果没有参数,可以不用加()
事件绑定指令的简写
@事件名=“表达式/方法名”
<button v-on:click="num++">点击++</button>
----事件的修饰符
vue提供了事件修饰符,用来提高开发效率
@事件名.修饰符.修饰符.修饰符…=触发方法
stop 阻止事件冒泡
prevent 阻止默认事件
once 只触发一次
self 当event.target是当前绑定元素的时候触发
capture 事件捕获的时候触发
passive 触发滚动事件的默认行为
----按键的修饰符
按键修饰符,当按下或者抬起对应修饰符的按键时触发事件,
.enter
.tab
.delete (捕获“Delete”和“Backspace”两个按键)
.esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
.exact 非系统键
数据的双向绑定
在表单元素中,使用v-model实现数据的双向绑定
双向绑定:简言之就是数据在视图层的更改,会立即体现在model层,反之在model层的改变也会立即体现在视图层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据的双向绑定</title>
</head>
<body>
<div id="app">
<input type="text" v-model="val"/>
<p>{{val2}}</p>
</div>
</body>
</html>
<script src="../vue.js"></script>
<script>
createApp({
data(){
return{
val:'人之初性本善',
}
}
}).mount('#app');
</script>
当用户在表单元素中输入内容时,v-model 会自动更新绑定的数据。
数据的变化也会反映在表单元素上。
常用指令
v-show 指令,如果他的值时true,则他所绑定的元素显示,如果为false,他所绑定的元素会自动添加一个display:none,元素隐藏,v-show改变的是css样式,属于元素重绘
v-if 指令,如果他的值是true,则他所绑定的元素从dom树对应的位置渲染,如果为false,所绑定的元素从dom树中删除,v-if改变的是dom树的结构,属于元素重排
v-else 需要结合v-if使用,用法同js的if-else
v-else-if 需要结合v-if使用,用法同js的if-else if
v-for 结合 template 标签(组件)循环数据(template 标签不会被渲染到页面上,一般用来实现数据绑定的功能)
v-for=“形参 in 数组/对象”
如果需要下标或者对象的属性名 v-for=“(形参,下标形参/属性名形参) in 数组/对象”
v-for的key属性
使用v-for的标签元素,都需要绑定一个key属性,属性的值一般都是要循环对象中的唯一值,例如id,
使用key属性的目的是,使已经渲染过的列表内容不再重新渲染,提高列表的渲染效率,注意index虽然可以设置key值,但是由于index的值会发生变化,这样会导致列表重新渲染,因此慎用
v-pre 他所绑定的元素不进行vue的编译,原样输出
v-once 他所绑定的元素只渲染一次,忽略之后所有的更新
v-memo 实现组件内容的缓存,如果他所绑定的值不发生改变,那么他绑定的元素内部不会根据数据的改变而重新渲染
自定义指令
createApp({
data(){
return{}
},
// 自定义指令
directives:{
/*
aa 是指令名,
参数
el 表示的是自定义指令绑定的元素
binding 是一个对象,包含的属性
value 表示的是指令的值
name 表示的是指令的名字
oldValue 指令绑定的数据或者元素内部发生改变的时候,改变前的值仅在beforeUpdate和updated钩子函数中有值
*/
aa(el,binding){
console.log(el,binding);
},
// 配置指令
abc:{
created(el,binding){
console.log('created',el,binding);
},
mounted(el,binding){
},
updated(el,binding){
console.log('更新了',binding.oldValue);
}
}
}
}).mount('#app');
计算属性
如果在插值表达式中直接渲染数据,非常方便,但是如果有比较复杂的逻辑运算,插值表达式的渲染效率就会受到影响,并且不利于后期维护,因此可以把比较复杂的运算逻辑放到计算属性中
----计算属性的优点:
1.数据没有发生变化的时候,优先读取缓存在computed中经过逻辑运算操作的数据,把数据渲染在dom树中,并且不用考虑methods和watch中数据的变化
计算属性值会基于其响应式依赖被缓存
2.他有get方法和set方法,可以进行灵活设置
get 在获取的时候被触发,如果没有set,则默认是get
set 在数据设置的时候被触发
<script>
createApp({
data(){
return{}
},
computed:{
changeStr(){
return this.str.split('').reverse().join('');
},
numArr(){
return this.arr.filter(item=>{
return item%2==0
});
},
}
}).mount('#app');
</script>
每一个计算属性,实质上都包含get和set两个方法,默认是get方法
在计算属性的get方法中,不要异步请求数据(ajax),或者操作dom
给计算属性设置值的时候,执行的是set方法
从计算属性获取值的时候,执行的是get方法
侦听器
如果需要根据数据的变化来执行操作,可以使用数据侦听器 watch
<script>
createApp({
data(){
return{}
},
/*
书写侦听器的方法,方法名就是侦听器侦听data数据中数据的属性名,该方法具有两个参数
第一个参数是数据当前的值(新值),第二个参数是数据变化之前的值(旧值)
*/
watch:{
val(newVal,oldVal){
console.log(newVal,oldVal);
},
// 设置侦听器的深度监听
obj:{
// 设置监听的回调方法
handler(newVal,oldVal){
console.log(newVal,oldVal);
},
// 设置立即监听
deep:true,
// 设置加载立即监听
immediate:true
},
}
}).mount('#app');
</script>
也可以设置外部的侦听器
// 可以设置外部的侦听器
app.$watch('name',(newVal,oldVal)=>{
console.log(newVal,oldVal);
});
生命周期
vue的生命周期主要经历了如下几个阶段,分别是 beforeCreate,created,beforeMount,mounted,beforeUpdate,update,beforeUnmount,unmounted ,主要由这八个阶段组成。
let app=createApp({
data(){
return{
str:'中午吃啥',
num:100,
arr:['郑州大学','西亚斯学院','郑州科技学院']
}
},
// 在组件实例初始化完成之后立即调用。此刻其他的属性和方法均没有执行或者编译。当这个钩子被调用时,以下内容已经设置完成:响应式数据(data函数)、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。
beforeCreate() {
console.log(this.str);
},
/*
在组件实例处理完所有与状态相关的选项后调用。当这个钩子被调用时,以下内容已经设置完成:响应式数据(data函数)、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。此时不能操作dom
*/
created(){
console.log('created',this.str);
},
/*
vue的组件模板,如果在实例中设置了有组件的模板,则会替换掉挂载的dom元素,并且模板的第一层需要是一个完整的dom元素(vue2版本)
*/
// template:`
// <h3>{{num}}</h3>
// <h2>{{str}}</h2>
// `
/*
在组件被挂载之前调用。此刻还没有挂载任何模板,因此还无法操作dom
*/
beforeMount(){
console.log('beforeMount');
let abc=document.querySelector('#abc');
console.log(abc);
this.num+=100;
},
/*
在组件被挂载之后调用。此时所有的模板内容都已经被挂载,可以操作组件中所有的dom
常用此钩子函数来进行 加载立即执行的操作
*/
mounted(){
console.log('mounted');
this.show();
},
methods:{
show(){
let abc=document.querySelector('#abc');
console.log(abc);
abc.style.border='1px #f00 solid'
},
add(){
this.arr.push('河南建筑职业技术学院');
},
die(){
// 卸载vue实例
app.unmount();
}
},
/*
beforeUpdate钩子 数据更新前触发,显示的数据是更新后的数据
但是如果改变了dom结构,那么该函数中的dom结构是更新前的内容
*/
beforeUpdate() {
let list=document.getElementById('list');
console.log('beforeUpdate',this.num,list.innerHTML);
},
/*
updated钩子 数据更新后触发,显示的数据是更新后的数据
但是如果改变了dom结构,那么该函数中的dom结构是更新后的内容
*/
updated() {
let list=document.getElementById('list');
console.log('updated',this.num,list.innerHTML);
},
// 组件卸载之前触发
beforeUnmount(){
console.log('beforeUnmount');
},
// 组件卸载之后触发
unmounted(){
console.log('unmounted');
}
});
// 挂载
app.mount('#app');
组件基础
组件,将来项目开发的时候使用的都是组件,组件具有极高的复用性
----注册组件
外部组件关键字是 component,在实例内部注册组件,属性名为 components
app.component(组件名,组建的配置)
1.命名不能和原生html冲突
2.可以使用驼峰,使用驼峰的时候,在视图模板中书写,驼峰的大写字母可以变为段横杠-,例如abCd,使用的时候写为
3.推荐使用 w3c的命名规则:aa-bb
createApp({
data(){},
components:{
// 驼峰,使用的时候需要my-show
'myShow':{
// 配置模板
template:'<p class="abc">最喜小儿无赖-{{name}}</p>',
data(){
return{
name:'孙仲谋'
}
}
},
// w3c 推荐
'my-run':{
template:'#run',
data(){
return{
name:'原神'
}
}
}
}
})
组件外部配置
在createApp中
components:{
abc:abc
}
在组件abc中
components:{
aa:{
template:'<h4>组件内部的内容-郑科</h4>'
}
}
使用的时候在html外部
<template id="abc">
<h1>我在郑州很想你{{num}}</h1>
</template>
在html中使用标签
<div id="app">
<abc></abc>
<abc></abc>
</div>
-----组件的属性
components:{
show:{
template:'#show',
// 给组件设置属性,属性名设置在一个数组中,简写
// props:['abc','aa','obj']
// 设置属性的具体类型,设置属性的多样性
props:{
abc:{
// 设置属性abc的值必须是一个字符串
type:String,
},
aa:{
type:String,
// 设置为必写属性
required:true,
// 设置属性的默认值,当属性没有值的时候默认显示的内容
default:'中午吃啥'
},
obj:{
type:Object,
// 设置默认值,对象类型的默认值是一个函数,返回一个默认对象
default(){
return{
name:'燕青',
age:20
}
}
}
}
}
}
组件的传值
组件中通过 this.
r
e
f
s
获取所有携带
r
e
f
属性的子组件实例,
t
h
i
s
.
refs 获取所有携带ref属性的子组件实例,this.
refs获取所有携带ref属性的子组件实例,this.refs 是一个集合
this.$refs.aa 就可以获取 ref=“aa” 的组件实例
----子传父
使用 e m i t ( ) 触发自定义事件,并且可以传递参数子组件中的操作 t h i s . emit()触发自定义事件,并且可以传递参数 子组件中的操作 this. emit()触发自定义事件,并且可以传递参数子组件中的操作this.emit(‘event-name’,args参数);
然后把event-name 绑定到子组件上
<son @event-name=“父组件的接收方法”>
过程:
子组件中执行 this.
e
m
i
t
(
)
,触发了绑定在子组件上的
e
v
e
n
t
−
n
a
m
e
,然后执行父组件的接收方法,父组件的接收方法接收一个默认的参数,参数的值是子组件中的
t
h
i
s
.
emit(),触发了绑定在子组件上的event-name,然后执行父组件的接收方法,父组件的接收方法接收一个默认的参数,参数的值是子组件中的this.
emit(),触发了绑定在子组件上的event−name,然后执行父组件的接收方法,父组件的接收方法接收一个默认的参数,参数的值是子组件中的this.emit传递的参数,这样通过参数的形式,把数据从子组件传递给父组件
$parent 获取当前组件的父组件,如果没有父组件,则返回null
$root 获取当前组件的根组件,如果当前组件没有父组件,则返回当前组件
----非父子关系的传值
外部引入 mitt 插件 的js
<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
引入mitt实例
// 引入mitt实例
let bus=mitt();
在父组件中声明两个子组件
components:{
son1:son1,
son2:son2
}
在外部声明两个组件
// 声明两个组件
let son1={
template:'#son1',
data(){
return{
msg:'假行僧'
}
},
methods: {
pass(){
// 执行 mitt 实例中的 emit() 方法,来触发并且传递参数
bus.emit('abc',this.msg);
}
},
};
let son2={
template:'#son2',
data(){
return{
info:'一块红布'
}
},
mounted(){
// 执行 mitt 实例中的 on() 方法,来监听 自定义事件是否被触发,如果被触发,则执行回调函数,回调函数默认传入一个参数,该参数的值即为 通过 emit 触发事件传递的数据
bus.on('abc',data=>{
this.info=data;
})
}
};
provide和inject传值
配置直接传入后代的值
provide:{
msg:'却惹三千烦恼丝'
},
如果直接获取data中的数据作为传递到后代组件的数据,则使用函数的形式返回数据
provide(){
return{
msg:this.str
}
},
在外部声明组件,通过inject接收数据
let sun={
template: '#sun',
// 通过 inject 直接接收 provide 传递的数据
inject:['msg']
};
动态组件
动态组件 使用标签 component
该组件具有一个 is 属性,is 属性的值是 要渲染组件的名字,即为 is 属性的值是哪一个组件名,component 标签就会渲染哪一个组件
缺点:component 可以动态的渲染组件的内容,但是每一次切换,都会重新渲染组件内容,降低渲染效率
使用 keep-alive 标签(组件),可以缓存曾经渲染过的组件,从而提高渲染效率
插槽
插槽 指的是 写入自定义组件中包裹的内容,一般都是html结构或者自定义组件
插槽的出口,在自定义组件内部设置一个 slot 标签,表示插槽的出口,将来插槽的内容会渲染在 slot 所在的位置
如果需要不同的插槽内容渲染在组件中不同的位置,那么就需要使用具名插槽,具名插槽的名字设置方式是:
1.在插槽的内容部分,设置v-slot:插槽名 来包裹需要渲染在具体位置的html结构
2.在自定义组件中,设置 slot 在对应的位置,并且slot的name属性值是设置的插槽名
注意 v-slot 可以简写成 #插槽名
使用指令 v-slot 绑定的插槽名字,可以是一个变量
v-slot:[变量名] #[变量名]
过度动画
----设置过度的流程
1.把需要使用过渡动画的元素使用 transition 组件包裹起来
2.给 transition 组件设置一个name属性
3.通过 name 属性的值设置过渡动画
4.通过 v-if 或者 v-show 控制元素的显示和隐藏
transition-group 组件,常用来操作一系列数据,他具有一个tag属性,可以把当前的transition-group组件渲染为 tag 属性设置的html标签
----过度动画的应用场景
1.结合v-if使用
2.结合v-show使用
3.结合 路由切换 使用
简单路由
router-view 组件,通过router-view组件来设置路由的出口,路由匹配到地址栏中的路由地址,会把和该地址对应的组件内容渲染到 router-view 组件中来
1.需要引入vue和router的js
2.设置路由配置
const routes =[
{
path:'/', //配置路由地址
component:Index //根据路由地址的变化,渲染的组件内容
},
{
path:'/list',
component:List
}
];
3.设置路由实例对象
let router=VueRouter.createRouter({
// 设置路由的显示方式
history: VueRouter.createWebHashHistory(),
// 在实例中配置
routes
});
4.在app中配置路由
5.设置路由
app.use(router);
6.在html中使用
<div id="app>
<!-- vue-router中使用 router-link 来设置路由的导航,通过他的to属性来配置路由的地址,每一个 router-link 组件最后会被渲染成一个a标签 -->
<router-link to="/">首页</router-link>
<router-link to="/list">列表页</router-link>
<router-view></router-view>
<!--router-view 组件,通过router-view组件来设置路由的出口,路由匹配到地址栏中的路由地址,会把和该地址对应的组件内容渲染到 router-view 组件中来-->
</div>