一篇由简到难的 Vue 面试题+详解答案(1),2024年最新最新前端面试题及答案

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

ref=“input”

:value=“value”

@input=“ e m i t ( ′ i n p u t ′ ,   emit('input',  emit(input, event.target.value)”

`,

props: [‘value’],

})

复制代码

17 v-for 为什么要加 key

如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速

「更准确」:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。

「更快速」:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快

相关代码如下

// 判断两个vnode的标签和key是否相同 如果相同 就可以认为是同一节点就地复用

function isSameVnode(oldVnode, newVnode) {

return oldVnode.tag === newVnode.tag && oldVnode.key === newVnode.key;

}

// 根据key来创建老的儿子的index映射表  类似 {‘a’:0,‘b’:1} 代表key为’a’的节点在第一个位置 key为’b’的节点在第二个位置

function makeIndexByKey(children) {

let map = {};

children.forEach((item, index) => {

map[item.key] = index;

});

return map;

}

// 生成的映射表

let map = makeIndexByKey(oldCh);

复制代码

diff 算法详解 传送门[6]

18 Vue 事件绑定原理

原生事件绑定是通过 addEventListener 绑定给真实元素的,组件事件绑定是通过 Vue 自定义的 $on 实现的。如果要在组件上使用原生事件,需要加.native 修饰符,这样就相当于在父组件中把子组件当做普通 html 标签,然后加上原生事件。

、 emit 是基于发布订阅模式的,维护一个事件中心,on 的时候将事件按名称存在事件中心里,称之为订阅者,然后 emit 将对应的事件进行发布,去执行事件中心里的对应的监听器

手写发布订阅原理 传送门[7]

19 vue-router 路由钩子函数是什么 执行顺序是什么

路由钩子的执行流程, 钩子函数种类有:全局守卫、路由守卫、组件守卫

「完整的导航解析流程:」

  1. 导航被触发。

  2. 在失活的组件里调用 beforeRouteLeave 守卫。

  3. 调用全局的 beforeEach 守卫。

  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。

  5. 在路由配置里调用 beforeEnter。

  6. 解析异步路由组件。

  7. 在被激活的组件里调用 beforeRouteEnter。

  8. 调用全局的 beforeResolve 守卫 (2.5+)。

  9. 导航被确认。

  10. 调用全局的 afterEach 钩子。

  11. 触发 DOM 更新。

  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

20 vue-router 动态路由是什么 有什么问题

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:

const User = {

template: “

User
”,

};

const router = new VueRouter({

routes: [

// 动态路径参数 以冒号开头

{ path: “/user/:id”, component: User },

],

});

复制代码

问题:vue-router 组件复用导致路由参数失效怎么办?

解决方法:

1.通过 watch 监听路由参数再发请求

watch: { //通过watch来监听路由变化

“$route”: function(){

this.getData(this.$route.params.xxx);

}

}

复制代码

2.用 :key 来阻止“复用”

复制代码

21 谈一下对 vuex 的个人理解

vuex 是专门为 vue 提供的全局状态管理系统,用于多个组件中数据共享、数据缓存等。(无法持久化、内部核心原理是通过创造一个全局实例 new Vue)

主要包括以下几个模块:

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

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

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

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

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

22 Vuex 页面刷新数据丢失怎么解决

需要做 vuex 数据持久化 一般使用本地存储的方案来保存数据 可以自己设计存储方案 也可以使用第三方插件

推荐使用 vuex-persist 插件,它就是为 Vuex 持久化存储而生的一个插件。不需要你手动存取 storage ,而是直接将状态保存至 cookie 或者 localStorage 中

23 Vuex 为什么要分模块并且加命名空间

「模块」:由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

「命名空间」:默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

24 使用过 Vue SSR 吗?说说 SSR

SSR 也就是服务端渲染,也就是将 Vue 在客户端把标签渲染成 HTML 的工作放在服务端完成,然后再把 html 直接返回给客户端。

「优点:」

SSR 有着更好的 SEO、并且首屏加载速度更快

「缺点:」 开发条件会受到限制,服务器端渲染只支持 beforeCreate 和 created 两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于 Node.js 的运行环境。

服务器会有更大的负载需求

25 vue 中使用了哪些设计模式

1.工厂模式 - 传入参数即可创建实例

虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode

2.单例模式 - 整个程序有且仅有一个实例

vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉

3.发布-订阅模式 (vue 事件机制)

4.观察者模式 (响应式数据原理)

5.装饰模式: (@装饰器的用法)

6.策略模式 策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案-比如选项的合并策略

…其他模式欢迎补充

26 你都做过哪些 Vue 的性能优化

这里只列举针对 Vue 的性能优化 整个项目的性能优化是一个大工程 可以另写一篇性能优化的文章 哈哈

  • 对象层级不要过深,否则性能就会差

  • 不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)

  • v-if 和 v-show 区分使用场景

  • computed 和 watch 区分使用场景

  • v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if

  • 大数据列表和表格性能优化-虚拟列表/虚拟表格

  • 防止内部泄漏,组件销毁后把全局变量和事件销毁

  • 图片懒加载

  • 路由懒加载

  • 第三方插件的按需引入

  • 适当采用 keep-alive 缓存组件

  • 防抖、节流运用

  • 服务端渲染 SSR or 预渲染


困难

27 Vue.mixin 的使用场景和原理

在日常的开发中,我们经常会遇到在不同的组件中经常会需要用到一些相同或者相似的代码,这些代码的功能相对独立,可以通过 Vue 的 mixin 功能抽离公共的业务逻辑,原理类似“对象的继承”,当组件初始化时会调用 mergeOptions 方法进行合并,采用策略模式针对不同的属性进行合并。当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。

相关代码如下

export default function initMixin(Vue){

Vue.mixin = function (mixin) {

//   合并对象

this.options=mergeOptions(this.options,mixin)

};

}

};

// src/util/index.js

// 定义生命周期

export const LIFECYCLE_HOOKS = [

“beforeCreate”,

“created”,

“beforeMount”,

“mounted”,

“beforeUpdate”,

“updated”,

“beforeDestroy”,

“destroyed”,

];

// 合并策略

const strats = {};

// mixin核心方法

export function mergeOptions(parent, child) {

const options = {};

// 遍历父亲

for (let k in parent) {

mergeFiled(k);

}

// 父亲没有 儿子有

for (let k in child) {

if (!parent.hasOwnProperty(k)) {

mergeFiled(k);

}

}

//真正合并字段方法

function mergeFiled(k) {

if (strats[k]) {

options[k] = stratsk;

} else {

// 默认策略

options[k] = child[k] ? child[k] : parent[k];

}

}

return options;

}

复制代码

Vue.mixin 原理详解 传送门[8]

28 nextTick 使用场景和原理

nextTick 中的回调是在下次 DOM 更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。主要思路就是采用微任务优先的方式调用异步方法去执行 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) {

// 除了渲染watcher  还有用户自己手动调用的nextTick 一起被收集到数组

callbacks.push(cb);

if (!pending) {

// 如果多次调用nextTick  只会执行一次异步 等异步队列清空之后再把标志变为false

pending = true;

timerFunc();

}

}

复制代码

nextTick 原理详解 传送门[9]

29 keep-alive 使用场景和原理

keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。

  • 常用的两个属性 include/exclude,允许组件有条件的进行缓存。

  • 两个生命周期 activated/deactivated,用来得知当前组件是否处于活跃状态。

  • keep-alive 的中还运用了 LRU(最近最少使用) 算法,选择最近最久未使用的组件予以淘汰。

相关代码如下

export default {

name: “keep-alive”,

abstract: true, //抽象组件

props: {

include: patternTypes, //要缓存的组件

exclude: patternTypes, //要排除的组件

max: [String, Number], //最大缓存数

},

created() {

this.cache = Object.create(null); //缓存对象  {a:vNode,b:vNode}

this.keys = []; //缓存组件的key集合 [a,b]

},

destroyed() {

for (const key in this.cache) {

pruneCacheEntry(this.cache, key, this.keys);

}

},

mounted() {

//动态监听include  exclude

this.$watch(“include”, (val) => {

pruneCache(this, (name) => matches(val, name));

});

this.$watch(“exclude”, (val) => {

pruneCache(this, (name) => !matches(val, name));

});

},

render() {

const slot = this.$slots.default; //获取包裹的插槽默认值

const vnode: VNode = getFirstComponentChild(slot); //获取第一个子组件

const componentOptions: ?VNodeComponentOptions =

vnode && vnode.componentOptions;

if (componentOptions) {

// check pattern

const name: ?string = getComponentName(componentOptions);

const { include, exclude } = this;

// 不走缓存

if (

// not included  不包含

(include && (!name || !matches(include, name))) ||

// excluded  排除里面

(exclude && name && matches(exclude, name))

) {

//返回虚拟节点

return vnode;

}

const { cache, keys } = this;

const key: ?string =

vnode.key == null

? // same constructor may get registered as different local components

// so cid alone is not enough (#3269)

componentOptions.Ctor.cid +

(componentOptions.tag ? ::${componentOptions.tag} : “”)

: vnode.key;

if (cache[key]) {

//通过key 找到缓存 获取实例

vnode.componentInstance = cache[key].componentInstance;

// make current key freshest

remove(keys, key); //通过LRU算法把数组里面的key删掉

keys.push(key); //把它放在数组末尾

} else {

cache[key] = vnode; //没找到就换存下来

keys.push(key); //把它放在数组末尾

// prune oldest entry  //如果超过最大值就把数组第0项删掉

if (this.max && keys.length > parseInt(this.max)) {

pruneCacheEntry(cache, keys[0], keys, this._vnode);

}

}

vnode.data.keepAlive = true; //标记虚拟节点已经被缓存

}

// 返回虚拟节点

return vnode || (slot && slot[0]);

},

};

复制代码

扩展补充:LRU 算法是什么?

lrusuanfa.png

LRU 的核心思想是如果数据最近被访问过,那么将来被访问的几率也更高,所以我们将命中缓存的组件 key 重新插入到 this.keys 的尾部,这样一来,this.keys 中越往头部的数据即将来被访问几率越低,所以当缓存数量达到最大值时,我们就删除将来被访问几率最低的数据,即 this.keys 中第一个缓存的组件。

30 Vue.set 方法原理

了解 Vue 响应式原理的同学都知道在两种情况下修改数据 Vue 是不会触发视图更新的

1.在实例创建之后添加新的属性到实例上(给响应式对象新增属性)

2.直接更改数组下标来修改数组的值

Vue.set 或者说是 $set 原理如下

因为响应式数据 我们给对象和数组本身都增加了__ob__属性,代表的是 Observer 实例。当给对象新增不存在的属性 首先会把新的属性进行响应式跟踪 然后会触发对象__ob__的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去更新数组

相关代码如下

export function set(target: Array | Object, key: any, val: any): any {

// 如果是数组 调用我们重写的splice方法 (这样可以更新视图)

if (Array.isArray(target) && isValidArrayIndex(key)) {

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

target.splice(key, 1, val);

return val;

}

// 如果是对象本身的属性,则直接添加即可

if (key in target && !(key in Object.prototype)) {

target[key] = val;

return val;

}

const ob = (target: any).ob;

// 如果不是响应式的也不需要将其定义成响应式属性

if (!ob) {

target[key] = val;

return val;

}

// 将属性定义成响应式的

defineReactive(ob.value, key, val);

// 通知视图更新

ob.dep.notify();

return val;

}

复制代码

响应式数据原理详解 传送门[10]

31 Vue.extend 作用和原理

官方解释:Vue.extend 使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。

其实就是一个子类构造器 是 Vue 组件的核心 api 实现思路就是使用原型继承的方法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并

相关代码如下

export default function initExtend(Vue) {

let cid = 0; //组件的唯一标识

// 创建子类继承Vue父类 便于属性扩展

Vue.extend = function (extendOptions) {

// 创建子类的构造函数 并且调用初始化方法

const Sub = function VueComponent(options) {

this._init(options); //调用Vue初始化方法

};

Sub.cid = cid++;

Sub.prototype = Object.create(this.prototype); // 子类原型指向父类

Sub.prototype.constructor = Sub; //constructor指向自己

Sub.options = mergeOptions(this.options, extendOptions); //合并自己的options和父类的options

return Sub;

};

}

复制代码

Vue 组件原理详解 传送门[11]

32 写过自定义指令吗 原理是什么

指令本质上是装饰器,是 vue 对 HTML 元素的扩展,给 HTML 元素增加自定义功能。vue 编译 DOM 时,会找到指令对象,执行指令的相关方法。

自定义指令有五个生命周期(也叫钩子函数),分别是 bind、inserted、update、componentUpdated、unbind

1. bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  1. inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  2. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。

  3. componentUpdated:被绑定元素所在模板完成一次更新周期时调用。

  4. unbind:只调用一次,指令与元素解绑时调用。

复制代码

「原理」

1.在生成 ast 语法树时,遇到指令会给当前元素添加 directives 属性

2.通过 genDirectives 生成指令代码

3.在 patch 前将指令的钩子提取到 cbs 中,在 patch 过程中调用对应的钩子

4.当执行指令对应钩子函数时,调用对应指令定义的方法

33 Vue 修饰符有哪些

「事件修饰符」

  • .stop 阻止事件继续传播

  • .prevent 阻止标签默认行为

  • .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理

  • .self 只当在 event.target 是当前元素自身时触发处理函数

  • .once 事件将只会触发一次

  • .passive 告诉浏览器你不想阻止事件的默认行为

「v-model 的修饰符」

  • .lazy 通过这个修饰符,转变为在 change 事件再同步

  • .number 自动将用户的输入值转化为数值类型

  • .trim 自动过滤用户输入的首尾空格

「键盘事件的修饰符」

  • .enter

  • .tab

  • .delete (捕获“删除”和“退格”键)

  • .esc

  • .space

  • .up

  • .down

  • .left

  • .right

「系统修饰键」

  • .ctrl

  • .alt

  • .shift

  • .meta

「鼠标按钮修饰符」

  • .left

  • .right

  • .middle

34 Vue 模板编译原理

Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步

第一步是将 模板字符串 转换成 element ASTs(解析器)

第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)

第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器)

复制代码

相关代码如下

export function compileToFunctions(template) {

// 我们需要把html字符串变成render函数

// 1.把html代码转成ast语法树  ast用来描述代码本身形成树结构 不仅可以描述html 也能描述css以及js语法

// 很多库都运用到了ast 比如 webpack babel eslint等等

let ast = parse(template);

// 2.优化静态节点

// 这个有兴趣的可以去看源码  不影响核心功能就不实现了

//   if (options.optimize !== false) {

//     optimize(ast, options);

//   }

// 3.通过ast 重新生成代码

// 我们最后生成的代码需要和render函数一样

// 类似_c(‘div’,{id:“app”},_c(‘div’,undefined,_v(“hello”+_s(name)),_c(‘span’,undefined,_v(“world”))))

// _c代表创建元素 _v代表创建文本 _s代表文Json.stringify–把对象解析成文本

let code = generate(ast);

//   使用with语法改变作用域为this  之后调用render函数可以使用call改变this 方便code里面的变量取值

let renderFn = new Function(with(this){return ${code}});

return renderFn;

}

复制代码

模板编译原理详解 传送门[12]

35 生命周期钩子是如何实现的

Vue 的生命周期钩子核心实现是利用发布订阅模式先把用户传入的的生命周期钩子订阅好(内部采用数组的方式存储)然后在创建组件实例的过程中会一次执行对应的钩子方法(发布)

相关代码如下

export function callHook(vm, hook) {

// 依次执行生命周期对应的方法

const handlers = vm.$options[hook];

if (handlers) {

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

handlers[i].call(vm); //生命周期里面的this指向当前实例

}

}

}

// 调用的时候

Vue.prototype._init = function (options) {

const vm = this;

vm.$options = mergeOptions(vm.constructor.options, options);

callHook(vm, “beforeCreate”); //初始化数据之前

// 初始化状态

initState(vm);

callHook(vm, “created”); //初始化数据之后

if (vm.$options.el) {

vm. m o u n t ( v m . mount(vm. mount(vm.options.el);

}

};

复制代码

生命周期实现详解 传送门[13]

36 函数式组件使用场景和原理

函数式组件与普通组件的区别

1.函数式组件需要在声明组件是指定 functional:true

2.不需要实例化,所以没有this,this通过render函数的第二个参数context来代替

3.没有生命周期钩子函数,不能使用计算属性,watch

4.不能通过 $emit 对外暴露事件,调用事件只能通过context.listeners.click的方式调用外部传入的事件

5.因为函数式组件是没有实例化的,所以在外部通过ref去引用组件时,实际引用的是HTMLElement

6.函数式组件的props可以不用显示声明,所以没有在props里面声明的属性都会被自动隐式解析为prop,而普通组件所有未声明的属性都解析到 $attrs里面,并自动挂载到组件根元素上面(可以通过inheritAttrs属性禁止)

复制代码

优点 1.由于函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件 2.函数式组件结构比较简单,代码结构更清晰

使用场景:

一个简单的展示组件,作为容器组件使用 比如 router-view 就是一个函数式组件

“高阶组件”——用于接收一个组件作为参数,返回一个被包装过的组件

相关代码如下

if (isTrue(Ctor.options.functional)) {

// 带有functional的属性的就是函数式组件

return createFunctionalComponent(Ctor, propsData, data, context, children);

}

const listeners = data.on;

data.on = data.nativeOn;

installComponentHooks(data); // 安装组件相关钩子 (函数式组件没有调用此方法,从而性能高于普通组件)

复制代码

37 能说下 vue-router 中常用的路由模式实现原理吗

「hash 模式」

  1. location.hash 的值实际就是 URL 中#后面的东西 它的特点在于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。

  2. 可以为 hash 的改变添加监听事件

window.addEventListener(“hashchange”, funcRef, false);

复制代码

每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一个记录利用 hash 的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了

特点:兼容性好但是不美观

「history 模式」

利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。

这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。

特点:虽然美观,但是刷新会出现 404 需要后端进行配置

38 diff 算法了解吗

diff算法.png

最后

为了帮助大家更好的了解前端,特别整理了《前端工程师面试手册》电子稿文件。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
属性的就是函数式组件

return createFunctionalComponent(Ctor, propsData, data, context, children);

}

const listeners = data.on;

data.on = data.nativeOn;

installComponentHooks(data); // 安装组件相关钩子 (函数式组件没有调用此方法,从而性能高于普通组件)

复制代码

37 能说下 vue-router 中常用的路由模式实现原理吗

「hash 模式」

  1. location.hash 的值实际就是 URL 中#后面的东西 它的特点在于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。

  2. 可以为 hash 的改变添加监听事件

window.addEventListener(“hashchange”, funcRef, false);

复制代码

每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一个记录利用 hash 的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了

特点:兼容性好但是不美观

「history 模式」

利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。

这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。

特点:虽然美观,但是刷新会出现 404 需要后端进行配置

38 diff 算法了解吗

diff算法.png

最后

为了帮助大家更好的了解前端,特别整理了《前端工程师面试手册》电子稿文件。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-rvRtid0r-1713055633196)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 29
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值