前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Vuejs】625- Vue常见的考点

【Vuejs】625- Vue常见的考点

作者头像
pingan8787
发布2020-06-18 16:41:22
2.4K0
发布2020-06-18 16:41:22
举报
文章被收录于专栏:前端自习课前端自习课

在你看到这篇文章时刻,说明你多少是一个前端开发这,或者对前端开发有一定的应用和喜好;对于当下的前端来说,了解一个前端的流行框架,是多么一个势在必行的趋势,也是找工作中经常会询问到的,在这里就列举一些流行框架vue的常见考点,希望这些考点能给你带来一定的帮助!

1.页面中定义一个定时器,在哪个阶段清除?

答案:在 beforeDestroy 中销毁定时器。

① 为什么销毁它:

在页面 a 中写了一个定时器,比如每隔一秒钟打印一次 1,当我点击按钮进入页面 b 的时候,会发现定时器依然在执行,这是非常消耗性能的。

② 解决方案 1:

代码语言:javascript
复制
mounted(){
 this.timer = setInterval(()=>{
    console.log(1)
 },1000)
},
beforeDestroy(){
 clearInterval(this.timer)
}

方案 1 有两点不好的地方,引用尤大的话来说就是:

它需要在这个组件实例中保存这个 timer,如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。

我们的建立代码独立于我们的清理代码,这使得我们比较难于程序化的清理我们建立的所有东西。

方案 2(推荐):该方法是通过$once 这个事件侦听器在定义完定时器之后的位置来清除定时器

代码语言:javascript
复制
mounted(){
 const timer = setInterval(()=>{
    console.log(1)
 },1000)
 this.$once('hook:beforeDestroy',()=>{
  clearInterval(timer)
 })
}

官网参考链接:https://cn.vuejs.org/v2/guide/components-edge-cases.html

2.父组件如何获取子组件的数据,子组件如何获取父组件的数据,父子组件如何传值?

① 先说,父组件如何主动获取子组件的数据?

方案 1:$children

$children 用来访问子组件实例,要知道一个组件的子组件可能是不唯一的,所以它的返回值是数组。

现在,我们定义 Header,HelloWorld 两个组件

代码语言:javascript
复制
<template>
  <div class="index">
    <Header></Header>
    <HelloWorld :message="message"></HelloWorld>
    <button @click="goPro">跳转</button>
  </div>
</template>
mounted(){
 console.log(this.$children)
}

打印的是一个数组,可以用 foreach 分别得到所需要的的数据

缺点:

无法确定子组件的顺序,也不是响应式的。如果你确切的知道要访问子组件建议使用$refs。

方案 2 :$refs

代码语言:javascript
复制
<HelloWorld ref="hello" :message="message"></HelloWorld>

调用 helloworld 子组件的时候直接定义一个 ref,这样就可以通过 this.$refs 获取所需要的的数据。

代码语言:javascript
复制
this.$refs.hello.属性
this.$refs.hello.方法

② 子组件如何主动获取父组件中的数据?

通过 :$parent

parent用来访问父组件实例,通常父组件都是唯一确定的,跟

children 类似

代码语言:javascript
复制
this.$parent.属性
this.$parent.方法

父子组件通信除了以上三种,还有 props 和 attrs

③inheritAttrs

这是@2.4 新增的属性和接口。inheritAttrs 属性控制子组件 html 属性上是否显示父组件的提供的属性。

如果我们将父组件 Index 中的属性 desc、keysword、message 三个数据传递到子组件 HelloWorld 中的话,如下

父组件 Index 部分

代码语言:javascript
复制
<HelloWorld ref="hello" :desc="desc" :keysword="keysword" :message="message"></HelloWorld>

子组件:HelloWorld,props 中只接受了 message

代码语言:javascript
复制
props: {
    message: String
},

实际情况,我们只需要 message,那其他两个属性则会被当做普通的 html 元素插在子组件的根元素上。

如图

这样做会使组件预期功能变得模糊不清,这个时候,在子组件中写入,inheritAttrs:false ,这些没用到的属性便会被去掉,true 的话,就会显示。

如果,父组件中没被需要的属性,跟子组件本来的属性冲突的时候,则依据父组件

代码语言:javascript
复制
<HelloWorld ref="hello" type="text" :message="message"></HelloWorld>

子组件:HelloWorld

代码语言:javascript
复制
<template>
  <input type="number">
</template>

