【面试题】2023 vue高频面试知识点汇总(二)

简介: 【面试题】2023 vue高频面试知识点汇总(二)

【面试题】2023 vue高频面试知识点汇总(一):https://developer.aliyun.com/article/1414287

三十、vue中的原生事件


vue中可以通过@或者v-on的方式绑定事件,也可为其添加修饰符。

newVue({
  el: '#app',
  template: `<div @click='divClick'><a @clickt='aClick' href=''>点击</a></div>`,
  methods: {
    divClick() {
      console.log('divClick')
    },
    aClick() {
      console.log('aClick')
    },
  }
})
复制代码

以上例子如果点击a会触发其默认行为,如果href不为空还会进行跳转。除此之外,点击还会继续触发div上绑定的点击事件。

如果通过@click.stop.prevent='aClick'的方式为a标签的点击事件添加修饰符stop和prevent,那么就不会触发其a的默认行为,即使href不为空也不会进行跳转,同时,div上的点击事件也不会进行触发。

模板的渲染一般分为编译生成render函数、render函数执行生成vNode和patch进行渲染。下面按照这步骤进行简单分析。

1、render

通过编译生成的render函数:

with(this) {
    return _c('div', {
        on: {
            "click": divClick
        }
    }, [_c('a', {
        attrs: {
            "href": "http://www.baidu.com"
        },
        on: {
            "click": function ($event) {
                $event.stopPropagation();
                $event.preventDefault();
                return aClick($event)
            }
        }
    }, [_v("点击")])])
}
复制代码

其中div的on作为div事件描述。a标签的attrs作为属性描述,on作为事件描述,在描述中.stop被编译成了$event.stopPropagation()来阻止事件冒泡,.prevent被编译成了$event.preventDefault()用来阻止a标签的默认行为。

2、vNode

通过执行Vue.prototype._render将render函数转换成vNode。

3、patch

patch的过程中,当完成$el节点的渲染后会执行invokeCreateHooks(vnode, insertedVnodeQueue)逻辑,其中,针对attrs会将其设置为$el的真实属性,当前例子中会为a标签设置herf属性。针对on会通过target.addEventListener的方式将其处理过的事件绑定到$el上,当前例子中会分别对div和a中的click进行处理,再通过addEventListener的方式进行绑定。

小结

vue中的事件,从编译生成render再通过Vue.prototype._render函数执行render到生成vNode,主要是通过on作为描述。在patch渲染阶段,将on描述的事件进行处理再通过addEventListener的方式绑定到$el上。

三十一、常用修饰符


1、表单修饰符

(1).lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 ,可以添加 lazy 修饰符,从而转为在 change 事件之后进行同步:

<input v-model.lazy="msg">
复制代码
(2).number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">
复制代码
(3).trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">
复制代码

2、事件修饰符

(1).stop

阻止单击事件继续传播。

<!--这里只会触发a--><div @click="divClick"><av-on:click.stop="aClick">点击</a></div>复制代码
(2).prevent

阻止标签的默认行为。

<a href="http://www.baidu.com" v-on:click.prevent="aClick">点击</a>
复制代码
(3).capture

事件先在有.capture修饰符的节点上触发,然后在其包裹的内部节点中触发。

<!--这里先执行divClick事件,然后再执行aClick事件--><div @click="divClick"><av-on:click="aClick">点击</a></div>复制代码
(4).self

只当在 event.target 是当前元素自身时触发处理函数,即事件不是从内部元素触发的。

<!--在a标签上点击时只会触发aClick事件,只有点击phrase的时候才会触发divClick事件--><div @click.self="divClick">phrase<av-on:click="aClick">点击</a></div>复制代码
(5).once

不像其它只能对原生的 DOM 事件起作用的修饰符,.once 修饰符还能被用到自定义的组件事件上,表示当前事件只触发一次。

<a v-on:click.once="aClick">点击</a>
复制代码
(6).passive

.passive 修饰符尤其能够提升移动端的性能

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 --><!-- 而不会等待 `onScroll` 完成 --><!-- 这其中包含 `event.preventDefault()` 的情况 --><divv-on:scroll.passive="onScroll">...</div>复制代码

3、其他修饰符

除了表单和事件的修饰符,Vue还提供了很多其他修饰符,在使用的时候可以查阅文档。

