美文网首页Vue技术
最新面试题目 vue (二)

最新面试题目 vue (二)

作者: symY_Y | 来源:发表于2022-05-13 18:27 被阅读0次

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

    路由中有三个基本的概念 route, routes, router
    1, route,它是一条路由,由这个英文单词也可以看出来,它是单数, Home按钮 => home内容, 这是一条route, about按钮 => about 内容, 这是另一条路由。
    2, routes 是一组路由,把上面的每一条路由组合起来,形成一个数组。[{home 按钮 =>home内容 }, { about按钮 => about 内容}]
    3, router 是一个机制,相当于一个管理者,它来管理路由。因为routes 只是定义了一组路由,它放在哪里是静止的,当真正来了请求,怎么办? 就是当用户点击home 按钮的时候,怎么办?这时router 就起作用了,它到routes 中去查找,去找到对应的 home 内容,所以页面中就显示了 home 内容。
    4,客户端中的路由,实际上就是dom 元素的显示和隐藏。当页面中显示home 内容的时候,about 中的内容全部隐藏,反之也是一样。客户端路由有两种实现方式:基于hash 和基于html5 history api.
    路由跳转等都需要vue-router


    image.png

    2.active-class 是哪个组件的属性?

    首先 active-class 是 vue-router 模块中 router-link 组件中的属性,主要作用是用来实现选中样式的切换,在 vue-router 中要使用 active-class 。
    1,在 router-link 中写入 active-class
    active-class 选择样式时根据路由中的路径(to=“/home”)去匹配,然后显示

    <router-link to="/home" class="menu-home" active-class="active">首页</router-link>
    

    2,直接在路由 js 文件中配置 linkActiveClass

    export default new Router({
        linkActiveClass: 'active',
    })
    
    <div class="menu-btn">
        <router-link to="/" class="menu-home" active-class="active">首页</router-link>
    </div>
    <div class="menu-btn">
        <router-link to="/my" class="menu-my" active-class="active">我的</router-link>
    </div>
    

    二、引起的问题
    因为 to="/" 引起的,active-class 选择样式时根据路由中的路径去匹配,然后显示,
    例如在 my 页面中,路由为 localhost:8081/#/my,那么 to="/”和 to="/my"都可以匹配到,所有
    都会激活选中样式
    三、解决方法

    1. 在 router-link 中写入 exact
    <router-link to="/" class="menu-home" active-class="active" exact>首页</router-link>
    

    2. 在路由中加入重定向

    <router-link to="/" class="menu-home" active-class="active" exact>首页</router-link>
    {path: '/', redirect: '/home' }
    

    3.怎么定义 vue-router 的动态路由? 怎么获取传过来的值?

    我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如, 我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲 染。那么,我们可以在 Vue-Router 的路由路径中使用“动态路径参数”来达到这个效果
    可以通过query,param两种方式
    区别:query通过url传参,刷新页面还在;params属性页面不在

    1,params的类型:

    配置路由格式:/router/:id
    传递的方式:在path后面跟上对应的值
    传递后形成的路径:/router/123

    // 动态路由params
    在App.vue中
    <router-link :to="'/user/'+userId" replace>用户</router-link>
    在index.js
        {
        path:"/user/:userid",
        component:User,
        }
    

    跳转方式

    // 方法1:
    <router-link :to="{ name: 'users', params: { uname: wade }}">按钮</router-link>
    // 方法2:
    this.$router.push({name:'users',params:{uname:wade}})
    // 方法3:
    this.$router.push('/user/' + wade)
    

    可以通过$route.params.userid 获取你说传递的值

    2,query的类类型

    配置路由格式:/router,也就是普通配置
    传递的方式:对象中使用query的key作为传递方式
    传递后形成的路径:/route?id=123

    <!--动态路由-query -->
    //01-直接在router-link 标签上以对象的形式
    <router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">档案</router-link>
    /*
        02-或者写成按钮以点击事件形式
        <button @click='profileClick'>我的</button>    
    */
     
     //点击事件
     profileClick(){
       this.$router.push({
            path: "/profile",
            query: {
              name: "kobi",
              age: "28",
              height: 198
            }
          });
     }
    

    跳转方式

    // 方法1:
    <router-link :to="{ name: 'users', query: { uname: james }}">按钮</router-link>
    // 方法2:
    this.$router.push({ name: 'users', query:{ uname:james }})
    // 方法3:
    <router-link :to="{ path: '/user', query: { uname:james }}">按钮</router-link>
    // 方法4:
    this.$router.push({ path: '/user', query:{ uname:james }})
    // 方法5:
    this.$router.push('/user?uname=' + jsmes)
    

    可以通过$route.query 获取你所传递的值

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

    第一种全局导航钩子
    const router = new VueRouter({ ... });
    router.beforeEach((to, from, next) => {
     // do someting
    });
    

    这三个参数 to 、from 、next 分别的作用:
    1.to: Route,代表要进入的目标,它是一个路由对象
    2.from: Route,代表当前正要离开的路由,同样也是一个路由对象
    3.next: Function,这是一个必须需要调用的方法,而具体的执行效果则依赖 next 方法调用的参数

    1.next():进入管道中的下一个钩子,如果全部的钩子执行完了,则导航的状态就是 confirmed(确认的)
    2.next(false):这代表中断掉当前的导航,即 to 代表的路由对象不会进入,被中断,此时该表 URL 地址会 
        被重置到 from 路由对应的地址
    3.next(‘/’) 和 next({path: ‘/’}):在中断掉当前导航的同时,跳转到一个不同的地址
     4.next(error):如果传入参数是一个 Error 实例,那么导航被终止的同时会将错误传递给 router.onError() 注册过的回调
    
    第二种组件内导航钩子

    组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他们是直接在路由组件内部直接进行定义的

    const File = {
        template: `<div>This is file</div>`,
        beforeRouteEnter(to, from, next) {
            // do someting
            // 在渲染该组件的对应路由被 confirm 前调用
        },
        beforeRouteUpdate(to, from, next) {
            // do someting
            // 在当前路由改变,但是依然渲染该组件是调用
        },
        beforeRouteLeave(to, from ,next) {
            // do someting
            // 导航离开该组件的对应路由时被调用
        }
    }
    
    第三种单独路由独享组件

    即单个路由独享的导航钩子,它是在路由配置上直接进行定义的

    const router = new VueRouter({
        routes: [
            {
                path: '/file',
                component: File,
                beforeEnter: (to, from ,next) => {
                    // do someting
                }
            }
        ]
    });
    

    5.route和router 的区别?

    可以理解为,一个是用来获取路由信息的,一个是用来操作路由的

    一.$route

    route是路由信息对象,每一个路由都会有一个route对象,是一个局部的对象,里面主要包含路由的一些基本信息,包括name、meta、path、hash、query、params、fullPath、matched、redirectedFrom等等

    1. $route.path 字符串,对应当前路由的路径,总是解析为绝对路径,如"/foo/bar"。
    2. $route.params 一个 key/value 对象,包含了 动态片段 和 全匹配片段, 如果没有路由参数,就是一个空对象。
    3. route.query 一个 key/value 对象,表示 URL 查询参数。 例如,对于路径 /foo?user=1,则有route.query.user == 1, 如果没有查询参数,则是个空对象。
    4. $route.hash 当前路由的hash值 (不带#) ,如果没有 hash 值,则为空字符串。锚点*
    5. $route.fullPath 完成解析后的 URL,包含查询参数和hash的完整路径。
    6. $route.matched 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
    7. $route.name 当前路径名字
    8. $route.meta 路由元信息
      ( 导航钩子的参数:router.beforeEach((to,from, next)=>{//to 和from都是 路由信息对象,后面使用路由的钩子函数就容易理解了}) )
    二.$router

    router是VueRouter的实例,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由包含了许多关键的对象和属性
    router对象是全局路由的实例,是router构造方法的实例) 1、push: 1.字符串this.router.push('home')
    2. 对象this.router.push({path:'home'}) 3. 命名的路由this.router.push({name:'user',params:{userId:123}})
    4.带查询参数,变成 /register?plan=123this.router.push({path:'register',query:{plan:'123'}}) push方法其实和<router-link :to="...">是等同的。 注意:push方法的跳转会向 history 栈添加一个新的记录,当我们点击浏览器的返回按钮时可以看到之前的页面。 2、go 页面路由跳转 前进或者后退this.router.go(-1) // 后退
    3、replace
    push方法会向 history 栈添加一个新的记录,而replace方法是替换当前的页面, 不会向 history 栈添加一个新的记录
    4.一般使用replace来做404页面
    this.$router.replace('/')
    配置路由时path有时候会加 '/' 有时候不加,以'/'开头的会被当作根路径,就不会一直嵌套之前的路径。

    6.vue-router响应路由参数的变化?

    当使⽤路由参数时,例如从 /user/aside导航到 /user/foo,原来的组件实例会被复⽤。因为两个路由都渲染同个组件,⽐起销毁再创建,复
    ⽤则更加⾼效。不过,这也意味着组件的⽣命周期钩⼦不会再被调⽤。
    注意:
    (1)从同⼀个组件跳转到同⼀个组件。
    (2)⽣命周期钩⼦created和mounted都不会调⽤。
    可以使⽤router的组件内钩⼦函数

    beforeRouteUpdate(to,from,next){
    //在这个钩⼦函数中:to表⽰将要跳转的路由对象,from表⽰从哪个路由跳转过来,next多数就是需要调⽤
    //created和mounted不调⽤,⽆法拿到需要的动态值,就通过to.path,to.params等
    //可以在这个函数中打印to,具体看to对象有什么可以使⽤的属性
    }
    

    添加watch监听

    watch: {
     // ⽅法1 //监听路由是否变化
      '$route' (to, from) {
       if(to.query.id !== from.query.id){
                this.id = to.query.id;
                this.init();//重新加载数据
            }
      }
    }
    //⽅法 2  设置路径变化时的处理函数
    watch: {
    '$route': {
        handler: 'init',
        immediate: true
      }
    }
    

    为了实现这样的效果可以给router-view添加⼀个不同的key,这样即使是公⽤组件,只要url变化了,就⼀定会重新创建这个组件。

    <router-view :key="$route.fullpath"></router-view>
    

    7.vue-router传参?

    1.router-link
    1.不带参数
    <router-link :to="{name:'home'}" />
    <router-link :to="{path:'/home'}" />
    //router-link中链接如果是'/'开始就是从跟路由开始,如果开始不带'/',则从当前路由开始
    2.带参数
    <router-link :to="{name:'home',params:{id:1}}" />
    //params传参数(类似post)
    //路由里面配置path:"/home/:id" 或者 path:"/home:id"
    //冒号用params取参,用"/:"拼接
    //不配置path,刷新页面id会保留
    //html取参 $route.params.id
    //script 取参 this.$route.params.id 
    //params是path的一部分,所以params和name一起使用,不能和path一起使用
    
    <router-link :to="{name:'home',query:{id:1}}" /> 或者
    <router-link :to="{path:'home',query:{id:1}}" /> 
    //query传参数(类似get,url后面会显示参数)
    //路由可不配置   因为带参数了
    //html 取参 $route.query.id
    // script 取参 this.$route.query.id
    
    2.this.$router.push() 在函数里调用
    1.不带参数
    this.$router.push('/home')
    this.$router.push({name:'home'})
    this.$router.push({path:'/home'})
    2.带参数(query传参和params传参以及两者区别)
    //query传参 三种写法
    第一种:this.$router.push({name:'home',query:{id:1,age:2}})
    //基于name配置路由
    {
    path: '/hhhhhhh', //这里可以任意
    name: 'home', //这里必须是home
    component: Home
    }
    http://localhost:8080/#/hhhhhhh?id=1&age=2
    第二种:this.$router.push({path:'/home',query:{id:1,age:2}}) 
    //基于path配置路由
    {
    path: '/home', //这里必须是home
    name: 'hhhhhhhh', //这里任意
    component: Home
    }
    url:http://localhost:8080/#/home?id=1234&age=12
    第三种:this.$router.push(`/home?id=1`);
    //query是name和path都可以用
    //html取参   $route.query.id 
    //script 取参 this.$route.query.id 
    
    //params传参
    this.$router.push({name:'home',params:{id:1,age:2}})  //params只能和name一起用哟
    //路由配置:
    {
        path:'/home/:id/:age',
        name:'W',
        component:W
    }
    //路由配置path:"/home/:id" 或者path:"/home:id",
    //不配置path,第一次可以请求实现跳转,也能通过this.$router.params.id获取传过来的值,但是刷新页面id会消失  params比query严格
    //配置path,刷新页面id会保留
    //html 取参 $route.params.id
    //script 取参 this.$router.params.id 
    query和params的区别
    query类似get,跳转之后页面url后面会拼接参数,类似?id=1,非重要性可以这样传,密码之类用params
    params类似post,跳转之后页面url后面不会拼接参数,但是刷新页面id会消失
    
    3.this.$router.replace() (用法同上,push)
    4.this.$router.go(n)

    向前或者向后跳转n个页面,n可为正整数或负整数

    区别:
    this.router.push 跳转到指定url路径,并向history栈中添加一个记录,点击后退会返回上一个页面 this.router.replace
    跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面(就是直接替换了当前页面)
    this.$router.go(n)
    向前或者向后跳转n个页面,n可为正整数或负整数

    8.vue-router的两种模式(hash,history)?

    路由主要分为两种模式,分别是hash和history模式
    <router-link to=""/> 相当于路由入口,会被渲染成一个a标签,to会被渲染成href属性
    <router-view /> 相当于一个出口,用来做预渲染的,他会根据路由来匹配组件,然后把组件渲染出来
    

    在vue的router中,通过修改vueRouter的mode属性来决定使用history还是hash。默认为hash模式。

    const router = new VueRouter({
      mode: 'history',
      base: process.env.BASE_URL,
      routes
    })
    export default router
    
    hash和history的区别

    hash模式的url后跟hash值#…,它的原理就是使用window.onHashChange来监听hash值的改变,一旦发生变化就找出此hash值所匹配的组件,进而将组件渲染到页面中。但是hash模式这种带hash值的url是非常丑的,项目中也很少用hash模式。
    history模式中的url是以/user这种格式,比较常见,它的原理是通过window.onpopstate来监听路由变化,进而匹配不同的组件来渲染出来。

    vue的hash模式实现原理

    hash模式主要是根据url的hash值来跳转不同的路由页面。
    采用hash模式的路由模式中,url后面有一个#,#后面包括#就是此路由的hash值,hash模式背后的原理是onhashchange事件,可以在window对象上监听这个事件:
    vue中hash模式的原理就是通过监听hash值的变化来匹配不同的组件,进而来重新渲染视图。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <button id="btn">点我修改hash值呦</button>
        <script>
            let btn = document.querySelector("#btn")
            btn.addEventListener("click", () => {
                console.log(1);
                location.href = "#/user"
            })
            // hash值一旦修改就会触发
            window.onhashchange = (e) => {
                console.log(e.oldURL);
                console.log(e.newURL);
                console.log(location.hash);
            }
        </script>
    </body>
    
    </html>
    
    vue的history模式实现原理

    history路由中我们使用onpopstate事件函数来监听history路由的变化,但是popstate事件函数只能监听到history.go、forward、back的切换路由方式,但是它不能够监听到pushState添加历史记录(就是在页面中点击某个a标签进行跳转的方式,点击页面顺序:a->b->c,记录的历史记录中a、b、c都存在,而replaceState则不同)、replaceState(点击页面顺序:a->b->c,记录的历史记录中只有a->c,即用c代替了b记录,b记录被删除了)切换路由的方式。

        // 1监听路由变化
        // 路由的两种模式:hash、history
        // 监听hash路由使用window.onhashchange
        //history路由:采用history.go、history.back、history.forword方法来进行路由跳转
        // 在history路由中我们使用onpopstate事件函数来监听history路由的变化,但是popstate事件函数只能监听到history.go、forward、back的切换路由方式,
        window.addEventListener("popstate", () => {
            console.log(11);
        })
        // 但是它不能够监听到pushState添加历史记录(就是在页面中点击某个a标签进行跳转的方式,点击页面顺序:a->b->c,记录的历史记录中a、b、c都存在,而replaceState则不同)、replaceState(点击页面顺序:a->b->c,记录的历史记录中只有a->c,即用c代替了b记录,b记录被删除了)切换路由的方式
        // 对于pushState、replaceState需要通过函数重写的方式进行劫持,也就是说我们重写pushState和replaceState
        // 但是我们一般都是pushState来跳转链接,是通过this.$router.replace()来触发;而pushState()是通过this.$router.push()来触发
        // 重写pushState方法
        const rawPushState = window.history.pushState
        window.history.pushState = function (...args) {
            rawPushState.apply(window.history, args)
            console.log("终于监视到pushState了");
        }
        // 重写replaceState方法
        const rawReplaceState = window.history.replaceState
        window.history.replaceState = function (...args) {
            rawReplaceState.apply(window.history, args)
            console.log("终于监视到replaceState了");
        }
    

    9.vue-router实现路由懒加载( 动态加载路由 )?

    路由懒加载也叫延迟加载,即在需要的时候进行加载,随用随载

    官方解释:

    1:当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。
    2:如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
    官方在说什么呢?为什么需要懒加载?
    1:首先,我们知道路由中通常会定义很多不同的页面。
    2:这个页面这项目build打包后,一般情况下,会放在一个单独的js文件中
    3:但是,如果很多的页面都放在同一个js文件中,必然会造成这个页面非常大
    4:如果我们一次性的从服务器中请求下来这个页面,可能会花费一定时间,用户体验不好
    5:如何避免这种情况发生呢?使用路由懒加载就可以了

    继续解释原由?

    1:像vue这种单页面应用,如果没有应用懒加载,运用webpack打包后的文件将会异常的大。
    2:造成进入首页时,需要加载的内容过多,时间过长,会出现长时间的白屏,即使做了loading也是不利于用户体验。
    3:而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时
    也就是说:进入页面不用也不需要一次性加载过多资源造成加载时间过长!

    路由懒加载做了什么事情?

    1:主要作用是将路由对应的组件打包成一个个的js代码块
    2:只有在这个路由被访问到的时候,才加载对应的组件,否则不加载!

    如何实现路由懒加载?

    vue项目实现路由按需加载(路由懒加载)的三种方式:
    1:Vue异步组件
    2:ES6标准语法import()---------推荐使用!!!!!
    3:webpack的require,ensure()

    1.Vue异步加载技术

    1:vue-router配置路由,使用vue的异步组件技术,可以实现懒加载,此时一个组件会生成一个js文件。
    2:component: resolve => require(['放入需要加载的路由地址'], resolve)

    {
    path: '/problem',
    name: 'problem',
    component: resolve => require(['../pages/home/problemList'], resolve)
    }
    
    2.ES6推荐方式imprort ()----推荐使用

    1:直接将组件引入的方式,import是ES6的一个语法标准,如果需要浏览器兼容,需要转化成es5的语法。
    2:推荐使用这种方式,但是注意wepack的版本>2.4
    3:vue官方文档中使用的也是import实现路由懒加载
    4:上面声明导入,下面直接使用

    import Vue from 'vue';
    import Router from 'vue-router';
    // 官网可知:下面没有指定webpackChunkName,每个组件打包成一个js文件。
    const Foo = () => import('../components/Foo')
    const Aoo = () => import('../components/Aoo')
    // 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
    // const Foo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/Foo')
    // const Aoo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/Aoo')
    export default new Router({
     routes: [
      {
       path: '/Foo',
       name: 'Foo',
       component: Foo
      },
      {
       path: '/Aoo',
       name: 'Aoo',
       component: Aoo
      }
     ]
    })
    
    3.webpack提供的require.ensure()实现懒加载

    1:vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。
    2:这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
    3:require.ensure可实现按需加载资源,包括js,css等。他会给里面require的文件单独打包,不会和主文件打包在一起。
    4:第一个参数是数组,表明第二个参数里需要依赖的模块,这些会提前加载。
    5:第二个是回调函数,在这个回调函数里面require的文件会被单独打包成一个chunk,不会和主文件打包在一起,这样就生成了两个chunk,第一次加载时只加载主文件。
    6:第三个参数是错误回调。
    7:第四个参数是单独打包的chunk的文件名

    import Vue from 'vue';
    import Router from 'vue-router';
    const HelloWorld=resolve=>{
            require.ensure(['@/components/HelloWorld'],()=>{
                resolve(require('@/components/HelloWorld'))
            })
        }
    Vue.use('Router')
    export default new Router({
        routes:[{
        {path:'./',
        name:'HelloWorld',
        component:HelloWorld
        }
        }]
    })
    
    import和require的比较(了解)

    1:import 是解构过程并且是编译时执行
    2:require 是赋值过程并且是运行时才执行,也就是异步加载
    3:require的性能相对于import稍低,因为require是在运行时才引入模块并且还赋值给某个变量

    10.vue-router怎么重定向页面?

    路由重定向指的是:用户载访问A的时候,强制用户跳转带地址C从而展示特定的组件页面
    通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由重定向

    const router = new VueRouter({
      routes: [
        //其中,paht 表示需要被重定向的原地址,redirect 表示将要被重定向到的新地址
       {path: '/', redirect: User },
        { path: '/user', component: User },
        { path: '/register', component: Register }
      ]
    })
    

    11.vue-router怎么配置404页面?

    在路由配置中使用通配符 * 号来指明路径属性path,示例如下

      {
        path: '*',
        name: '404',
        component: () => import(/* webpackChunkName: "404" */ '../views/404.vue')
      }
    

    其中404.vue为404页面,这个路由配置可以配置到路由的任何位置

    12.vue跳转新路由 滚动到固定位置?

    ######## 方法一:锚点
    锚点通过在元素上设置id,然后用a标签的href="#id"属性跳转到指定位置。
    也可以通过js中 window.location.hash= ‘#id’ 或 window.location.href = ‘#id’ 来跳转。

    image.png
        //dom
        <a href="#li50">跳到50</a>
        <ul class="ul" id="ul">
          <li v-for="(item, index) in new Array(100)" :key="index" :id="'li' + (index + 1)">
            {{ index + 1 }} 只羊
          </li>
        </ul>
    
        //或者js
         window.location.hash= '#li50';
    

    hash和href的区别:

    • hash为锚链接,并不会跳转到新的链接,只会在当前链接里面改变锚链。
    • href表示重定向,得到完整的url,页面跳转到新的页面。
    方法二:scrollTop

    先计算指定dom距离顶部的高度offsetTop,再设置父元素的滚动距离scrollTop即可。

        //dom
        <el-button type="primary" @click="link">跳到30</el-button>
    
        //js
        link() {
            let target = document.getElementById('li30');
            let parent = document.getElementById('ul');
            parent.scrollTop = target.offsetTop - parent.offsetTop;
        },
    

    由于此处父元素距离顶部也有一定的高度,所以滚动距离需要先减去父元素的offsetTop,才是正确的滚动高度。

    方法三:scrollTo

    同scrollTop,只是换了种写法

        link() {
            let target = document.getElementById('li30');
            let parent = document.getElementById('ul');
            // parent.scrollTop = target.offsetTop - parent.offsetTop;
            parent.scrollTo(0, target.offsetTop - parent.offsetTop);
        },
    

    也可以通过ref来指定dom

    this.$refs.ul.scrollTo(0, 300);
    
    方法四:scrollIntoView

    简单粗暴,将指定元素滚动到可视区域顶部、底部或中间:

    //顶部
    document.getElementById('li60').scrollIntoView();
    //底部
    document.getElementById('li60').scrollIntoView(false);
    //中间
    document.getElementById('li60').scrollIntoView({ block: 'center' });
    

    Element.scrollIntoView():https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

    13.vue 路由去掉#?

    vue开发时地址栏上的ip后面会跟着一个#号,如果想去掉这个井号,可以在路由上加上 mode: 'history', 即可去掉

    //设置路由
    const router = new VueRouter({
        mode: "history",
        base: __dirname,
        routes: [
            { path: "/",component: Customers },
            { path: "/about",component: About },
            { path:"/add",component:Add },
            { path:"/customers/:id",component:CustomerDetails },
            { path:"/edit/:id",component:Edit }
        ]
    })
    

    14.Vue-router跳转和location.href有什么区别?

    ①vue-router使用pushState进行路由更新,静态跳转,页面不会重新加载;location.href会触发浏览器,页面重新加载一次
    ②vue-router使用diff算法,实现按需加载,减少dom操作
    ③vue-router是路由跳转或同一个页面跳转;location.href是不同页面间跳转;
    ④vue-router是异步加载this.$nextTick(()=>{获取url});location.href是同步加载

    this.$router.push({path:'/fillinformation',
            query: {applicationNo: this.applicationNo,contractNo:this.contractNo}})
        }
    this.$route.query.applicationNo//页面跳转后获取携带参数applicationNo参数
    //此用法参数会展示在跳转地址上---图一
    
    this.$router.push({
            name: 'clientdetail',
            params: {
              clientCode: clientCode,
              clientType: clientType
            }
          })
     this.$route.params.clientCode//页面跳转后获取携带参数clientCode
    //此用法参数不会展示在跳转地址--图二
    

    使用location.href实现页面div块的快速定位

    location.href='#divClass'//<div id = "divClass"></div>,通过事件直接跳转到该dev
    

    location.href可直接获取当前路径
    parent.location.href跳转至上一层页面
    top.location.href跳转至最外层页面

    15.Vue里面router-link在电脑上有用,在安卓上没反应怎么解决?

    Vue路由在Android机上有问题,babel问题,安装babel polypill 插件解决

    16.Vue2中注册在router-link上事件无效解决方法?

    使用@click.native。原因:router-link会阻止click事件,.native指直接监听一个原生事件

    17.RouterLink在IE和Firefox中不起作用(路由不跳转)的问题?

    问题描述
    假设路由已配置好,但运行如下代码后,点击 Admin 按钮却没有跳转至 admin 界面。(clrDropdownItem为按钮样式)
    main.component.html

    <button type="button" clrDropdownItem >
        <a routerLink="/admin"> Admin </a>
    </button>
    

    解决方法
    1,方法一
    将 clrDropdownItem 样式放在<a>标签,不使用<button>标签

    <a clrDropdownItem routerLink="/admin"> Admin </a>
    

    2,方法二
    使用<button>标签和Router.navigate方法
    main.component.html

    <button type="button" clrDropdownItem (click)="gotoAdmin()">
        Admin
    </button>
    复制代码
    
    import { Router } from '@angular/router';
    ...
    export class MainComponent  {
        constructor(
            private router: Router
        ) {}
        
        gotoAdmin() {
            this.router.navigate(['/admin']);
        }
    }
    

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

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理插件。(公共数据库)
    使用步骤:在main.js引入store,注入。新建了一个目录store.js,…… export 。
    当项目遇到以下两种场景时::单页应用中,组件之间的状态,音乐播放、登录状态、加入购物车

    1. 多个组件依赖于同一状态时。
    2. 来自不同组件的行为需要变更同一状态。
      解决的问题:多个视图依赖同一个状态来自不同视图的行为需要变更同一状态适用于中大型的单页面应用。

    2.vuex有哪几种属性?

    Vuex里面有五个特别重要的属性,分别是state,mutations,actions,getters,modules。

    1.state

    放置状态相关的信息,vue是使用单一状态树的,也就是单一数据源,也就是说我们的state只能有一个


    image.png
    2.mutations

    mutations其实就相当于我们vue里面的methods,也是定义方法的,只不过这个方法可以在多个组件调用就是了。


    image.png

    他是这样子调用的:使用我们的commit


    image.png
    最后在使用就行了:
    image.png
    3.actions

    mutations是写同步操作的,在他里面是不能写异步操作的,那我们就需要在actions里面来写我们的异步操作。并且写异步操作的话,调用的话就不是使用commit了,二十使用我们的dispatch


    image.png
    image.png
    image.png

    如上图所示,我们首先在mutations里面写好我们的方法,然后再actions里面写好异步操作,然后调用我们在mutations里面写好的方法,然后回到我们的组件,在组件里面使用我们的dispatch就可以了。

    4.getters

    getters其实就是相当于vue里面的计算属性,我们在这个里面使用的都是一些计算的方法,使用起来也是蛮简单的,只需要在组件里面直接使用就好了。操作如下图。


    image.png
    5.modules
    image.png
    image.png
    image.png

    最后一个就是我们的modules,就是模块化的意思,因为是单一状态树,怕我们在state里面写的东西太多了,不好进行查找,那我们可以在我们的modules里面重新定义我们的一个模块,就是相当于一个store,里面也有我们所需要的五个属性,所进行的操作也是一样的。

    3.不使用Vuex会带来什么问题?

    可维护性会下降,想修改数据要维护三个地方;
    可读性会下降,因为一个组件里的数据,根本就看不出来是从哪来的;
    增加耦合,大量的上传派发,会让耦合性大大增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。

    4.Vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?

    如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
    如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用。

    5.vuex一个例子方法?

    在testApp中建store文件 ==》 store文件下又有modules文件夹和getter.js 和 index.js ==》 store文件下建user.js
    在store文件下的index.js中引入

    import Vue from 'vue'
    import Vuex from 'vuex'
    import user from './modules/user'
    import global from './modules/global'
    import getters from './getters'
     
    Vue.use(Vuex)
     
    const store = new Vuex.Store({
      modules: {
        user
      },
      getters
    })
     
    export default store
    

    在store文件下的getters.js中引入

    const getters = {
      self: state => state.user.self,
      token: state => state.user.token,
      currentCommunity: (state, getters) => {
        let cid = getters.currentCommunityId
        return getters.communities.filter(item => {
          return item.communityId === cid
        })
      }
    }
     
    export default getters
    

    在modules文件下的user.js写代码

    const user = {
      state:{
          self: null,
          token: '',
      },
      mutations:{
          SET_SELF: (state, self) => {
               state.self = self
           },
           SET_TOKEN: (state, token) => {
               state.token = token
           }
      },
      actions:{
           login ({ commit }, res) {
                commit('SET_SELF', res.self)
                commit('SET_TOKEN', res.token)
          }
       }
    }
    export default user   
    

    使用下面这两种方法存储数据:
      dispatch:异步操作,写法: this.store.dispatch('actions方法名',值)   commit:同步操作,写法:this.store.commit('mutations方法名',值)

    6.Vuex中如何异步修改状态?

    actions与mutations作用类似,都是可以对状态进行修改。不同的是actions是异步操作的。
    actions是可以调用Mutations里的方法的。

    const actions={
        addActions(context){
            context.commit('add',10);//调用mutations中的方法
            setTimeout(()=>{context.commit('reduce')},5000)
        //  setTimeOut(()=>{context.commit('reduce')},3000);
            console.log('我比reduce提前执行');
        },
        
        reduceActions({commit}){
            commit('reduce');
        }
    }
    

    7.Vuex中actions和mutations的区别?

    Mutation 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数

    const store = new Vuex.Store({
      state: {
        count: 1
      },
      mutations: {
        increment (state) {
          // 变更状态
          state.count++
        }
      }
    })
    

    Action Action 类似于 mutation,不同在于:
    Action 提交的是 mutation,而不是直接变更状态。
    Action 可以包含任意异步操作。

    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          context.commit('increment')
        }
      }
    })
    

    8.页面刷新后vuex的state数据丢失怎么解决?

    1,页面刷新数据丢失的原因?
    vuex是将state当做全局变量存储。刷新页面之后自然随着页面的刷新重新初始化state。所以存的数据也会丢失。
    2,解决数据丢失,一般都会用localstorage或者cookie去缓存数据。
    3,推荐新的插件vuex-along,使用方法:
    第一步:npm install vuex-along或者yarn add vuex-along
    第二步:引入:import vuexAlong from ‘vuex-along’
    最后在 store中注册一下

    export default new Vuex.store({
        state:{...},
        plugins:[vuexAlong]
    })
    

    9.vuex怎么知道state是通过mutation修改还是外部直接修改的?

    通过$watch监听mutation的commit函数中_committing是否为true;严格模式下不允许直接修改。
    Vuex 中修改 state 的唯一渠道就是执行 commit('xx', payload) 方法,其底层通过执行 this._withCommit(fn) 设置_committing 标志变量为 true,然后才能修改 state,修改完毕还需要还原_committing 变量。外部修改虽然能够直接修改 state,但是并没有修改_committing 标志位,所以只要 watch 一下 state,state change 时判断是否_committing 值为 true,即可判断修改的合法性。

    1.vue 如何mock数据?

    引用文章

    2.顶部悬停效果?

    引用文章

    7.Vue和原生(ios和安卓)的交互

    1.Vue和原生(ios和安卓)的交互(第一种方法)
    一、原生调用Vue方法

    1、Vue

    created() {
        //Vue的方法给原生调用,则需要把方法挂在Window下面
        window.getDataFromNative = this.getDataFromNative;
    },
    methods: {
      getDataFromNative(params) {
        //params: 原生调用Vue时传值(params)给Vue
        console.log("得到原生传值结果:" + params);
        var dic = {
            'name': "vicky"
        };
        return dic; //回调给原生,可写可不写
      },
    }
    

    2、原生
    安卓

    String toVueSting = "vickylizy";
     
    webView.loadUrl("javascript:getDataFromNative('"+toVueSting+"')");
    or:
    webView.evaluateJavascript("javascript:getDataFromNative('" + toVueSting + "')", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String s) {
            //此方法可以得到回调值
        }
    });
    

    IOS

    NSString *toVueSting = @"vickylizy";
     
      NSString *jsStr = [NSString stringWithFormat:@"getDataFromNative('%@')",toVueSting];
     
      [self->_wkWebView evaluateJavaScript:jsStr completionHandler:^(id _Nullable d, NSError * _Nullable error) {
     
                NSLog(@"返回---%@",d);//回调值
     
        }];
    
    二、Vue 调用原生

    1、Vue

    //vue调用Android方法,且传值给Android (Android方法名为 getDataFormVue)
      $App.getDataFormVue({
          title: this.money, //vue给android传值
      });  
     
    //vue调用iOS方法,且传值给iOS (iOS 方法名为 getDataFormVue)
    window.webkit.messageHandlers.getDataFormVue.postMessage({
        title: this.money, //vue给iOS传值
    });
    

    2、原生
    安卓
    //Vue调用Android方法
    webView.addJavascriptInterface(this,"$App");//添加js监听 这样html就能调用客户端

    @JavascriptInterface
    public void getDataFormVue(String msg) {
         //做原生操作
    }
    

    IOS

    #pragma mark -WKScriptMessageHandler
     
    - (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message{
     
        if ([message.name isEqualToString:@"getDataFormVue"]) {
     
            NSLog(@"是什么?---%@",message.body);
     
           //做原生操作
     
        }
     
    }
    
    2.Vue和原生(ios和安卓)的交互(第二种方法)

    统一使用WebViewJavascriptBridge:
    Github地址
    在Vue中引入WebViewJavascriptBridge:
    创建 src/config/bridge.js 文件,用于封装 WebViewJavascriptBridge,将以下代码拷贝到 bridge.js 文件中:

    function setupWebViewJavascriptBridge (callback) {
      if (window.WebViewJavascriptBridge) {
        return callback(window.WebViewJavascriptBridge)
      }
      if (window.WVJBCallbacks) {
        return window.WVJBCallbacks.push(callback)
      }
      window.WVJBCallbacks = [callback]
      var WVJBIframe = document.createElement('iframe')
      WVJBIframe.style.display = 'none'
      WVJBIframe.src = 'https://__bridge_loaded__'
      document.documentElement.appendChild(WVJBIframe)
      setTimeout(function () {
        document.documentElement.removeChild(WVJBIframe)
      }, 0)
    }
    
    export default {
      callhandler (name, data, callback) {
        setupWebViewJavascriptBridge(function (bridge) {
          bridge.callHandler(name, data, callback)
        })
      },
      registerhandler (name, callback) {
        setupWebViewJavascriptBridge(function (bridge) {
          bridge.registerHandler(name, function (data, responseCallback) {
            callback(data, responseCallback)
          })
        })
      }
    }
    

    在 main.js 中引入该文件

    import Bridge from './config/bridge.js'
    Vue.prototype.$bridge = Bridge
    

    在需要调用客户端方法的组件中(事先需要与iOS端同事约定好方法名)

    //客户端已经注册好一个名为“ObjC Echo”的方法,H5直接进行调用(方法名也为“ObjC Echo”)就行,调用的时候可以传客户端需要的参数
    this.$bridge.callhandler('ObjC Echo', params, (data) => {
      // 处理返回数据
    })
    

    当客户端需要调用 js 函数时,事先注册约定好的函数即可

    //注册一个方法(方法名是“JS Echo”),客户端进行调用(方法名也是“JS Echo”),responseCallback是回调函数
    this.$bridge.registerhandler('JS Echo', (data, responseCallback) => {
      alert('JS Echo called with:', data)
      responseCallback(data)
    })
    

    vue3.0新特性

    1,压缩包体积更小

    当前最小化并被压缩的 Vue 运行时大小约为 20kB(2.6.10 版为 22.8kB)。Vue 3.0捆绑包的大小大约会减少一半,即只有10kB!

    2,Object.defineProperty -> Proxy

    Object.defineProperty是一个相对比较昂贵的操作,因为它直接操作对象的属性,颗粒度比较小。将它替换为es6的Proxy,在目标对象之上架了一层拦截,代理的是对象而不是对象的属性。这样可以将原本对对象属性的操作变为对整个对象的操作,颗粒度变大。
    javascript引擎在解析的时候希望对象的结构越稳定越好,如果对象一直在变,可优化性降低,proxy不需要对原始对象做太多操作。

    3,Virtual DOM 重构

    vdom的本质是一个抽象层,用javascript描述界面渲染成什么样子。react用jsx,没办法检测出可以优化的动态代码,所以做时间分片,vue中足够快的话可以不用时间分片。
    传统vdom的性能瓶颈:
    虽然 Vue 能够保证触发更新的组件最小化,但在单个组件内部依然需要遍历该组件的整个 vdom 树。
    传统 vdom 的性能跟模版大小正相关,跟动态节点的数量无关。在一些组件整个模版内只有少量动态节点的情况下,这些遍历都是性能的浪费。
    JSX 和手写的 render function 是完全动态的,过度的灵活性导致运行时可以用于优化的信息不足
    那为什么不直接抛弃vdom呢?
    高级场景下手写 render function 获得更强的表达力
    生成的代码更简洁
    兼容2.x
    vue的特点是底层为Virtual DOM,上层包含有大量静态信息的模版。为了兼容手写 render function,最大化利用模版静态信息,vue3.0采用了动静结合的解决方案,将vdom的操作颗粒度变小,每次触发更新不再以组件为单位进行遍历,主要更改如下
    将模版基于动态节点指令切割为嵌套的区块
    每个区块内部的节点结构是固定的
    每个区块只需要以一个 Array 追踪自身包含的动态节点
    vue3.0将 vdom 更新性能由与模版整体大小相关提升为与动态内容的数量相关

    4, 更多编译时优化

    Slot 默认编译为函数:父子之间不存在强耦合,提升性能
    Monomorphic vnode factory:参数一致化,给它children信息,
    Compiler-generated flags for vnode/children types

    5,选用Function_based API

    为什么撤销 Class API ?
    1,更好地支持TypeScript
    Props 和其它需要注入到 this 的属性导致类型声明依然存在问题
    Decorators 提案的严重不稳定使得依赖它的方案具有重大风险
    2,除了类型支持以外 Class API 并不带来任何新的优势
    3,vue中的UI组件很少用到继承,一般都是组合,可以用Function-based API
    1,vue3.0将组件的逻辑都写在了函数内部,setup()会取代vue2.x的data()函数,返回一个对象,暴露给模板,而且只在初始化的时候调用一次,因为值可以被跟踪。
    2,新的函数api:const count = value(0)
    value是一个wrapper,是一个包装对象,会包含数字0,可以用count.value来获取这个值。在函数返回的时候会关心是value wrapper,一旦返回给模版,就不用关心了。
    优点:即使count包含的是基本类型,例如数字和字符串,也可以在函数之间来回传递,当用count.value取值的时候会触发依赖,改值的时候会触发更新。
    3,计算属性返回的也是这个值的包装。
    4,onMounted生命周期函数直接注入。
    Function-based API 对比Class-based API有以下优点
    1,对typescript更加友好,typescript对函数的参数和返回值都非常好,写Function-based API既是javascript又是typescript,不需要任何的类型声明,typescript可以自己做类型推导。
    2,静态的import和export是treeshaking的前提,Function-based API中的方法都是从全局的vue中import进来的。
    3,函数内部的变量名和函数名都可以被压缩为单个字母,但是对象和类的属性和方法名默认不被压缩(为了防止引用出错)。
    4,更灵活的逻辑复用。
    目前如果我们要在组件之间共享一些代码,则有两个可用的选择:mixins 和作用域插槽( scoped slots),但是它们都存在一些缺陷:
    1,mixins 的最大缺点在于我们对它实际上添加到组件中的行为一无所知。这不仅使代码变得难以理解,而且还可能导致名称与现有属性和函数发生冲突。
    2,通过使用作用域插槽,我们确切地知道可以通过 v-slot 属性访问了哪些属性,因此代码更容易理解。这种方法的缺点是我们只能在模板中访问它,并且只能在组件作用域内使用。
    高阶组件在vue中比较少,在react中引入是作为mixins的替代品,但是比mixins更糟糕,高阶组件可以将多个组件进行包装,子组件通过props接收数据,多个高阶组件一起使用,不知道数据来自哪个高阶组件,存在命名空间的冲突。而且高阶组件嵌套得越多,额外的组件实例就越多,造成性能损耗。
    · 3.0比2.0 快2倍
    · 3.0去掉了filter, 么有beforeCreate created,用setup取代
    · reactivity是可以单独作为库使用的
    · 单独功能可以抽离 取代了mixin 优于mixin 解决上下反复横跳
    · 支持多个子节点 fragment
    · setup里没有this
    · Proxy实现响应式不需要set delete 兼容性并不好
    · 响应式方面 性能得到很大提升 不用初始化的时候就递归遍历属性
    · 响应式不区分数组和对象
    · 3.0兼容IE12以上
    · composition api 可以和 options API 同时存在

    相关文章

      网友评论

        本文标题:最新面试题目 vue (二)

        本文链接:https://www.haomeiwen.com/subject/rkjuurtx.html