这个时候父组件中 type=“text”,而子组件中 type=”number”,而实际中最后显示的是 type=”text”,这并不是我们想要的,所以只要设置:inheritAttrs:false,type 便会成为 number

上述这些没被用到的属性,如何被获取呢?这就用到了$attrs

③$attrs

作用:可以获取到没有使用的注册属性,如果需要,我们在这也可以往下继续传递。

就上上述没有被用到的 desc 和 keysword 就能通过$attrs 获取到。

通过$attrs 的这个特性可以父组件传递到孙组件,免除父组件传递到子组件,再从子组件传递到孙组件的麻烦

代码如下 父组件 Index 部分

代码语言:javascript
复制
<div class="index">
  <HelloWorld ref="hello" :desc="desc" :keysword="keysword" :message="message"></HelloWorld>
</div>
 data(){
  return{
   message:'首页',
   desc:'首页描述',
   keysword:'我是关键词key'
 }
},

子组件 HelloWorld 部分

代码语言:javascript
复制
<div class="hello">
   <sunzi v-bind="$attrs"></sunzi>
   <button @click="aa">获取父组件的数据</button>
</div>

孙子组件 sunzi 部分

代码语言:javascript
复制
<template>
  <div class="header">
    {{$attrs}}
    <br>
  </div>
</template>

可以看出通过 v-bind=”$attrs”将数据传到孙组件中

除了以上,provide / inject 也适用于 隔代组件通信,尤其是获取祖先组件的数据,非常方便。

简单的说,当组件的引入层次过多,我们的子孙组件想要获取祖先组件的资源,那么怎么办呢,总不能一直取父级往上吧,而且这样代码结构容易混乱。这个就是 provide / inject 要干的事情。

代码语言:javascript
复制
<template>
  <div>
<childOne></childOne>
  </div>
</template>

<script>
  import childOne from '../components/test/ChildOne'
  export default {
    name: "Parent",
    provide: {
      for: "demo"
    },
    components:{
      childOne
    }
  }

在这里我们在父组件中 provide for 这个变量,然后直接设置三个组件(childOne、childTwo 、childThird)并且一层层不断内嵌其中, 而在最深层的 childThird 组件中我们可以通过 inject 获取 for 这个变量

代码语言:javascript
复制
<template>
  <div>
    {{demo}}
  </div>
</template>

<script>
  export default {
    name: "",
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    }
  }
</script>

3.自定义指令如何定义,它的生命周期是什么?

通过 Vue.directive() 来定义全局指令

有几个可用的钩子(生命周期), 每个钩子可以选择一些参数. 钩子如下:

bind: 一旦指令附加到元素时触发

inserted: 一旦元素被添加到父元素时触发

update: 每当元素本身更新(但是子元素还未更新)时触发

componentUpdate: 每当组件和子组件被更新时触发

unbind: 一旦指令被移除时触发。

bind 和 update 也许是这五个里面最有用的两个钩子了

每个钩子都有 el, binding, 和 vnode 参数可用.

update 和 componentUpdated 钩子还暴露了 oldVnode, 以区分传递的旧值和较新的值.

el 就是所绑定的元素.

binding 是一个保护传入钩子的参数的对象. 有很多可用的参数, 包括 name, value, oldValue, expression, arguments, arg 及修饰语.

vnode 有一个更不寻常的用例, 它可用于你需要直接引用到虚拟 DOM 中的节点.

binding 和 vnode 都应该被视为只读.

现在,自定义一个指令,添加一些样式,表示定位的距离

代码语言:javascript
复制
Vue.directive('tack',{
 bind(el,binding){
  el.style.position='fixed';
  el.style.top=binding.value + 'px'
 }
})
<div class="header" v-tack="10" >我是header</div>

假设我们想要区分从顶部或者左侧偏移 70px, 我们可以通过传递一个参数来做到这一点

代码语言:javascript
复制
Vue.directive('tack', {
 bind(el, binding, vnode) {
  el.style.position = 'fixed';
  const s = (binding.arg === 'left' ? 'left' : 'top');
  el.style[s] = binding.value + 'px';
 }
})

也可以同时传入不止一个值

代码语言:javascript
复制
Vue.directive('tack', {
 bind(el, binding, vnode) {
 el.style.position = 'fixed';
 el.style.top = binding.value.top + 'px';
 el.style.left = binding.value.left + 'px';
 }
})
<div class="header" v-tack="{left:’20’,top:’20’}" >我是header</div>

