vue中MVVM的理解
M:模型(Model):数据模型;负责数据存储。泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开。
V就是:View 视图: 负责页面展示,也就是用户界面。主要由 HTML 和 CSS 来构建
VM就是:视图模型(View-Model): 负责业务逻辑处理(比如Ajax请求等),对数据进行加工后交给视图展示
通过vue类创建的对象叫Vue实例化对象,这个对象就是MVVM模式中的VM层,模型通过它可以将数据绑定到页面上,视图可以通过它将数据映射到模型上
优点
1.低耦合。视图(View)可以独立于Model变化和修改,
2.可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑
3.前后端分离,开发人员可以专注于业务逻辑(ViewModel)和数据的开发,设计人员可以专注于页面设计
为什么说VUE是一个渐进式的javascript框架, 渐进式是什么意思?
VUE允许你将一个网页分割成可复用的组件,每个组件都包含属于自己的HTML、CSS、JAVASCRIPT以用来渲染网页中相应的地方。对于VUE的使用可大可小,它都会有相应的方式来整合到你的项目中。所以说它是一个渐进式的框架。VUE是响应式的(reactive)这是VUE最独特的特性,也就是说当我们的数据变更时,VUE会帮你更新所有网页中用到它的地方。
vue生命周期
beforeCreate(创建前) :组件实例被创建之初,组件的属性生效之前
//beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
created(创建后) :组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
// data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
beforeMount(挂载前) :在挂载开始之前被调用:相关的 render 函数首次被调用
//执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
mounted(挂载后) :在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
//到mounted周期的时候,Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
beforeUpdate(更新前) :组件数据更新之前调用,真实DOM还没被渲染
// 当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的,页面还没有和最新的数据保持同步
update(更新后) :组件数据更新之后
//页面显示的数据和data中的数据已经保持同步了,都是最新的
activated(激活前) :keep-alive专属,组件被激活时调用
//当组件被切回来时,再去缓存里找这个组件、触发 activated钩子函数。
deactivated(激活后) :keep-alive专属,组件被销毁时调用
//当组件被换掉时,会被缓存到内存中、触发 deactivated 生命周期
beforeDestory(销毁前) :组件销毁前调用
//Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
destoryed(销毁后) :组件销毁前调用
//这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
Vue子组件和父组件执行顺序
加载渲染过程:beforeCreate(父) —> created(父)—>beforeMount(父)—>beforeCreate(子)—>created(子)—>beforeMount(子)—>mounted(子)—>mounted(父)
更新过程:beforeUpdate(父) —> beforeUpdate(子) —> update(子) —> update(父)
父组件更新:beforeUpdate(父) —> updated(父)
销毁过程:beforeDestory(父) —> beforeDestory(子) —> destoryed(子) —> destoryed(父)
v-el 作用是什么
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
Vue的el属性和$mount优先级?
new Vue({
router,
store,
el: '#app',
render: h => h(App)
}).$mount('#div')
/*当出现上面的情况就需要对el和$mount优先级进行判断,从下面的官方图片我们可以看出来,
el的优先级是高于$mount的,因此以el挂载节点为准*/
Vue实现数据双向绑定的原理:Object.defineProperty()
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
数据双向绑定示例:
<body>
<div id="app">
<input type="text" id="txt">
<p id="show"></p>
</div>
</body>
<script type="text/javascript">
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
</script>
假如data里面的数据不想做响应式,该怎么做
1、数据放在vue实例外(vue template中访问不到数据)
2、created, mounted钩子函数中定义(注意data中不要声明该变量名)
3、自定义Options
4、Object.freeze()
如何将获取data中某一个数据的初始状态?
data() {
return {
num: 10
},
mounted() {
this.num = 1000
},
methods: {
countNum() {
// 可以通过this.$options.data().keyname来获取初始值
// 计算出num增加了多少
console.log(1000 - this.$options.data().num)
}
}
动态给vue的data添加一个新的属性时为什么不刷新?怎样解决?
**原因:**一开始data里面的obj的属性会被设成了响应式数据,而后面新增的属性,并没有通过Object.defineProperty设置成响应式数据,所以当我们动态添加的时候并不会刷新
解决:
1、Vue.set()
/*参数
{Object | Array} target
{string | number} propertyName/index
{any} value*/
Vue.set(this.list, 0, {name: 'zhangsan', age: 18})
this.$set(this.list, 0, {name: 'zhangsan', age: 18})
//如果是在组件中使用,就不能直接使用Vue实例来调用,但是可以使用this
2、Object.assign()
//直接使用Object.assign()添加到对象的新属性不会触发更新应创建一个新的对象,合并原对象和混入对象的属性
this.someObject = Object.assign({},this.someObject,{newProperty1:1,newProperty2:2 ...})
3、$ forcecUpdated()
//$forceUpdate迫使Vue 实例重新渲染,且仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
小结:
- 如果为对象添加少量的新属性,可以直接采用Vue.set()
- 如果需要为新对象添加大量的新属性,则通过Object.assign()创建新对象
- 如果你实在不知道怎么操作时,可采取$forceUpdate()进行强制刷新 (不建议)
PS:vue3是用过proxy实现数据响应式的,直接动态添加新属性仍可以实现数据响应式
Vue.observable
Vue.observable,让一个对象变成响应式数据。Vue 内部会用它来处理 data 函数返回的对象返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器
使用场景:
/*在非父子组件通信时,可以使用通常的bus或者使用vuex,但是一些小项目上功能不是太复杂,
而使用上面两个又有点繁琐。这时,observable就是一个很好的选择*/
// 先新建一个js文件,引入vue
import Vue from 'vue
// 创建state对象,使用observable让state对象可响应
export let state = Vue.observable({
name: '张三',
age: 18
})
// 创建对应的方法
export let mutations = {
changeName(name) {
state.name = name
},
setAge(age) {
state.age = age
}
}
//在需要使用的文件中获取
<template>
<div>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<button @click="changeName('李四')">改变姓名</button>
<button @click="setAge(20)">改变年龄</button>
</div>
</template>
import { state, mutations } from '@/store
export default {
// 在计算属性中拿到值
computed: {
name() {
return state.name
},
age() {
return state.age
}
},
// 调用mutations里面的方法,更新数据
methods: {
changeName: mutations.changeName,
setAge: mutations.setAge
}
}
简单实现数据响应(数据劫持)
var demo={
name:'张三'
}
//Object.defineProperty(obj,prop,descriptor)
//obj:目标对象,prop:需要新增,或者修改的属性名,descriptor:定义的特性
Object.defineProperty(demo,'name',{
set(value){
console.log('重新设为:'+value)
name=value
},
get(){
return name
}
})
demo.name='李四'
console.log(demo.name)//李四
动态指令设置及动态传参
<template>
...
<child @[someEvent]="handleSomeEvent()" :[someProps]="1000" />...
</template>
<script>
...
data(){
return{
...
someEvent: type ? "click" : "dbclick",
someProps: type ? "num" : "price"
}
},
methods: {
handleSomeEvent(){
// do some
}
}
</script>
//应用场景:用于页面中根据不同的返回值进行事件的触发和值的传参
Vue组件间的参数传递
1.父组件传给子组件:子组件通过props方法接受数据;
2.子组件传给父组件:$emit方法传递参数
3.非父子组件间的数据传递,兄弟组件传值借用eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。发送数据使用 $emi t方法,使用 $on 接收
更多详细内容可以查看我的另一篇文章Vue父子组件间传值方法集
provide和inject使用(响应式)
provide和inject是用来实现父组件向深层的子组件传值和接收的语法,具体如下:
// 祖先组件
provide(){
return {
// keyName: { name: this.name }, // value 是对象才能实现响应式,也就是引用类型
keyName: this.changeValue // 通过函数的方式也可以[注意,这里是把函数作为value,而不是this.changeValue()]
// keyName: 'test' value 如果是基本类型,就无法实现响应式
}
},
data(){
return {
msg:'初始mesg'
}
},
methods: {
changeValue(){
this.msg= '改变后的msg'
}
}
// 后代组件
inject:['keyName']
create(){
console.log(this.keyName) // 改变后的msg
}
详情参考我的这篇文章
provide / Inject浅析
Vue的路由实现:hash模式 和 history模式
hash模式: 在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
history模式: history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
$nextTick原理及运用
1.nextTick是啥?
Vue.nextTick( [callback, context] ):在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
2.为什么需要它呢?
Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop)
当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。
这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
假使你设置 vm.someData = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环
开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。
为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。
这样回调函数在 DOM 更新完成后就会调用。
3.我在什么地方用它呢?
1、在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。
原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,
所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,
因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
2、在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候
(譬如v-if/v-show根据字段变化显隐),这个操作都应该放进Vue.nextTick()的回调函数中。
Compute和watch区别和应用场景
computed
//计算属性中的属性不需要在data中定义,而且必须有return
data(){
return{
firstname:"张",
lastname:"三"
}
}
computehd(){
fullname(){
return this.firstname+this.lastname
}
}
/*计算属性具有缓存,计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值。
只要计算属性的依赖没有改变,那么调用它就会直接返回之前的缓存。 同时computed对于其中变量的依赖时多个
的时候,只要其中一个发生了变化都会触发这个函数*/
//应用场景:当一个变量的值受多个变量的值影响
watch
//监听器watch中的值需要在data中定义,且函数有参数,newval和oldval
data: {
firstName: '张',
lastName: '三',
fullName: '张三r'
},
watch: {
firstName: function (oval,nval) {
this.fullName = nval + ' ' + this.lastName
},
lastName: function (oval,nval) {
this.fullName = this.firstName + ' ' + nval
},
immediate: true,// 代表在wacth里声明了firstName之后立即先去执行其函数方法
deep: true //深度监听
}
//watch的依赖是单个的,它每次只可以对一个变量进行监控,并且区别于computed属性,监听器watch可以是异步的而computed则不行
//应用场景:当一个变量的值影响着多个变量的值
filters
//过滤器分为全局过滤和局部过滤,当命名冲突时以局部过滤器权重高
//插值中
{{msg|filterMsg}}
//bind中
<div v-bind:"id|filterId"></div>
//运用场景:
//一般来说我们用过滤器来格式化一些数据或者渲染的文本对于格式展现的要求
全局:
Vue.filter('过滤器名',function(value){
//do some
})
局部:
filters:{
过滤器名称:function(value){
//do some
}
}
vuex
vuex是什么?怎么使用?哪种功能场景使用它?
//是什么
vue框架中状态管理。在main.js引入store注入。新建一个目录store 。场景有:单页应用中,组件之间的状态,
音乐播放、登录状态、加入购物车等。
//属性:
State、 Getter、Mutation 、Action、 Module
//State
state是数据源存放地,对应于一般Vue对象里面的data。state里面存放的数据是响应式的,
Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
需要通过mapState把全局 state 和 getters 映射到当前组件的 computed 计算属性中。
//Getter
getters 可以对State进行计算操作,在多个组件间复用
//Mutation 、Action
Action 类似于 mutation,不同在于Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。
//Module
Vuex允许我们将store分隔成模块(module),每个模块拥有自己的state,mutation,action,getter,甚至是嵌套子模块
//使用场景
一句话,不要为了使用vuex而去使用vuex,推荐组件间数据复用,记录登录及其它状态值数据,一些需要缓存的数据使用vuex都能达到很好的管理
v-show 与 v-if 的区别,两者的优先级
v-show指令是通过修改元素的display的CSS属性让其显示或者隐藏;
v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果;
使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
//优先级
v-for优先级比v-if高(vue2.x中)
//注意事项
不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
正确的做法应该是再v-for的外面新增一个模板标签template,在template上使用v-if也能结合filters或者是computed属性对数据进行加工,避免v-if判断,更好的渲染
注意:在vue2.x中,v-for的优先级会比v-if的优先高,但在vue3.x中这一判断优先级被换了过来,在3.x的环境下v-if优先级更高,除此之外v-for和v-if在同一标签中使用也会报错,解决办法可以在外面套一个template标签,或者使用computed来对数组进行过滤然后渲染
vue中key的原理及其必要性
vue中key作用:key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
key是每一个节点的唯一标识
必要性:当我们对数据进行更新的时候,譬如在数组中插入、移除数据时,设置的key值能让vue底层高效的对新旧vnode进行diff,然后将比对出的结果用来更新真实的DOM
vue路由传参
//通过 params 传参
this.$router.push({
name: '目标组件名',
params: {
id: id
}
})
//接收:
this.$route.params
//通过 query 传参
this.$router.push({
path: '目标组件路径',
query: {
id: id
}
})
//接收
this.$route.query
//区别:query使用path来引入,params使用name来引入,接收方式是this.$route.query.name和this.$route.params.name,值得注意的是query传递的参数会显示在url后面以?id=?形式展示。
//动态路由传参
//直接调用$router.push 实现携带参数的跳转
this.$router.push({
path: `/particulars/${id}`,
})
//通过this.$route.params.id接收,可以看到,和上面传参不一样的是我们直接把动态参数加在路径后面实现动态路由
vue路由的钩子函数
导航钩子种类
全局导航钩子、组件内钩子、单独路由独享组件
//路由的钩子函数总结有6个
全局的路由钩子函数:beforeEach、afterEach
单个的路由钩子函数:beforeEnter
组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate
全局导航钩子
全局前置守卫:beforeEach
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法不然会阻塞路由。执行效果依赖 next 方法的调用参数。
next()方法接收的参数:
全局后置钩子:afterEach
router.afterEach((to, from) => {
// do someting
});
//后置钩子并没有 next 函数
路由独享的钩子
路由独享的导航钩子,它是在路由配置上直接进行定义的,参数的使用,和全局前置守卫是一样的
使用的钩子函数与全局路由守卫一致,为beforeEnter,不同的是,路由独享守卫是定义在路由记录中,全局路由守卫是定义在入口文件中,路由独享守卫只在路由进入时有效,全局路由守卫是所有路由跳转都会被拦截。
组件内的导航钩子
beforeRouteEnter:在渲染该组件的对应路由前调用
beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用
beforeRouteLeave:导航离开该组件的对应路由时调用
//注意:beforeRouteEnter 不能获取组件实例 this,因为当守卫执行前,组件实例被没有被创建出来,剩下两个钩子则可以正常获取组件实例 this
共享组件将不会重新渲染问题
我们有时候开发中会把多个路由解析为同一个Vue组件。问题是,Vue默认情况下共享组件将不会重新渲染,如果你尝试在使用相同组件的路由之间进行切换,则不会发生任何变化,此时我们需要传递key来区分,达到刷新的目的
const routes = [
{
path: "/a",
component: MyComponent
},
{
path: "/b",
component: MyComponent
},
];
<template>
<router-view :key="$route.path"></router-view>
</template>
v-slot插槽
插槽就是子组件中用slot标签定义的预留位置,有name属性叫具名插槽,不设置name属性的叫不具名插槽,使用插槽主要是为了在父组件中使用子组件标签的时候可以往子组件内写入html代码。
插槽使用:
//父组件:
<template>
<div>
<div>这是父组件</div>
<son>slot内容</son>
</div>
</template>
//子组件
<template>
<div>
<div>这是子组件</div>
<input type="text" placeholder="请输入">
</div>
</template>
//一般情款下想在子组件内插入内容像上面直接在标签里书写时不显示的需要以slot为媒介
//改写后:
//子组件:
<template>
<div>
<div>这是子组件</div>
<input type="text" placeholder="请输入">
<slot></slot>
</div>
</template>
//此时我们没有给插槽设置name,所以这是一个不具名插槽
//具名插槽:
//父组件:
<template>
<div>
<div>这是父组件</div>
<son>
<template slot="myslot">
<div>
实践具名slot
</div>
</template>
</son>
</div>
</template>
//子组件
<template>
<div>
<div>这是子组件</div>
<input type="text" placeholder="请输入">
<slot name="myslot"></slot>
</div>
</template>
//此时设置name属性的插槽为具名插槽,与之相对应的用了slot的设置为相同属性名的内容则会被渲染在插槽中,此时如果有未设置slot插槽名的内容则会被渲染在不具名插槽中
插槽作用域:
//父组件:
<template>
<div>
<div>父组件</div>
<son>
<template slot="myslot" slot-scope="props">
<ul>
<li v-for="item in props.data">
{{item}}
</li>
</ul>
</template>
</son>
</div>
</template>
//子组件:
<template>
<div>
<div>子组件</div>
<input type="text" placeholder="请输入">
<slot name="myslot" :data='list'></slot>
</div>
</template>
<script>
export default {
name:'Son',
data(){
return{
list:[
{name:"张三",age:3},
{name:"李四",age:4},
{name:"王五",age:5}
]
}
}
}
</script>
相关v-slot的详细文章大家可以看我这篇博文→v-slot插槽浅析传送门
mixins
mixins是一种分发Vue组件中可复用功能的一种灵活方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
mixins是一个JavaScript对象,可以包含组件中的任意选项,比如Vue实例中生命周期的各个钩子函数,也可以是data、components、methods或directives等
运用:
//mixin文件
export const myMixin={
data(){
return{
msg:1
}
},
created(){
console.log('myMixin')
},
methods:{
Fn(){
console.log('myMixin')
}
}
}
//引入
<template>
<div>运用mixin的组件</div>
</template>
<script>
import {myMixin} from'目标文件路径'
export default{
mixins:[myMixin]
}
</script>
特点:
1、在组件A对混入的数据做出更改后组件B获取到的仍是混入初始设置的数据,组件间操作互不污染。
2、值为对象的如methods,components等,选项会被合并,组件会覆盖混入对象的方法。
比如混入对象里有个方法A,组件里也有方法A,这时候在组件里调用的话,执行的是组件里的A方法。
3、created,mounted等,就会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用,
同一个钩子函数里,会先执行混入对象的东西,再执行本组件的。
4、在mixins里面包含异步请求函数的时候,通过直接调用异步函数获取返回数据
运用场景区别:
vuex:用来做状态管理,可以看做全局变量,里面定义的变量在每个组件中均可以使用和修改,
在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改。
mixins:可以定义共用的变量,在每个组件中使用,引入组件中之后,各个变量是相互独立的,
值的修改在组件中不会相互影响。
父子组件:父子组件相对来说比较独立,只是父组件将一部分使用子组件,而mixins更像是对于组件的拓展,并且
组件可以对于混入的数据和方法进行多样化操作。
vue自定义组件添加事件
使用修饰符.native 监听组件根元素的原生事件
<my-button @click.native="alert()" names="点击触发"></my-button>
axios
//axios特点:
在浏览器中创建XMLHttpRequest请求
在node.js中发送http请求
支持Promise API
拦截请求和响应
转换请求和响应数据
取消要求
自动转换JSON数据
客户端支持防止CSRF/XSRF(跨域请求伪造)
//axios的请求方式:
axios(config)
axios.request(config)
axios.get(url [,config])
axios.post(url [,data [,config]])
axios.put(url [,data [,config]])
axios.delete(url [,config])
axios.patch(url [,data [,config]])
axios.head(url [,config])
axios一般用法配置与请求
//引入axios
import axios from 'axios'
//定义axios请求接口的baseURL
axios.default.baseURL = 'http://localhost:8080/api/products'
//执行GET请求
axios.get('/user?ID=12345') //返回的是一个Promise
.then(res=>console.log(res))
.catch(err=>console.log(err));
//可配置参数的方式
axios.get('/user',{
params:{
ID:12345
}
}).then(res=>console.log(res))
.catch(err=>console.log(err));
//发送post请求
axios.post('/user',{
firstName: 'simon',
lastName:'li'
}).then(res=>console.log(res))
.catch(err=>console.log(err));
//发送post请求
axios({
method: 'post', //请求方式,默认是get请求
url:'/user/12345', //地址
data:{ //参数
firstName: 'simon',
lastName: 'li'
}
});
发送并发请求:
//发送多个请求(并发请求),类似于promise.all,若一个请求出错,那就会停止请求
const get1 = axios.get('/user/12345');
const get2 = axios.get('/user/12345/permission');
axios.all([get1,get2])
.then(axios.spread((res1,res2)=>{
console.log(res1,res2);
}))
.catch(err=>console.log(err))
//函数返回的是一个数组axios.spread(callback)可用于将结果数组展开
请求配置:
{
//服务器的地址,是必须的选项
url: '/user',
//请求的方式,若没有则默认是get
method:'get',
//如果url不是绝对地址,则会加上baseURL
baseURL: 'http://localhost:3000/',
//transformRequest允许请求的数据在发送至服务器之前进行处理,这个属性只适用于put、post、patch方式
//数组的最后一个函数必须返回一个字符串或者一个'ArrayBuffer'或'Stream'或'Buffer' 实例或'ArrayBuffer','Formdata',
//若函数中用到了headers,则需要设置headers属性
transformRequest: [function(data,headers){
//根据需求对数据进行处理
return data;
}],
//transformResponse允许对返回的数据传入then/catch之前进行处理
transformResponse:[function(data){
//依需要对数据进行处理
return data;
}],
//headers是自定义的要被发送的信息头
headers: {'X-Requested-with':'XMLHttpRequest'},
//params是请求连接中的请求参数,必须是一个纯对象
params:{
ID:12345
},
//paramsSerializer用于序列化参数
paramsSerializer: function(params){
return Qs.stringify(params,{arrayFormat:'brackets'});
},
//data是请求时作为请求体的数据——request.body
//只适用于put、post、patch请求方法
//浏览器:FormData,File,Blob;Node:stream
data:{
firstName: 'simon',
},
//timeout定义请求的时间,单位是毫秒,如果请求时间超过设定时间,请求将停止
timeout:1000,
//withCredentials表明跨跨域请求书否需要证明。
withCredentials:false, //默认值
//adapter适配器,允许自定义处理请求
//返回一个promise
adapter:function(config){
/*...*/
},
//auth表明HTTP基础的认证应该被使用,并提供证书
auth:{
username:'simon',
password:'123456',
},
//responseType表明服务器返回的数据类型,这些类型包括:json/blob/document/ arraybuffer/text/stream
responseType: 'json',
//proxy定义服务器的主机名和端口号
//auth属性表明HTTP基本认证应该跟proxy相连接,并提供证书
//这将设置一个'Proxy-Authorization'头(header),覆盖原来自定义的
proxy:{
host:127.0.0.1,
port:8080,
auth:{
username:'simon',
password:'123456'
}
},
//取消请求
cancelToken: new CancelToken(cancel=>{})
}
ajax
详情可以查看我之前写过的ajax封装文章,详细了说明封装过程ajax优劣分析及封装
keep-alive
活动组件实例在切换离开时将被卸载。这将导致它持有的任何更改状态丢失
我们在开发过程中经常会有tab切换的这种需求,此时如果说有接口请求或者组件的刷新将会导致我们原来的数据重置,如果不想组件重置数据,我们可以用< keep-alive >标签包裹
上面是官网的一个例子,当从A切换到B组件再切回A时候,count计数将会重置,但是当用< keep-alive >包裹时候,再切换,我们会发现count能够得以保留了
//Inactive components will be cached!
<keep-alive>
<component :is="activeComponent" />
</keep-alive>
includes和exclude:
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
/*匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称
(父组件 components 选项的键值),匿名组件不能被匹配*/
设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activated与deactivated):
- 首次进入组件时:beforeRouteEnter > beforeCreate > created> mounted >
activated > … … > beforeRouteLeave > deactivated - 再次进入组件时:beforeRouteEnter >activated > … … > beforeRouteLeave >
deactivated
Vue常用的修饰符及其使用
修饰符是用于限定类型以及类型成员的声明的一种符号
常见修饰符种类:
- 表单修饰符
- 事件修饰符
- 鼠标按键修饰符
- 键值修饰符
- v-bind修饰符
1、表单修饰符
//lazy、trim、number
//光标离开标签的时候,才会将值赋予给value
<input type="text" v-model.lazy="value">
//过滤用户输入的首尾空格字符,注意,如果是中间的空格则不会过滤
<input type="text" v-model.trim="value">
//自动将用户的输入值转为数值类型,但如果这个值无法被parseFloat解析,则会返回原来的值
<input v-model.number="age" type="number">
2、事件修饰符
//stop、prevent、self、once、capture、passive、native
//阻止了事件冒泡
<button @click.stop="btn()">ok</button>
//阻止了事件的默认行为
<form v-on:submit.prevent="onSubmit"></form>
//只当在 event.target 是当前元素自身时触发处理函数
<div v-on:click.self="doSome">...</div>
//事件只能触发一次
<button @click.once="btn()">ok</button>
//事件触发从包含这个元素的顶层开始往下触发 //输出结构: 1 2
<div @click.capture="btn(1)">
1
<div @click.capture="btn(2)">
2
</div>
</div>
/* 在移动端,当我们在监听元素滚动事件的时候,会一直触发onscroll事件会让我们的网页变卡,
因此我们使用这个修饰符的时候,相当于给onscroll事件整了一个.lazy修饰符*/
<div v-on:scroll.passive="onScroll">...</div>
/*让组件变成像html内置标签那样监听根元素的原生事件,否则组件上使用 v-on 只会监听自定义事件
注意:使用.native修饰符来操作普通HTML标签是会令事件失效的*/
<my-component v-on:click.native="doSomething"></my-component>
3、鼠标按钮修饰符
//left 左键点击、right 右键点击、middle 中键点击
<button @click.left="btn('left')">ok</button>
<button @click.right="btn('right')">ok</button>
<button @click.middle="btn('middle')">ok</button>
4、键盘修饰符
//onkeyup,onkeydown,后面需要跟keycode编码名或者按键编码
<input type="text" @keyup.keyCode="btn()">
5、v-bind修饰符
//async、prop、camel
//能对props进行一个双向绑定
//父组件
<comp :myMessage.sync="bar"></comp>
//子组件
this.$emit('update:myMessage',params);
//上面代码与以下代码是等同的,实际上就是实现了获取子组件信息处理并回传
//父亲组件
<comp :myMessage="bar" @update:myMessage="func"></comp>
func(e){
this.bar = e;
}
//子组件js
func2(){
this.$emit('update:myMessage',params);
}
注意:
1、使用sync的时候,子组件传递的事件名格式必须为update:value,其中value必须与子组件中props中
声明的名称完全一致
2、注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用
3、将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的
//设置自定义标签属性,避免暴露数据,防止污染HTML结构
<input id="uid" title="title1" value="1" :index.prop="index">
//将命名变为驼峰命名法,如将view-Box属性名转换为 viewBox
<svg :view-Box.camel="viewBox"></svg>
SSR
什么是ssr:Server-Side Rendering 我们称其为SSR,意为服务端渲染,展开说就是通过服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程
ssr作用:
- seo:搜索引擎优先爬取页面HTML结构,使用ssr时,服务端已经生成了和业务想关联的HTML,有利于seo
- 首屏呈现渲染:用户无需等待页面所有js加载完成就可以看到页面视图(压力来到了服务器,所以需要权衡哪些用服务端渲染,哪些交给客户端)
缺点:
- 项目复杂度高
- 需要库的支持性,代码兼容
- 服务器负载变大,相对于前后端分离务器只需要提供静态资源来说,服务器负载更大