Vue2
vue项目运行时npm版本过低
在终端先输入$env:NODE_OPTIONS="--openssl-legacy-provider"
然后 npm run serve
vue特点
- 组件化开发,提高开发效率
- 声明式编码,不需要操作DOM,提高开发效率
- diff算法,DOM元素复用,提高效率
创建vue实例
为了构建一个Vue应用程序,就是说可以使用vue这种技术了
new Vue({
el: '#root',
data: {
name: "尚硅谷"
},
//方法都写在methods里
methods: {
showInfo1() {
alert("hello")
}
}
});
js表达式和js语句
js表达式:会产生一个值(a,b,a+b,demo(1))
js语句:控制代码的运行 (for循环,if判断)
数据动态绑定
-
1.单向绑定
v-bind 可以简写为 :
<input type="text" :value="name"> <!-- 单向绑定 -->
只能从data中将数据渲染到页面,页面数据的变化不能影响到data中的数据变化
-
2.双向绑定
v-model=“”,只能用于表单类元素
<input type="text" v-model="name"> <!-- 双向绑定 -->
可以从data中将数据渲染到页面,页面数据的变化也可以改变data中的数据
v-model的修饰符
- number
就是将输入的值转为数字类型
<input type="text" v-model.number="n1"/>
2.trim 过滤首位的空格
<input type="text" v-model.trim="msg"/>
- lazy 光标移走才将输入的数据存储到data中
<input type="text" v-model.lazy ="msg"/>
MVVM模型
M :data中的数据
V:页面
VM:创建的vue实例
总的来说,就是将data中的数据通过vue实例渲染在页面,页面数据的变化也可以改变data中的数据
data中所有属性最终都会出现在vm上
vm上所有属性和vue原型上所有属性都可以在{{ }}中直接输出
defineProperty
-
给对象添加属性
Object.defineProperty(person, 'age',{value:18 })
第一个是对象名,第二个为要添加的key,第三个为配置项{value:19}
defineProperty这个方法增加的属性不能被遍历、删除、更改
-
get() set()方法
let number = 18;
let person = {
name: "张三",
sex: "男"
};
Object.defineProperty(person, 'age', {
//当有人读取person.age时,函数被调用,就会返回一个值
get() {
console.log("有人读取了age的值");
return number;
},
//当有人修改person.age时,函数被调用,且会收到修改的值
set(value) {
console.log("有人修改了age的值");
number = value;
}
});
数据代理
-
通过一个对象代理另一个对象中属性的操作
let obj1 = { x: 200 }; let obj2 = { y: 300 }; Object.defineProperty(obj2, "x", { get() { return obj1.x; }, set(value) { obj1.x = value; } })
vue中的数据代理
- 将data中的数据挂载到vm上的_data属性中
- _data中的每一个属性配置一个getter、setter,进行数据代理,就是进行数据的读写操作,也是挂载到vm实例上
- vm.name可以修改_data中name的值,这就是数据代理,通过一个对象来修改另一个对象的属性值
插值语法
- 可以在{{}}中进行一些简单的js表达式运算
{{num+1}} {{message.split(‘’)}}
- vm上存在的属性,以及vue原型上的属性,都可以插入
事件处理
-
事件的基本使用
1.用v-on:xxx 或@xxx绑定事件 xxx是事件名
2.事件的回调函数需要配置在methods中
3.methods中的函数中this指向的是vm或是组件实例对象
-
事件的传参
-
@click="add"不传参,但在methods中add(e)方法会接收到一个参数e,也就是鼠标事件,就会获取的事件触发的DOM元素e.target
-
click="add(1)"就是简单传参 在methods中add(n),n就是1
-
@click=“add(1,$event)”,第一个参数就是n,第二个参数就是鼠标事件,在methods中add接收两个参数
add(n,e)
-
<button @mouseover="showInfo1(66,$event)">点我提示</button>
new Vue({
el: '#root',
data: {
name: "尚硅谷"
},
//方法都写在methods里
methods: {
showInfo1(number) {
alert(number)
}
}
});
事件修饰符
修饰符可以连续写(.prevent.stop)
-
prevent:阻止默认事件发生
<a href="http:baidu.com" @click.prevent="showInfo1">点我</a>
-
stop:阻止冒泡事件发生
<div @click="showInfo1">1
<div @click.stop="showInfo1">2</div>
</div>
- once:事件只发生一次
<button @click.once="showInfo1">点击</button>
键盘事件
- 键盘输入完成之后,按下enter键,调用函数
<input type="text" @keyup.enter="showInfo1">
- 配合keyup使用
-
配合keydown使用 (tab\alt\shift\meta\ctrl)
计算属性
- 计算属性要写在computed中,必须要return
- vm直接调用computed里的函数,将结果返回,所以使用时直接用插值语法写方法名,不用调用了
- 计算属性最终会出现在vm上,所以直接当属性使用即可
- 计算属性只调用一次,有缓存机制,重复使用时只执行一次,效率更高,并且方便调试
- 计算属性靠get和set来实现,只要计算属性用到的变量发生变化,就会重新渲染
computed: {
fullName() {
return this.firstName + this.lastName;
}
}
//对象形式(可以进行修改操作setter)
computed:{
fullName:{
get(){
return this.firstName + this.lastName;
},
set(newVal){
let arr = newval.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
过滤器
定义在filters下
在过滤器函数中一定要有一个return,不改变原数据,产生新的相对应的数据,多个过滤器可以串联
过滤器函数有一个形参,就是管道符前面的参数
//跟methods一样
filters:{
cai(val){
return val+1; //val是
}
}
<p> {{number | cai}}</p>
全局过滤器
Vue.filter()接收两个参数,第一个是全局过滤器的名字,第二个是全局过滤器的处理函数
Vue.filter('cap',(num)=>{
return num++;
})
使用场景:1. 配合插值语法,对数据进行特定的格式化 <p> {{number | cai}}</p>
2.配合v-bind
监视属性
- 监视数据的(data)变化,只要发生变化就被调用
- 要监视那个数据变化,就把数据名作为方法名
- 深度监视可以监视一个对象内部属性的变化,函数格式不可以监视,vue自身可以监视对象内部属性,watch默认不可以!
- 可以用来判断用户名是否被占用,可以开启定时器等异步任务
//对象格式
watch:{
data上的属性名:{
immediate:true; //mounted时被调用
deep:true; //深度监视
handler(newValue,oldValue){
}
}
}
//函数格式
watch: {
data上的属性名(newValue,oldValue){
}
//如果监听对象的子属性,深度监听
'info.username'(){
}
}
class绑定
将要动态切换的class用v-bind绑定即可,最终class样式会将动态变化和不变的合并起来
-
class绑定对象 :class=“对象名” 用来控制哪几个样式显示或隐藏
<div class="box" :class="activeObj"></div> new Vue({ el: '#root', data: { activeObj:{ active: true }, }, methods: { btnClick() { this.activeObj.active = !this.activeObj.active; //太妙了!!! } }, });
-
数组绑定class 增加或删除样式
<div class="basic" :class="mood"></div>
new Vue({
el: '#root',
data: {
mood: ["happy", "sad", "normal"]
},
methods: {
btnClick() {
this.mood.push('sad');
this.mood.pop();
}
},
});
- 字符串类型 直接更改class名称,点击切换class类型
<div class="box" :class = "active"></div>
new Vue({
el: '#root',
data: {
active:'sad';
},
methods: {
btnClick() {
this.active = 'happy';
}
},
});
绑定style样式
<div :style="styleObj">hello</div>
new Vue({
el: '#root',
data: {
styleObj: {
fontSize: "40px",
color: "white"
}
},
});
条件渲染
-
v-if
1.适用于切换频率较低的场景
2.不展示的dom元素直接被删除,只能与v-if配合使用,不可以跟v-show
3.可以连用
v-if=" "
v-else-if =" "
v-else=" "
必须要写在一起,结构不能被打断
<h2 v-if="n===1">1</h2> <h2 v-else-if="n===2">2</h2> <h2 v-else-if="n===3">3</h2> <h2 v-else>4</h2>
-
v-show
1.适用于切换频率较高的场景
2.不展示的dom元素被隐藏 (display:none类似)
<h2 v-show="n===1">hello</h2>
列表展示
-
遍历数组 item是每一个对象,index为索引
<li v-for="(item,index) in person" :key="item.id"> {{item.name}}---{{item.age}} </li>
-
遍历对象 第一个收到的是值,第二个是键
<li v-for="(value,key) in car" :key="k"> {{value}}---{{key}} </li>
-
遍历数组 item就是每一个数组元素
-
生成1-n的数字,索引还是从0开始
v-for='item in 10' ==>1,2,3...10
列表过滤
使用filter函数对数组操作
filterPerson(){
return this.person.filter((item)=>{
return item.name.indexOf(this.keyWord) != -1; //===> return []
})
将filter产生的新数组return出去,数组名为filterPserson
列表排序
使用sort方法对数组操作
filterPerson(){
const arr = this.person.filter((item)=>{
return item.name.indexOf(this.keyWord) != -1;
})
if(this.sortType){
arr.sort((a,b)=>{
return this.sortType == 1 ? a.age - b.age : b.age - a.age;
})
}
return arr;
}
key和diff算法
key是虚拟DOM对象的标识,当数据发生变化时,vue会根据根据新的数据形成新的虚拟DOM,然后新的DOM会与旧的DOM元素进行比对,最终将虚拟DOM转换为真实DOM
- 新旧的key相同的虚拟DOM:比较他们的内容是否一致,一致则直接复用。不一致的部分重新生成真实的DOM
- key不同的虚拟DOM:创建新的真实的DOM,渲染到页面
vue的响应式
为了实现响应式页面,通过对data中的数据进行加工,为每一个data中的属性加了getter和setter,只要data中数据一改变,set就被调用,并且重新解析模板,实现页面的更新。
Vue.set
vue中如果要给对象直接加一个属性如this.person.age=18
,vue会监听不到对象的改变,页面也不会显示,age也不会配置get,set
如果想让后增加的属性具有get,set,可以使用vue.set('this.person','age',18)
这种方式,set的第一个配置对象不能是vm和vm.data,只能是data中的对象
只要是data中的对象,vue就会为其属性配置setter和getter,在数组中的对象也是,只要修改对象中的属性就会重新解析模板
-
只能给data数据中的对象数据上增添一个新的属性
Vue.set(target,key,value)
Vue.set(this.person, "sex", "男")
数组元素的更新
vue没有为数组元素配置get,set,直接更改数组元素,vue不会监听改变,也不会渲染到页面
-
vue中数组元素如果
arr[0]='hello'
这样进行修改,vue将无法监听到数组的变化,因为没有配置get,set -
正确做法是使用一下7个方法或创建一个新的数组来接收过滤后的元素,来展示到页面
-
可以用Vue.set(this.person, 0, “男”) 0代表数组中元素的索引值,将数组中索引为0的元素改为男
-
vm.person.push(“男”) 使用数组方法 vm.person.splice(0,1,“男”)
-
splice(从第几个元素开始,删除几个,在删除元素的位置添加的新元素)
var arr = [1,2,3,4,5]; // 添加 arr.splice(0,0,0);// [0,1,2,3,4,5] // 修改 arr.splice(0,1,23);// [23,1,2,3,4,5] // 删除 arr.splice(0,1);//[1,2,3,4,5]
这些方法可以实现数组元素的更新,让页面发生更新
这些方法都改变的是原数组,不产生新数组
##收集表单提交数据
爱好<input type="checkbox" v-model="hobby" value="足球">足球 //复选框的value配置之后,hobby写成[],收集到的就是['足球'],如果不配置value,hobby是'',收集的
true/false
性别<input type="radio" v-model="sex" name="sex" value="男">男
<input type="radio" v-model="sex" value="女">女 //单选框value必须配置,name属性必须一致
@change 和 @click的区别
@change 是在内容改变的时候触发 @click是在点击的时候触发,此时内 容还没有改变
##v-html
可以直接解析html模板,但具有安全性的风险,一般表单提交不能使用v-html
v-cloak
防止页面因为网速慢没有解析完vue模板而出现{{XXX}}的问题
在vue实例创建完毕并接管容器之后就会删除该属性
配合css使用[ v-cloak]:{display:none} [v-clock]属性选择器
<h2 v-cloak>{{name}}</h2>
[v-cloak]: {
display:none;
}
v-once
-
从vue的data中第一次渲染到界面之后,data中数据的变化不再影响页面
<h2 v-once>{{name}}</h2>
v-pre
直接跳过vue的解析,渲染到页面
一般在没有用到vue的插值语法、指令语法的节点,加快编译速度
<h2 v-pre>hello,world</h2>
<h2>{{name}}</h2>
自定义指令函数式
-
把要构造的指令写在directives:{ }, 跟methods类似
-
在标签里写v-big,在directives中构造是就只用big(){}
-
只要data中数据发生改变,模板重新解析时,就会重新调用该函数
-
相当于对象式的bind和update
<h2 v-big="n"></h2> directives: { big(element, binding) { element.innerHTML = binding.value * 10; }
element是添加指令所对应的DOM元素,binding是将指令与元素绑定,binding.value就是n
自定义指令对象
对象中包含三个回调函数
1.bind(element,binding){ } 当指令与元素绑定成功时调用
2.inserted(element,binding){ } 当指令所对应的元素插入到页面后调用
3.update(element,binding){ } 当data中数据发生变化时调用
directives: {
fbind: {
//当元素与指令绑定成功时,相当于初始化时就别调用
bind(element, binding) {
element.value = binding.value;
},
//当vue将元素插入到页面后
inserted(element, binding) {
element.focus();
},
//当data数据变化时,重新调用
update(element, binding) {
element.value = binding.value;
}
}
}
生命周期
beforeCreate:无法通过vm去访问 data/props/methods,初始化声明周期、事件,没有开始数据代理,数据监测
created: data/props/methods都可用,可以通过vm去访问,但组件的结构尚未生成,不能操作DOM,只是在内存中生成了 HTML结构
beforeMount: 页面生成vue未解析的DOM元素,只是将要将内存中的HTML结构渲染到页面
mounted:生成经过vue解析后DOM元素,页面渲染到浏览器,可以操作DOM元素了
数据变化之后执行
{
beforeUpdate:data中数据更新完成,页面还没修改,还是旧数据,相当于data中数据是新数据,页面还是旧数据,还没有将data数据渲染到页面
当数据变化之后,操作DOM元素,就用updated
updated:页面和数据都是data更新后的,最新的数据
}
this. d e s t o r y ( ) 销毁组件 v c , v m 。执行 t h i s . destory()销毁组件vc,vm。执行this. destory()销毁组件vc,vm。执行this.destory()之后,vc,vm上的自定义事件全部不奏效了
//vue解析完模板挂载到页面上
mounted(){
},
//vm销毁时
beforeDestory(){
clearInterval(timer)
}
组件
实现局部功能代码和资源的集合(将html,css,js,视频、文字样式等资源统一起来)
复用率高,好维护
单文件组件 .vue 一个文件只有一个组件
创建组件 :
const school = Vue.extend({
data() {
return {
studentName: "小王",
age: 18
}
}
})
//可以简写为
const school = {
data() {
return {
studentName: "小王",
age: 18
}
}
}
Vue.extend({})返回一个构造函数vueComponent
组件名
可以在组件中配置name:‘school’,在vue开发者工具中就会显示该组件名
data为什么要写成函数形式
data(){ return{ a:1, b:3 } }
这样当不同的地方使用该组件时,都会返回一个全新的对象,别的地方修改对象数据,互不影响
VueComponent
每一个组件就是一个构造函数vueComponent,是Vue.extend生成的
当在模板中写时,vue会帮我们创建一个vc的实例对象,即new VueComponent({options}) options是Vue.extend({})中配置的属性
不同的组件是不同的构造函数vueComponent,虽然都差不多,但是return出来的,是不同的vueComponent
vc和vm区别
vc和vm99%相似,都有数据代理,数据监测,methods,computed…
区别:vm有el来确定放置DOM元素的位置,vc没有el
vc中的data必须写成函数式,vm的data可以直接写成对象(不在脚手架中)
生成模板快捷键
<v
##一个重要的内置关系
为了让vc也可以访问到vue原型上的属性和方法
构造函数有一个显式原型属性prototype来找到构造函数原型对象
构造函数的实例对象有一个隐式原型属性–proto–也是指向构造函数的原型对象
构造函数的原型对象有一个–proto–属性来指向object的原型对象
初始化脚手架
先设置一下淘宝镜像,在create项目前
npm config set registry https://registry.npm.taobao.org/
脚手架结构
执行npm run serve 之后
- 执行main.js文件 该文件是整个项目的入口文件
import Vue from 'vue' //引入vue
import App from './App.vue' //引入App组件,他是所有组件的父组件
Vue.config.productionTip = false //关闭vue的生产提示
new Vue({
render: h => h(App),
}).$mount('#app')
- assets文件夹中配置静态资源(图片,音频等)
- components文件夹中写组件
render函数
创建html元素,解析组件模板
##vue的项目流程
- 就是通过main.js将App组件渲染到页面指定位置
- main.js就是vue项目的入口文件,是将App组件渲染到index.html文件的预留区域
- render函数是创建App.vue的模板
- 先访问main.js
- 访问App.vue ,再访问App.vue中的子组件
- 解析App.vue模板,并挂载到指定的容器中
vue.js和vue.runtime.vue.js
只要有runtime就代表是运行版本的vue,不包含模板解析器,只能用render函数来解析模板
$nextTick
-
this.$nextTick(回调函数)
-
vue在解析模板时,不会立即更改,只有将代码执行一遍后统一重新解析
-
只有在DOM元素更新到页面之后才执行函数体内代码
this.$nextTick( () =>{ this.$refs.inputTitle.focus(); });
mixin混入
-
配置mixin.js文件,写一些共用的方法,数据
export const hunru = { methods: { showName() { alert(this.name) } } }
-
在需要用到的组件里引入mixin.js文件
import { hunru } from "../mixin";
-
且要在组件里配置mixins属性
mixins: [hunru],
-
==全局混合 > vm和所有vc都会得到mixin.js中的方法
(1)将mixin.js引入到main.js中
(2)Vue.mixin(hunru)
插件(plugins.js)
-
为了增强vue,在插件里写一些方法和数据,在vm和vc中都可以使用
-
定义插件
创建一个插件的js文件
-
使用插件
1.在main.js中引入插件js文件
2.使用插件
Vue.use(插件名)
scoped
-
写在style标签里,为了使相同class名在不同组件中使用
<style scoped> </style>
-
父组件里修改子组件的样式,在使用第三方组件库时,修改组件默认样式
/deep/ h5{}
浏览器本地储存
1.浏览器通过localStorage和sessionStorage属性来实现本地储存
2.localStorage保存一些数据,关闭浏览器,再次打开是数据还在
3.sessionStorage存一些数据,随着浏览器的关闭而消失
-
添加一个数据
localStorage.setItem('msg','hello') sessionStorage.setItem('msg','hello')
都是以字符串形式储存,第一个是key,第二个是value
-
读取数据
localStorage.getItem('msg') //没有对应的value会返回NUll
-
删除某个数据
localStorage.removeItem('msg')
-
删除所有数据
localStorage.clear()
父子组件传参
父向子传参,props
props属性,父向子传参
-
props传过来的属性,会直接出现在vc实例上,所以可以在插值语法中直接使用
-
props是只读的,不能修改props的值
-
如果从父组件里传的参数是通过axios请求得来的,在created、mounted中无法得到该值,可以通过watch属性监听该props属性,将值存入data中。而且必须用watch的对象方式监听。
-
让组件接受外部传来的数据
(1)传递数据到组件
<Student name="郭彦孚" sex="男" :age="1" />
(2)接受数据
1.简单接收
props: ["name", "sex", "age"]
2.限制类型 限制必要性 指定默认值
-
props中的数据名不能和data中的数据名相同,props的优先级更高,会优先显示props中属性名的值
-
:age=“js表达式”
-
组件中先读取props中的数据
-
props中传过来的数据不能改变,如果要改变复制一份放入data中,且不能同名
props: ["name", "sex", "age"], data() { return { myAge: this.age, }; }, methods: { add() { this.myAge++; }, },
##自定义事件(子向父传值)
1.主要实现子向父组件进行数据传递
2.和click等内置事件一样(@click=“”),要在父组件中给子组件添加一个自定义事件,父组件会收到子组件传过来的值
也可以添加事件修饰符.once之类的
<School @getSchoolName="schoolName" />
methods: {
schoolName(name) {
console.log("我的学校是" + name);
},
},
3.在子组件内用$emit(“自定义事件名”,要传的参数) 来使用该自定义事件
methods: {
send() {
this.$emit("getSchoolName", this.name);
},
},
4.解绑自定义事件
-
解绑一个自定义事件
this.$off(“自定义事件”)
-
解绑多个自定义事件
this.$off([“自定义事件“,”自定义事件”])
-
解绑所有自定义事件
this.$off()
第二中方法:父组件先给子组件一个函数,子组件调用该函数进行传值
子组件使用原生事件
使用一个事件修饰符(.native)
vue会将写在组件中的事件都看做自定义事件,要想触发原生的事件,要加.native
<School @click.native="schoolName" />
##ref属性
-
就是id的替代者
(1)可以在组件标签和html标签中添加ref=“xxx”
(2)应用在html标签上时获取的是真实DOM元素,在组件中获取的是组件的实例对象vc,然后就可以调用该组件的方法和数据
(3)获取时this.$refs.xxx
-
ref可以加在组价标签里,得到这个组件的实例对象,实现父组件直接调用子组件的方法和数据,给子组件绑定事件
this.$ref.student.$on('自定义事件名',回调函数)
,这个要在mounted函数中执行
##全局事件总线
-
实现任意组件间的通信
-
数据发送方,调用 e m i t ( ′ 事件名 称 ′ , 要发送的数据 ) 数据接收方,调用 emit('事件名称',要发送的数据) 数据接收方,调用 emit(′事件名称′,要发送的数据)数据接收方,调用on(‘事件名称’,事件处理函数) 最后在beforeDestory()函数中解绑自定义事件
-
安装全局事件总线$bus
o n , on, on,emit, o f f 属性只有在 v u e . p r o t o t y p e 上才有,而 v c , v m 都可以访问 v u e . p r o t o t y p e ,所以每个组件都可以获得 off属性只有在vue.prototype上才有,而vc,vm都可以访问vue.prototype,所以每个组件都可以获得 off属性只有在vue.prototype上才有,而vc,vm都可以访问vue.prototype,所以每个组件都可以获得on, e m i t , emit, emit,off属性,将 b u s 绑定在 v u e . p r o t o t y p e 上,每个组件都可以看见它,并且将 bus绑定在vue.prototype上,每个组件都可以看见它,并且将 bus绑定在vue.prototype上,每个组件都可以看见它,并且将bus配置为vm,即 b u s 也会拥有 bus也会拥有 bus也会拥有on, e m i t , emit, emit,off属性
new Vue({ render: h => h(App), beforeCreate() { Vue.prototype.$bus = this //this就是vm } }).$mount('#app')
-
使用事件总线
-
接受数据:A组件想接受数据,就在A组件中给$bus绑定自定义事件,事件的回调留在A组件
methods: { //改变done的值 checkTodos(id) { this.todos.forEach((todo) => { if (todo.id == id) todo.done = !todo.done; }); }, }, mounted() { this.$bus.$on("checkTodos", this.checkTodos); //第一个是事件名,第二个是要执行的回调函数,也可以直接写成箭头函数 this.$bus.$on('checkTodos',(val)=>{ this.todos = val }) }, beforeDestroy() { this.$bus.$off("checkTodos"); },
5. 提供数据 this.$bus.$emit("checkTodos", id);
-
消息订阅与发布
一种任意组件间通信的方式
使用步骤:
1. 安装pubsub:``npm i pubsub-js``
1. 在需要通信的组件中引入 ``import pubsub from 'pubsub-js'``
1. 在接受数据的组件中订阅消息
mounted(){
this.pid = pubsub.subscribe('事件名',回调函数) //每一个订阅消息都会返回一个id,要在beforeDestory中取消订阅
},
beforeDestroy() {
pubsub.unsubscribe(pid)
},
- 提供数据的组件中发布消息
pubsub.publish('事件名',数据)
动画效果
用把实现动画的内容包裹,中可以增加name属性,appear:页面一出现就有动画效果
css样式
.v-enter-active{
animation: change 1s;
}
.v-leave-active{
animation: change 1s reverse;
}
@keyframes change {
from{
transform: translateX(-100%);
}
to{
transform: translateX(0px);
}
}
##第三方动画库
-
安装动画库
npm install animate.css
-
引入
import 'animate.css'
-
配置样式
<transition name="animate__animated animate__bounce" enter-active-class="animate__rubberBand" leave-active-class="animate__bounceOut">
<h1 v-show="isShow" class="title">hello</h1>
</transition>
动态切换组件
<component is="组件名"> </component>
is表示出现的组件是那个,组件名要和引入的一致
<div>
<component :is="comName"></component>
</div>
data() {
return {
comName: "Left",
};
},
在切换组件时,隐藏的组件会被销毁,显示的组件会重新渲染,之前的数据不会被保留
使用keepalive可以保留组件不被销毁,数据保存
<keep-alive> <component :is="comName"></component></keep-alive>
可以使用include=“组件名” 来选择要进行缓存的组件
<keep-alive include=“Left,Right”>
<component :is="comName"></component>
</keep-alive>
插槽
就是在复用组件时,对复用组件的html结构进行调整
- 默认插槽
在组件标签里添加html结构,该html结构会插入到该组件里的位置
//在引入top组件的文件里,在top标签里直接添加html结构
<Top>
<p>hello,world</p>
</Top>
//top组件中,在想插入的位置摆放<slot>标签
<template>
<div>
<slot></slot>
</div>
</template>
- 具名插槽
每个slot标签都有name属性 <slot name='left'></slot>
组件标签里加的元素要加上slot="left"
<Left>
<template v-slot:left> //v-solt:指定插槽,只有在template中 v-slot:简写为#
<p>hello</p>
</template>
</Left>
<Top>
<p slot="left">hello,world</p>
<p slot="left">hhhh</p>
</Top>
//left组件
<div>
<slot name="left"> </slot> //可以直接在slot标签中写内容,称为后备内容,当组件没有传内容时,默认显示
</div>
- 作用域插槽
还可以传值 父组件直接可以获得子组件传的值
数据在子组件,结构由父组件决定
//app组件
<Left>
<template v-solt:header='params'>
<p>{{ scope.params }}</p> //hello
</template>
</Left>
//left组件
<div>
<slotname:'header' msg="hello"></slot>
</div>
ESLint
//在方法形参()之前,不需要空格
“space-before-function-paren”:[“error”,“never”]
配置代理
-
在vue.config.js中进行配置
module.exports = { devServer: { proxy: { '/api': { //匹配所有以api开头的路径 target: '<url>', //代理目标的基本路径 pathRewrite: { '^/api': '' }, //修改传回服务器的路径 ws: true, changeOrigin: true }, '/foo': { target: '<other_url>' } } } }
axios在vue中的封装
-
安装axios
npm i axios -S
-
引入axios
import axios from ‘axios’
axios.defaults.baseURL = ‘http://www.baidu.com’ //全局配置默认请求根路径
Vue.prototype.$http = axios
- axios的返回值是promise实例对象,用await和async进行修饰和简化
Vuex
vuex中一般存储组件之间共享的数据,适用于多组件共享某些数据,将这些共享的数据集中起来管理
vuex中数据都是响应式的,能够保持数据与页面的同步,数据改变重新解析模板
是实现集中式状态管理的一个Vue插件,适用于任意组件间通信
-
搭建vuex环境
安装vuex
npm i vuex
安装的是vuex4版本,该版本只能用在vue3,vue2只能用vuex3版本npm i vuex@3
(1)创建store文件:src/store/index.js (先要有vuex,再创建store实例)
store实例对象是由Vuex.store({})构造出来的,构造之后,在vc,vm上就会出现$store属性
$store属性中 有dispatch,commit方法
//引入vue import Vue from 'vue' //引入vuex import Vuex from 'vuex' Vue.use(Vuex) //用于响应组件中的动作 const actions = {} //用于储存数据 const state = {} //用于操作数据 const mutations = {} //创建并导出store export default new Vuex.Store({ actions, mutations, state })
(2)在main.js中引入store
//引入store import store from './store' //创建vm new Vue({ render: h => h(App), store, beforeCreate() { Vue.prototype.$bus = this } }).$mount('#app')
-
基本使用
1.在组件中使用dispatch给actions
this.$store.dispatch("jia", this.n);
2.在store中配置actions,actions主要写业务逻辑,将数据commit给mutations进行操作
如果没有网络请求或业务逻辑,可以直接越过actions,直接编写commit
const actions = { //context上下文,有commit,dispatch,state属性 jia(context, value) { context.commit('JIA', value) }, jian(context, value) { if(context.state.sum % 2){ context.commmit('JIA',value) } } }
3.mutations主要进行数据加工
mutations会收到两个参数,第一个是state,第二个就是传递过来的数据
const mutations = { JIA(state, value) { state.sum += value; }, JIAN(state, value) { state.sum -= value; } }
4.state 就是将一些共享的数据存放进来,类似与组件中的data
在组件模板中想使用state中数据
$store.state.sum
//用于储存数据 const state = { sum: 0 }
5.getters 就是将state中的数据进行运算加工,类似与组件中的computed
在组件中读取
$store.getters.bigData
const getters = { bigData(state){ return state.sum*10 //必须要用return语句,收到的参数就是state } }
6.mapState帮助程序猿简化代码,优化computed计算属性
首先要在组件中引入mapState
import {mapState} from 'vuex'
computed: { //借助mapState生成计算属性,从state中读取sum,并且形成sum函数 ...mapState(["sum"]),//==>sum(){ //return this.$store.state.sum } },
7.mapActions,模板中调用的函数要传参increment(n)
帮我们生成与actions对话的方法,即
this.$store.dispatch("jia", this.n);
methods: { // increment() { // this.$store.dispatch("jia", this.n); // }, ...mapActions({ increment: "jia" }), },
mapActions使用时若需要传参,则要在模板绑定事件时传递参数,否则参数是事件对象
<button @click="increment(n)">+</button>
- mapMutations,模板中调用的函数要传参increment(n)
methods: { // increment() { // this.$store.commit("jia", this.n); // }, ...mapMutations({ increment: "jia" }), },
-
vuex模块化
让代码更好维护,让数据分类更加明确
(1)修改store.js
// count相关配置
const countOptions = {
namespaced:true, //开启命名空间,mapState,mapActions等才能识别是哪个配置中的state,actions等
actions:{
addOdd(context,value){
if(context.state.sum % 2){
context.commit('ADDODD',value);
}
}
},
mutations:{
ADD(state,value){
state.sum += value;
},
DECREASE(state,value){
state.sum -= value;
},
ADDODD(state,value){
state.sum += value;
}
},
state:{
sum:0,
},
getters:{
}
}
//person相关配置
const personOptions = {
namespaced:true,
actions:{
},
mutations:{
ADDPERSON(state,value){
console.log(value);
state.personList.unshift(value)
}
},
state:{
personList:[{id:'000',name:'李四'}]
},
getters:{
length(state){
return state.personList.length
}
}
}
//导出时要加modules:{}
export default new Vuex.Store({
modules:{
countAbout:countOptions,
personAbout:personOptions
}
})
(2)组件读取数据
- 读state中数据
//直接读取
this.$store.state.count.sum
//借助mapState
...mapState('countAbout',['sum'])
- 组件中调用commit,dispatch,getters的方式都类似
//直接读取
this.$store.commit('countAbout/ADD',person)
//借助mapState
...mapMutations('countAbout',{add:'ADD'})
路由
一个路由就是一个对应关系,key为路径,value为组件
用来实现单页面应用,每条路径对应一个页面
基本使用
1.安装vue-router npm i vue-router@3.5.2 -S (vue2版本只能使用 vue-router3)
//在src目录下创建一个router文件夹,里面创建index.js文件
1.导入Vue和VueRouter的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// vue使用vueRouter插件
Vue.use(VueRouter)
// 创建路由实例对象
const router = new VueRouter({
//routes中定义hash地址和组件之间的对应关系
routes:[
{path:'/home',component:要展示的组件}
]
})
export default router
2.要在main.js中挂载router实例
import router from './router/index.js'
new Vue({
render: h => h(App),
router //
}).$mount('#app')
注意点
- 切换路径时,原来的组件会被销毁,切换到原路径时会重新挂载
- 路由组件通常写在pages文件夹中,普通组件写在components文件夹中
- 每个组件有 r o u t e 和 route和 route和router,在 r o u t e 中有自己的路由信息,整个应用只有一个 route中有自己的路由信息,整个应用只有一个 route中有自己的路由信息,整个应用只有一个router,所有组件的$router相同
router-view组件占位符
指定组件的呈现的位置
将该路由下路径对应的组件放入
##router-link
用来替换a链接
<router-link to='/home'>首页</router-link>
浏览器历史记录有两种写入方式 push和replace
router-link默认是push方式,每一条记录都会记录,从栈底开始,先进后出
replace是代替当前记录,浏览器只保存一条记录
首页
##redirect重定向
重新跳转到指定位置
routes:[
{path:'/',redirect:'/home'},
{path:'/home',component:Home}
]
嵌套路由
1.在router中配置 children:[{}]
注意:在path中不需要 /
{
path: '/home', component: Home,
children: [
{ path: 'news', component: News },
{ path: 'message', component: Message },
]
},
2.在组件跳转时要添加前缀
<router-link to="/home/message">Message</router-link>
路由的query传参
1.组件跳转,对象写法,要传递的参数写在query里
<router-link
:to="{
path: '/home/message/detail',
query: {
id: '',
title: ''
},
}"
>
</router-link>
2.接受参数
$route.query.id
$route.query.title
路由的params传参
传参前先占位
params传参时,to写成对象写法时,传参不能用path,只能用name
参数不能是数组
params传参时,若想让参数传不传都行,在占位时在参数后加?
<router-link :to="`/home/message/${m.id}/${m.title}`">Message</router-link>
3.接受参数
$route.params.id
$route.params.title
路由的props配置
方便路由组件接受参数
1.在router中配置props,return一个对象
{
path: 'message', component: Message,
children: [{
path: 'detail', component: Detail,
props($route) {
return { id: $route.query.id, title: $route.query.title }
}
}]
},
2.在组件接受数据,之后便可以在组件模板中使用
export default {
name: "Detail",
props: ["id", "title"],
};
声明式导航&编程式导航
- 声明式导航
普通网页中点击a链接,在vue中点击都属于声明式导航
- 编程式导航
调用Api实现页面hash地址的变化,to可以怎么写,push()括号内就可以怎么写
this.$router.push('hash地址')
跳转到指定位置,并增加一条历史记录,可以前进和回退。this.$router.replace('hash地址')
跳转到指定位置,并且代替原来页面,不能回退。this.$router.back()
后退一步this.$router.forward()
前进一步
this.$router.push({
path:'/home',
query:{
id:'002',
name:'tom'
}
})
缓存路由组件
为了将不展示的组件数据不丢失(路由一切换,默认组件自动销毁)
include=“组件名” 代表要缓存的组件是xxx
:include=[“组件名”,“组件名”] 缓存多个组件
<keep-alive include="Home">
<router-view></router-view>
</keep-alive>
路由中独有的声明周期
-
activated(){} 组件被激活时调用
-
deactivated(){} 组件失活时调用
路由守卫
就是为了控制路由的访问权限,满足一些条件才能进入页面
-
全局前置守卫 router.beforeEach((to,from,next)=>{})
- to 是将要访问的路由的信息 to.path 是hash地址
- from 是将要离开的路由的信息
- next( ) 是放行 next(‘/login’) 跳转到登录页面
在进行权限校验的路由里配置
meta:{isAuth:true}
router.beforeEach((to, from, next) => { if (to.meta.isAuth) { if (localStorage.getItem('school') === 'guigu') { next() } else { alert('sorry') } } else { next() } })
全局后置守卫,在组件跳转后执行
router.afterEach((to) => { document.title = to.meta.title || "vue" })
-
独享守卫,配置在path中
是某个路由独享的,配置在该路由下
{ path:'news', component:News, beforeEnter:(to,from,next)=>{ if (to.meta.isAuth) { if (localStorage.getItem('school') === 'guigu') { next() } else { alert('sorry') } } else { next() } } }
-
组件内路由守卫
路由进入该组件时进行一些判断
- 进入守卫
beforeRouteEnter(to,from,next){}
- 离开守卫
beforeRouteLeave (to, from, next) {}
路由器的两种工作模式
路由器默认是hash模式(#),可以在路由器中添加
mode:'history'
,转变为history模式
UI组件库
1.Element UI https://element.eleme.cn