4、vue 生命周期,各个阶段简单讲一下?

breforeCreate():实例创建前,这个阶段实例的 data 和 methods 是读不到的。

created():实例创建后,这个阶段已经完成数据观测,属性和方法的运算,watch/event 事件回调,mount 挂载阶段还没有开始。$el 属性目前不可见,数据并没有在 DOM 元素上进行渲染。

created 完成之后,进行 template 编译等操作,将 template 编译为 render 函数,有了 render 函数后才会执行 beforeMount()

beforeMount():在挂载开始之前被调用:相关的 render 函数首次被调用

mounted():挂载之后调用,el 选项的 DOM 节点被新创建的 vm.$el 替换,并挂载到实例上去之后调用此生命周期函数,此时实例的数据在 DOM 节点上进行渲染

后续的钩子函数执行的过程都是需要外部的触发才会执行

有数据的变化,会调用 beforeUpdate,然后经过 Virtual Dom,最后 updated 更新完毕,当组件被销毁的时候,会调用 beforeDestory,以及 destoryed。

5、watch 和 computed 的区别?

computed:

① 有缓存机制;② 不能接受参数;③ 可以依赖其他 computed,甚至是其他组件的 data;④ 不能与 data 中的属性重复

watch:

① 可接受两个参数;② 监听时可触发一个回调,并做一些事情;③ 监听的属性必须是存在的;④ 允许异步

watch 配置:handler、deep(是否深度)、immeditate (是否立即执行)

总结:

当有一些数据需要随着另外一些数据变化时,建议使用 computed

当有一个通用的响应数据变化的时候,要执行一些业务逻辑或异步操作的时候建议使用 watch

6、请说一下 computed 中的 getter 和 setter

① computed 中可以分成 getter(读取) 和 setter(设值)

② 一般情况下是没有 setter 的,computed 预设只有 getter ,也就是只能读取,不能改变设值。

一、默认只有 getter 的写法

代码语言:javascript
复制
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})
//其实fullName的完整写法应该是如下:
fullName: {
 get(){
   return this.firstName + ' ' + this.lastName
 }
}

注意:不是说我们更改了 getter 里使用的变量,就会触发 computed 的更新,前提是 computed 里的值必须要在模板里使用才行。如果将{{fullName}}去掉,get()方法是不会触发的。

二、setter 的写法,可以设值

代码语言:javascript
复制
<template>
   <div id="demo">
       <p> {{ fullName }} </p>
       <input type="text" v-model="fullName">
       <input type="text" v-model="firstName">
       <input type="text" v-model="lastName">
   </div>
</template>

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'zhang',
    lastName: 'san'
  },
  computed: {
    fullName: {
      //getter 方法
     get(){
       console.log('computed getter...')
        return this.firstName + ' ' + this.lastName
       },
   //setter 方法
    set(newValue){
      console.log('computed setter...')
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
      return this.firstName + ' ' + this.lastName
     }

    }
  }
})

在这里,我们修改 fullName 的值,就会触发 setter,同时也会触发 getter。

注意:并不是触发了 setter 也就会触发 getter,他们两个是相互独立的。我们这里修改了 fullName 会触发 getter 是因为 setter 函数里有改变 firstName 和 lastName 值的代码,这两个值改变了,fullName 依赖于这两个值,所以便会自动改变。

7、导航钩子有哪几种,分别如何用,如何将数据传入下一个点击的路由页面?

① 全局导航守卫

前置守卫

代码语言:javascript
复制
router.beforeEach((to, from, next) => {
  // do someting
});

后置钩子(没有 next 参数)

代码语言:javascript
复制
router.afterEach((to, from) => {
  // do someting
});

② 路由独享守卫

代码语言:javascript
复制
cont router = new  VueRouter({
 routes: [
  {
    path: '/file',
    component: File,
    beforeEnter: (to, from ,next) => {
       // do someting
    }
   }
 ]
});

顺便看一下路由里面的参数配置:

③ 组件内的导航钩子

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

beforeRouteEnter

代码语言:javascript
复制
data(){
 return{
   pro:'产品'
 }
},
beforeRouteEnter:(to,from,next)=>{
  console.log(to)
  next(vm => {
   console.log(vm.pro)
  })
}

注:beforeRouteEnter 不能获取组件实例 this,因为当守卫执行前,组件实例被没有被创建出来,我们可以通过给 next 传入一个回调来访问组件实例。在导航被确认时,会执行这个回调,这时就可以访问组件实例了

