当前位置: 首页 > 面试经验 >

携程前端开发一面

优质
小牛编辑
69浏览
2024-04-08

携程前端开发一面

  1. 自我介绍

  2. 介绍一下你的项目

  3. 你在项目过程中学到了什么

  4. 你在项目中学习到了哪些技术

  5. 怎么解决跨域问题的?

    出现跨域问题:Access-Control-Allow-Origin — 受同源策略限制:同协议同域名同端口

    在前端使用代理,通过代理访问后端,首先配置请求baseURL,然后在vue.config.js中配置proxy设置代理,target即接口域名,将changeOrigin设为true并在pathRewrite中将baseURL重写为""。

    原理:浏览器禁止跨域,但服务器不禁止,proxyTable为我们提供了一个可以跨域的代理中转服务器服务,实际上是将请求发给自己的服务器,再由服务器转发给后端服务器,做了一层代理。

  6. 你说你使用了vue-Router?那你知道怎么动态定义path吗?

    动态路由匹配:把某种模式匹配到的所有路由,使用动态路径参数映射到同个组件

    // 有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染
    const User = {
      template: '<div>User:{{ this.$route.params.id }}</div>'
    }
     
    // 一个“路径参数”使用冒号:标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。
    const router = new VueRouter({
      routes: [
        // 动态路径参数 以冒号开头
        { path: '/user/:id', component: User }
      ]
    })
    
    // 页面跳转时
    this.$router.push({ path: `/user/${value.id}`, });
    

    在js中实现页面跳转并传参的方法:

    1. this.$router.push({path: '/t',query:{ index:'1'}});
       接收参数:this.$route.query.index
    2.this.$router.push({name: 'Test',params:{ index:'1'}});
       接收参数:this.$route.params.index
    
  7. 为什么要定义公共组件?

    使用场景:项目中若多个页面都显示有相同的区域内容,则该公共区域内容可以封装成公共组件进行使用。

    提升整个项目的开发效率,能够把页面抽象成多个相对独立的模块,让我们使用小型、独立和可复用的组件构建大型应用,让代码变得“高内聚”和“低耦合”,解决了项目开发中效率低难维护复用性低等问题。

  8. vue的生命周期?

    vue的生命周期分为四个阶段:创建、挂载、更新、销毁。

    一个生命周期中有八个钩子函数:

    ​ 初始化:生命周期、事件、但数据代理还未开始;

    beforeCreate:无法访问data中的数据、methods中的方法;

    ​ 初始化:数据监测、数据代理;

    created:可访问data中的数据、methods中的方法,在这个阶段请求数据为mounted渲染做准备;

    ​ 生成:在内存中生成虚拟dom;页面还不能显示解析好的内容;

    beforeMount:此时dom未经Vue编译,所有对dom的操作都不奏效;

    ​ 生成:将虚拟dom转为真实dom插入页面;

    mounted:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作;

    ​ 当data有更新时:

    beforeUpdate:数据是新的,页面是旧的;

    ​ 根据新数据生成新的虚拟dom树并与旧的虚拟dom比较,完成页面更新;

    updated:数据是新的,页面也是新的;

    ​ 当vm.$destroy() 被调用:

    beforeDestroy:关闭定时器、取消订阅消息、解绑自定义事件。

    destroyed:组件销毁时触发,vue实例解除事件监听以及和dom的绑定,但DOM节点依旧存在

  9. destroyed时候vue会干什么?

    Vue 实例指示的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

  10. 怎么清除定时器?

    定时器(timer)由 setTimeout() 和 setInterval() 两个函数完成。

    timer = setTimeout( func|code, delay) 用于在指定的毫秒数后调用函数或计算表达式;
    timer = setInterval()( func|code, delay) 用于每隔一定时间就调用函数,方法或对象;
    清理定时器:clearInterval(timer),clearTimeout(timer);
    
  11. 事件处理过程?

    一个事件的处理过程分为捕获、目标、冒泡三个阶段。

    捕获阶段:当 DOM 树的某个节点发生了一个事件,这个事件会从 dom树根节点发出,不断经过下级节点直到触发事件的目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)( 所有经过的节点,都会触发这个事件。捕获阶段的任务就是建立这个事件传递路线,以便后面冒泡阶段逆着这条路线返回根节点);

    目标阶段:在目标节点上触发这个事件;

    冒泡阶段:事件开始时,由目标节点接收事件,然后逐级传播到其父节点

  12. 怎么注册捕获阶段的事件处理函数和冒泡阶段的事件处理函数?

    标准事件模型(DOM2级):

    document.getElementById("myElement").addEventListener("event", eventHandler, flag);
    // flag : true:捕获阶段执行;
    		  false:冒泡阶段执行;
    

    ​ 特性:可在同一DOM元素上绑定多个事件处理函数;

    ​ 可设置在冒泡阶段执行还是在捕获阶段执行。

    原始事件模型(DOM0级):

    document.getElementById("myElement").onclick = function() {};
    

    ​ 只支持冒泡、不支持捕获,同一类型事件只能绑定一次,但是绑定速度快。

  13. 怎么阻止捕获/冒泡?

    要阻止事件冒泡,可以使用 event.stopPropagation() 方法。在事件监听器中调用该方法可以阻止事件继续向上冒泡到父级元素。这样,其他父级元素上的相同类型的事件监听器将不会被触发。

    但是使用event.stopPropagation()不能完全阻止事件的传播。事件仍会在当前目标元素上继续进行处理,例如执行默认行为,要完全取消事件传播并阻止默认行为,可以使用 event.preventDefault() 方法。

  14. 事件代理是什么?

    利用事件冒泡机制,将一个或一组元素的事件委托道它的父层或者更外层元素上。

  15. 你的项目有用到事件代理吗?你之后可以思考一下为什么没用

    为什么没用:

    适合事件委托的事件:clickmousedownmouseupkeydownkeyupkeypress

    优点:1. 减少整个页面所需的内存,提升整体性能;

    ​ 2. 减少重复工作;

    局限性:1. focusblur这些事件没有事件冒泡机制,所以无法进行委托绑定事件;

    ​ 2. mousemovemouseout这样的事件需要通过位置去计算定位,对性能消耗高,不适合;

    ​ 3. 如果把所有事件都用事件代理,可能会出现事件误判,即本不该被触发的事件被绑定上了事件;

  16. 你了解promise吗?

    promise是实现异步编程的一种解决方案,可以解决传统回调函数引起的回调地狱的问题。

    回调地狱是指回调函数中嵌套回调函数的情况,为实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护

    Promise构造函数接收一个函数作为参数,这个函数就是我们要处理的异步任务,函数的两个参数是resolve,reject。

    promise对象有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)

    异步任务执行成功时调用resolve函数返回结果,不成功则调用reject。 then()方法是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数。then方法返回的是一个新的Promise实例,所以promise能够链式书写。

    catch()方法是用于指定发生错误时的回调函数,Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。

    finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

    总结一下,当我们写代码遇到异步回调时,我们想让异步代码按照我们想要的顺序执行,如果按照传统的嵌套方式,就会出现回调地狱,这样的代码不利于维护,我们可以通过 Promise 对象进行链式编程来解决,这样尽管可以解决问题,但是ES7给我们提供的 async/await 语法糖,可以使得异步代码看起来更像是同步代码。
    
    //封装一个返回promise的异步任务
    function fn(str) {
    	var p = new Promise(function (resolve, reject) {
            var flag = true;
            setTimeout(function () {
                if (flag) {
                    resolve(str)
                } else {
                    reject('处理失败')
                }
            })
    	})
    	return p;
    }
    
    //封装一个执行上述异步任务的async函数
    async function test(){
    	var res1 = await fn('武林要以和为贵'); 
        	//await直接拿到fn()返回的resolve的数据,并且赋值给res
    	var res2 = await fn('要讲武德');
    	var res3=await fn('不要搞窝里斗');
    	console.log(res1,res2,res3);
    }
    //执行函数
    test();
    
  17. 你用过npm包吗?

    没用过,不打算准备这部分内容,所以不写了。

  18. css怎么实现一个header?

    都用的是elementUI框架做的,没咋写过css,不打算准备这部分内容,所以也不写了。

  19. 你知道html有哪些标签?

    <font color="" size="" face="字体类型"> 字体标签
    
    <br/> 换行
    
    <p align="left/right/center"> 段落(段前段后自动加空白行)
    
    <h1> <h2> <h3>... 标题标签
    
    &nbsp 空格
    
    <!--html标签--> 注释
    
    <img src="绝对路径/相对路径" width="" height="">
    
    <ul>
     <li></li>列表条目项标签,用于在效果中定义一个列表的条目
    </ul>无序列表标签
    
    <ol>
     <li></li>列表条目项标签,用于在效果中定义一个列表的条目
    </ol>有序列表标签
    
    <a href="跳转路径">text</a> 超链接标签
    
    <span></span>	行级的块标签,用于在效果中一行上定义一个块,进行内容显示。
    <div></div>	块级的块标签,用于在效果中 定义一块,默认占满一行,进行内容的显示。
    
    <table border="边框粗细" width=">
    	<tr>	//表格的行标签,用于在效果中定义一个表格行
    		<td>表格的单元格标签,用于在效果中定义一个表格行中的单元格</td>
    	</tr>
    </table>
    	<th>表格的表头单元格标签,用于在效果中定义一个表格行中的表头单元格</th>
    	<th>和<td>唯一区别:<th>内容 居中加粗
    
  20. 你项目用的vue2还是vue3?

    会根据回答问一些特性

  21. method和computed有什么区别?

    computed可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果就可以

    methods: {
        sumScore: function () {
            console.log("methods方式调用!");
            return (this.mathScore - 0) + (this.englishScore - 0);
        }
    },
    computed: {
        // 默认是纯get方式,也是单项绑定
        sumScore1: function () {
            console.log("compute的纯get方式调用");
            return (this.mathScore - 0) + (this.englishScore - 0);
    	},
        // 采用get加set方式
        sumScore2: {
            get: function () {
                console.log("compute的get方式调用");
                return (this.mathScore - 0) + (this.englishScore - 0);
            },
            // 当在输入框中更改了总分后,两项成绩就会分别取到新总分的平均值,从而实现双向绑定
            set: function (newValue) {
                console.log("compute的set方式调用");
                var avgScore = newValue / 2;
                this.avgScore = avgScore;
            }
        }
    }
    

    用 computed 属性方法编写的逻辑运算,返回的结果直接当作一个变量值使用。computed 具有缓存功能,在系统刚运行的时候调用一次。只有当计算结果发生变化才会被调用。 用 methods 方法编写的逻辑运算,返回的是一个函数,调用时要加()。methods方法页面刚加载时调用一次,以后只有被调用的时候才会被调用。

    methods与compute纯get方式都是单向绑定,不可以更改输入框中的值。compute的get与set方式是真正的双向绑定。

  22. data和computed有什么区别?

    data 和 computed 最核心的区别在于 data 中的属性并不会随赋值变量的改动而改动,而computed 会。

    但是computed也监听不到数组或对象中元素的变化。

    父组件改变props时:
    ​	子组件如果直接使用 props,会触发子组件更新;
    ​	子组件如果将 props 放进 data 中 再使用,不会触发子组件更新;
    ​	子组件如果将 props 放进 computed 中再使用,会触发子组件更新
    

    Vue 把数据抽象成了两层,第一层就是简单的数据(data),第二层就是 computed (依赖于 data,也就是依赖于前一层)。第二层可以引用第一层的数据,而第一层却不能引用第二层的数据。

    它的本质是 Vue 实例在渲染时数据解析的顺序为

    props->methods->data->computed->watch->created

    关于 computed 和 watch 的差异:

    1.computed 是计算一个新的属性 默认初始化会执行一次,并将该属性挂载到 vm(Vue 实例)上,而 watch 是监听已经存在且已挂载到 vm 上的数据 默认需配置才会执行,所以用 watch 同样可以监听 computed 计算属性的变化(其它还有 data、props)
    2.computed 具有缓存性,只有当依赖变化后,才会计算新的值,computed 第一次加载就监听,而 watch 则是当数据发生变化便会调用执行函数
    3.从使用场景上说,computed 适用一个数据被多个数据影响,而 watch 适用一个数据影响多个数据
    
  23. 父子组件通信的方式?

    父组件传递数据给子组件:

    props:
    1. 子组件设置props属性,定义接收父组件传递过来的参数;
    2. 父组件在使用子组件标签中通过字面量来传递值;
    
    // Children.vue
    props:{   
     	name:String // 接收的类型参数  
        age:{    
            type:Number, // 接收的类型为数值  
            default:18,  // 默认值为18  
           	require:true // age属性必须传递  
        }  
    }  
    
    // Father.vue
    <Children name="jack" age=18 />  
    

    子组件传递数据给父组件:

    $emit:
    1. 子组件通过$emit触发自定义事件,$emit第二个参数为传递的数值
    2. 父组件绑定监听器获取到子组件传递过来的参数
    
    // Children.vue
    this.$emit('add', good)  
    
    // Father.vue
    <Children @add="cartAdd" />  
    
    components: {Children},
    methods:{
        cartAdd(good){//触发子组件城市选择-选择城市的事件
            console.log('Father cartAdd:'+good)
    	}
    }
    
#前端#
 类似资料: