前端已死,机械狗一个月速成找实习经历

108 阅读28分钟

5月30日

1.  VUE生命周期和如何去使用?

在创建前有生命周期SETUP

创建前后:beforeCreate created

挂载前后:beforeMount mounted

更新前后:beforeUpdate updated

销毁前后:beforeDestory destroyed

2.  挂载生命周期,标志性处理。销毁生命周期呢?

挂载过程指的是 app.mount()过程,这是一个初始化过程,整体上做了两件事情:初始化和建立更新机制。

初始化会创建组件实例、初始化组件状态、创建各种响应式数据。

建立更新机制这一步会立即执行一次组件的更新函数,这会首次执行组件渲染函数并执行patch将vnode 转换为 dom; 同时首次执行渲染函数会创建它内部响应式数据和组件更新函数之间的依赖关系,这使得以后数据发生变化时会执行对应的更新函数。

3.  VUEX和VUE router的了解

blog.csdn.net/m0_37247632…

juejin.cn/post/719132…

概念:

Vuex 是 Vue 专用的状态管理库,它以全局方式集中管理应用的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

解决的问题: Vuex 主要解决的问题是多组件之间状态共享。利用各种通信方式,虽然也能够实现状态共享,但是往往需要在多个组件之间保持状态的一致性,这种模式很容易出问题,也会使程序逻辑变得复杂。Vuex 通过把组件的共享状态抽取出来,以全局单例模式管理,这样任何组件都能用一致的方式获取和修改状态,响应式的数据也能够保证简洁的单向流动,使代码变得更具结构化且易于维护。

什么时候用:

Vuex 并非是必须的,它能够管理状态,但同时也带来更多的概念和框架。如果我们不打算开发大型单页应用或应用里没有大量全局的状态需要维护,完全没有使用Vuex的必要,一个简单的 store 模式就够了。反之,Vuex将是自然而然的选择。

用法:

Vuex 将全局状态放入state对象中,它本身是一颗状态树,组件中使用store实例的state访问这些状态;然后用配套的mutation方法修改这些状态,并且只能用mutation修改状态,在组件中调用commit方法提交mutation;如果应用中有异步操作或复杂逻辑组合,需要编写action,执行结束如果有状态修改仍需提交mutation,组件中通过dispatch派发action。最后是模块化,通过modules选项组织拆分出去的各个子模块,在访问状态(state)时需注意添加子模块的名称,如果子模块有设置namespace,那么提交mutation和派发action时还需要额外的命名空间前缀。

4.  JS中数组常用方法,filter有什么作用?map和forEach有什么区别?

juejin.cn/post/684490…

5.  对flex布局的了解,有哪些常用的属性?

Flex 布局(Flexbox)是一种用于创建灵活的、自适应的布局的 CSS3 模块。它提供了一组属性,可以轻松地实现元素在容器中的弹性布局。

6.  如何实现子元素在父元素中的水平竖直居中,使用flex布局实现

将父元素 .container 的 display 属性设置为 flex,这将创建一个 Flex 容器。然后,通过设置 justify-content: center 将子元素水平居中,align-items: center 将子元素垂直居中。

7.  什么是原型链,沿着原型链一直往上找能找到什么?

原型链是 JavaScript 中对象之间的一种关联机制。每个对象都有一个原型(prototype),原型又是一个对象。当访问对象的属性或方法时,如果对象本身没有该属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到对应的属性或方法或者到达原型链的顶端(null)。

在 JavaScript 中,对象可以通过 proto 属性来访问其原型对象,而原型对象也可以有自己的原型,这样就形成了一个由原型对象组成的链条,即原型链。

当我们访问一个对象的属性时,首先会在对象自身上查找,如果找到则返回该属性的值。如果没有找到,则会继续沿着原型链向上查找,直到找到属性或者到达原型链的顶端。

原型链的顶端是 null,即没有原型,它是原型链的终点。在原型链的顶端,无论查找什么属性都会返回 undefined。

需要注意的是,原型链是在对象创建时就确定的,如果在原型链上的某个原型对象中添加、修改或删除属性,对于已经创建的对象不会立即生效,但对后续创建的对象仍然有效。

8. 对闭包的理解,有什么样的场景是需要使用闭包的,有什么优缺点?

9. Webpack和vite有什么区别

10. 漫画小说阅读器是H5的还是移动端的?

移动端的

