框架名 | 出现时间 | 所属 | 一开始特色 |
---|---|---|---|
Angular | 2009年 | 谷歌 | 指令系统、双向数据绑定 |
React | 2013年 | 虚拟DOM、组件化 | |
Vue | 2015年 | 尤玉溪 | 指令系统、双向数据绑定、虚拟DOM、组件化 |
MVC
M:模型:(Model)
V :视图 (view)
C : 控制器 (Controller)
各部分之间的通信:
Model—>View–>Controller—>Model
所有通信都是单向的
MVVM
M :模型:(Model)
V :视图 (view)
VM : 视图模型 (ViewModel)
各部分之间的通信:
View<—>ViewModel<—>Model
虚拟DOM
Vue的简单使用步骤
页面中需要提供一个挂载点(用来承载Vue实例)。就提供一个div并设置一个id即可
引入Vue的核心js文件
实例化Vue ,并做相关设置
引入Vue核心js文件之后,就给window对象上加了一个Vue对象(Vue是一个类)
/实例化Vue
var vm = new Vue({
//配置选项
//el:配置挂载点,也就是说这里生成的Vue实例会应用到哪个页面位置上,1.值可以使一个css选择器写法'#app',2.可以是一个元素的Dom对象document.getElementById('app')
el:"",
//data:声明式变量,当前实例的数据源,data选项中自己定义的数据可以通过实例对象直接访问,设置。
data:{
//这块是自己写的
}
})
v-text
将数据绑定到元素的textContent上面
<P v-text='name'></p>
v-html
v-html与v-text基本一样
v-html会 解析html元素 ,v-text 不会解析html元素。
<P v-html='name'></p>
v-once
它只能和文本插值进行使用,它只渲染一次
<P v-once>{{name}}</p>
{{}}
文本插值:渲染到页面的时候会有一闪而过的情况
<p>{{name}}</p>
v-bind
(简写 :)用于动态绑定html标签属性
语法:
v-bind:attrname="数据"
:attrname="数据"
v-bind=对象
v-bind="{name:'张三',age:18}"==>v-bind:name="张三" v-bind:age="18"
v-bind="{name:name,age:age}"==>v-bind:name="name" v-bind:age="age"
Vue中class 属性是一个特殊的属性 ,使用v-bind绑定时有很多不同的用法
v-bind:class="'hello'"
v-bind:class="hello"
v-bind:class="{key1:value1,key2:value2}"
根据value1这个数据是否为真值,来控制是否要加上key1这个 类名
根据value2这个数据是否为真值,来控制是否要加上key2这个 类名
v-bind:class="{ active: isActive, 'text-danger': hasError }"
v-bind:class="[isActive ? activeClass : '', errorClass]"
数组 v-bind:class="[value1, 'value2',value3,{key:value}]"
Vue中style 属性是一个特殊的属性 ,使用v-bind绑定时有很多不同的用法
v-bind:style="color : red"
这种是错误的写法会报错
v-bind:style="{key1:value1,key2:value2,key3:'value3'}"
key1、key2、key3、都是css的属性名
value1的值作为key1这个css属性名的值
value2的值作为key2这个css属性名的值
value3这个字符串会作为key3这个css属性名的值
v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"
v-bind:style="[value1, value2,{color:myColor}]"
使用value1这个数据来控制,data中的value1必须为对象{font-size:‘20px’}
使用value2这个数据来控制,data中的value2必须为对象{font-size:‘20px’}
使用myColor这个数据来控制color属性的值
v-bind 在组件时的sync修饰符
sync—update:prop 搭配使用 prop传到子组件的变量
<div id="app">
<hello v-bind:value1.sync="msg1" v-bind:value2.sync="msg2"></hello>
</div>
Vue.component("hello", {
props: {
value1: {
type: String,
},
value2: {
type: String,
},
},
template: `
{{ value1 }}
{{ value2 }}
`,
methods: {
fn1() {
// 不能直接去修改prop数据
// this.msg = "我的天";
// 通知父亲去修改
this.$emit("update:value1", "我的天");
},
fn2() {
this.$emit("update:value2", "我的地");
},
},
});
var vm = new Vue({
el: "#app",
data: {
msg1: "hello",
msg2: "world",
},
});
v-show
根据表达式的真假值,切换元素的display CSS显隐属性
<div id= 'app'>
<button v-show=' !islogined'>登录</button>
<button v-show=' islogined'>退出登录</button>
</div>
<script>
var vm = new Vue({
el:'#app'
data:{
islogined:true
}
})
</script>
v-if
v-if与v-show的效果基本一致
v-if与v-show的区别
v-if是真正的条件渲染 ,条件为true元素渲染出来,默认条件为flase时,不渲染,条件变为flase时会销毁,条件变成 true的时候渲染出来。
v-show只是简单去控制CSS的display属性
v-show有更高的初始渲染开销,(因为不管默认条件是true还是false,都会渲染出来)
v-if有更高的切换开销(切换时做重建销毁的操作)
如果需要频繁显示隐藏切换用v-show更好,其余情况使用v-if
v-if有v-else和v-else-if配套使用,v-show没有
v-if可以配合template元素一起使用,v-show不可以。
因为v-show是要控制元素的css属性的,渲染页面的,template是不会被渲染出来的。
<div id= 'app'>
<button v-if=' !islogined'>登录</button>
<button v-if=' islogined'>退出登录</button>
</div>
<script>
var vm = new Vue({
el:'#app'
data:{
islogined:true
}
})
</script>
v-else
与v-else-if
这两个指令都需要与v-if搭配才可以使用
v-else不需要表达式
<div id= 'app'>
<button v-if=' !islogined'>登录</button>
<button v-else>退出登录</button>
<p v-if='score>=90'>优秀</p>
<p v-else-if='score>=80'>良好</p>
<p v-else-if='score>=60'>及格</p>
<p v-else>不及格</p>
</div>
<script>
var vm = new Vue({
el:'#app'
data:{
islogined:true
sorce:100
}
})
</script>
v-else-if需要表达式
<div id= 'app'>
<button v-if=' !islogined'>登录</button>
<button v-else>退出登录</button>
<p v-if='score>=90'>优秀</p>
<p v-else-if='score>=80'>良好</p>
<p v-else-if='score>=60'>及格</p>
<p v-else>不及格</p>
</div>
<script>
var vm = new Vue({
el:'#app'
data:{
islogined:true
sorce:100
}
})
</script>
v-for
列表循环
循环数组
v-for=“item in xxx”
v-for=“(item,index) in xxx”
<div id= 'app'>
<div v-for='item in arr'>
<span></span>
<span></span>
<span></span>
</div>
</div>
<script>
var vm = new Vue({
el:'#app'
data:{
arr:[
{id:1,name:'yeshuai'},
{id:2,name:'xiezhaoyi'}
]
}
})
</script>
循环对象
v-for=“item in xxx”
v-for="(value,key,index) in xxx"
循环字符串
v-for=“item in "张三"
v-for=“item in "10"
template
元素
Vue给我们提供了一个template 元素,他的作用是包裹元素,不会渲染到浏览器上。
<div id= 'app'>
<template v-if='isshow'>
<p>优秀</p>
<p>良好</p>
<p>及格</p>
<p>不及格</p>
</template>
</div>
<script>
var vm = new Vue({
el:'#app'
data:{
isshow:true
}
})
</script>
v-on
绑定事件v-on
语法:
v-on:xxxx= 'yyyy'
@xxxx = 'yyyy'
v-model
v-model
将数据与表单相关元素 input、textarea、select绑定起来,实现双向数据绑定
input为输入框的时候,v-model就双向绑定了输入框的值和data里对应的代理属性的值
当为单选框的时候设置v-model且data对应的代理属性一致,那么就 可以单选,且绑定值
多选框,定义一组数组类型的数据,然后通过v-model绑定到多选框上就可以了
<label><input type="checkbox" value="apple" v-model="loveFruits" />apple</label>
<label><input type="checkbox" value="banana" v-model="loveFruits" />banana</label>
<label><input type="checkbox" value="orange" v-model="loveFruits" />orange</label>
下拉选择框,v-model要绑定到select上,myCity不是数组
<select v-model="myCity">
<option value="shenzhen">深圳</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州 </option>
<option value="beijing">北京</option>
</select>
多选下拉选择框,v-model要绑定到select上,loveCity是数组
<select v-model="loveCity">
<option value="shenzhen">深圳</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州 </option>
<option value="beijing">北京</option>
</select>
多行文本输入框 ,v-model绑定到textarea
v-mode有三个修饰符
v-mode.lazy
相当于change事件,比如失去焦点v-mode.number
文本框的值变成number型v-mode.trim
给文本去除前后空格v-mode其实是一个语法糖(写起来方便,让我们甜甜的)
<input type="text" v-model="msg" />
===
<input type="text" v-bind:value="msg" v-on:input="msg=$event.target.value" />
$event
单个复选框v-model用法
<input type="checkbox" v-model="checked" />我们的协议
<button @click="handleRegister" :disabled="!checked">注册</button>
//页面打开时,调用后台接口,通过接口返回的数据来控制这个勾选框是否勾上。这时发现后台接口返回的数据不是一个 boolean 类型。而是字符串的 Y / N
<input
type="checkbox"
v-model="isChecked"
true-value="Y"
false-value="N"
/>
var vm = new Vue({
el: "#app",
data: {
checked: false,
isChecked: "N",
},
methods: {
handleRegister() {
alert("注册");
},
handleRegister2() {
if (this.checked) {
alert("注册2");
}
},
},
});
v-pre
有些时候我们需要在页面上 就直接输出{{}}这种格式的字符串,不让vue对其做解析的操作。这是只需要使用上v-pre就可以了
v-pre没有参数,也没有表达式
<p v-pre>{{msg}}</p>
.v-cloak
vue页面闪烁问题:
有时会在页面出现插值表达式的语句
vue解析页面需要时间
解决方案:
[v-cloak]{
display:none;
}
- unshift() 前面增加
- push() 后面增加
- shift() 前面删除
- pop() 后面删除
- splice() 高级方法删除、添加、删除
- sort() 排序
- reverse() 倒序
直接通过下标的方式修改数组 vm.arr[10]=“halo”
通过length修改数组 vm.arr.length=num
解决办法:
Vue.set(target,index,value)
的静态方法vm.$set(target,index,value)
方法
Vue.set(target,index,value)
直接给对象新增一个属性是不会引起页面更新的
解决办法:
Vue.set(target,key,value)
或者vm.$set(target,key,value)
Vue.set(target,key,value)
1.修饰符
- stop 阻止事件传播
- prevent 阻止默认事件
- self 只点自己
- capture 事件流-----事件捕获
- {kecode}|{keyAlias} 特定键盘修饰符
- keyAlias
- enter
- tab
- delete
- esc
- space
- up
- down
- left
- right
- Vue.config.keycodes.fn1=13
- once 只绑定一次
- left 鼠标左键
- right 鼠标右键
- middle 鼠标滚轮
- passive
2.系统修饰符
- ctrl
- shift
- alt
- meta windos键
- exact 精准匹配
native 事件修饰符
作用:将事件绑定到组件的根元素上
在调用组件时去绑定一些原生事件,比如 click dblclick mousedown。他们是不生效的。因为会将他们任何是自定义事件的监听
解决方法:
子组件触发这个事件
<div id="app">
<base-button @click="fn1">按钮1</base-button>
</div>
Vue.component("baseButton", {
template: `
`,
});
var vm = new Vue({
el: "#app",
methods: {
fn1() {
console.log("fn1");
},
},
});
使用 native 事件修饰符
<div id="app">
<base-button @click.native="fn1">按钮1</base-button>
</div>
Vue.component("baseButton", {
template: `
`,
});
var vm = new Vue({
el: "#app",
methods: {
fn1() {
console.log("fn1");
},
},
});
监听数据的变化,当数据发生变化的时候,可以去做一些额外的操作
语法1:
watch:{
key1:value1,
key2:value2
}
key1,key2是要监听的数据表达式
value1,value2是处理函数。string|function|object|Array
处理函数有两个参数,newVal,oldVal
watch:{
a:function(newVal,oldVal){
console.log(newVal,oldVal)
}//不可以写箭头函数
a:'hello' //要在methods配置选项里写hello函数,不然会报错
a:{ //一定要提供handler属性,属性值就是处理函数,
handler:function(newVal,oldVal){
console.log(newVal,oldVal)
}
}
////写成对象的写法可以监听,做更多的事情。
//深度监听;对象的属性值更改默认是监听不到的
//immediate 默认触发一次监听
obj:{
handler:function(newVal,oldVal){
console.log(newVal,oldVal)
},
deep:true//深度监听
immediate;true
}
//key的表达式的写法,直接监对象里的属性
"obj.name":function(newVal,oldVal){
console.log(newVal,oldVal)
}
}
computed 配置选项(计算属性,衍生属性,派生属性)
其基于其余的数据算出来的一分新数据
computed 的依赖项可以是 data 数据 props 数据或者其余的 computed 数据
语法
computed:{
key1:value1,
key2:value2
}
computed:{
key1:function(){
return 计算属性+计算属性
},
key2:function(){
return 计算属性+计算属性
},
}
computed:{
key1:{
get:function(){
return 计算属性+计算属性
},
set:function(value){
return 计算属性+计算属性
}
}
}
计算属性的特点:
beforeCreate 实例创建之前
created 实例创建之后
beforeMount 实例挂载之前
mount 实例挂载之后
beforeUpdate 数据更新之前
updated 数据更新之后
beforeDestroy 实例销毁之前
destroyed 实例销毁之后
swiper 引入接口的图片会有小问题,图片是异步回调,
解决方案:
使用updated钩子函数,等图片都回来渲染到视图上,在this.swiper.update()
在ajax请求成功的回调函数内
this.$nextTick(()=>{
this.swiper.update()
})
function hello(props) {
}
hello({name: 张三, age: 18, sex: '男'})
props => {name: 张三, age: 18, sex: '男'} 形参
name - prop
age - prop
sex - prop
//这里就是举一个例子
Vue.component('hello', {
//最简单的方式,写成数组形式,数组中每一项就是一个 prop
//注意:调用时不传递是没问题的
//定义的每一个 prop 。可以直接通过实例对象去访问到,就向访问 data 数据和 computed 数据那样
props: ['name', 'age', 'sex'],
template: `
Hello {{ name }} - {{ age }}
`,
methods: {
hi() {
console.log(this.name)
}
}
})
hello 组件中的 name 得到的是 “张三”
hello 组件中的 age 得到的是 “18”
hello 组件中的 sex 得到的是 undefined
如果调用组件时,没有传递某个 porp 下来,那么组件中的这个 prop 的值是 undefined
我们希望能够设置 prop 的默认值,也就是说调用时如果没有传递的化,我就使用默认值
function hello(name = ‘张三’) {
}
hello()
组件中如何给 prop 设置默认值呢?关键点将 props 选项从数组格式修改成 对象格式
Vue.component("hello", {
props: {
key1: { type: String, default: 'xxx' },
key2: { type: Number, default: 'yyy' }
},
template: `
Hello. {{ name }}
`,
})
//key1、key2 就是 prop 的名字。
//type: String, type: Number, 代表对应 prop 希望接收到的数据类型
//default: 'xxx', 默认值设置。
比如说:
我希望你给我传 String 类型, 调用时就要传 String 类型过来。
props: {
// value 直接写要的类型
key1: String
// value 写成对象,在对象中再通过 type 属性去控制类型
key2: {
type: Number
}
}
单向数据流:数据的流向是从高往地流。不能反过来
root ->prop-> hello √
hello ->prop-> root ×
Vue.component('hello', {
props: {
name: String,
age: Number
},
template: `hello`
})
<hello name="张三" age="18" id="box" title="我的天" class="box1" style="color: red"></hello>
注意:class 与 style 不是非prop的特性。他们两个是非常非常特殊的一个属性
非prop的attribute 的特点
class与style是非常特殊的
注意:用处。调用别人的组件的时候,这个组件的源代码我不能修改,这时如果要修改样式,就可以在调用时
设置自己的 class 或 style
Vue.component("hello", {
inheritAttrs: false,
props: {
name: String,
age: Number,
},
template: `
hello
`,
});
Vue.component("baseInput", {
inheritAttrs: false,
props: {
label: {
type: String,
required: true,
},
},
// 不使用prop数据,而是使用非prop特性数据
// template: `
//
//
// {{ label }}
//
// :type="$attrs.type"
// :placeholder="$attrs.placeholder"
// :disabled="$attrs.disabled"
// :readonly="$attrs.readonly" />
//
//
// `,
// 一个一个的去绑定 $attrs 也挺费劲。这时可以使用 v-bind 直接绑定一个对象
// v-bind="{key1: value1, key2: value2}" => v-bind:key1="value1" v-bind:key2="value2"
template: `
`,
created() {
console.log(this.$attrs);
},
});
子组件通过$emit() 去出发一个自定义事件
//子组件的$emit()去触发一个自定义事件
methods:{
fn1(){
//触发自定义事件
this.$emit('abc')
//'abc'是父组件自定义的监控子组件的事件
}
}
//同时父组件在调用子组件的时候也要去监听这个自定义事件
<son @abc='父组件methods里的事件处理函数'></son>
中央事件管理器(中央事件总线),适用于: 亲兄弟之间或者关系特别复杂的组件之间
先创建一个空的vue实例对象。也就是不需要额外配置什么选项。
const bus = new Vue()
A B 两个组件,A 通信 B
先在 B 组件的 created 中去通过 bus 来监听一个自定义事件
bus.$on(eventName, callback)
- eventName 要监听的事件的名字
- callback 事件触发时的一个回调函数
在 A 组件的某个时候去触发自定义事件
bus.$emit(eventName, payload)
// 创建一个事件总线
const bus = new Vue();
Vue.component("hello", {
data() {
return {
name: "hello",
};
},
template: `
hello - {{ name }}
`,
created() {
// 监听
bus.$on("abc", () => {
console.log("abc事件被触发了");
// this === hello 组件实例
this.name = "hellohello";
});
},
});
Vue.component("world", {
data() {
return {
name: "world",
};
},
template: `
world - {{ name }}
`,
methods: {
fn1() {
// 触发abc
bus.$emit("abc");
},
},
});
var vm = new Vue({
el: "#app",
});
slot内容、插槽内容、插槽模板内容
调用组件时写在组件标签内的内容就叫做 slot 内容
slot标签、slot组件、slot插槽、插槽 (坑、茅坑)
Vue 内置的一个组件
定义组件时想好要在哪个位置去渲染 slot 内容
在想好的位置哪里,放置一个 slot 标签 即可
slot 内容 就会自动去替换 slot 标签
<div id="app">
<hello>
<p>我的天,我不帅吗?</p>
</hello>
</div>
Vue.component("hello", {
template: `
Hello
`,
});
<div id="app">
<hello>
<p slot="bottom">我的天,我不帅吗?</p>
<button>点我</button>
</hello>
</div>
Vue.component("hello", {
template: `
Hello
`,
});
<!-- 老语法的作用域插槽使用 -->
<world>
<p slot="top" slot-scope="{ msg, age }">
我的天-{{ msg }} - {{ age }}
</p>
</world>
<!-- 新语法的作用域插槽使用 -->
<world>
<template #top="obj">
<p>
我的天-{{ obj.msg }} - {{ obj.age }}
</p>
</template>
</world>
<!-- 新语法的作用域插槽使用 解构赋值-->
<world>
<template #top="{ msg, age }">
<p>
我的天-{{ msg }} - {{ age }}
</p>
</template>
</world>
Vue.component("world", {
data() {
return {
msg: "张三",
age: 18,
};
},
template: `
world
`,
});
slot 插槽 在 2.6.0 这个版本的时候,做了更新。提供了一个新的指令叫做 v-slot。后续实现具名插槽与作用域插槽都使用 v-slot 来实现
2.6.0 版本在什么时候发布的:2019-02-04号(农历2018年过年那天)
v-slot语法:
v-slot:xxxx=yyyy
- xxxx 插槽名字
- yyyy 作用域插槽数据
v-slot 必须用在 template 元素上。也就是说 插槽内容 需要通过 template 元素进行包裹
如果插槽没有设置 name 名字的化,v-slot === v-slot:default
v-bind 指令有个简写 :
v-on 指令有个简写 @
v-slot 指令有个简写 #
注意,使用 简写时必须携带名字。 默认的名字需要写成 #default
<component is="hello" ></component> hello 组件替换这个内容
<component is="world" ></component> world 组件替换这个内容
<keep-alive>
<component :is="curPage"></component>
</keep-alive>
keep-alive 的更多属性设置:
include 包含
exclude 排除
max 规定最大能缓存组件的数量,默认是没有限制的
max=x2 规定最大能缓存组件的数量为2
假定缓存队列是 [home, list]
现在进入about的时候 about 也会被缓存上,这时会将之前的第一个给排出去 [home, list, about] => [list, about]
先进先出原则
$ npm install @vue/cli -g
$ vue --version
$ vue create 项目名称
$ npm run serve
//外部引入的组件不能直接用,要先转换成局部组件
import xxx from './components/HelloWorld.vue'
xxx 是组件名
'' 是组件的地址
6. exports default {} //当前组件的选项
export default {
//
name: 'App',
//从外部引入的组件变成局部组件
components: {
HelloWorld
}
//数据要使用工厂函数
data(){
return {
msg:'hello app'
}
}
}
4.项目文件夹下有一个 main.js 文件
//main.js是整个应用程序的入口文件
import Vue from 'vue' //引入Vue这个类
import App from './App.vue' //引入App这个根组件
Vue.config.productionTip = false //生产环境不提示
new Vue({
render: h => h(App),
}).$mount('#app') //创建Vue的实例 并挂载在#app上
后端路由:应用程序如何去处理不同的url地址发送过来的请求。
POST localhost:3000/api/login
GET localhost:3000/list
前端路由:不同的url地址要渲染不同的页面效果出来
1.1 GET localhost:8080/index.html
1.2 GET localhost:8080/list.html
1.3 GET localhost:8080/about.html //多页面应用
2.1 GET localhost:8080/index
2.2 GET localhost:8080/list
2.3 GET localhost:8080/about //单页面应用
3.1 GET localhost:8080/#/index
3.2 GET localhost:8080/#/list
3.3 GET localhost:8080/#/about //单页面应用
1.x 的是以往看到的最多的
2.x 与 1.x 的很类似, history 模式路由
3.x 修改hash的操作 hash 模式路由
后端路由与前端路由最本质的一个区别:
前端路由只处理 GET 请求
Vue 中 路由有两种不同的模式
hash模式与history模式的区别
如何切换模式呢
$ npm install vue-router
//模块引入
import Vue from 'vue' //引入Vue这个类
import VueRouter from 'vue-router' //引入App这个根组件
//注册使用路由
Vue.use(VueRouter)
//引入组件
import Todolist from './views/Todolist.vue'
const router = new VueRouter({
//定义路由匹配规则
routes:[
{path:'todo',
component:Todolist},
]
})
// 把路由实例抛出去
exports default router
// 把路由实例抛出去以后,要在main.js里面接收一下
import router from './router'
//main.js接收完以后,还要在vue的实例中挂载一下
new Vue({
router:router
render: h => h(APP)
}).$mount('#app')
router-link
与 router-view 一样,都是引入了 vue-router 之后,提供的全局组件
router-link的作用就是实现路由的跳转(url的跳转)它本质上就是一个a标签。我们推荐使用它,而不是去用a标签来跳转
语法:
使用router-link
的好处?
路径不需要去写 # 号了。它会根据当前路由的模式来决定最终渲染出去的a标签的href属性有没有# 号。
能够很方便的去实现高亮效果
默认情况下,当前的路由地址与某个router-link
的to属性匹配时,会给渲染出去的a元素添加上两个classs
router-link-exact-active
router-link-active
这两个类可以添加高亮效果
方案一、只需要去设置一下这两个类中的某个类的样式就可以了
方案二、设置 router-link 组件的 active-class 属性,属性值为某个class名字
active-class=“active”
exact-active-class=“exact-active”
{
path: "/",
// 重定向,值设置为一个要重定向到的路径
redirect: "/home",
},
//路由规则中配置 alias 配置项
{
{
path: "/home",
component: home,
alias: "/a",
},
{
path: "/list",
component: list,
alias: "/b",
},
{
path: "/about",
component: about,
alias: "/c",
},
}
在router.js里配置路由,代码如下:
```
{ path: '/detail/:abc/:title', component: Detail }
```
在编程式路由跳转,代码如下:
```
this.$router.push('/detail/'+id)
```
在详情页面中,如何接收动态参数?代码:
```
this.$route.params.id
const Home = ()=>import('@/views/Home.vue')
声明式路由跳转 ,方便实现高亮样式
编程式路由跳转 this.$router.push() / replace() / back()
$router
使用 vue-router 之后,在 vue 的原型上提供了一个 $router 对象
使用这个对象的一些方法来进行路由的跳转
this.$router.push()
- 就相当于普通的 router-link
- 它的参数可以是:
- 字符串,要跳转到路由的path
- 对象,对象中配置 path 属性。
push效果:[空白页, home,] => push(’/list’) => [空白页, home, list]
// 使用编程式导航的方式来跳转到 /home 这个路由
//1. 传字符串的形式
this.$router.push("/home");
//2. 传对象的形式
this.$router.push({
name: "abc", //先给路由取个名字
params: {
id: 1,
},//params不能和path一起使用,要配合name一起使用
});
//3.传对象错误的演示
//this.$router.push({
//path: "/detail",
// query: {
// name: "张三",
// age: 18,
// },
// params: {
// id: 1,
// }, ///params不能和path一起使用,要配合name一起使用
//});
this.$router.replace()
replace效果:[空白页, home,] => replace(’/list’) => [空白页, list ]
this.$router.back()
后退 history.back()
this.$router.go(num)
前进或后退,根据传递参数是正数还是负数来决定 history.go()
this.$router.forward()
前进 history.forward()
$route 与 $router 的区别
1.$route 是当前匹配的路由信息对象,可以通过他获取一些当前路由的一些信息。比如 params
+动态参数。vue 将 $route 挂到了每个组件的 data 数据里面。
2.$router 就是 new VueRouter() 生成的那个实例对象,vue 将他挂载到了 Vue 的原型上面
+所以每个组件都通过访问,然后使用来完成编程式导航的一些操作
嵌套路由,一般情况工作中,到两级足够了。尽量不要超过两级。
router-link的to永远写的都是完整路径(端口号后端的全部路径)
{
path: "/home",
component: home,
children:[
{ path: "cn",
component: Cn,
}
]
},
一、全局守卫(通过 new VueRouter() 生成的这个实例对象去使用)
全局前置守卫 beforeEach
router.beforeEach((to, from, next) => {
//to 代表着将要去的路由的信息对象
//from 当前导航正要离开的路由
//next 是一个函数,必须要调用 //1. next() 进入下一个
//2. next('/') next({ path: '/list' }) 重定向到某个路由
})
全局后置守卫 afterEach
router.afterEach((to, from) => {
//to 代表着将要去的路由的信息对象
//from 当前导航正要离开的路由
// afterEach 是在导航已经完成了触发。这时有 next 也没用。
})
全局解析守卫 beforeResolve
router.beforeResolve((to, from, next) => {
//to 代表着将要去的路由的信息对象
//from 当前导航正要离开的路由
//next 是一个函数,必须要调用 //1. next() 进入下一个
//2. next('/') next({ path: '/list' }) 重定向到某个路由
})//用的比较少
二、路由独享 (路由规则中的一个配置项)
beforeEnter
new VueRouter({
routes: [
{
path: '/hello',
component: hello,
beforeEnter: (to, from, next) => {
next()//next执行才可进入/hello页面
}
}
]
})
三、路由组件中 (在组件的配置选项中定义的,也就是与那个 data 、props 一起的那个)
export default {
name: "Cinemas",
beforeRouteLeave(to, from, next) {
console.log("beforeRouteLeave");
next();
},
beforeRouteUpdate(to, from, next) {
console.log("beforeRouteUpdate");
next();
},
beforeRouteEnter(to, from, next) {
console.log("beforeRouteEnter");
next();
}
};
一、/home -> /list
- /home 组件中的 beforeRouteLeave
- 全局前置 beforeEach
- /list 的配置中触发 beforeEnter
- /list 组件中的 beforeRouteEnter
- 全局后置 afterEach
二、/detail/1 -> /detail/2
- 全局前置 beforeEach
- detail 组件中的 beforeRouteUpdate
- 全局后置 afterEach
- 在全局前置的 beforeEach 中让进度条开始滚动
- 在全局后置的 afterEach 中让进度条完成
借助 nprogress 插件实现
安装指令
$ npm install --save nprogress
引入他的核心
import nprogress from 'nprogress '
import 'nprogress /nprogress .css'
//配置不转圈
nprogress.configure({
showSpinner:false,
})
nprogress.start() //开始
nprogress.done() //结束
方案一、
对个人中心页面配置路由独享守卫 beforeEnter ,在里面判断是否有登录
new VueRouter({
routes: [
{
path: '/hello',
component: hello,
beforeEnter: (to, from, next) => {
if(window.isLogined){
next()
}else{
next("/login")
}
}
}
]
})
方案二、
给需要身份认证的页面配置路由元信息 (meta),然后在全局前置守卫 beforeE ach 中处理即可。
元信息是给某个路由配置一些额外的信息
这时请使用推荐的方案二
state: 状态
getters: 对 state 的派生,可以理解为 store 的计算属性
mutations: 修改 state 的唯一操作就是提交 mutation 。
actions: 类似于 mutations , 用来处理异步。
modules: 对仓库 分割成 模块
$ npm install vuex
创建 src/store.js 文件。这个文件用来生效仓库的实例
// 1. 引入 Vue
import Vue from "vue";
// 2. 引入 Vuex
// vuex 完整的取出赋值给 Vuex
// vuex.Store 赋值给 Store
import Vuex, { Store } from "vuex";
// 3. !!进行调用!!
Vue.use(Vuex);
// 4. 实例化 Vuex 的一个 仓库实例对象
const store = new Vuex.Store({
// 配置选项
// 1. state
state: {},
// 2. getters 里面 设置 getter
getters: {},
// 3. mutations 里面 设置 mutation
mutations: {},
// 4. actions 里面 设置 action
actions: {},
// 5. modules 里面 设置 module
modules: {},
});
// end. 不要忘了暴露 store
export default store;
getters
getters里是以key:value形式存在
key 一个getter的名字
value 是一个函数,进行getter计算函数,与computed一样,必须有返回值,并且会自动接收到一个state这个参数,参数的内容就是当前仓库的数据
需要在 src/main.js 文件的 new Vue() 的位置去配置 store 选项,选项值是上一个步骤中暴露出来的 store 的实例
import Vue from "vue";
import App from "./App.vue";
//引入store
import store from "./store";
Vue.config.productionTip = false;
new Vue({
store,//挂载在Vue实例上
render: (h) => h(App),
}).$mount("#app");
$store
对象。这个 $store
就是 new Vuex.Store()
生成的仓库实例对象$store.state
$store.getters
方案二 (推荐)、使用辅助函数
组件中从 vuex 中引入你需要的辅助函数
调用辅助函数,并将其返回的值赋给组件的 computed 选项
<script>
// 引入 辅助函数 。采用类似 解构赋值的方式
import { mapState } from 'vuex'
export default {
// mapState 返回值是一个 对象
computed: mapState(['curCity', 'cart'])
}
script>
当本组件中也需要computed计算属性时?
<script>
// 引入 辅助函数 。采用类似 解构赋值的方式
import { mapState } from 'vuex'
export default {
// mapState 返回值是一个 对象
computed: {
...mapState(['curCity', 'cart']),
a:function(){
},
}
}
script>
mapState的语法和mapGetters的语法的区别
// mapState 接收一个数组做为参数,参数中的每一项,就是在仓库中的 state 数据
mapState([state1, state2, state3])
// mapGetters 接收一个数组做为参数,参数中的每一项,就是在仓库中的 getter 数据
mapGetters([getter1, getter2])
state可以修改,getter不能修改
步骤:
调用mutation的方案:
方案一:直接使用 vuex 绑定到 vue 原型上的 $store 这个对象的 commit() 方法
在store.js文件中
mutations:{
SETCURCITY(state,payload){
state.curCity=payload
}
}
在组建中
methods:{
fn1(){
this.$store.commit("SETCURCITY",'修改的内容')
}
}
方案二、使用 mapMutations 辅助函数
// mapMutations的语法
// 接收一个数组作为参数,数组中的每一项是一个 mutation 的名字
mapMutations([mutation1, mutation2, mutation3])
实例代码:
{
methods: mapMutations(["SETCURCITY", "ADDCART"])
}
转换代码:
{
methods: {
SETCURCITY(payload) {
this.$store.commit('SETCURCITY', payload)
},
ADDCART(payload) {
this.$store.commit('ADDCART', payload)
}
}
}
首先需要知道,mutation 里面只允许同步的去修改 state 数据。(虽然在mutation中可以异步的去修改state数据不会报错,但是会导致时间旅行等机制没有效果)
如果异步的修改的化,有两个大方案
不涉及action。在组件上异步代码走完之后再去调用 mutation
使用 action,使用 action。首先需要在 actions 选项中定义 action 函数
注意:action 中不能直接去修改 state,要修改是通过 context.commit() 去执行某个 mutation 来修改
方案一、直接使用 vuex 绑定到 vue 原型上的 $store 这个对象的 dispatch() 方法
在store.js文件中
actions:{
SYNCSETCURCITY({commit},payload){
setTimeout(()=>{
commit("SETCURCITY",payload)
},2000)
}
}
在组件中
methods:{
fn1(payload){
this.$store.dispatch(actionName, payload)
}
}
方案二、使用 mapActions 这个辅助函数
// mapActions 的语法
// 接收一个数组作为参数,数组中的每一项是一个 action 的名字
mapActions([action1, action2, action3])
示例代码
{
methods: mapActions(["SYNCSETCURCITY"])
}
转换之后如下所示
{
methods: {
SYNCSETCURCITY(payload) {
this.$store.dispatch('SYNCSETCURCITY', payload)
}
}
}
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成****模块(module)****。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
项目越做越大,功能点越写越多。需要使用 vuex 共享的数据越来越庞大时,就需要使用 module 来进行仓库模块拆分啦。
// 拆分的仓库子模块A
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... },
// 仓库子模块也可以继续去做拆分,但是没必要搞这么复杂
modules: {
aa,
ab,
}
}
// 拆分的仓库子模块B
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
// 仓库根模块
const store = new Vuex.Store({
// 通过 modules 选项配置子模块
modules: {
// key: value
// key - 仓库子模块的名字
// value - 对应的仓库子模块的对象
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的
给 仓库子模块的 那个对象配置一个 namespaced 属性。属性值为 true
模块内部的 action、mutation 和 getter 是注册在自己命名空间的
基本使用步骤不变,只是要处理处理命名空间
下面的 xx 代表着某个仓库子模块的名字
// 1. 直接通过 $store
this.$store.state.xx
// 2. computed
computed: {
name () {
return this.$store.state.xx.name
}
}
// 3. mapState
computed: {
...mapState('xx', [state1, state2, state3])
}
// 4. mapState 的转换
computed: {
state1() {
return this.$store.state.xx.state1
}
}
// !如果要在组件中同时拿到多个仓库子模块的同名state数据,不要使用 mapState 请使用方案二 !
// 1. 直接通过 $store
this.$store.getters['ma/firstName']
// 2. computed
computed: {
firstName() {
return this.$store.getters['ma/firstName']
}
}
// 3. mapGetters
computed: {
...mapGetters('xx', [getter1, getter2])
}
// 4. mapGetters 的转换
compouted: {
getter1() {
return this.$store.getters['xx/getter1']
}
}
// 1. 直接通过 $store
this.$store.commit('ma/SET_NAME', payload)
// 2. methods
methods: {
SET_NAME(payload) {
this.$store.commit('ma/SET_NAME', payload)
}
}
// 3. mapMutations
methods: {
...mapMutations('xx', [mutation1, mutation2])
}
// 4. mapMutations 的转换
methods: {
mutation1(payload) {
this.$store.commit('xx/mutation1', payload)
}
}
// 1. 直接通过 $store
this.$store.dispatch('ma/SYNC_SET_NAME', payload)
// 2. methods
methods: {
SYNC_SET_NAME(payload) {
this.$store.dispatch('ma/SYNC_SET_NAME', payload)
}
}
// 3. mapActions
methods: {
...mapActions('xx', [action1, action2])
}
// 4. mapActions 的转换
methods: {
action1(payload) {
this.$store.dispatch('xx/action1', payload)
}
}
getter语法
getters:{
//state - 当前模块的state
//getters - 当前模块的getters
//rootState - 跟模块的State数据,根据他可以去获取到其余模块的state数据
getter1(state,getters,rootState){
}
}
action语法
action:{
//context是一个对象,这个对象中有一些属性
// state - 当前模块的局部state
// getters - 当前模块的getters
// commit - 提交mutation的方法
// dispatch- 派发action的方法
// rootState- 跟模块的State数据,根据他可以去获取到其余模块的state数据
action(context,payload){
}
}