文章目录
- VUE
- vue -router
- 小点
VUE
vue的核心
【vue最大的特点或者vue核心是什么?】
vue最大特点我感觉就是“组件化“和”数据驱动“
组件化就是可以将页面和页面中可复用的元素都看做成组件,写页面的过程,就是写组件,然后页面是由这些组件“拼接“起来的组件树
数据驱动就是让我们只关注数据层,只要数据变化,页面(即视图层)会自动更新,至于如何操作dom,完全交由vue去完成,咱们只关注数据,数据变了,页面自动同步变化了,很方便
vue常用指令
v-if:根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。
v-show:根据表达式之真假值,切换元素的 display CSS 属性。
v-for:循环指令,基于一个数组或者对象渲染一个列表,vue 2.0以上必须需配合 key值 使用。
v-bind:动态地绑定一个或多个特性,或一个组件 prop 到表达式。
v-on:用于监听指定元素的DOM事件,比如点击事件。绑定事件监听器。
v-model:实现表单输入和应用状态之间的双向绑定
v-pre:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
v-once:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
Vue修饰符
【vue中常用的修饰符】
v-on 指令常用修饰符
.stop - 调用 event.stopPropagation(),禁止事件冒泡。
.prevent - 调用 event.preventDefault(),阻止事件默认行为。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
如果是在自己封装的组件或者是使用一些第三方的UI库时,会发现并不起效果,这时就需要用`·.native修饰符了
<el-input v-model="inputName" placeholder="搜索你的文件" @keyup.enter.native="searchFile(params)" > </el-input>
v-model 指令常用修饰符:
.lazy - 取代 input 监听 change 事件
.number - 输入字符串转为数字
.trim - 输入首尾空格过滤
vue自定义指令
【使用场景:】
vue除有了v-for,v-if等自带vue指令外,但不能满足所有的开发需求,有时需要自定义指令
-
全局自定义指令:Vue.directive(‘指令名’,{ inserted(el) { } })
-
局部自定义指令:directives:{ }
vue过滤器
【作用:】主要是用于对渲染出来的数据进行格式化处理
【使用场景:】
后台返回的数据性别用0和1表示,但渲染到页面上不能是0和1我得转换为“男“和”女”,这时就会用到过滤器,还有商品价格读取出来的是普通数值,例如:230035,但我要在前面加个货币符号和千分分隔等,例如变成:¥230,035,都得需要vue过滤器。
【使用:】跟创建自定义指令类似,也有全局和局部过滤器的形式
// 全局过滤器:
Vue.filter('过滤器名',function(参数1,参数2,…) {
//………..
return 要返回的数据格式
})
// 局部过滤器:在组件内部添加filters属性来定义过滤器
fitlers:{
过滤器名(参数1,参数2,,…参数n) {
//………..
return 要返回的数据格式
}
}
vue生命周期
主要分为四个阶段:创建、挂载、更新、销毁
-
创建
创建前:beforeCreate =》vue实例被完全初始化前调用。data和methods不可以使用
创建后:created =》实例被完全初始化后调用。data和methods可以使用
-
挂载
挂在前:beforeMount =》模板已经在内存编译好了,还未挂载到页面
挂载后:mounted =》内存中的模板已经被挂载到页面,用户看到的就是已经渲染好的页面
-
更新
更新前:beforeUpdate =》数据更新但是页面还未更新
更新后:updated =》数据和页面都已经更新了
-
销毁
销毁前:beforeDestory =》实例从运行阶段进入销毁阶段,此时data和methods还是可以使用
销毁后:destoryed =》实例组件完全被销毁,此时data和methods不可以使用了
我平时用的比较多的钩了是created和mounted。
- created用于获取后台数据
- mounted用于dom挂载完后做一些dom操作,以及初始化插件等.beforeDestroy用户清除定时器以及解绑事件等
除了上面的钩子函数之外,因为vue中还新增了keep-alive
来缓存组件,因此还新增了active
和deactived
两个声明周期钩子
- actived :实例激活 =》keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用。
- deactived: 实例失效 =》keep-alive 组件停用时调用。该钩子在服务器端渲染期间不被调用。
MVVM
Model层
- 数据层
- 数据可能是我们固定的死数据,也可以是来自服务器上(从网络上下载的数据)
- 在计数器的案例中相当于抽取的obj
View层
- 视图层,主要作用就是给用户展示各种数据
- 在前端开发中就是我们的DOM层
- 在计数器案例中就是我们的html标签+css样式组成的前台界面
VueModel层
- 视图模型层,是view和model沟通的桥梁
- 一方面可以实现dataBinding,将Model中数据的变化实时反映到View中
- 另一方面实现了DOM Listener,当DOM发生一些事件时(点击、滚轮、touch等),可以监听到,并在需要的情况下改变对应的data
vue的响应式原理
- 遍历每一个data属性给
- 当前属性创建Dep实例(data中的属性和dep是一一对应的关系)
- 通过通过defineProperty()给该属性添加对应的set和get,从而来监该属性的变化
- 每次获取属性值时,就会调用属性的getter,对watcher进行判断并添加到该属性的dep中(依赖收集器中)
- 每次更新属性值时,就会调用该属性的setter,并通知当前属性的dep所关联到的所有watcher进行更新
vue中的模板解析
-
首先会根据我们在创建vue实例中的配置参数里面的el,来去页面查找对应的标签。
-
将该标签下所有的子元素都拷贝到一个容器中。(documentFragment节点中)
-
遍历该容器下所有的元素。
分别对每一个元素进行判断,判断它是文本节点(nodeType=3)还是元素节点(nodeType=1)
【元素节点】:就获取该元素的所有属性,并对获取的所有属性判断他是否是指令(v-开头)。如果是指令,就判断它是一般指令还是事件指令(v-on开头)。如果是事件指令,就获取对应的事件名和事件处理回调。并通过addEventListener进行绑定;如果是一般指令,则根据一般指令的类型来操作对应的dom。比如v-text =》 textContent; v-html =》 innerHtml ; v-class =》class属性
【文本节点】:获取该文本节点的textContent的值,并判断它里面是否有{{}}},如果有,则说明这个是一个大括号表达式,此时就根据大括号里面的值从data里面获取对应的数据。然后在通过操作dom的textContent赋值给这个节点即可
单向数据流
单向数据流:主要是vue 组件间传递数据是单向的,即数据总是由父组件传递给子组件,子组件在其内部维护自己的数据,但它无权修改父组件传递给它的数据,当开发者尝试这样做的时候,vue 将会报错。这样做是为了组件间更好的维护。
在开发中可能有多个子组件依赖于父组件的某个数据,假如子组件可以修改父组件数据的话,一个子组件变化会引发所有依赖这个数据的子组件发生变化,所以 vue 不推荐子组件修改父组件的数据
pros赋值给data
用props初始化data中变量,data无法随着props的变化而更新
【解决办法】:在computed中对props值转换后输出
methods, computed、watch
methods
methods中的函数主要用于执行具体的业务逻辑,是没有缓存的。只要达到触发条件就会执行
【methods执行时机】:只要达到触发条件就会执行
在methods中不要使用箭头函数,因为箭头函数绑定了父级作用域的上下文。
【执行顺序:】
- 默认加载:先computed再watch,不执行methods
- 触发某一事件后:先methods再watch。
【为什么要用缓存?什么时候用缓存比较合适?】
假如我们需要遍历一个巨大的数组并做出了大量的运算得到了属性A,如果页面中有多个地方都需要使用到这个属性A
- 如果是使用的计算属性的话,那个即使多次调用计算属性中的方法,它还是只会执行一次。
- 如果是使用的方法,那么属性A使用多少次就会被调用多少次
computed
- 它是基于data 或 props 中的数据通过计算得到的一个新值,这个新值根据已知值的变化而变化;
- computed 属性值默认会缓存计算结果,在重复的调用中,只要依赖数据不变,直接取缓存中的计算结果,只有依赖型数据发生改变,computed 才会重新计算;
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用 computed
比如:例如处理模板中的复杂表达式、购物车里面的商品数量和总金额之间的变化关系等。
watch
-
watch是监听data,props,computed中的新旧变化。并且当数据发生变化时,执行某些具体的业务逻辑操作支持异步。
-
相对于computed来讲,watch是可以知道该数据变化前和变化后的值
-
不支持缓存,数据变,直接会触发相应的操作
-
当一个属性发生变化时,需要执行对应的操作;一对多
比如:城市联动、监控路由、inpurt 输入框值的特殊处理等。
注意:
watch回调函数不可以使用箭头函数,因为箭头函数绑定了父级作用域的上下文,所以this 不是指向 Vue 实例。
mutation和action
Mutation:专注于修改State,理论上是修改State的唯一途径。
Action:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
style scoped原理
【作用:】局部化css
【原理:】
-
给
HTML
的DOM
节点加一个不重复data
属性(形如:data-v-2311c06a)来表示他的唯一性 -
在每句
css
选择器的末尾(编译后的生成的css语句)加一个当前组件的data
属性选择器(如[data-v-2311c06a])来私有化样式scoped 实质就是添加了属性选择器增加了10的权重
【scope穿透】:或者说 vue第三方ui样式库如何实现样式穿透的
scoped 这个属性就是专门用于实现样式的模块化的,使用这个属性意味着样式不能经过外部或者全局的调整,在使用之初就应该规划好。
但有时候我们可能需要引入第三方组件,修改他的样式又不想去除 scoped 造成组件之间的样式污染,所以才有了下面的特殊用法。
如果你希望 scoped 样式中的一个选择器能够作用得“更深”,例如影响子组件,你可以使用 >>> 操作符
<style scoped>
/**方法一:**/
.外层 >>> .第三方组件 { /* ... */ }
.wrapper >>> .swiper-pagination-bullet-active{
background: #fff
}
/**方法二:**/
.外层 /deep/ .第三方组件{ /* ... */ }
.wrapper /deep/ .swiper-pagination-bullet-active{
background: #fff
}
</style>
上述代码将会编译成:
.外层[data-v-7668812d] .第三方组件 { /* ... */ }
通过 >>> 可以使得在使用scoped属性的情况下,穿透scoped,修改其他组件的值
实现原理其实就是加权重
【权重等级】
!important,加在样式属性值后,权重值为 10000
内联样式,如:style=””,权重值为1000
ID选择器,如:#content,权重值为100
类,伪类和属性选择器,如: .content、:hover、[data-v-7668812d] 权重值为10
标签选择器和伪元素选择器,如:div、p、:before 权重值为1
通用选择器(*)、子选择器(>)、相邻选择器(+)、同胞选择器(~)、权重值为0
相同权重下:内嵌样式 > 内部样式表 > 外联样式表
我们可以通过改变权重修改样式,scoped 实质就是添加了属性选择器增加了10的权重,我们只要超过他就可以了。
如 vue-loader 提供的 >>> 实质就是为第三方组件增加了外层属性的类,并且该类也带有属性选择器,相当于增加了20的权重。
那么我们可以为组件增加一个命名空间,防止变量污染,然后在当前命名空间下添加选择器增加权重达到修改样式的目的。
v-if和v-show
【相同点:】v-if
和v-show
他们都是可以实现元素的显示和隐藏
【不同点:】
-
v-if:是做
dom
节点的添加和删除,真正意义上的条件渲染 -
v-show:
display:none
和display:block
,首次加载页面时,v-show的开销更大
如果一个元素是经常的显示隐藏,应该使用
v-show
nextTick
https://segmentfault.com/a/1190000012861862
【作用:】
vue中的nextTick主要用于处理数据动态变化后,DOM还未及时更新的问题,用nextTick就可以获取数据更新后最新DOM的变化
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。
如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。
【适用场景一:】
有时需要根据数据动态的为页面某些dom元素添加事件,这就要求在dom元素渲染完毕时去设置,但是created与mounted函数执行时一般dom并没有渲染完毕,所以就会出现获取不到,添加不了事件的问题,这就要用到nextTick处理。
注意
mounted
不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在mounted
内部使用 vm.$nextTick:mounted: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been rendered }) }
【场景二:】
在使用某个第三方插件时 ,希望在vue生成的某些dom动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法,例如:应用滚动插件better-scroll时
mounted(){
this.$nextTick(() => {
this._initScroll();
this._initPics();
})
},
methods:{
_initScroll(){
if(!this.scroll){
this.scroll = new BScroll(this.$refs.seller,{
click: true
})
}else{
this.scroll.refresh();
}
}
}
【场景三:】数据改变后获取焦点
v-for和v-if
当它们处于同一节点,****v-for的优先级比v-if更高****,这意味着 v-if将分别重复运行于每个 v-for循环中。
当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用
<!--只传递了未完成的 todos-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 )上
data必须是函数
一个组件的 data
选项必须是一个函数
【原因:】
因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象,返回一个唯一的对象,不要和其他组件共用一个对象。
而单纯的写成对象形式,就使得所有组件实例共用了一份data
,就会造成一个变了全都会变的结果
vuex的理解
vuex是一个状态管理工具,主要解决大中型复杂项目的数据共享问题,主要包括state,actions,mutations,getters和modules 5个要素
【主要流程】:组件通过dispatch到 actions,actions是异步操作,再actions中通过commit到mutations,mutations再通过逻辑操作改变state,从而同步到组件,更新其数据状态,而getters相当于组件的计算属性,是对组件中获取到的数据做提前处理的
vuex如何实现数据持久化
【 vuex如何实现数据持久化(即刷新后数据还保留)?】
因为vuex中的state是存储在内存中的,一刷新就没了,例如登录状态
解决方案:
第一种:利用H5的本地存储(localStorage,sessionStorage)
第二种:利用第三方封装好的插件,例如:vuex-persistedstate
// 下载
npm install vuex-persistedstate --save
// 引入及配置
import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
...
plugins: [createPersistedState()]
})
方法三:使用vue-cookie插件来做存储
// 下载
npm install vue-cookie --save
// store/index.js中配置
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)
var VueCookie = require('vue-cookie');
export default new Vuex.Store({
state: {
// 持久化存储
token: VueCooke.get('token')
},
mutations: {
saveToken(state, token){
state.token = token;
// 设置村粗
VueCookie.set('token', token, {expires: '30s'})
}
},
acitons: {
...
}
})
vue转场动画
【说一下vue中转场动画的效果是如何实现的:】
主要通过vue中的提供的transition组件实现的
<transition name='名称'>
<router-view></router-view>
</transition>
其中name为转场的名称,自己定义,可通过定义进入和离开两种转场动画,格式为:
.名称-enter { } //将要进入动画
.名称-enter-active { } //定义进入的过程动画
.名称-leave { } //将要离开的动画
.名称-leave-active { } //定义离开过程中的动画
vue中slot的作用
【说一下vue封装组件中slot的作用:】
组件传值|通信
【父-子】:
- 主要通过
props
来实现
父组件通过import引入子组件,并注册,在子组件标签上添加要传递的属性,子组件通过props接收
- slot
用于父组件向子组件传递"标签数据"。也即子组件先有一个"占位符", 等待父组件传入具体的标签,子组件在进行渲染。
<!--父组件:-->
<div>
<TodoFooter>
<input slot="selectAll" type="checkbox" v-model="selectAll"/>
<span slot="count">已完成{{completeNum}} / 全部{{this.todos.length}}</span>
<button slot="deleteChecked" class="btn btn-danger" @click="deleteCompleted" v-show="completeNum">清除已完成任务</button>
</TodoFooter>
</div>
<!--子组件-->
<div class="todo-footer">
<label>
<!-- 使用slot将页面分为三个插槽 -->
<!-- <input type="checkbox" v-model="selectAll"/> -->
<slot name="selectAll"></slot>
</label>
<span>
<!-- <span>已完成{{completeNum}} / 全部{{this.todos.length}}</span> -->
<slot name="count"></slot>
</span>
<!-- <button class="btn btn-danger" @click="deleteCompleted" v-show="completeNum">清除已完成任务</button> -->
<slot name="deleteChecked"></slot>
</div>
注意:然后通过slot将子组件中的标签插到父组件中,此时在子组件中定义的方法、数据(即被插入的标签中使用到的)也需要移动到父组件中。
【子-父】:主要通过:$emit
来实现 和 slot
-
$emit
子组件通过通过绑定事件触发函数,在其中设置this. e m i t ( ‘ 要 派 发 的 自 定 义 事 件 ’ , 要 传 递 的 值 ) , emit(‘要派发的自定义事件’,要传递的值), emit(‘要派发的自定义事件’,要传递的值),emit中有两个参数一是要派发的自定义事件,第二个参数是要传递的值
然后父组件中,在这个子组件身上@派发的自定义事件,绑定事件触发的methods中的方法接受的默认值,就是传递过来的参数
-
$parent
/$children
与ref
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent
/$children
:访问父 / 子实例
// component-a 子组件 export default { data () { return { title: 'Vue.js' } }, methods: { sayHello () { window.alert('Hello'); } } }
// 父组件 <template> <component-a ref="comA"></component-a> </template> <script> export default { mounted () { const comA = this.$refs.comA; console.log(comA.title); // Vue.js comA.sayHello(); // 弹窗 } } </script>
【兄弟】: PubSubJS库、vuex
方法一:消息订阅与发布(PubSubJS库)
通过PubSubJS库来实现组件之间的通信(组件之间关系无要求)
订阅消息 PubSub.subscribe(‘msg’,function(msg,data){} ) =》 绑定事件,接收数据
发布消息 : PubSub.publish(‘msg’,data) =》触发事件,传递数据
先要导入 PubSub组件 import PubSub from ‘pubsub-js’
方法二:vuex
vue首屏加载慢或白屏
方法一:路由懒加载
const HelloWord = () => import('@/components/HelloWorld.vue')
export default new Router({
routes: [
{
path: '/', name: 'HelloWorld', component: HelloWorld
}
]
})
方法二:开启Gzip压缩
config/index.js
module.exports = {
...
productionGzip: false,
...
}
方法三:使用webpack的externals属性把不需要打包的库文件分离出去,减少打包后文件的大小
export default {
...
externals: {
jquery: 'jQuery'
}
}
vue开发环境和线上环境
vue开发环境和线上环境如何切换?
主要是通过检测process.env.NODE_ENV === "production"
和process.env.NODE_ENV === "development"
环境,来设置线上和线下环境地址。从而实现线上和线下环境地址的切换。
vue中请求管理
【vue中htpp请求是如何管理的:】
vue中的http请求如果散落在vue各种组件中,不便于后期维护与管理,所以项目中通常将业务需求统一存放在一个目录下管理,例如src下的API文件夹,这里面放入组件中用到的所有封装好的http请求并导出,再其他用到的组件中导入调用。
vue目录结构
build:项目构建目录
config:项目配置,包括代理配置,线上和线下环境配置
node_modules:node包目录, npm安装的包都在这个目录
src:平常开发是的目录
static:保存静态资源:图片、字体、样式
.eslintrc.js:Eslint代码检查配置文件
.gitignore:忽略提交到远程仓库的配置
vue和jquery的区别
jquery主要是玩dom操作的“神器“,强大的选择器,封装了好多好用的dom操作方法和如何获取ajax方法 例如:$.ajax()非常好用
vue:主要用于数据驱动和组件化,很少操作dom,当然vue可能通过ref来选择一个dom或组件
webpack理解
webpack是一个前端模块化打包构建工具,webpack本身需要的入口文件通过entry来指定,出口通过output来指定,默认只支持js文件,其他文件类型需要通过对应的loader来转换,例如:less需要less,less-loader,sass需要sass-loader,css需要style-loader,css-loader来实现。当然本身还有一些内置的插件来对文件进行压缩合并等操作
vue -router
路由间的通信
【方法一:路径传参】:params参数和query参数
prams参数:
// router/index.js
{
path: '/components/user/:id', // :id 占位
component: user
}
// 组件
<router-link to="/components/user/123">user123</router-link>
// es6动态拼接数据
<router-link to="`/components/user/${user.id}`">user456</router-link>
获取数据:this.$route.params.id
query参数:
// router/index.js
{
path: '/components/user',
component: user
}
// 组件
<router-link to="/components/user?id=123">user123</router-link>
<router-link to="`/components/user?id=${user.id}`">user456</router-link>
获取数据:this.$route.query.id
【方法二:router-view标签属性携带数据】
要知道的我们是不可以通过路由组件来携带属性的。因为路由组件此时还不是标签,只是注册路由,因此不可以使用props
属性
组件是通过标签来显示的,路由组件router-link
是通过router-view
标签来显示的,因此我们可以使用router-view
标签的props
来传递数据
【步骤】:
-
通过
router-view
传递数据<template> <!--生成路由链接--> <router-link to="/about" class="list-group-item">About</router-link> <router-link to="/home" class="list-group-item">Home</router-link> <!--用来显示路由组件的标签--> <router-view msg="abc"></router-view> <!--此时About路由和Home路由都可以获取msg的数据--> </template>
-
获取数据(about.vue或home.vue)
<template> <h1>{{msg}}</h1> </template> <script> //声明得到的数据 props: { msg: String } </script>
vue路由模式
【或者问:vue路由、前端路由的实现原理】
Vue-Router的存在意义:为了构建 SPA(单页面应用)
Vue-Router的核心: 改变视图的同时不会向后端发出请求。
第一种:利用url的hash来实现
使用 URL hash 值来作路由。通过hashchange事件来监听hash值的变化,来渲染不同的视图
它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
这种方式也是vue-router的默认的模式
【监听hash】
window.onhashchange = (e) => {
// e.oldURL 老的url
// e.newURL 新的url
// locaion.hash 当前url中的hash的值
}
【改变hash的方式】:
-
手动在浏览器上改变
-
点击浏览器的前进和后退
-
利用js修改
btn.addEventListener('click', ()=> { location.href = '#/user' })
第二种:利用H5的history API实现
一般场景下,hash 和 history 都可以,除非你更在意颜值,
#
符号夹杂在 URL 里看起来确实有些不太美丽。
如果不想要很丑的 hash,我们可以利用这种方法
主要通过history.pushState 和 history.replaceState来实现,不同之处在于,pushState会增加一条新的历史记录,而replaceState则会替换当前的历史记录
在使用history模式时,还应该对后端进行配置:主要目的就是让我们在访问任意路由时,都会默认返回一个index.html。
history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)
【两种方式的总结:】
- hash 方案兼容性好,而H5的history主要针对高级浏览器。
- hash带有#,没有history的美观
const state = { 'page_id': 1, 'user_id': 5 }
const title = ''
const url = 'hello-world.html'
// 创建一条新的历史记录。
history.pushState(state, title, url)
window.addEventListener('DOMContentLoaded', ()=>{
// 当dom下载就绪 localhsot:8000/index.html
console.log('当前路径:', location.path)
})
// 点击按钮,修改url。即 'localhost:8000/user'
btn.addEventListener('click', ()=> {
const state = {name: '梦梦'}
history.pushState({
'',
state,
'user'
})
})
// 监听浏览器的前进和后退,获取前进或后退的路径名称,根据它来进行页面切换
window.onpopstate = (e) => {
console.log(e.state) // {name: '梦梦'}
}
route和router、push、replace
路由组件对象中有两个特别的属性:
$route
代表当前组件:存放数据$router
代表路由器:提供操作路由的功能方法
【从A页面跳转到B页面的做法:】
- 使用
<router-link>
创建 a 标签来定义导航链接 - 借助 router 的实例方法,通过编写代码来实现
-
this.$router.push(location, onComplete?, onAbort?)
:这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。当你点击
<router-link>
时,这个方法会在内部调用,所以说,点击<router-link :to="...">
等同于调用router.push(...)
。 -
this.$router.replace(location, onComplete?, onAbort?)
:跟router.push
很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。 -
this.$router.go(n)
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似window.history.go(n)
keep-alive
主要用于保留组件状态或避免重新渲染。
keep-alive
是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。被包含的组件可以是普通组件也可以是路由组件(router-view)
router-view
也是一个组件,如果直接被包在keep-alive
里面,所有路径匹配到的视图组件都会被缓存:
【属性:】
include:字符串或正则表达式。只有匹配的组件会被缓存。
exclude:字符串或正则表达式。任何匹配的组件都不会被缓存。
【要知道的:】
当组件在 内被切换,在 2.2.0 及其更高版本中,activated 和 deactivated生命周期 将会在树内的所有嵌套组件中触发
【场景:】
比如: 有一个列表页面和一个 详情页面,那么用户就会经常执行打开详情=>返回列表=>打开详情
这样的话 列表 和 详情 都是一个频率很高的页面,那么就可以对列表组件使用进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染。
【各种使用场景总结:】
https://www.jonhuu.com/sample-post/388.html
vueRouter和路由守卫
说一下vue路由钩子或vue路由守卫的理解:
【使用场景:】
vue路由钩子是在路由跳转过程中拦截当前路由和要跳转的路由的信息
-
全局路由钩子:
beforeEach(to,from,next) {}
和afterEach(to, from,next){}
-
路由独享的钩子:
beforeEnter(to,from,next) {}
-
组件内的钩子:
beforeRouteEnter(to,from,next) { //… } beforeRouteUpdate(to,from,next) { //… } beforeRouteLeave(to,from,next) { //… }
【适用场景】:动态设置页面标题,判断用户登录权限等
//全局路由导航守卫
vueRouter.beforeEach(function (to, from, next) {
const nextRoute = [ 'detail'];
const auth = sessionStorage.getItem("username");
let FROMPATH = from.path;
//跳转至上述3个页面
if (nextRoute.indexOf(to.name) >= 0) {
//上述数组中的路径,是相当于有权限的页面,访问数组列表中的页面就应该是在登陆状态下
if (!auth) {
let params = Object.assign({frompath:FROMPATH},from.query);
next({path: '/newlogin',query:params});
}
}
//已登录的情况再去登录页,跳转至首页
if (to.name === 'newlogin') {
if (auth) {
// vueRouter.push({name: 'index'});
next({path: '/'});
}
}
next();
});
路由懒加载
vue路由懒加载解决了什么问题?
主要解决打包后文件过大的问题,事件触发之后才加载对应组件中的js
axios拦截器
【axios拦截器的理解:】
axios拦截器可以让我们在项目中对后端http请求和响应自动拦截处理,减少请求和响应的代码量,提升开发效率同时也方便项目后期维护
const service = axios.create({
baseURL: process.env.BASE_API,
timeout: 666;
})
// 请求拦截器
service.interceptors.request.use(config => {
....
return config;
}, error => {
....
return Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(response => {
return resposne
}, error => {
....
return Promise.reject(error)
})
小点
url中的#
#
代表网页中的一个位置。其右面的字符,就是该位置的标识符
http://www.example.com/index.html#print
就代表:网页index.html
的浏览器读取这个URL后,会自动将print位置滚动至可视区域。
为网页位置指定标识符,有两个方法。
- 一是使用锚点,比如,
- 二是使用id属性,比如
。
【要知道的:】
-
HTTP请求不包括#
#是用来指导浏览器动作的,对服务器端完全无用。所以,HTTP请求中不包括#。
-
在第一个#后面出现的任何字符,都会被浏览器解读为位置标识符
// http://www.example.com/index.html#print GET /index.html HTTP/1.1 Host: www.example.com
// http://www.example.com/?color=#fff GET /?color= HTTP/1.1 Host: www.example.com
- 单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页。
从http://www.example.com/index.html#location1
改成http://www.example.com/index.html#location2,
浏览器不会重新向服务器请求index.html
应用一:例如单页面应用SPA一样,路由的切换,不会重新加载页面,只是切换位置,或者切换组件
应用二:在单个页面里面通过name和id切换当前显示的位置。