11. 用户的登录是否是登录状态,这套逻辑是怎么做的?登录状态过期有没有什么设置?

12. Cookie,localstorage,session storage有什么区别。如果浏览器有多个标签页,关闭一个标签页session storage会消失吗?

 

5/31

用到了哪些技术栈,项目有什么优点,你觉得哪方面比较有挑战性?

你有没有给用户提供漫画资源?

介绍一下flex布局,flex:1是哪几个属性的缩写。

Flex布局(Flexbox)是一种用于页面布局的CSS3模块,它提供了一种灵活的方式来排列、对齐和分配空间,适用于各种不同尺寸和屏幕上的元素。

Flexbox布局的主要思想是通过设置容器和其内部项目的属性来实现布局。容器称为Flex容器(flex container),内部项目称为Flex项目(flex items)。

Flex:1是flex属性的缩写,其实际上是以下几个属性的简写:

flex-grow:该属性决定了项目在主轴方向上的伸展能力。如果设置为1,表示项目将按比例伸展以填充剩余空间。如果所有项目都设置为1,则它们将平均分配剩余空间。

flex-shrink:该属性决定了项目在主轴方向上的收缩能力。如果容器空间不足,项目将根据其设置的flex-shrink值进行收缩。默认值为1,表示所有项目将以相同的比例收缩。

flex-basis:该属性定义了项目在主轴方向上的初始大小。它可以设置为具体的长度值(如像素或百分比),也可以设置为关键字如auto(表示项目的本来大小)或content(表示项目的内容大小)。

综上所述,flex:1的缩写相当于flex:1 1 0,即项目在主轴方向上会根据剩余空间等比例伸展,并且可以收缩,初始大小为0。

grid布局是什么?,和flex布局有什么区别,他们的应用场景你会怎么选?

Grid布局(Grid Layout)是一种二维网格系统,可以用于构建复杂的网页布局。它通过在容器中定义行(rows)和列(columns)来创建网格,然后将元素放置在网格的单元格中,从而实现灵活的布局。

与Flex布局相比,Grid布局更适用于构建复杂的、基于网格的布局结构。它具有以下一些特点和区别:

二维布局:Grid布局是一个二维布局系统,可以同时控制行和列,可以在水平和垂直方向上对元素进行精确的定位和调整。

自由布局:Grid布局可以创建多个网格单元格,并通过指定行列的起始和结束位置来放置元素,使得元素可以在网格中自由定位。

对齐和分布控制:Grid布局提供了更丰富的对齐和分布控制选项,可以精确控制元素在网格单元格中的对齐方式、尺寸和间距。

复杂布局:Grid布局适用于构建复杂的布局结构,如多列布局、定位布局、层叠布局等。它支持嵌套和多级网格,可以实现更高级的布局效果。

Flex布局和Grid布局有各自适用的场景:

Flex布局适合用于单行或单列的布局,适用于构建简单的自适应布局,如导航菜单、卡片布局等。它更适合于一维的布局需求,更简单直观。

Grid布局适合用于构建复杂的网格布局,适用于需要更精确控制的布局需求,如网格状的图库、表格布局等。它更适合于二维的布局需求,可以实现更复杂的布局结构。

选择使用Flex布局还是Grid布局应根据具体的布局需求来决定。如果需要构建简单的一维布局或需要更灵活的元素排序和尺寸调整,可以选择Flex布局。而如果需要构建复杂的二维布局或对元素的位置和间距有更精确的控制,可以选择Grid布局。在某些情况下,两种布局也可以结合使用,根据具体需求选择合适的布局方式。

flex布局还有什么常用的属性?justify-content有哪些可选项?如果让他们都靠左对齐如何实现?

  • 1.flex-direction:指定主轴的方向,可选值包括:

row:水平方向,从左到右排列(默认值)

row-reverse:水平方向,从右到左排列

column:垂直方向,从上到下排列

column-reverse:垂直方向,从下到上排列

  • 2.align-items:指定项目在交叉轴上的对齐方式,可选值包括:

flex-start:顶部对齐

flex-end:底部对齐

center:居中对齐

baseline:基线对齐

stretch:拉伸以填充交叉轴(默认值)

  • 3.align-self:单个项目覆盖align-items设置,可选值与align-items相同。

  • 4.justify-content:指定项目在主轴上的对齐方式,可选值包括:

flex-start:左对齐

flex-end:右对齐

center:居中对齐

space-between:两端对齐,项目之间间隔相等

space-around:每个项目两侧的间隔相等

space-evenly:项目之间和两侧的间隔相等

如果要实现所有项目都靠左对齐的效果,可以将justify-content设置为flex-start。这样,项目将从容器的起始位置开始排列,即左对齐。例如:

.container {

  display: flex;

  justify-content: flex-start;

}

这样设置后,所有的项目将靠左对齐,剩余的空间将会在容器的右侧。

盒子与盒子之间的距离是盒子与边框距离的两倍,这个需要使用哪个属性?两边的盒子贴边,中间的距离等分,这是哪一种属性?

要实现盒子与盒子之间的距离是盒子与边框距离的两倍,可以使用margin属性。将margin属性设置为所需距离的一半即可。

例如,假设每个盒子的边框距离为10px,希望盒子与盒子之间的距离是边框距离的两倍(20px),可以将margin属性设置为10px即可。

.box {

  margin: 10px;

}

这样设置后,每个盒子的外边距为10px,因此盒子与盒子之间的距离为20px。

至于两边的盒子贴边,中间的距离等分的效果,可以使用justify-content: space-between;属性来实现。这会将中间的空间等分分配给各个盒子,让它们在主轴上均匀分布,并将第一个和最后一个盒子紧贴容器的两侧。

例如:

.container {

  display: flex;

  justify-content: space-between;

}

这样设置后,盒子之间的距离将会等分,且第一个和最后一个盒子会贴边。

漫画的项目有没有做目录?目录是怎么设计的?从数据结构来讲应该是怎么样的结构?

目录设计思路: 目录作为漫画或小说的章节索引,可以让用户快速导航和选择感兴趣的章节。 目录应包含章节的标题和对应的章节页码或链接。 目录可以以树状结构展示,支持多级目录嵌套。 目录可以提供展开/折叠的功能,方便用户浏览大量章节。

数据结构示例:

为了表示目录的树状结构,可以使用嵌套的对象或数组来表示章节的层级关系。下面是一个示例数据结构的简化版本:

[

  {

    title: '第一章',

    page: 1,

    children: [

      {

        title: '第一节',

        page: 5

      },

      {

        title: '第二节',

        page: 10

      }

    ]

  },

  {

    title: '第二章',

    page: 20,

    children: [

      {

        title: '第一节',

        page: 25

      },

      {

        title: '第二节',

        page: 30,

        children: [

          {

            title: '附录',

            page: 35

          }

        ]

      }

    ]

  },

  // 更多章节...

]

在上述示例中,每个章节都有一个标题(title)和对应的页码(page)。如果章节下还有子章节,可以在该章节的对象中添加一个名为children的数组,用于嵌套表示子章节的层级关系。  

通过上述数据结构,你可以在Vue组件中根据数据动态渲染目录,并使用递归的方式处理多级目录的展示和交互。例如,使用v-for指令遍历目录数据,根据children数组的存在与否判断是否需要展开或折叠子目录。

树状结构用JS如何实现?根据动态返回的数据去更新目录,那应该怎么办?(这里我回答li标签面试官反问我,可能不是两级,可能是很多级呢?)

// 创建树状结构

const tree = {

  name: 'Root',

  children: [

    {

      name: 'Node 1',

      children: [

        {

          name: 'Node 1.1'

        },

        {

          name: 'Node 1.2'

        }

      ]

    },

    {

      name: 'Node 2',

      children: [

        {

          name: 'Node 2.1'

        },

        {

          name: 'Node 2.2'

        }

      ]

    }

  ]

};

 

// 遍历树状结构

function traverse(node) {

  console.log(node.name);

  if (node.children) {

    node.children.forEach(child => {

      traverse(child);

    });

  }

}

 

// 调用遍历函数

traverse(tree);

在这个示例中,tree对象表示树的根节点,每个节点都有一个name属性来表示节点的名称。通过children属性,可以嵌套子节点形成树的层级关系。遍历函数traverse使用递归的方式遍历树状结构,打印出每个节点的名称。

 

对于动态返回的数据更新目录的情况,你可以在获取到新数据后,根据数据更新已有的目录结构。可以通过递归的方式遍历新数据,然后更新已有的目录数据。具体的实现方式取决于项目中的具体需求和目录组件的实现方式。

  在Vue项目中,你可以将目录数据绑定到Vue组件的数据属性中,然后在获取到新数据后,更新该数据属性即可。Vue的响应性系统会自动更新视图。如果需要手动控制更新,你可以使用Vue提供的$forceUpdate方法。

  例如,在Vue组件中的更新目录数据的方法可能如下所示:

  javascript

Copy code

methods: {

  updateDirectory(newData) {

    this.directoryData = newData;

    this.$forceUpdate();

  }

}

这样,当获取到新的目录数据后,可以调用updateDirectory方法来更新目录的数据,并触发组件的重新渲染。

说说你对VUE3的了解(回答完四大周期八个函数后反问)setup呢?

Vue 3是Vue.js框架的最新版本,相较于Vue 2,它引入了许多新的特性和改进,旨在提供更好的性能、更好的开发体验和更好的可维护性。以下是我对Vue 3的一些了解:

  Composition API(组合式API):Vue 3引入了Composition API,它是一种基于函数的API风格,可以让开发者更灵活地组织和复用组件的逻辑。Composition API使用setup函数来替代Vue 2中的data、computed、methods等选项,使得组件逻辑更可组合、可测试和可维护。  

更好的性能:Vue 3通过优化响应式系统和虚拟DOM算法,提供了更好的性能表现。使用Proxy作为底层响应式系统,Vue 3在访问和观察数据时更高效,并减少了不必要的触发。另外,Vue 3还引入了静态树提升(Static Tree Hoisting)和更高效的diff算法,以提升渲染性能。  

更小的包大小:Vue 3在包的体积方面也做了一些优化,通过模块化的设计和更精细的按需加载,可以减小最终构建出的应用程序的大小,提升加载速度。  

TypeScript支持:Vue 3对TypeScript的支持更加友好。Vue 3的代码库本身就是使用TypeScript编写的,并提供了更好的类型推断和类型检查,使得在使用TypeScript时的开发体验更加顺畅。

  其他改进:除了上述特性外,Vue 3还带来了许多其他的改进,如Teleport(瞬移)组件、Fragments(片段)、新的生命周期钩子等。它还提供了更多的编译时提示和警告,以帮助开发者更早地发现潜在问题。

如果我想操纵一个DOM元素,我应该在哪个生命周期里面去操作?Updated可以更新DOM节点吗,或者说更新数据吗?那beforeUpdate可以吗?

如果你想操纵一个DOM元素,你可以在Vue组件的生命周期钩子函数中进行操作。以下是几个常用的生命周期钩子函数:  

mounted:在组件被挂载到DOM后调用。此时,组件的DOM元素已经渲染完毕,并且可以通过this.$el访问到DOM元素。你可以在该钩子函数中执行DOM操作或初始化第三方库。  

updated:在组件更新完成后调用。当组件的依赖项发生变化或$forceUpdate方法被调用时,会触发组件的更新过程,此时updated钩子函数会被调用。你可以在该钩子函数中执行DOM操作,但要注意避免无限循环更新。  

beforeUpdate:在组件更新之前调用。在这个钩子函数中,可以访问到更新前的DOM状态和数据。然而,不推荐在beforeUpdate钩子函数中直接操作DOM,因为此时DOM尚未更新。  

需要注意的是,尽量避免在生命周期钩子函数中频繁地直接操作DOM。Vue提供了虚拟DOM来管理和优化DOM更新,直接操纵DOM可能会导致与Vue的更新机制冲突。

  如果需要操作DOM元素,推荐使用Vue的指令(如v-if、v-show、v-bind、v-on等)来与DOM进行交互,或者使用Vue提供的方法修改组件的数据,然后通过数据的变化来触发DOM的更新。

  综上所述,updated钩子函数可以用于更新DOM节点,但要注意控制更新的触发,以避免无限循环更新。beforeUpdate钩子函数也可以在更新之前操作DOM,但不推荐直接操作DOM。

VUE中有父子组件嵌套,当子组件加载完成以后,那父组件是什么状态?就是当子组件Unmounted,父组件是什么状态。即父子组件加载顺序应该是什么样的?

在Vue中,父子组件的加载顺序是先加载父组件,然后加载子组件。当子组件加载完成后,父组件仍然处于mounted状态,并没有发生变化。父组件的生命周期并不会受到子组件的加载或卸载的影响。  

下面是父子组件加载的典型顺序:

  父组件开始加载。

父组件的生命周期钩子函数依次触发:beforeCreate、created、beforeMount。

