vue高频面试题

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来缓存组件,因此还新增了activedeactived两个声明周期钩子

  • 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

【原理:】

  • HTMLDOM节点加一个不重复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-ifv-show他们都是可以实现元素的显示和隐藏

【不同点:】

  • v-if:是做dom节点的添加和删除,真正意义上的条件渲染

  • v-show:display:nonedisplay: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的作用:】

组件传值|通信

【父-子】:

  1. 主要通过props来实现

父组件通过import引入子组件,并注册,在子组件标签上添加要传递的属性,子组件通过props接收

  1. 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

  1. $emit

    子组件通过通过绑定事件触发函数,在其中设置this. e m i t ( ‘ 要 派 发 的 自 定 义 事 件 ’ , 要 传 递 的 值 ) , emit(‘要派发的自定义事件’,要传递的值), emit()emit中有两个参数一是要派发的自定义事件,第二个参数是要传递的值

然后父组件中,在这个子组件身上@派发的自定义事件,绑定事件触发的methods中的方法接受的默认值,就是传递过来的参数

  1. $parent / $childrenref

    • 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来传递数据

【步骤】:

  1. 通过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>
    
  2. 获取数据(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

路由组件对象中有两个特别的属性:

  1. $route代表当前组件:存放数据
  2. $router代表路由器:提供操作路由的功能方法

【从A页面跳转到B页面的做法:】

  1. 使用 <router-link> 创建 a 标签来定义导航链接
  2. 借助 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.htmlprint位置。

浏览器读取这个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切换当前显示的位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值