仅仅是 beforRouteEnter 支持给 next 传递回调,其他两个并不支持,因为剩下两个钩子可以正常获取组件实例 this

如何通过路由将数据传入下一个跳转的页面呢?

答:params 和 query

params

代码语言:javascript
复制
传参
this.$router.push({
 name:"detail",
 params:{
   name:'xiaoming',
 }
});
接受
this.$route.params.name

query

代码语言:javascript
复制
传参
this.$router.push({
  path:'/detail',
  query:{
    name:"xiaoming"
  }
 })
接受 //接收参数是this.$route
this.$route.query.id

那 query 和 params 什么区别呢?

① params 只能用 name 来引入路由,query 既可以用 name 又可以用 path(通常用 path)

② params 类似于 post 方法,参数不会再地址栏中显示

query 类似于 get 请求,页面跳转的时候,可以在地址栏看到请求参数

那刚才提到的 this.

router 和this.

route 有何区别?

先打印出来看一下

router.push 方法

$route 为当前 router 跳转对象,里面可以获取 name、path、query、params 等

8、es6 的特有的类型, 常用的操作数组的方法都有哪些?

es6 新增的主要的特性:

① let const 两者都有块级作用域

② 箭头函数

③ 模板字符串

④ 解构赋值

⑤ for of 循环

⑥ import 、export 导入导出

⑦ set 数据结构

⑧ ...展开运算符

⑨ 修饰器 @

⑩ class 类继承

⑪ async、await

⑫ promise

⑬ Symbol

⑭ Proxy 代理

操作数组常用的方法:

es5:concat 、join 、push、pop、shift、unshift、slice、splice、substring 和 substr 、sort、 reverse、indexOf 和 lastIndexOf 、every、some、filter、map、forEach、reduce

es6:find、findIndex、fill、copyWithin、Array.from、Array.of、entries、values、key、includes

9、vue 双向绑定原理?

通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调

10、vue-router 的实现原理,history 和 hash 模式有什么区别?

vue-router 有两种模式,hash 模式和 history 模式

hash 模式

url 中带有#的便是 hash 模式,#后面是 hash 值,它的变化会触发 hashchange 这个事件。

通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听 hashchange 来实现更新页面部分内容的操作:

代码语言:javascript
复制
window.onhashchange = function(event){
  console.log(event.oldURL, event.newURL);
  let hash = location.hash.slice(1);
  document.body.style.color = hash;
}

另外,hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。

history 模式

history api 可以分为两大部分,切换和修改

① 切换历史状态

包括 back,forward,go 三个方法,对应浏览器的前进,后退,跳转操作

代码语言:javascript
复制
history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进

② 修改历史状态

包括了 pushState,replaceState 两个方法,这两个方法接收三个参数:stateObj,title,url

代码语言:javascript
复制
history.pushState({color:'red'}, 'red', 'red'})
window.onpopstate = function(event){
  console.log(event.state)
  if(event.state && event.state.color === 'red'){
    document.body.style.color = 'red';
  }
}
history.back();
history.forward();

通过 pushstate 把页面的状态保存在 state 对象中,当页面的 url 再变回这个 url 时,可以通过 event.state 取到这个 state 对象,从而可以对页面状态进行还原,这里的页面状态就是页面字体颜色,其实滚动条的位置,阅读进度,组件的开关的这些页面状态都可以存储到 state 的里面。

history 缺点:

1:hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如http://www.a12c.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。

2:history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致。如http://www.a12c.com/book/a。如果后端缺少对/book/a 的路由处理,将返回 404 错误

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端自习课 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.页面中定义一个定时器,在哪个阶段清除?
    • 2.父组件如何获取子组件的数据,子组件如何获取父组件的数据,父子组件如何传值?
      • 3.自定义指令如何定义,它的生命周期是什么?
      • 4、vue 生命周期,各个阶段简单讲一下?
      • 5、watch 和 computed 的区别?
      • 6、请说一下 computed 中的 getter 和 setter
      • 7、导航钩子有哪几种,分别如何用,如何将数据传入下一个点击的路由页面?
      • 8、es6 的特有的类型, 常用的操作数组的方法都有哪些?
      • 9、vue 双向绑定原理?
      • 10、vue-router 的实现原理,history 和 hash 模式有什么区别?
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档