子组件开始加载。

子组件的生命周期钩子函数依次触发:beforeCreate、created、beforeMount、mounted。

子组件加载完成后,父组件的生命周期钩子函数继续触发:mounted。

在子组件被卸载(unmounted)后,父组件的状态不受影响,仍然保持在mounted状态。  

需要注意的是,如果父组件中有依赖于子组件的逻辑或数据,当子组件被卸载后,这些依赖可能无法满足,需要进行相应的处理,以避免潜在的错误。例如,可以使用v-if指令来根据条件判断是否渲染子组件,或者使用$refs来检查子组件的存在。

  综上所述,父子组件的加载顺序是先加载父组件,然后加载子组件。当子组件加载完成后,父组件仍然保持在mounted状态,父组件的生命周期不受子组件的加载或卸载影响。

父子组件之间如何进行通信?VUE3中如何对传过来的数据类型进行限制,应该用哪个API?

在Vue中,父子组件之间可以通过props和事件来进行通信。

  Props:父组件可以通过props向子组件传递数据,子组件可以接收并使用这些数据。在父组件中通过子组件的属性进行数据绑定,子组件可以在props选项中声明需要接收的属性,并在模板或组件逻辑中使用。

事件:子组件可以通过自定义事件向父组件发送消息。在子组件中使用$emit方法触发事件,父组件可以通过监听子组件的自定义事件来接收并处理消息。  

对于数据类型的限制,Vue 3提供了Props选项的新API PropsType来对传入的属性进行类型验证。你可以在子组件的props选项中使用PropsType来指定属性的类型,并在开发过程中捕获类型错误。

示例:

 

在上述示例中,使用PropsType将type属性指定为String类型,这样传递给message属性的值将被强制为字符串类型。

 

TypeScript:如果你的Vue项目使用了TypeScript,你可以在Vue组件中使用TypeScript的类型注解来定义props的类型。

// ChildComponent.vue

VUE3没有传值的时候需要设置默认值,默认值如何去设置?(面试官忍不了了直接和我说withdefault,反问我这个东西是给什么用的?)

这里withdefault存疑,查了官方文档写的是props里面的应该是default属性设置默认值

在Vue 3中,可以使用default属性来设置props的默认值。当父组件没有传递该props时,子组件将使用默认值。   示例:

 

在上述示例中,message属性的默认值被设置为'Default message'。如果父组件没有传递message属性或传递的值为undefined,则子组件将使用默认值。

 

另外,如果需要动态计算默认值,可以将default属性的值设置为一个函数,该函数会在每次创建新的子组件实例时调用。

 

示例:

 

在上述示例中,default属性的值是一个返回当前日期的函数。每当创建新的子组件实例时,该函数都会被调用,从而动态计算默认值。

兄弟组件如何进行通信?有很多方法,请列举出来

使用共享状态(Shared State):创建一个共享的Vue实例或使用VueX等状态管理工具来管理兄弟组件之间共享的状态。兄弟组件可以通过读取和修改共享状态来进行通信。

 

使用事件总线(Event Bus):创建一个空的Vue实例作为事件中心,兄弟组件通过该实例来触发和监听事件。一个组件可以通过emit方法触发事件,而另一个组件可以通过emit方法触发事件,而另一个组件可以通过on方法监听事件。

 

使用父组件作为中介:如果兄弟组件有共同的父组件,可以借助父组件来传递信息。兄弟组件可以通过父组件作为中介,将信息传递给另一个兄弟组件,通过props或自定义事件来实现。

 

使用vuex-pubsub插件:vuex-pubsub是一个为Vuex设计的插件,可以实现发布订阅模式的兄弟组件通信。兄弟组件可以订阅和发布事件,从而进行通信。

 

使用组件引用(Component Reference):一个兄弟组件可以通过$ref来获取另一个兄弟组件的引用,然后直接调用该组件的方法或访问其属性。

Webpack的输出目录应该如何配置?输入目录如何配置?中间可能会经过预处理,应该如何增加其他插件?

在Webpack中,输出目录和输入目录可以通过配置文件进行设置。

 

输出目录的配置:在Webpack配置文件中,通过output选项来配置输出目录的路径和文件名。常见的配置选项包括:

 

path:指定输出目录的路径。

filename:指定输出文件的名称。

publicPath:指定输出文件的公共路径(可选)。

示例:

 

