「自我检验」熬夜总结50个Vue知识点,全都会你就是神!!!

上面的写法是v-forv-if同时存在,会先把7个元素都遍历出来,然后再一个个判断是否为3,并把3给隐藏掉,这样的坏处就是,渲染了无用的3节点,增加无用的dom操作,建议使用computed来解决这个问题:

{{item}}

computed() {

list() {

return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)

}

}

复制代码

17. vuex的有哪些属性?用处是什么?

image.png

  • State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。

  • Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。

  • Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。

  • Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。

  • Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

至尊星耀


18. 不需要响应式的数据应该怎么处理?

在我们的Vue开发中,会有一些数据,从始至终都未曾改变过,这种死数据,既然不改变,那也就不需要对他做响应式处理了,不然只会做一些无用功消耗性能,比如一些写死的下拉框,写死的表格数据,这些数据量大的死数据,如果都进行响应式处理,那会消耗大量性能。

// 方法一:将数据定义在data之外

data () {

this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }

this.list2 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }

this.list3 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }

this.list4 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }

this.list5 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }

return {}

}

// 方法二:Object.freeze()

data () {

return {

list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),

list2: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),

list3: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),

list4: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),

list5: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),

}

}

19. watch有哪些属性,分别有什么用?

当我们监听一个基本数据类型时:

watch: {

value () {

// do something

}

}

当我们监听一个引用数据类型时:

watch: {

obj: {

handler () { // 执行回调

// do something

},

deep: true, // 是否进行深度监听

immediate: true // 是否初始执行handler函数

}

}

20. 父子组件生命周期顺序

父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

21. 对象新属性无法更新视图,删除属性无法更新视图,为什么?怎么办?

  • 原因:Object.defineProperty没有对对象的新属性进行属性劫持

  • 对象新属性无法更新视图:使用Vue.$set(obj, key, value),组件中this.$set(obj, key, value)

  • 删除属性无法更新视图:使用Vue.$delete(obj, key),组件中this.$delete(obj, key)

22. 直接arr[index] = xxx无法更新视图怎么办?为什么?怎么办?

  • 原因:Vue没有对数组进行Object.defineProperty的属性劫持,所以直接arr[index] = xxx是无法更新视图的

  • 使用数组的splice方法,arr.splice(index, 1, item)

  • 使用Vue.$set(arr, index, value)

23. 自定义指令

建议看这篇文章8个非常实用的Vue自定义指令[3]

24. 插槽的使用以及原理?

建议看我这篇文章「Vue源码学习」你真的知道插槽Slot是怎么“插”的吗[4]

25. 为什么不建议用index做key,为什么不建议用随机数做key?

举个例子:

{{item.name}}

list: [

{ name: ‘小明’, id: ‘123’ },

{ name: ‘小红’, id: ‘124’ },

{ name: ‘小花’, id: ‘125’ }

]

渲染为

小明
小红
小花

现在我执行 list.unshift({ name: ‘小林’, id: ‘122’ })

渲染为

小林
小明
小红
小花

新旧对比

小明
  
小林
小红
  
小明
小花
  
小红
小花

可以看出,如果用index做key的话,其实是更新了原有的三项,并新增了小花,虽然达到了渲染目的,但是损耗性能

现在我们使用id来做key,渲染为

小明
小红
小花

现在我执行 list.unshift({ name: ‘小林’, id: ‘122’ }),渲染为

小林
小明
小红
小花

新旧对比

小林
小明
  
小明
小红
  
小红
小花
  
小花

可以看出,原有的三项都不变,只是新增了小林这个人,这才是最理想的结果

index和用随机数都是同理,随机数每次都在变,做不到专一性,很渣男,也很消耗性能,所以,拒绝渣男,选择老实人

26. 说说nextTick的用处?

我举个例子,在vue中:

this.name = ‘林三心’

this.age = 18

this.gender = ‘男’

我们修改了三个变量,那问题来了,是每修改一次,DOM就更新一次吗?不是的,Vue采用的是异步更新的策略,通俗点说就是,同一事件循环内多次修改,会统一进行一次视图更新,这样才能节省性能嘛

看懂了上面,那你应该也看得懂下面的例子了吧:

{{name}}

name: ‘小林’

this.name = ‘林三心’

console.log(this.$refs.testDiv.innerHTML) // 这里是啥呢

答案是“小林”,前面说了,Vue是异步更新,所以数据一更新,视图却还没更新,所以拿到的还是上一次的旧视图数据,那么想要拿到最新视图数据怎么办呢?

this.name = ‘林三心’

this.$nextTick(() => {

console.log(this.$refs.testDiv.innerHTML) // 林三心

})

27. Vue的SSR是什么?有什么好处?

  • SSR就是服务端渲染

  • 基于nodejs serve服务环境开发,所有html代码在服务端渲染

  • 数据返回给前端,然后前端进行“激活”,即可成为浏览器识别的html代码

  • SSR首次加载更快,有更好的用户体验,有更好的seo优化,因为爬虫能看到整个页面的内容,如果是vue项目,由于数据还要经过解析,这就造成爬虫并不会等待你的数据加载完成,所以其实Vue项目的seo体验并不是很好