小结

Vue中提供了很多好用的功能和api,那么修饰符的出现就为功能和api提供了更为丰富的扩展属性和更大的灵活度。

三十二、vue-router


vue路由是单页面中视图切换的方案,有三种mode:

  • hash,#后的仅仅作为参数,不属于url部分
  • history,路径作为请求url请求资源链接,如果找不到会出现404错误
  • abstract,服务端渲染场景 hash场景下,会出现url链接,再修改其view-router中对应的值。

三十三、vuex


vuex是状态管理仓库,一般使用的场景为:多个视图依赖于同一状态,来自不同视图的行为需要变更同一状态。其管理的状态是响应式的,修改也只能显式提交mutation的方式修改。vuex有state、getter、mutation、action和module五个核心,并且通过module实现了vuex树的管理。

三十四、eventBus


使用场景:兄弟组件传参

const eventBus = newVue();
const A = {
  template: `<div @click="send">component-a</div>`,
  methods: {
    send() {
      eventBus.$emit('sendData', 'data from A')
    }
  },
}
const B = {
  template: `<div>component-b</div>`,
  created() {
    eventBus.$on('sendData', (args) => {
      console.log(args)
    })
  },
}
newVue({
  el: '#app',
  components: {
    A,
    B,
  },
  template: `<div><A></A><B></B></div>`,
})
复制代码

在当前例子中,A组件和B组件称为兄弟组件,A组件通过事件总线eventBus中的$emit分发事件,B组件则通过$on来监听事件。

实现原理:eventsMixin