const path = require('path');

 

module.exports = {

  // 入口文件配置等省略...

  output: {

    path: path.resolve(__dirname, 'dist'),

    filename: 'bundle.js',

    publicPath: '/'

  }

};

在上述示例中,输出目录被配置为dist文件夹,输出文件名为bundle.js。

 

输入目录的配置:输入目录即Webpack的入口文件,它可以通过配置文件中的entry选项来设置。entry选项可以是一个字符串、一个对象或一个数组。

 

示例:

module.exports = {

  entry: './src/index.js',

  // ...

};

在上述示例中,输入目录被配置为./src/index.js,这是Webpack的入口文件。

 

预处理和插件的配置:Webpack支持使用各种插件来进行预处理、优化和其他自定义操作。你可以在Webpack配置文件的plugins选项中配置这些插件。

 

示例:  

const HtmlWebpackPlugin = require('html-webpack-plugin');  

module.exports = {

  // 其他配置省略...

  plugins: [

    new HtmlWebpackPlugin({

      template: './src/index.html',

      filename: 'index.html'

    })

  ]

};

在上述示例中,使用了HtmlWebpackPlugin插件来生成HTML文件。它会将./src/index.html作为模板,然后将生成的HTML文件输出为index.html。  

你可以根据需要选择和配置各种Webpack插件来满足你的需求,例如压缩插件、提取CSS插件、代码分割插件等。

  通过合理配置输出目录、输入目录和插件,可以对Webpack的构建过程进行定制和优化,以满足项目的需求。

Webpack里面可以开启source maps,这是干什么的?

在Webpack中,开启source maps可以将编译后的代码映射回原始源代码,以方便调试和定位问题。它是一种文件,用于建立源代码和最终生成的代码之间的映射关系。

 

当开启source maps后,浏览器在调试时会使用source maps来解析和显示源代码,而不是显示经过压缩和合并的代码。这样可以提供更好的开发体验,因为你可以在浏览器中直接调试源代码,而不需要查看和调试经过转换和优化的代码。

 

通过开启source maps,你可以:

 

进行准确的错误追踪:当出现错误时,浏览器会指示源代码中的准确位置,而不是编译后的代码位置。这样可以更轻松地定位并修复问题。

 

进行更高效的调试:你可以在浏览器的开发者工具中直接查看和调试源代码,而不需要通过编译后的代码。

 

提供更好的开发体验:source maps可以帮助你更好地理解和理解代码的结构和执行过程,提高开发效率。

 

在Webpack中,你可以通过配置选项来开启source maps。常用的source maps类型包括:

 

eval:将source maps作为eval代码附加到打包后的文件中。

source-map:生成独立的source map文件。

cheap-source-map:生成较小的source map,但可能不会精确到行级别。

inline-source-map:将source maps作为Data URL嵌入到打包后的文件中。

示例:

module.exports = {

  // 其他配置省略...

  devtool: 'source-map'

};

在上述示例中,通过设置devtool选项为source-map,开启了生成独立的source map文件。

 

请注意,开启source maps会增加构建时间和文件大小,因此在生产环境中,通常建议关闭source maps或选择更轻量级的source maps类型。

另注:****

提到sourceMap,很多小伙伴可能会立刻想到Webpack配置里边的devtool参数,以及对应的eval,eval-cheap-source-map等等可选值以及它们的含义。除了知道不同参数之间的区别以及性能上的差异外,我们也可以一起了解一下sourceMap的实现方式。

sourceMap是一项将编译、打包、压缩后的代码映射回源代码的技术,由于打包压缩后的代码并没有阅读性可言,一旦在开发中报错或者遇到问题,直接在混淆代码中debug问题会带来非常糟糕的体验,sourceMap可以帮助我们快速定位到源代码的位置,提高我们的开发效率。sourceMap其实并不是Webpack特有的功能,而是Webpack支持sourceMap,像JQuery也支持souceMap。

既然是一种源码的映射,那必然就需要有一份映射的文件,来标记混淆代码里对应的源码的位置,通常这份映射文件以.map结尾,里边的数据结构大概长这样:

js复制代码{

  "version" : 3,                          // Source Map版本

  "file": "out.js",                       // 输出文件(可选)

  "sourceRoot": "",                       // 源文件根目录(可选)

  "sources": ["foo.js", "bar.js"],        // 源文件列表

  "sourcesContent": [null, null],         // 源内容列表(可选,和源文件列表顺序一致)

  "names": ["src", "maps", "are", "fun"], // mappings使用的符号名称列表

  "mappings": "A,AAAB;;ABCDE;"            // 带有编码映射数据的字符串

}

 