最强王者


28. Vue响应式是怎么实现的?

整体思路是数据劫持+观察者模式

对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的dep属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)。

想详细了解过程,建议阅读我的Vue源码解析系列[5]

const { arrayMethods } = require(‘./array’)

class Observer {

constructor(value) {

Object.defineProperty(value, ‘ob’, {

value: this,

enumerable: false,

writable: true,

configurable: true

})

if(Array.isArray(value)) {

value.proto = arrayMethods

this.observeArray(value)

} else {

this.walk(value)

}

}

walk(data) {

let keys = Object.keys(data)

for(let i = 0; i < keys.length; i++) {

const key = keys[i]

const value = data[key]

defineReactive(data, key, value)

}

}

observeArray(items) {

for(let i = 0; i < items.length; i++) {

observe(items[i])

}

}

}

function defineReactive(data, key, value) {

const childOb = observe(value)

const dep = new Dep()

Object.defineProperty(data, key, {

get() {

console.log(‘获取值’)

if (Dep.target) {

dep.depend()

if (childOb) {

childOb.dep.depend()

if (Array.isArray(value)) {

dependArray(value)

}

}

}

return value

},

set(newVal) {

if (newVal === value) return

observe(newVal)

value = newVal

dep.notify()

}

})

}

function observe(value) {

if (Object.prototype.toString.call(value) === ‘[object Object]’ || Array.isArray(value)) {

return new Observer(value)

}

}

function dependArray(value) {

for(let e, i = 0, l = value.length; i < l; i++) {

e = value[i]

e && e.ob && e.ob.dep.depend()

if (Array.isArray(e)) {

dependArray(e)

}

}

}

// array.js

const arrayProto = Array.prototype

const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [

‘push’,

‘pop’,

‘shift’,

‘unshift’,

‘splice’,

‘reverse’,

‘sort’

]

methodsToPatch.forEach(method => {

arrayMethods[method] = function (…args) {

const result = arrayProto[method].apply(this, args)

const ob = this.ob

var inserted

switch (method) {

case ‘push’:

case ‘unshift’:

inserted = args

break;

case ‘splice’:

inserted = args.slice(2)

default:

break;

}

if (inserted) ob.observeArray(inserted)

ob.dep.notify()

return result

}

})

29. 为什么只对对象劫持,而要对数组进行方法重写?

因为对象最多也就几十个属性,拦截起来数量不多,但是数组可能会有几百几千项,拦截起来非常耗性能,所以直接重写数组原型上的方法,是比较节省性能的方案

30. Vue的模板编译原理?

因为这个问题讲起来可能比较长,所以:

建议看我这篇「Vue源码学习\(二\)」你不知道的-模板编译原理[6]

31. Vue的computed和watch的原理?

因为这个问题讲起来可能比较长,所以:

建议看我这篇「Vue源码学习\(四\)」立志写一篇人人都看的懂的computed,watch原理[7]

32. Vue.set方法的原理?

function set(target, key, val) {

// 判断是否是数组

if (Array.isArray(target)) {

// 判断谁大谁小

target.length = Math.max(target.length, key)

// 执行splice

target.splice(key, 1, val)

return val

}

const ob = target.ob

// 如果此对象没有不是响应式对象,直接设置并返回

if (key in target && !(key in target.prototype) || !ob) {

target[key] = val

return val

}

// 否则,新增属性,并响应式处理

defineReactive(target, key, val)

return val

}

33. Vue.delete方法的原理?

function del (target, key) {

// 判断是否为数组

if (Array.isArray(target)) {

// 执行splice

target.splice(key, 1)

return

}

const ob = target.ob

// 对象本身就没有这个属性,直接返回

if (!(key in target)) return

// 否则,删除这个属性

delete target[key]

// 判断是否是响应式对象,不是的话,直接返回

if (!ob) return

// 是的话,删除后要通知视图更新

ob.dep.notify()

}

34. nextTick的原理?

let callbacks = []; //回调函数

let pending = false;

function flushCallbacks() {

pending = false; //把标志还原为false

// 依次执行回调

for (let i = 0; i < callbacks.length; i++) {

callbacksi;

}

}

let timerFunc; //先采用微任务并按照优先级优雅降级的方式实现异步刷新

if (typeof Promise !== “undefined”) {

// 如果支持promise

const p = Promise.resolve();

timerFunc = () => {

p.then(flushCallbacks);

};

} else if (typeof MutationObserver !== “undefined”) {

// MutationObserver 主要是监听dom变化 也是一个异步方法

let counter = 1;

const observer = new MutationObserver(flushCallbacks);

const textNode = document.createTextNode(String(counter));

observer.observe(textNode, {

characterData: true,

});

timerFunc = () => {

counter = (counter + 1) % 2;

textNode.data = String(counter);

};

} else if (typeof setImmediate !== “undefined”) {

// 如果前面都不支持 判断setImmediate

timerFunc = () => {

setImmediate(flushCallbacks);

};

} else {

// 最后降级采用setTimeout

timerFunc = () => {

setTimeout(flushCallbacks, 0);

};

}