exportfunctioneventsMixin (Vue: Class<Component>) {
  const hookRE = /^hook:/Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
    constvm: Component = thisif (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$on(event[i], fn)
      }
    } else {
      (vm._events[event] || (vm._events[event] = [])).push(fn)
      // optimize hook:event cost by using a boolean flag marked at registration// instead of a hash lookupif (hookRE.test(event)) {
        vm._hasHookEvent = true
      }
    }
    return vm
  }
  Vue.prototype.$once = function (event: string, fn: Function): Component {
    constvm: Component = thisfunctionon () {
      vm.$off(event, on)
      fn.apply(vm, arguments)
    }
    on.fn = fn
    vm.$on(event, on)
    return vm
  }
  Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
    constvm: Component = this// allif (!arguments.length) {
      vm._events = Object.create(null)
      return vm
    }
    // array of eventsif (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$off(event[i], fn)
      }
      return vm
    }
    // specific eventconst cbs = vm._events[event]
    if (!cbs) {
      return vm
    }
    if (!fn) {
      vm._events[event] = nullreturn vm
    }
    // specific handlerlet cb
    let i = cbs.lengthwhile (i--) {
      cb = cbs[i]
      if (cb === fn || cb.fn === fn) {
        cbs.splice(i, 1)
        break
      }
    }
    return vm
  }
  Vue.prototype.$emit = function (event: string): Component {
    constvm: Component = thisif (process.env.NODE_ENV !== 'production') {
      const lowerCaseEvent = event.toLowerCase()
      if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
        tip(
          `Event "${lowerCaseEvent}" is emitted in component ` +
          `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
          `Note that HTML attributes are case-insensitive and you cannot use ` +
          `v-on to listen to camelCase events when using in-DOM templates. ` +
          `You should probably use "${hyphenate(event)}" instead of "${event}".`
        )
      }
    }
    let cbs = vm._events[event]
    if (cbs) {
      cbs = cbs.length > 1 ? toArray(cbs) : cbs
      const args = toArray(arguments, 1)
      const info = `event handler for "${event}"`for (let i = 0, l = cbs.length; i < l; i++) {
        invokeWithErrorHandling(cbs[i], vm, args, vm, info)
      }
    }
    return vm
  }
}
复制代码

在Vue构造函数定义完执行的eventsMixin函数中,在Vue.prototype上分别定义了$on、$emit、$off和$once的方法易实现对事件的绑定、分发、取消和只执行一次的方法。eventBus就是利用了当new Vue实例化后实例上的$on、$emit、$off和$once进行数据传递。

三十五、ref


使用场景: 父组件获取子组件数据或者执行子组件方法

const A = {
  template: `<div>{{childData.age}}</div>`,
  data() {
    return {
      childData: {
        name: 'qb',
        age: 30
      },
    }
  },
  methods: {
    increaseAge() {
      this.childData.age++;
    }
  }
}
newVue({
  el: '#app',
  components: {
    A,
  },
  template: `<A ref='childRef' @click.native='changeChildData'></A>`,
  methods: {
    changeChildData() {
      // 执行子组件的方法this.$refs.childRef.increaseAge()
      // 获取子组件的数据console.log(this.$refs.childRef.childData);
    },
  }
})
复制代码

在当前例子中,通过ref='childRef'的方式在当前组件中定义一个ref,可以通过this.$refs.childRef的方式获取到子组件A。可以通过this.$refs.childRef.increaseAge()的方式执行子组件中age增加的方法,也可以通过this.$refs.childRef.childData的方式获取到子组件中的数据。

三十六、props


使用场景: 父子传参

const A = {
  template: `<div @click='emitData'>{{childData}}</div>`,
  props: ['childData'],
  methods: {
    emitData() {
      this.$emit('emitChildData', 'data from child')
    }
  },
}
newVue({
  el: '#app',
  components: {
    A
  },
  template: `<A :childData='parentData' @emitChildData='getChildData'></A>`,
  data() {
    return {
      parentData: 'data from parent'
    }
  },
  methods: {
    getChildData(v) {
      console.log(v);
    }
  }
})
复制代码

从当前例子中可以看出,数据父传子是通过:childData='parentData'的方式,数据子传父是通过this.$emit('emitChildData', 'data from child')的方式,然后,父组件通过@emitChildData='getChildData'的方式进行获取。

大厂面试题分享 面试题库

前端后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库

1、父组件render函数

new Vue中传入的模板template经过遍历生成的render函数如下:

with(this) {
    return _c('A', {
        attrs: {
            "childData": parentData
        },
        on: {
            "emitChildData": getChildData
        }
    })
}
复制代码

其中data部分有attrs和on来描述属性和方法。

在通过createComponent创建组件vnode的过程中,会通过const propsData = extractPropsFromVNodeData(data, Ctor, tag)的方式获取props,通过const listeners = data.on的方式获取listeners,最后将其作为参数通过new VNode(options)的方式实例化组件vnode。

2、子组件渲染

在通过const child = vnode.componentInstance = createComponentInstanceForVnode( vnode, activeInstance )创建组件实例的过程中,会执行到组件继承自Vue的._init方法,通过initEvents将事件处理后存储到vm._events中,通过initProps将childData赋值到子组件A的vm实例上,并进行响应式处理,让其可以通过vm.childData的方式访问,并且数据发生变化时视图也可以发生改变。

组件模板编译后对应的render函数是:

with(this) {
    return _c('div', {
        on: {
            "click": emitData
        }
    }, [_v(_s(childData))])
}
复制代码

在createElm完成节点的创建后,在invokeCreateHooks(vnode, insertedVnodeQueue)阶段,给DOM原生节点节点绑定emitData。

3、this.$emit

在点击执行this.$emit时,会通过var cbs = vm._events[event]取出_events中的事件进行执行。

至此,父组件中的传递的数据就在子组件中可以通过this.xxx的方式获得,也可以通过this.$emit的方式将子组件中的数据传递给父组件。

三十七、$attrs和$listeners


使用场景: 父子组件非props属性和非native方法传递

// main.js文件importVuefrom"vue";
const B = {
  template: `<div @click="emitData">{{ formParentData }}</div>`,
  data() {
    return {
      formParentData: ''
    }
  },
  inheritAttrs: false,
  created() {
    this.formParentData = this.$attrs;
    console.log(this.$attrs, '--------------a-component-$attrs')
    console.log(this.$listeners, '--------------b-component-$listeners')
  },
  methods: {
    emitData() {
      this.$emit('onFun', 'form B component')
    }
  },
}
const A = {
  template: `<B v-bind='$attrs' v-on='$listeners'></B>`,
  components: {
    B,
  },
  props: ['propData'],
  inheritAttrs: false,
  created() {
    console.log(this.$attrs, '--------------b-component-$attrs')
    console.log(this.$listeners, '--------------b-component-$listeners')
  }
}
newVue({
  el: '#app',
  components: {
    A,
  },
  template: `<A :attrData='parentData' :propData='parentData' @click.native="nativeFun" @onFun="onFun"></A>`,
  data() {
    return {
      parentData: 'msg'
    }
  },
  methods: {
    nativeFun() {
      console.log('方法A');
    },
    onFun(v) {
      console.log('方法B', v);
    },
  }
})
复制代码

当前例子中,new Vue的template模板中有attrData、propData、click.native和onFun在进行传递。实际运行后,在A组件中this.$attrs为{attrData: 'msg'},this.$listeners为{onFun:f(...)}。在A组件中通过v-bind='$attrs'和v-on='$listeners'的方式继续进行属性和方法的传递,在B组件中就可以获取到A组件中传入的$attrs和$listeners。

当前例子中完成了非props属性和非native方法的传递,并且通过v-bind='$attrs'和v-on='$listeners'的方式实现了属性和方法的跨层级传递。

同时通过this.$emit的方法触发了根节点中onFun事件。

关于例子中的inheritAttrs: false,默认情况下父作用域的不被认作props的attribute绑定将会“回退”且作为普通的HTML属性应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置inheritAttrs到false,这些默认行为将会被去掉。

三十八、$parent和$children


使用场景: 利用父子关系进行数据的获取或者方法的调用

const A = {
  template: `<div @click="changeParentData">{{childRandom}}</div>`,
  data() {
    return {
      childRandom: Math.random()
    }
  },
  mounted() {
    console.log(this.$parent.parentCount, '--child-created--'); // 获取父组件中的parentCount
  },
  methods: {
    changeParentData() {
      console.log(this.$parent); // 打印当前实例的$parentthis.$parent.changeParentData(); // 调用当前父级中的方法`changeParentData`
    },
    changeChildData() {
      this.childRandom = Math.random();
    }
  }
}
const B = {
  template: `<div>b-component</div>`,
}
newVue({
  el: '#app',
  components: {
    A,
    B,
  },
  template: `<div><A></A><B></B><p>{{parentCount}}</p><button  @click="changeChildrenData">修改子组件数据</button></div>`,
  data() {
    return {
      parentCount: 1
    }
  },
  mounted() {
    console.log(this.$children[0].childRandom, '--parent-created--'); // 获取第一个子组件中的childRandom
  },
  methods: {
    changeParentData() {
      this.parentCount++;
    },
    changeChildrenData() {
      console.log(this.$children); // 此时有两个子组件this.$children[0].changeChildData(); // 调起第一个子组件中的'changeChildData'方法
    }
  }
})
复制代码

在当前例子中,父组件可以通过this.$children获取所有的子组件,这里有A组件和B组件,可以通过this.$children[0].childRandom的方式获取子组件A中的数据,也可以通过this.$children[0].changeChildData()的方式调起子组件A中的方法。

子组件可以通过this.$parent的方式获取父组件,可以通过this.$parent.parentCount获取父组件中的数据,也可以通过this.$parent.changeParentData()的方式修改父组件中的数据。

Vue中$parent和$children父子关系的底层构建请参考杂谈:parent/parent/children的底层逻辑

三十九、inject和provide


使用场景:嵌套组件多层级传参

const B = {
  template: `<div>{{parentData1}}{{parentData2}}</div>`,
  inject: ['parentData1', 'parentData2'],
}
const A = {
  template: `<B></B>`,
  components: {
    B,
  },
}
new Vue({
  el: '#app',
  components: {
    A,
  },
  template: `<A></A>`,
  provide: {
    parentData1: {
      name: 'name-2',
      age: 30
    },
    parentData2: {
      name: 'name-2',
      age: 29
    },
  }
})
复制代码

例子中在new Vue的时候通过provide提供了两个数据来源parentData1和parentData2,然后跨了一个A组件在B组件中通过inject注入了这两个数据。

1、initProvide

在执行组件内部的this._init初始化方法时,会执行到initProvide逻辑:

export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}
复制代码

如果在当前vm.$options中存在provide,会将其执行结果赋值给vm._provided。

2、initInjections

functioninitInjections (vm: Component) {
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    toggleObserving(false)
    Object.keys(result).forEach(key => {
      /* istanbul ignore else */if (process.env.NODE_ENV !== 'production') {
        defineReactive(vm, key, result[key], () => {
          warn(
            `Avoid mutating an injected value directly since the changes will be ` +
            `overwritten whenever the provided component re-renders. ` +
            `injection being mutated: "${key}"`,
            vm
          )
        })
      } else {
        defineReactive(vm, key, result[key])
      }
    })
    toggleObserving(true)
  }
}
functionresolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cachedconst result = Object.create(null)
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // #6574 in case the inject object is observed...if (key === '__ob__') continueconst provideKey = inject[key].fromlet source = vm
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      if (!source) {
        if ('default'in inject[key]) {
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } elseif (process.env.NODE_ENV !== 'production') {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result
  }
}
复制代码

如果当前组件中有选项inject,会以while循环的方式不断在source = source.$parent中寻找_provided,然后获取到祖先组件中提供的数据源,这是实现祖先组件向所有子孙后代注入依赖的核心。

四十、Vue项目能做的性能优化


1、v-if和v-show

  • 频繁切换时使用v-show,利用其缓存特性
  • 首屏渲染时使用v-if,如果为false则不进行渲染

2、v-for的key

  • 列表变化时,循环时使用唯一不变的key,借助其本地复用策略
  • 列表只进行一次渲染时,key可以采用循环的index

3、侦听器和计算属性

  • 侦听器watch用于数据变化时引起其他行为
  • 多使用compouter计算属性顾名思义就是新计算而来的属性,如果依赖的数据未发生变化,不会触发重新计算

4、合理使用生命周期

  • 在destroyed阶段进行绑定事件或者定时器的销毁
  • 使用动态组件的时候通过keep-alive包裹进行缓存处理,相关的操作可以在actived阶段激活

5、数据响应式处理

  • 不需要响应式处理的数据可以通过Object.freeze处理,或者直接通过this.xxx = xxx的方式进行定义
  • 需要响应式处理的属性可以通过this.$set的方式处理,而不是JSON.parse(JSON.stringify(XXX))的方式

6、路由加载方式

  • 页面组件可以采用异步加载的方式

7、插件引入

  • 第三方插件可以采用按需加载的方式,比如element-ui。

8、减少代码量

  • 采用mixin的方式抽离公共方法
  • 抽离公共组件
  • 定义公共方法至公共js中
  • 抽离公共css

9、编译方式

  • 如果线上需要template的编译,可以采用完成版vue.esm.js
  • 如果线上无需template的编译,可采用运行时版本vue.runtime.esm.js,相比完整版体积要小大约30%

10、渲染方式

  • 服务端渲染,如果是需要SEO的网站可以采用服务端渲染的方式
  • 前端渲染,一些企业内部使用的后端管理系统可以采用前端渲染的方式

11、字体图标的使用

  • 有些图片图标尽可能使用字体图标

四十一、Vue项目白屏问题


  • 1、开启gzip压缩减小文件体积。
  • 2、webpack设置productionSourceMap:false,不在线上环境打包.map文件。
  • 3、路由懒加载
  • 4、异步组件的使用
  • 5、静态资源使用cdn链接引入
  • 6、采用ssr服务端渲染方案
  • 7、骨架屏或者loading效果填充空白间隙
  • 8、首次不渲染的隐藏采用v-if
  • 9、注重代码规范:抽取公共组件,公共js,公共css样式,减小代码体积。删除无用代码,减少非必要注释。防止写出死循环等等
  • 10、删除辅助开发的console.log
  • 11、非Vue角度思考:非重要文件采用异步加载方式、css样式采用媒体查询、采用域名分片技术、http1升级成http2、如果是SSR项目考虑服务端渲染有没有可优化的点、请求头是否带了多余信息等思路

内容有些多,大体可以归类为从服务端拿到资源的速度、资源的体积和渲染是否阻塞的角度去作答。

四十二、从0到1构建一个Vue项目需要注意什么


  • 架子:选用合适的初始化脚手架(vue-cli2.0或者vue-cli3.0)
  • 请求:数据axios请求的配置
  • 登录:登录注册系统
  • 路由:路由管理页面
  • 数据:vuex全局数据管理
  • 权限:权限管理系统
  • 埋点:埋点系统
  • 插件:第三方插件的选取以及引入方式
  • 错误:错误页面
  • 入口:前端资源直接当静态资源,或者服务端模板拉取
  • SEO:如果考虑SEO建议采用SSR方案
  • 组件:基础组件/业务组件
  • 样式:样式预处理起,公共样式抽取
  • 方法:公共方法抽离

四十三、SSR


1、什么是服务端渲染(SSR)?

Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。

2、为什么使用服务端渲染(SSR)?

与传统 SPA (单页应用程序 (Single-Page Application)) 相比,服务器端渲染 (SSR) 的优势主要在于:

  • 更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
  • 更快的内容到达时间 (time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备。

3、使用服务器端渲染 (SSR) 时需要考虑的问题?

使用服务器端渲染 (SSR) 时还需要有一些权衡之处

  • 开发条件所限。浏览器特定的代码,只能在某些生命周期钩子函数 (lifecycle hook) 中使用;一些外部扩展库 (external library) 可能需要特殊处理,才能在服务器渲染应用程序中运行。
  • 涉及构建设置和部署的更多要求。与可以部署在任何静态文件服务器上的完全静态单页面应用程序 (SPA) 不同,服务器渲染应用程序,需要处于 Node.js server 运行环境。
  • 更多的服务器端负载。在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 (high traffic) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。

四十四、scoped


在Vue项目开发的项目中如果样式中未使用scoped,组件间的样式会出现覆盖的问题。

反例:

// app.vue文件
<template><div><h3class="title">app-txt</h3><child></child></div></template><script>import child from"@/components/child";
exportdefault {
  components: { child },
};
</script><style>.title {
  color: red;
}
</style>复制代码
// child.vue文件
<template><h3class="title">child-txt</h3></template><style>.title {
    color: green;
  }
</style>复制代码

父组件和子组件的样式颜色都为green,子组件中的样式覆盖了父组件的样式。

正例:

<template><h3class="title">child-txt</h3></template><stylescoped>.title {
    color: green;
  }
</style>复制代码

此时,父组件中颜色为red,子组件中颜色为green。

主要原因:

例子中的DOM节点和CSS层叠样式中都被添加了data-v-xxx来表示唯一,所以scoped是给当前组件的节点和样式唯一标识为data-v-xxx,避免了样式覆盖。


大厂面试题分享 面试题库

后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库


相关文章
|
4天前
|
存储 缓存 前端开发
100道 IT名企前端面试真题,Web前端阿里等大厂面试题汇总
100道 IT名企前端面试真题,Web前端阿里等大厂面试题汇总
|
4天前
|
移动开发 前端开发 JavaScript
【前端面试】前端面试题300道~~熬夜吐血整理,2024年最新大厂面试经验分享稿
【前端面试】前端面试题300道~~熬夜吐血整理,2024年最新大厂面试经验分享稿
|
4天前
|
移动开发 前端开发 JavaScript
Web前端开发之面试题全解析 一(3),前端面试题背不下来怎么办
Web前端开发之面试题全解析 一(3),前端面试题背不下来怎么办
|
4天前
|
前端开发 算法 搜索推荐
Web前端开发之面试题全解析 一(1),2024年最新前端组件化面试
Web前端开发之面试题全解析 一(1),2024年最新前端组件化面试
|
4天前
|
存储 缓存 JavaScript
web前端常见的面试题汇总(一),web前端面试题目
web前端常见的面试题汇总(一),web前端面试题目
|
4天前
|
存储 缓存 前端开发
web前端常见的面试题总结,2024年最新面试时有哪些技巧
web前端常见的面试题总结,2024年最新面试时有哪些技巧
web前端常见的面试题总结,2024年最新面试时有哪些技巧
|
4天前
|
JSON JavaScript 前端开发
web前端入门到实战:32道常见的js面试题,2024年最新秋招是直接面试吗
web前端入门到实战:32道常见的js面试题,2024年最新秋招是直接面试吗
|
4天前
|
JavaScript 前端开发 程序员
web前端入门到实战:32道常见的js面试题(1),面试哪些
web前端入门到实战:32道常见的js面试题(1),面试哪些
|
4天前
|
前端开发 JavaScript
web前端JS高阶面试题,2024我的前端大厂面试之旅
web前端JS高阶面试题,2024我的前端大厂面试之旅
|
4天前
|
JavaScript 前端开发
web前端JS高阶面试题(1),高级开发工程师面试
web前端JS高阶面试题(1),高级开发工程师面试