其中mappings数据有如下规则:

 

生成文件中的一行的每个组用“;”分隔;

每一段用“,”分隔;

每个段由1、4或5个可变长度字段组成;

 

有了这份映射文件,我们只需要在我们的压缩代码的最末端加上这句注释,即可让sourceMap生效:

js复制代码//# sourceURL=/path/to/file.js.map

 

有了这段注释后,浏览器就会通过sourceURL去获取这份映射文件,通过解释器解析后,实现源码和混淆代码之间的映射。因此sourceMap其实也是一项需要浏览器支持的技术。

如果我们仔细查看webpack打包出来的bundle文件,就可以发现在默认的development开发模式下,每个_webpack_modules__文件模块的代码最末端,都会加上//# sourceURL=webpack://file-path?,从而实现对sourceMap的支持。

sourceMap映射表的生成有一套较为复杂的规则,有兴趣的小伙伴可以看看以下文章,帮助理解soucrMap的原理实现:

Webpack如何进行打包?

首先我们应该简单了解一下webpack的整个打包流程:  

1、读取webpack的配置参数;

2、启动webpack,创建Compiler对象并开始解析项目;

3、从入口文件(entry)开始解析,并且找到其导入的依赖模块,递归遍历分析,形成依赖关系树;

4、对不同文件类型的依赖模块文件使用对应的Loader进行编译,最终转为Javascript文件;

5、整个过程中webpack会通过发布订阅模式,向外抛出一些hooks,而webpack的插件即可通过监听这些关键的事件节点,执行插件任务进而达到干预输出结果的目的。  

其中文件的解析与构建是一个比较复杂的过程,在webpack源码中主要依赖于compiler和compilation两个核心对象实现。

compiler对象是一个全局单例,他负责把控整个webpack打包的构建流程。

compilation对象是每一次构建的上下文对象,它包含了当次构建所需要的所有信息,每次热更新和重新构建,compiler都会重新生成一个新的compilation对象,负责此次更新的构建过程。

而每个模块间的依赖关系,则依赖于AST语法树。每个模块文件在通过Loader解析完成之后,会通过acorn库生成模块代码的AST语法树,通过语法树就可以分析这个模块是否还有依赖的模块,进而继续循环执行下一个模块的编译解析。

Git基本命令,你了解哪些?我把当前的修改加入缓存区,应该用哪个API?将当前缓存区的文件提交到本地仓库,应该怎么提交?如何新建一个分支?

以下是一些常用的Git基本命令:

  1. git init:在当前目录初始化一个新的Git仓库。
  2. git clone <repository>:克隆远程仓库到本地。
  3. git add <file>:将文件添加到缓存区。
  4. git commit -m "commit message":将缓存区的文件提交到本地仓库,并附上提交信息。
  5. git status:查看当前仓库的状态,包括修改、新增或删除的文件。
  6. git diff:查看当前修改的文件与上次提交的文件差异。
  7. git log:查看提交记录的历史。
  8. git branch:查看当前分支列表。
  9. git checkout <branch>:切换到指定分支。
  10. git checkout -b <branch>:创建并切换到一个新的分支。
  11. git merge <branch>:将指定分支的修改合并到当前分支。
  12. git push <remote> <branch>:将本地分支推送到远程仓库。
  13. git pull <remote> <branch>:从远程仓库拉取并合并最新的修改。
  14. git remote add <name> <url>:添加远程仓库地址。
  15. git remote -v:查看当前仓库关联的远程仓库。

如果你想将当前的修改加入缓存区,可以使用git add命令。例如,如果你想将所有修改的文件都添加到缓存区,可以运行以下命令:

csharpCopy code
git add .

如果你想将当前缓存区的文件提交到本地仓库,可以使用git commit命令,并在后面加上提交信息。例如:

sqlCopy code
git commit -m "提交修改"

如果你想新建一个分支,可以使用git checkout -b <branch>命令。例如,如果你想创建一个名为feature的新分支并切换到该分支,可以运行以下命令:

cssCopy code
git checkout -b feature

以上是一些常用的Git基本命令,涵盖了Git的基本操作,可以帮助你进行版本控制和代码管理。