export function nextTick(cb) {

callbacks.push(cb);

if (!pending) {

pending = true;

timerFunc();

}

}

35. key有什么用?说说diff算法吧?

直接看这篇吧:为什么 Vue 中不要用 index 作为 key?(diff 算法详解)[8]

我讲的没他好

冷门的知识点


36. 如果子组件改变props里的数据会发生什么

  • 改变的props数据是基本类型

如果修改的是基本类型,则会报错

props: {

num: Number,

}

created() {

this.num = 999

}

0458e2ff1538ee85d42953cec9a94ca.png

  • 改变的props数据是引用类型

props: {

item: {

default: () => {},

}

}

created() {

// 不报错,并且父级数据会跟着变

this.item.name = ‘sanxin’;

// 会报错,跟基础类型报错一样

this.item = ‘sss’

},

37. props怎么自定义验证

props: {

num: {

default: 1,

validator: function (value) {

// 返回值为true则验证不通过,报错

return [

1, 2, 3, 4, 5

].indexOf(value) !== -1

}

}

}

38. watch的immediate属性有什么用?

比如平时created时要请求一次数据,并且当搜索值改变,也要请求数据,我们会这么写:

created(){

this.getList()

},

watch: {

searchInputValue(){

this.getList()

}

}

复制代码

使用immediate完全可以这么写,当它为true时,会初始执行一次

watch: {

searchInputValue:{

handler: ‘getList’,

immediate: true

}

}

39. watch监听一个对象时,如何排除某些属性的监听

下面代码是,params发生改变就重新请求数据,无论是a,b,c,d属性改变

data() {

return {

params: {

a: 1,

b: 2,

c: 3,

d: 4

},

};

},

watch: {

params: {

deep: true,

handler() {

this.getList;

},

},

}

但是如果我只想要a,b改变时重新请求,c,d改变时不重新请求呢?

mounted() {

Object.keys(this.params)

.filter(() => ![“c”, “d”].includes()) // 排除对c,d属性的监听

.forEach((_) => {

this.$watch((vm) => vm.params[_], handler, {

deep: true,

});

});

},

data() {

return {

params: {

a: 1,

b: 2,

c: 3,

d: 4

},

};

},

watch: {

params: {

deep: true,

handler() {

this.getList;

},

},

}

40. 审查元素时发现data-v-xxxxx,这是啥?

image.png

这是在标记vue文件中css时使用scoped标记产生的,因为要保证各文件中的css不相互影响,给每个component都做了唯一的标记,所以每引入一个component就会出现一个新的’data-v-xxx’标记

41. computed如何实现传参?

// html

{{ total(3) }}

// js

computed: {

total() {

return function(n) {

return n * this.num

}

},

}

42. vue的hook的使用

  • 同一组件中使用

这是我们常用的使用定时器的方式

export default{

data(){

timer:null

},

mounted(){

this.timer = setInterval(()=>{

//具体执行内容

console.log(‘1’);

},1000);

}

beforeDestory(){

clearInterval(this.timer);

this.timer = null;

}

}

上面做法不好的地方在于:得全局多定义一个timer变量,可以使用hook这么做:

export default{

methods:{

fn(){

const timer = setInterval(()=>{

//具体执行代码

console.log(‘1’);

},1000);

this.$once(‘hook:beforeDestroy’,()=>{

clearInterval(timer);

timer = null;

})

}

}

}

  • 7.2 父子组件使用

如果子组件需要在mounted时触发父组件的某一个函数,平时都会这么写:

//父组件

<rl-child @childMounted=“childMountedHandle”

/>

method () {

childMountedHandle() {

// do something…

}

},

// 子组件

mounted () {

this.$emit(‘childMounted’)

},

使用hook的话可以更方便:

//父组件

<rl-child @hook:mounted=“childMountedHandle”

/>

method () {

childMountedHandle() {

// do something…

}

},

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
/具体执行内容

console.log(‘1’);

},1000);

}

beforeDestory(){

clearInterval(this.timer);

this.timer = null;

}

}

上面做法不好的地方在于:得全局多定义一个timer变量,可以使用hook这么做:

export default{

methods:{

fn(){

const timer = setInterval(()=>{

//具体执行代码

console.log(‘1’);

},1000);

this.$once(‘hook:beforeDestroy’,()=>{

clearInterval(timer);

timer = null;

})

}

}

}

  • 7.2 父子组件使用

如果子组件需要在mounted时触发父组件的某一个函数,平时都会这么写:

//父组件

<rl-child @childMounted=“childMountedHandle”

/>

method () {

childMountedHandle() {

// do something…

}

},

// 子组件

mounted () {

this.$emit(‘childMounted’)

},

使用hook的话可以更方便:

//父组件

<rl-child @hook:mounted=“childMountedHandle”

/>

method () {

childMountedHandle() {

// do something…

}

},

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-dMVzXtul-1715743996622)]

[外链图片转存中…(img-2UX9VPVb-1715743996623)]

[外链图片转存中…(img-3C331STo-1715743996623)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值