zoukankan      html  css  js  c++  java
  • Vue面试相关

    MVVM相关

    什么是MVVM, 谈谈你对MVVM开发模式的理解?

    ​ MVVM是是Model-View-ViewModel的缩写,Model代表数据模型,定义数据操作的业务逻辑,View代表视图层,负责将数据模型渲染到页面上,ViewModel通过双向绑定把View和Model进行同步交互,不需要手动操作DOM的一种设计思想。

    MVVM分为Model、View、ViewModel三者。

    Model:代表数据模型,数据和业务逻辑都在Model层中定义;

    View:代表UI视图,负责数据的展示;

    ViewModel:负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;

    Model和View并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。

    这种模式实现了Model和View的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作dom。

    MVVM和MVC区别?和其他框架(jquery)区别?那些场景适用?

    ​ MVVM和MVC都是一种设计思想,主要就是MVC中的Controller演变成ViewModel,,MVVM主要通过数据来显示视图层而不是操作节点,解决了MVC中大量的DOM操作使页面渲染性能降低,加载速度慢,影响用户体验问题。主要用于数据操作比较多的场景。
    场景:数据操作比较多的场景,更加便捷

    mvvm框架是什么?它和其它框架(jquery)的区别是什么?哪些场景适合?

    一个model+view+viewModel框架,数据模型model,viewModel连接两个

    区别:vue数据驱动,通过数据来显示视图层而不是节点操作。

    场景:数据操作比较多的场景,更加便捷

    自定义指令(v-check、v-focus)的方法有哪些?它有哪些钩子函数?还有哪些钩子函数参数?

    全局定义指令:在vue对象的directive方法里面有两个参数,一个是指令名称,另外一个是函数。组件内定义指令:directives

    钩子函数:bind(绑定事件触发)、inserted(节点插入的时候触发)、update(组件内相关更新)

    钩子函数参数:el、binding

    指令相关

    Vue 有哪些指令,请说出相关一些?

    • v-if?show:判断是否隐藏; 用来显示或隐藏元素,v-show 是通过display实现,v-if 是每次删除后在创建
    • v-for:数据循环出来; 对数组或对象进行循环操作
    • v-bind:class:绑定一个属性;
    • v-model:双向数据绑定,一般用于表单元素。

    v-model是什么?怎么使用? vue中标签怎么绑定事件?

    ​ 可以实现双向绑定,指令(v-class、v-for、v-if、v-show、v-on)。vue的model层的data属性。绑定事件:<input @click=doLog()/>

    为什么避免 v-if 和 v-for 用在一起

    ​ 当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,通过v-if 移动到容器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运算 v-for。

    Vue.cli相关

    请说出vue.cli项目中src目录每个文件夹和文件的用法?

    • assets文件夹是放静态资源;
    • components是放组件;
    • router是定义路由相关的配置;view视图;
    • app.vue是一个应用主组件;
    • main.js是入口文件

    vue.cli中怎样使用自定义的组件?有遇到过哪些问题吗?

    第一步:在components目录新建你的组件文件(smithButton.vue),script一定要export default {

    第二步:在需要用的页面(组件)中导入:import smithButton from ‘../components/smithButton.vue'

    第三步:注入到vue的子组件的components属性上面,components:{smithButton}

    第四步:在template视图view中使用,

    vue-cli如何新增自定义指令?

    1、创建局部指令

    var app = new Vue({
        el: '#app',
        data: {   
        },
        // 创建指令(可以多个)
        directives: {
            // 指令名称
            dir1: {
                inserted(el) {
                    // 指令中第一个参数是当前使用指令的DOM
                    console.log(el);
                    // 对DOM进行操作
                    el.style.width = '200px';
                    el.style.height = '200px';
                    el.style.background = '#000';
                }
            }
        }
    })
    

    2、全局指令

    Vue.directive('dir2', {
        inserted(el) {
            console.log(el);
        }
    })
    

    3、指令的使用

    <p>
        <p v-dir1></p>
    	<p v-dir2></p>
    </p>
    

    Vuex相关

    vuex是什么?怎么使用?哪种功能场景使用它?

    ​ vue框架中状态管理。在main.js引入store,注入。新建了一个目录store,….. export 。场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车

    vue框架中状态管理,就是把组建的共享状态取出来,以一个全局单例模式管理,这样,不管在任何时候,都能获取状态或者触发行为

    你是怎么认识vuex的?

    vuex可以理解为一种开发模式或框架。 通过状态(数据源)集中管理驱动组件的变化(好比spring的IOC容器对bean进行集中管理)。

    应用级的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。

    vuex 状态管理模式

    采用集中式存储,管理应用所有组件的状态,简单说集中管理数据,类似于react中的redux,基于flux前段状态管理框架。

    基本使用:nmp install vuex -S

    创建store.js 文件,vuex相关配置。在main.js 中导入,import store fomr './store.js main.js 配置store选项,子组this.$store

    import vue fomr vue import vuex from vuex vue.use(vuex);

    vuex核心store 相当于一个容器,一个store实例中包含一下属性和方法:

    getters 获取属性

    actions 定义方法 动作,如流程判断 异步请求 const action={ 方法名(context){})} context 对象有comit dispatch state

    commit 提交的意思,修改数据唯一方式, conmit('add) 提交一个叫add的变化

    mutations 定义变化

    var state= { count:6} //创建store对象 const store=new Vuex.Strore{{state}}; vargetters ={count)(){return state.count}} export default store;

    编辑 app。js 编辑store, 作为计算属性:computed:(){return this.$store.state.count};

    方式一 this.#store访问

    方式二 mapGetters mapActios访问

    mapGetter 获取属性

    mapActions 获取方法

    导入辅助函数 import {mapGetter} from vuex

    computed:mapGetters{('count')}

    Vue 生命周期

    什么是vue生命周期?

    ​ Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

    vue生命周期的作用是什么?

    ​ 它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。

    vue生命周期总共有几个阶段?

    ​ 它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后

    第一次页面加载会触发哪几个钩子?

    第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子

    DOM 渲染在 哪个周期中就已经完成?

    DOM 渲染在 mounted 中就已经完成了

    简单描述每个周期具体适合哪些场景?

    生命周期钩子的一些使用方法:

    beforecreate : 可以在这加个loading事件,在加载实例时触发

    created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用

    mounted : 挂载元素,获取到DOM节点

    updated : 如果对数据统一处理,在这里写上相应函数

    beforeDestroy : 可以做一个确认停止事件的确认框

    nextTick : 更新数据后立即操作dom

    Vue生命周期的理解?

    ​ 生命周期:就是一个组件或者实例开始被初始化、创建开始到这个实例被销毁或者结束的一个过程。

    ​ 这个过程比如官网表述的:在过程中需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等,同时,在这个过程中vue给我们提供了很多的方法,也就是所说的生命周期钩子函数。

    ​ 在实例生命周期的不同阶段借助这些钩子函数,“用户在不同阶段添加自己的代码”

    由此:真正的生命周期是一个流程,而不是单单那几个钩子函数,钩子函数只是用来在流程的不同阶段帮助我们做更多的事情。

    vue的生命周期统分为5大区块:

    • 创建(初始化)
    • 查找与处理(找到组件并渲染)
    • 挂载(插入)
    • 更新(重新渲染并插入)
    • 销毁(卸载所有)

    其中每一大块又分几个小的步骤,但是大体规律又是如出一辙:

    • 本区块开始前(一个区块流程开始的钩子告诉你)
    • 本区块开始中
    • 本区块开始后(一个区块流程完毕的钩子告诉你)

    一、创建部分

    new Vue 这句代码初始化一个vue实例。

    开始创建一个vue对象生命周期开始,init event初始化事件,为当前实例做基础配置;

    创建之前,这里提供一个钩子函数,beforeCreate 开始创建钩子,这个时候还啥也没做呢,页面一片空白,可以在页面中先展示一个loading组件,给用户一个友好体验;

    创建中,init injections(初始化注册) & reactivity 创建过程中,data属性被成功绑定,dom未生成;

    创建之后,这里提供一个钩子函数,created 创建完毕钩子,

      这个时候vue对象实例化完毕,dom树依旧未生成,页面还是一片空白,但是,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。

    可以在这里ajax获取数据赋给data属性了,以便日后使用;

    二、查找部分

    也就是new Vue()括号里边的参数开始被执行解析的过程:

    new Vue({
      el: '#app',
      router,
      store,
      template: '<App/>',
      components: { App }
    })
    

    ​ 查找el属性的对应内容,el对应的DOM 元素作为 Vue 实例的挂载目标。

    • 如果值可以被找到,那么实例将立即进入编译过程

    • 如果找不到,就去查是否在括号后边挂载了$.mount()并有内容,用于手动开启编译

    ​ 以上官网解说:“如果在实例化时存在el这个选项,实例将立即进入编译过程,否则,需要显式调用 vm.$mount() 手动开启编译。”官网:https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-DOM

    • 如果都没找到,生命周期结束;

    • 如果顺利的都找到了,就继续往下查找{}内的下一个属性template

      • 如果template对应的值当中有组件或者有html内容,那么也算查找成功,
      • 如果为空,继续查找render属性值是否为空
    • 紧接着,如果{}选项中存在渲染函数render,

      • 那么template将被忽略,因为render渲染函数是字符串模板的代替方案,
        • render可以让你发挥 JavaScript 最大的编程能力,而不用写template的静态模板
      • 如果没有或为空,template字符串模板将会替换挂载的元素,即el的属性值
        • 挂载元素el的内容都将被忽略,除非模板template的内容有分发插槽。
      • 如果template和render都不存在,则生命周期结束。

    三、挂载部分

    开始挂载之前,这里提供一个钩子函数,beforeMount挂载前钩子(编译template里的内容并在虚拟dom中执行,页面上依旧没有任何展示)

    • 挂载中,要做的事就是创建vm$.el,并替换到el元素,
    • 挂载完毕,这里提供一个钩子函数,mounted挂载完毕钩子,
      • el 被新创建的 vm.$el 替换,并挂载到实例上去
      • 至此,所有的dom结构和数据都被展示到页面当中,

    四、更新部分

    依旧是那个套路,触发了更新的开关后,会给一个开始更新的回调:

    • 更新之前,这里有一个钩子函数,beforeUpdate开始更新前钩子,
      • 在这个钩子里可以提供一个弹窗提示用户确认跟新啥的。或者再展示一个loading;
      • 也可以手动移除已添加的事件监听器
    • 更新中,vue实例要开始将旧数据替换为新数据,在虚拟dom中重新渲染
      • 虚拟dom开始改变,但是页面这时没有任何变化,因为只是改的虚拟dom,还并未真正修改dom结构;
    • 更新完毕,这里有一个钩子函数,updated更新后钩子,
      • 这时真正的dom结构被彻底替换,页面展示上也会发生改变,
      • 在这个钩子里可以提供一个弹窗告诉用户更新完毕。同时去掉loading弹层啥的;

    五、销毁部分

    • 开始销毁vue实例之前,会有一个钩子函数提示开发者组件要开始销毁:beforeDestory开始销毁钩子,
      • 在这个钩子中我们可以提醒用户是否删除等,或者做一些开发者与业务有关的相关操作;
    • 销毁中,vue这时的主要目标就是卸载,就像人要洗澡前各种脱一样(具体就不要想象了!),
      • 他要卸载在身上的各种监听、各种事件, 各种绑定以及各种子组件实例销毁,感觉像毁灭一切
        • 比如watchers(我没用过没有发言权)、子组件child components、事件event;
    • 销毁后,这时再次提供最后一个钩子函数,destoryed销毁完毕。
      • 在这里我们可以提示用户删除完毕啥的,也可以清空我们自己的定时器或者做一些其他善后工作。

    到这一步,此次整个vue实例的生命周期就彻底结束了。

    请详细说下你对vue生命周期的理解?

    总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后

    ① 创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。

    ② 载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。

    ③ 更新前/后:当data变化时,会触发beforeUpdate和updated方法。

    ④ 销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在

    什么是vue生命周期?

    Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

    vue生命周期的作用是什么?

    它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。

    vue生命周期总共有几个阶段?

    它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后

    请列举出3个Vue中常用的生命周期钩子函数

    ​ created: 实例已经创建完成之后调用,在这一步,实例已经完成数据观测, 属性和方法的运算 watch/event事件回调. 然而, 挂载阶段还没有开始, el属性目前还不可见

    ​ mounted : el被新创建的 vm.el属性目前还不可见mounted:el被新创建的vm.el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
    ​ activated: keep-alive组件激活时调用

    组件

    请说下封装 vue 组件的过程?

    ​ 首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。

    ​ 然后,使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。

    组件之间的传值?

    • 父组件与子组件传值
    • 父组件通过标签上面定义传值
    • 子组件通过props方法接受数据
    • 子组件向父组件传递数据
    • 子组件通过$emit方法传递参数

    Vue组件间的参数传递

    1.父组件与子组件传值
    父组件传给子组件:子组件通过props方法接受数据;
    子组件传给父组件:$emit方法传递参数
    2.非父子组件间的数据传递,兄弟组件传值
    eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适。(虽然也有不少人推荐直接用VUEX,具体来说看需求咯。技术只是手段,目的达到才是王道。)

    v-router 路由相关

    路由之间跳转

    声明式(标签跳转) 编程式( js跳转)

    怎么定义vue-router的动态路由?怎么获取传过来的动态参数?

    ​ 在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id

    vue-router是什么?它有哪些组件?

    vue用来写路由一个插件。router-link、router-view

    vue-router有哪几种导航钩子?

    有三种,

    第一种:是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。
    第二种:组件内的钩子
    第三种:单独路由独享组件

    过滤器

    vue-router路由

    使用vue.js 开发spa 单页面应用,根绝不同url地址,显示不同内容,但实现在统一页面红,称单页面应用。
    

    安装路由:cnpm install vue-router -S

    <p id='itsny'>
        <p>
        	<!--使用router-link组件来定义导航,to属性指定链接url -->
        	<router-link to="/home">主页</router-link>
    		<router-link to="/news">新闻</router-link>
    	</p>
    	<p>
          <!--使用router-view用来显示路由内容 -->
           <router-view></router-view>
    	</p>
    </p>
    
    <script>
    //1、定义组件
        var Home={
            template: '<h3>我是主页</h3>'
        }
    var News={
        template:'<h3>我是新闻</h3>'
    };
    
    // 2、配置路由
    const routes=[
        {path:'/home',component:Home},
        {path:'/news/:username/:password',component:News},
        {path:'*',redirect:'/home'} //重定向
    ];
    
    // 3、创建路由实例
    const router = new VueRouter({
        routes, //简写,相当于routers:routes
        //mode:'history' //更改模式
        linkActiveClass: 'active' //更新活动链接的class类名
    });
    
    // 4、创建根实例并将路由挂载到Vue实例上
    new Vue({
        el:'#itany',
        router //注入路由
    });
    </script>
    

    vue-router使用总结

    单页面应用的工作原理

    ​ 我理解的单页面工作原理是通过浏览器URL的#后面的hash变化就会引起页面变化的特性来把页面分成不同的小模块,然后通过修改hash来让页面展示我们想让看到的内容。

    ​ 那么为什么hash的不同,为什么会影响页面的展示呢?浏览器在这里面做了什么内容。以前#后面的内容一般会做锚点,但是会定位到一个页面的某个位置,这个是怎么做到的呢,和我们现在的路由有什么不同。(我能想到一个路由的展示就会把其他路由隐藏,是这样的吗)后面会看一看写一下这个疑惑,现在最重要的是先把基本概念弄熟。

    正文

    ​ 当你要把 vue-router 添加进来,我们需要做的是,将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们

    起步

    //*** router-link 告诉浏览器去哪个路由
    
    //*** router-view 告诉路由在哪里展示内容
    <p id="app">
        <h1>Hello App!</h1>
    	<p>
       	<!-- 使用router-link组件来导航 -->
        <!-- 通过传入`to`属性指定链接 -->
        <!-- <router-link> 默认会被渲染成一个`<a>`标签 -->
    		<router-link to="foo">Go to Foo</router-link>
    		<router-link to="bar">Go to Bar</router-link>
    	</p>
    	<!-- 路由出口 -->
        <!-- 路由匹配到的组件将渲染在这里 -->
    	<router-link></router-link>
    </p>
    
    // 1. 定义(路由)组件。
    // 可以从其他文件 import 进来
    const Foo = { template: 'foo' },
    const Bar = { template: 'bar' },
    
    // 2. 定义路由
    // 每个路由应该映射一个组件。 其中"component" 可以是
    // 通过 Vue.extend() 创建的组件构造器,
    // 或者,只是一个组件配置对象。
    const routes = [
     	{ path: '/foo', component: Foo },
     	{ path: '/bar', component: Bar }
    ]
    
    // 3. 创建 router 实例,然后传 `routes` 配置
    // 你还可以传别的配置参数, 不过先这么简单着吧。
    const router = new VueRouter({
     	routes // (缩写)相当于 routes: routes
    })
    
    // 4. 创建和挂载根实例。
    // 记得要通过 router 配置参数注入路由,
    // 从而让整个应用都有路由功能
    const app = new Vue({
        router
    }).$mount('#app')
    // 现在,应用已经启动了!
    

    动态路由匹配

    ​ 相当于同一个组件,因为参数不同展示不同的组件内容,其实就是在 vue-router 的路由路径中使用『动态路径参数』

    const router - new VueRouter({
        routes: [
            //动态路径参数,以冒号开头
            {path:'/user/:id',component:User}
        ]
    })
    
    那么我们进入uesr/001 和 user/002 其实是进入的同一个路由,可以根据参数的不同在内容页展示不同的内容。一般适用场景:列表,权限控制
    

    定义的时候用: 表示是动态路由,使用 {{ $route.params.id }} 来拿到本路由里面参数的内容

    当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。

    复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch(监测变化) $route 对象

    const User = {
        template: '...',
        watch: {
            '$route'(to,from){
                //对路径变化作出响应...
            }
        }
    }
    
    	有时候,同一个路径可以匹配多个路由,此时,**匹配的优先级**就按照路由的定义顺序:**谁先定义的,谁的优先级就最高。**
    

    嵌套路由

    //路由里面也会出现 这是嵌套路由展示内容的地方
    const User = {
    
     template: `
    	<p class="user">
    		<h2>User {{ $route.params.id }}</h2>
    		<router-view></router-view>
    	</p>
    	`
    }
    
    //定义路由的时候在 加children 子路由属性
    const router = new VueRouter({
     routes: [
     { path: '/user/:id', component: User,
      children: [
      {
       // 当 /user/:id/profile 匹配成功,
       // UserProfile 会被渲染在 User 的 中
       path: 'profile',
       component: UserProfile
      },
      {
       // 当 /user/:id/posts 匹配成功
       // UserPosts 会被渲染在 User 的 中
       path: 'posts',
       component: UserPosts
      }
      ]
     }
     ]
    })
    

    设置空路由,在没有指定路由的时候就会展示空路由内容

    const router = new VueRouter({
    
    routes: [
    {
    path: '/user/:id', component: User,
    children: [
    // 当 /user/:id 匹配成功,
    // UserHome 会被渲染在 User 的 中
    { path: '', component: UserHome },
    ]
    }
    ]
    })
    

    编程式导航

    声明式:
    编程式:router.push(...)

    可以想象编程式 push 可以理解为向浏览器历史里面push一个新的hash,导致路由发生变化

    router.replace() 修改路由但是不存在历史里面
    router.go(n) 有点像JS的window.history.go(n)

    命名路由 就是给每一个路由定义一个名字。

    命名视图

    ​ 有时候想同时(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。

    <router-view class="view one"></router-view>
    <router-view class="view two" name="a"></router-view>
    <router-view class="view three" name="b"></router-view>
    

    一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置(带上 s):

    const router = new VueRouter({
    
    routes: [
    {
     path: '/',
     components: {
      default: Foo,
      a: Bar,
      b: Baz
     }
    }
    ]
    })
    

    重定向和别名

    重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b:

    const router = new VueRouter({
    
     routes: [
      { path: '/a', redirect: '/b' }
     ]
    })
    

    一般首页的时候可以重定向到其他的地方

    重定向的目标也可以是一个命名的路由:

    const router = new VueRouter({
    
     routes: [
      { path: '/a', redirect: { name: 'foo' }}
     ]
    })
    

    甚至是一个方法,动态返回重定向目标:

    const router = new VueRouter({
    
     routes: [
      { path: '/a', redirect: to => {
       // 方法接收 目标路由 作为参数
       // return 重定向的 字符串路径/路径对象
      }}
     ]
    })
    

    『重定向』的意思是,当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,那么『别名』又是什么呢?

    ​ /a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。

    上面对应的路由配置为:

    const router = new VueRouter({
    
     routes: [
      { path: '/a', component: A, alias: '/b' }
     ]
    })
    

    『别名』的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。

    HTML5 History模式

    ue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。

    如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

    const router = new VueRouter({
     mode: 'history',
     routes: [...]
    })
    

    当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!

    不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

    所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

    给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。

    const router = new VueRouter({
     mode: 'history',
     routes: [
      { path: '*', component: NotFoundComponent }
     ]
    })
    

    或者,如果你使用 Node.js 服务器,你可以用服务端路由匹配到来的 URL,并在没有匹配到路由的时候返回 404,以实现回退。

    导航守卫

    我的理解 就是组件或者全局级别的 组件的钩子函数

    正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

    记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

    全局守卫

    const router = new VueRouter({ ... })
    router.beforeEach((to, from, next) => {
     // ...
    })
    

    每个守卫方法接收三个参数:

    • to: Route: 即将要进入的目标 路由对象

    • from: Route: 当前导航正要离开的路由

    • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

      • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
      • next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
      • next(‘/') 或者 next({ path: ‘/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
      • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

    确保要调用 next 方法,否则钩子就不会被 resolved。

    完整的导航解析流程

    • 导航被触发。
    • 在失活的组件里调用离开守卫。
    • 调用全局的 beforeEach 守卫。
    • 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
    • 在路由配置里调用 beforeEnter。
    • 解析异步路由组件。
    • 在被激活的组件里调用 beforeRouteEnter。
    • 调用全局的 beforeResolve 守卫 (2.5+)。
    • 导航被确认。
    • 调用全局的 afterEach 钩子。
    • 触发 DOM 更新。
    • 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

    路由元信息

    ​ 我的理解就是 他可以把路由的父路径都列举出来,完成一些任务,比如登录,user 组件需要登录,那么user下面的foo组件也需要,那么可以通过这个属性 来检测这个路由线上 的一些状态。

    定义路由的时候可以配置 meta 字段:

    const router = new VueRouter({
     routes: [
      {
       path: '/foo',
       component: Foo,
       children: [
        {
         path: 'bar',
         component: Bar,
         // a meta field
         meta: { requiresAuth: true }
        }
       ]
      }
     ]
    })
    

    首先,我们称呼 routes 配置中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可能匹配多个路由记录

    例如,根据上面的路由配置,/foo/bar 这个 URL 将会匹配父路由记录以及子路由记录。

    一个路由匹配到的所有路由记录会暴露为 $route 对象(还有在导航守卫中的路由对象)的 $route.matched 数组。因此,我们需要遍历 $route.matched 来检查路由记录中的 meta 字段。

    下面例子展示在全局导航守卫中检查元字段:

    router.beforeEach((to, from, next) => {
     if (to.matched.some(record => record.meta.requiresAuth)) {
      // this route requires auth, check if logged in
      // if not, redirect to login page.
      if (!auth.loggedIn()) {
       next({
        path: '/login',
        query: { redirect: to.fullPath }
       })
      } else {
       next()
      }
     } else {
      next() // 确保一定要调用 next()
     }
    })
    

    数据获取

    我的理解就是在哪里获取数据,可以再组件里面,也可以在组件的守卫里面,也就是组件的生命周期里面。

    有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:

    导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示『加载中』之类的指示。

    导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。

    从技术角度讲,两种方式都不错 —— 就看你想要的用户体验是哪种。

    导航完成后获取数据

    当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。

    假设我们有一个 Post 组件,需要基于 $route.params.id 获取文章数据:

    <template>
        <p class="post">
            <p class="loading" v-if="loading">
                Loding..
           	</p>
    		<p v-if="error" class="error">
                {{ error }}
            </p>
    		<p v-if="post" class="content">
                <h2>{{ post.title }}</h2>
    			<p>{{ post.body }}</p>
    		</p>
    	</p>   
    </template>
    export default {
     data () {
      return {
       loading: false,
       post: null,
       error: null
      }
     },
     created () {
      // 组件创建完后获取数据,
      // 此时 data 已经被 observed 了
      this.fetchData()
     },
     watch: {
      // 如果路由有变化,会再次执行该方法
      '$route': 'fetchData'
     },
     methods: {
      fetchData () {
       this.error = this.post = null
       this.loading = true
       // replace getPost with your data fetching util / API wrapper
       getPost(this.$route.params.id, (err, post) => {
        this.loading = false
        if (err) {
         this.error = err.toString()
        } else {
         this.post = post
        }
       })
      }
     }
    }
    

    在导航完成前获取数据

    通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。

    export default {
     data () {
      return {
       post: null,
       error: null
      }
     },
     beforeRouteEnter (to, from, next) {
      getPost(to.params.id, (err, post) => {
       next(vm => vm.setData(err, post))
      })
     },
     // 路由改变前,组件就已经渲染完了
     // 逻辑稍稍不同
     beforeRouteUpdate (to, from, next) {
      this.post = null
      getPost(to.params.id, (err, post) => {
       this.setData(err, post)
       next()
      })
     },
     methods: {
      setData (err, post) {
       if (err) {
        this.error = err.toString()
       } else {
        this.post = post
       }
      }
     }
    }
    

    在为后面的视图获取数据时,用户会停留在当前的界面,因此建议在数据获取期间,显示一些进度条或者别的指示。如果数据获取失败,同样有必要展示一些全局的错误提醒。

    vue-router是怎么传值的

    1、在路由处配置

    path:'/detail/:id'
    //调用:
    this.$router.push({
        path:'/home/${id}'
    })
    

    在组件内通过this.$route.params.id即可获取。

    2、在router-link标签中传递参数

    <router-link :to = {
        params:{
            id:1
        }
    }/>
    

    也可通过:this.$route.params.id获取

    这里通过router-link传参方式是隐形传参

    3、另一种params的是通过params传参,通过name配置路由。

    //路由处:
    {
        path:'/home',
        name:'Home',
        component:Home
    }
    //调用:
    this.$router.push({
        name:'Home',
        params:{
            id:id
        }
    })
    

    获取:this.$route.params.id

    4、通过query来传递参数,参数会在url后边的?id=?中显示

    //路由处:
    {
        path:'/home',
        name:'Home',
        component:Home
    }
    //调用:
    this.$router.push({
        path:'/home',
        query:{
           id:id
        }
    })
    

    获取:this.$route.query.id

    过滤器

    vue如何自定义一个过滤器?

    html代码

    <p id="app">
        <input type="text" v=model="msg" />
        {{ msg | capitalize }}
    </p>
    

    JS代码:

    var vm=new Vue({
        el:"#app",
        data:{
            msg:''
        },
        filters: {
          capitalize: function (value) {
            if (!value) return ''
            value = value.toString()
            return value.charAt(0).toUpperCase() + value.slice(1)
          }
        }
    })
    

    全局定义过滤器

    Vue.filter('capitalize', function (value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    })
    

    其他

    Vue公司的双向数据绑定原理是什么?

    ​ vue.js是采用数据劫持结合发布者 - 订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

    具体步骤:

    第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter
    这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化

    第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

    第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
    1、在自身实例化时往属性订阅器(dep)里面添加自己
    2、自身必须有一个update()方法
    3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。

    第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

    vue和react区别

    ​ 相同点:都鼓励组件化,都有’props’的概念,都有自己的构建工具,Reat与Vue只有框架的骨架,其他的功能如路由、状态管理等是框架分离的组件。

    ​ 不同点:React:数据流单向,语法—JSX,在React中你需要使用setState()方法去更新状态。Vue:数据双向绑定,语法--HTML,state对象并不是必须的,数据由data属性在Vue对象中进行管理。适用于小型应用,但对于对于大型应用而言不太适合。

    v-show和v-if指令的共同点和不同点?

    v-show指令是通过修改元素的displayCSS属性让其显示或者隐藏。
    v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果。

    v-show 仅仅控制元素的显示方式,将 display 属性在 block 和 none 来回切换;而v-if会控制这个 DOM 节点的存在与否。当我们需要经常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。

    Vue中如何在组件内部实现一个双向数据绑定?

    假设有一个输入框组件,用户输入时,同步父组件页面中的数据。

    具体思路:父组件通过props传值给子组件,子组件通过 $emit 来通知父组件修改相应的props值;当输入数据时,父子组件中的数据是同步改变的;我们在父组件中做了两件事,一是给子组件传入props,二是监听input事件并同步自己的value属性。那么这两步操作能否再精简一下呢?答案是可以的,你只需要修改父组件:

    v-model 实际上会帮我们完成上面的两步操作.

    Vue中如何监控某个属性值的变化?

    比如现在要监控data中,obj.a的变化。Vue中监控对象属性的办法你可以这样做:

    watch: {
          obj: {
          handler (newValue, oldValue) {
            console.log('obj changed')
          },
          deep: true
        }
      }
    

    deep属性表示深层遍历,但是这么写会监控obj的所有属性变化,并不是我们想要的效果,所以做点修改

    watch: {
       'obj.a': {
          handler (newName, oldName) {
            console.log('obj.a changed')
          }
       }
      }
    

    还有一种方法,可以通过computed 来实现,只需要:

    computed: {
        a1 () {
          return this.obj.a
        }
    }
    

    利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。

    前端如何优化网站性能?

    1、减少 HTTP 请求数量

    在浏览器与服务器进行通信时,主要是通过 HTTP 进行通信。浏览器与服务器需要经过三次握手,每次握手需要花费大量时间。而且不同浏览器对资源文件并发请求数量有限(不同浏览器允许并发数),一旦 HTTP 请求数量达到一定数量,资源请求就存在等待状态,这是很致命的,因此减少 HTTP 的请求数量可以很大程度上对网站性能进行优化。

    CSS Sprites

    国内俗称CSS精灵,这是将多张图片合并成一张图片达到减少HTTP请求的一种解决方案,可以通过CSS的background属性来访问图片内容。这种方案同时还可以减少图片总字节数。

    合并 CSS 和 JS 文件

    现在前端有很多工程化打包工具,如:grunt、gulp、webpack等。为了减少 HTTP 请求数量,可以通过这些工具再发布前将多个CSS或者多个JS合并成一个文件。

    采用 lazyLoad

    俗称懒加载,可以控制网页上的内容在一开始无需加载,不需要发请求,等到用户操作真正需要的时候立即加载出内容。这样就控制了网页资源一次性请求数量。

    2、控制资源文件加载优先级

    浏览器在加载HTML内容时,是将HTML内容从上至下依次解析,解析到link或者script标签就会加载href或者src对应链接内容,为了第一时间展示页面给用户,就需要将CSS提前加载,不要受 JS 加载影响。

    一般情况下都是CSS在头部,JS在底部。

    3、利用浏览器缓存

    浏览器缓存是将网络资源存储在本地,等待下次请求该资源时,如果资源已经存在就不需要到服务器重新请求该资源,直接在本地读取该资源。

    4、减少重排(Reflow)

    基本原理:重排是DOM的变化影响到了元素的几何属性(宽和高),浏览器会重新计算元素的几何属性,会使渲染树中受到影响的部分失效,浏览器会验证 DOM 树上的所有其它结点的visibility属性,这也是Reflow低效的原因。如果Reflow的过于频繁,CPU使用率就会急剧上升。

    减少Reflow,如果需要在DOM操作时添加样式,尽量使用 增加class属性,而不是通过style操作样式。

    5、减少 DOM 操作

    6、图标使用 IconFont 替换

    网页从输入网址到渲染完成经历了哪些过程?

    大致可以分为如下7步:

    • 输入网址;

    • 发送到DNS服务器,并获取域名对应的web服务器对应的ip地址;

    • 与web服务器建立TCP连接;

    • 浏览器向web服务器发送http请求;

    • web服务器响应请求,并返回指定url的数据(或错误信息,或重定向的新的url地址);

    • 浏览器下载web服务器返回的数据及解析html源文件;

    • 生成DOM树,解析css和js,渲染页面,直至显示完成;

    $route和$router的区别

    ​ $route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。而$router是“路由实例”对象包括了路由的跳转方法,钩子函数等

    vue的method,computed,watch的区别:

    ​ computed是对结果进行缓存的,只要依赖的值没有变化,结果就不会变化.

    ​ method就是普通的方法。

    ​ computed减少执行的getter减去了重复计算,节省内存。watch是一直在监听。比如this.num + new Date(),虽然new Date的值一直在变,但只要this.num没变,结果还是一样的。

    聊聊你对Vue.js的template编译的理解?

    简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)
    

    详情步骤:

    ​ 首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。

    ​ 然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

    vue的历史记录

    history 记录中向前或者后退多少步

    vue-loader是什么?使用它的用途有哪些?

    ​ 解析.vue文件的一个加载器,跟template/js/style转换成js模块。

    ​ 用途:js可以写es6、style样式可以scss或less、template可以加jade等

    vuex有哪几种属性?

    有五种,分别是 State、 Getter、Mutation 、Action、 Module

    vuex的State特性
    A、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data
    B、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
    C、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
    vuex的Getter特性
    A、getters 可以对State进行计算操作,它就是Store的计算属性
    B、 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
    C、 如果一个状态只在一个组件内使用,是可以不用getters
    vuex的Mutation特性
    Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。

  • 相关阅读:
    寒假自学1.18
    寒假自学笔记1.17
    寒假自学1.16
    寒假自学1.15
    寒假自学1.14
    寒假自学1.13
    寒假自学1.12
    CSS position 属性
    node 获取客户端请求 服务器响应客户端 页面显示客户端想要的格式
    使用 npm 安 装node_modules 总是提示报错
  • 原文地址:https://www.cnblogs.com/jiaxiaozia/p/13802950.html
Copyright © 2011-2022 走看看