模块化:意味着 JS 总算可以开发大型项目(解决 JS 的复用问题)
组件化:解决 HTML、CSS、JS 的复用问题
工程化:前端项目越来越复杂、前端项目模块(组件)越来越多
为什么前端项目越来越复杂?
在书写前端项目的时候,可能会涉及到使用其他的语言,typescript、less/sass、coffeescript,涉及到编译
在部署的时候,还需要对代码进行丑化、压缩
代码优化:为 CSS 代码添加兼容性前缀
前端项目模块(组件)越来越多?
专门有一个 node_modules 来管理这些可以复用的代码。
前端工程化的出现,归根结底,其实就是要解决开发环境和生产环境不一致的问题。
为了实现CommonJS规范,nodejs对模块做出了以下处理
require函数时才会加载并执行模块nodejs中导入模块,使用相对路径,并且必须以./或../开头,浏览器可以省略./,nodejs不行
module.exports = {}module.exports即模块的导出值module.exports后,又声明了一个变量exports = module.exports面试
经典面试题 util.js
index.js
经验:对exports赋值无意义,建议用module.exports
为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果
过去,JS很难编写大型应用,因为有以下两个问题:
这些问题,都导致了JS无法进行精细的模块划分,因为精细的模块划分会导致更多的全局污染以及更加复杂的依赖关系 于是,先后出现了两大模块化标准,用于解决以上两个问题:
注意:上面提到的两个均是模块化标准,具体的实现需要依托于JS的宿主环境
node环境支持 CommonJS 模块化标准,所以,要使用 CommonJS,必须要先安装node
官网地址:https://nodejs.org/zh-cn/
nodejs直接运行某个js文件,该文件被称之为入口文件
nodejs遵循EcmaScript标准,但由于脱离了浏览器环境,因此:
node中的所有代码均在CommonJS规范下运行 具体规范如下:
module.exports = xxx,该过程称之为模块的导出require("模块路径")
./或../开头同时也解决了JS的两个问题 浏览器里面可以直接用的函数变量都在全局,但是commonjs里面require不在全局,undefined,同理module
原理: require中的伪代码
根据传递的模块路径,得到模块完整的绝对路径。因为绝对路径不会重复。
require引入相同模块会有缓存,不会重复加载
如果没有缓存。真正运行模块代码的辅助函数
this === exports === module.exports === {}
由于种种原因,CommonJS标准难以在浏览器中实现,因此一直在浏览器端一直没有合适的模块化标准,直到ES6标准出现 ES6规范了浏览器的模块化标准,一经发布,各大浏览器厂商纷纷在自己的浏览器中实现了该规范
模块的引入:浏览器使用以下方式引入一个ES6模块文件
default注意
- CMJ 是社区标准,ESM 是官方标准
- CMJ 是使用 API 实现的模块化,ESM 是使用新语法实现的模块化
- CMJ 仅在 node 环境中支持,ESM 各种环境均支持
- CMJ 是动态的依赖,ESM 既支持动态,也支持静态
- ESM 导入时有符号绑定,CMJ 只是普通函数调用和赋值
CommonJS是社区模块化标准,node环境支持该标准。它不产生新的语法,是使用API实现的,本质是把要加载的模块放到一个闭包中执行(这里可以详细的阐述)。因此,node环境中的所有JS文件在执行的时候,都是放到一个函数环境中执行的,这对使得模块内部可以访问函数的参数,如exports、module、__dirname等,而且对this的指向也会有影响。CommonJS是动态的模块化标准,这就意味着依赖关系是在运行过程中确定的,同时也意味着在导入导出模块时,并不限制书写的位置。 ES Module是官方模块化标准,目前node和浏览器均支持该标准,它引入了新的语法。ES Module是静态的模块化标准,在模块执行前,会递归确定所有依赖关系,然后加载所有文件,加载完成后再运行,这在浏览器环境下会产生多次请求。由于使用的是静态依赖,因此,它要求导入导出的代码必须放置到顶层,因为只有这样才能在代码运行前就确定依赖关系。ES Module导入模块时会将导入的结果绑定到标识符中,该标识符是一个常量,不可更改。 CommonJS和ES Module都使用了缓存,保证每个模块仅执行一次。
1.讲讲模块化规范
2.import和require的区别
3.require是如何解析路径的
1、ESM是编译时导出结果,CommonJS是运⾏时导出结果
2、ESM是导出引⽤,CommonJS是导出⼀个值
3、ESM⽀持异步,CommonJS只⽀持同步
下面的模块导出了什么结果?
这三者中,模块化是基础,没有模块化,就没有组件化和工程化
模块化的出现,解决了困扰前端的两大难题:全局污染问题和依赖混乱问题,从而让精细的拆分前端工程成为了可能。
工程化的出现,解决了前端开发环境和生产环境要求不一致的矛盾。在开发环境中,我们希望代码使用尽可能的细分,代码格式尽可能的统一和规范,而在生产环境中,我们希望代码尽可能的被压缩、混淆,尽可能的优化体积。工程化的出现,就是为了解决这一矛盾,它可以让我们舒服的在开发环境中书写代码,然后经过打包,生成最合适的生产环境代码,这样就解放了开发者的精力,让开发者把更多的注意力集中在开发环境上即可。
组件化开发是一些前端框架带来的概念,它把一个网页,或者一个站点,甚至一个完整的产品线,划分为多个小的组件,组件是一个可以复用的单元,它包含了一个某个区域的完整功能。这样一来,前端便具备了开发复杂应用的能力。
它们都是 webpack 功能的扩展点。
loader 是加载器,主要用于代码转换,比如 JS 代码降级,CSS 预编译、模块化等
plugins 是插件,webpack 打包流程中每个环节都提供了钩子函数,可以利用这些钩子函数参与到打包生命周期中,更改或增加 webpack 的某些功能,比如生成页面和 css 文件、压缩打包结果等
参考答案:
- loader 加载器,主要用于代码转换,比如 JS 代码降级,CSS 预编译、模块化等
- plugin 插件,webpack 打包流程中每个环节都提供了钩子函数,可以利用这些钩子函数参与到打包生命周期中,更改或增加 webpack 的某些功能,比如生成页面和 css 文件、压缩打包结果等
- module 模块。webpack 将所有依赖均视为模块,无论是 js、css、html、图片,统统都是模块
- entry 入口。打包过程中的概念,webpack 以一个或多个文件作为入口点,分析整个依赖关系。
- chunk 打包过程中的概念,一个 chunk 是一个相对独立的打包过程,以一个或多个文件为入口,分析整个依赖关系,最终完成打包合并
- bundle webpack 打包结果
- tree shaking 树摇优化。在打包结果中,去掉没有用到的代码。
- HMR 热更新。是指在运行期间,遇到代码更改后,无须重启整个项目,只更新变动的那一部分代码。
- dev server 开发服务器。在开发环境中搭建的临时服务器,用于承载对打包结果的访问
使用动态导入即可,导入后,得到的是一个 Promise,完成后,得到一个模块对象,其中包含了所有的导出结果。
参考答案:
- hash hash 是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的 hash 值都会更改,并且全部文件都共用相同的 hash 值
- chunkhash 每个打包过程单独的 hash 值,如果一个项目有多个 entry,则每个 entry 维护自己的 chunkhash。
- contenthash 每个文件内容单独的 hash 值,它和打包结果文件内容有关,只要文件内容不变,contenthash 不变。
参考答案:
不会。它跟关联的内容是否有变化有关系,如果没有变化,hash 就不会变。具体来说,contenthash 和具体的打包文件内容有关,chunkhash 和某一 entry 为起点的打包过程中涉及的内容有关,hash 和整个工程所有模块内容有关。
参考答案:
webpack 本身不处理图片,它会把图片内容仍然当做 JS 代码来解析,结果就是报错,打包失败。如果要处理图片,需要通过 loader 来处理。其中,url-loader 会把图片转换为 base64 编码,然后得到一个 dataurl,file-loader 则会将图片生成到打包目录中,然后得到一个资源路径。但无论是哪一种 loader,它们的核心功能,都是把图片内容转换成 JS 代码,因为只有转换成 JS 代码,webpack 才能识别。
说明:这道题的表述是有问题的,webpack 本身并不打包 html,相反,它如果遇到 html 代码会直接打包失败,因为 webpack 本身只能识别 JS。之所以能够打包出 html 文件,是因为插件或 loader 的作用,其中,比较常见的插件是 html-webpack-plugin。所以这道题的正确表述应该是:「html-webpack-plugin 打包出来的 html 为什么 style 放在头部 script 放在底部?」
要配置 CDN,有两个步骤:
- 在 html 模板中直接加入 cdn 引用
- 在 webpack 配置中,加入
externals配置,告诉 webpack 不要打包其中的模块,转而使用全局变量
若要在开发环境中不使用 CDN,只需根据环境变量判断不同的环境,进行不同的打包处理即可。
- 在 html 模板中使用 ejs 模板语法进行判断,只有在生产环境中引入 CDN
- 在 webpack 配置中,可以根据
process.env中的环境变量进行判断是否使用externals配置- 在
package.json脚本中设置不同的环境变量完成打包或开发启动。
推荐阅读:https://tsejx.github.io/webpack-guidebook/principle-analysis/operational-principle/tree-shaking
参考答案:
用于转换代码。有时是因为 webpack 无法识别某些内容,比如图片、css 等,需要由 loader 将其转换为 JS 代码。有时是因为某些代码需要被特殊处理,比如 JS 兼容性的处理,需要由 loader 将其进一步转换。不管是什么情况,loader 的作用只有一个,就是转换代码。
参考答案:
实际上就是一个版本管理的问题。
如果此次模块升级只是修复了某一些 bug,作为补丁版本升级即可,不影响主版本和次版本号
如果此次模块升级会新增一些内容,完全兼容之前的 API,作为次版本升级即可
如果此次模块升级会修改之前的 API,则作为主版本升级
在开发项目时,让项目依赖模块的主版本,因此,当模块更新时,只要不是主版本更新,项目都可以非常方便的升级模块版本,无须改动任何代码。但若涉及主版本更新,项目可以完全无视此次版本更新,仍然使用之前的旧版本,无须改动任何代码;当然也可以升级主版本,但就会涉及代码的改动,这就好比跟将 vue2 升级到 vue3 会涉及大量改动一样。
而在开发模块时,在一开始就要精心设计 API,尽量保证 API 的接口稳定,不要经常变动主版本号。如果实在要更新主版本,就需要在一段时间内同时维护两个版本(新的主版本,旧的主版本),给予其他项目一定的升级时间。
参考答案:
export 为普通导出,又叫做具名导出,顾名思义,它导出的数据必须带有命名,比如变量定义、函数定义这种带有命名的语句。在导出的模块对象中,命名即为模块对象的属性名。在一个模块中可以有多个具名导出
export default 为默认导出,在模块对象中名称固定为 default,因此无须命名,通常导出一个表达式或字面量。在一个模块中只能有一个默认导出。
参考答案:
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的
run方法开始执行编译- 确定入口:根据配置中的
entry找出所有的入口文件- 编译模块:从入口文件出发,调用所有配置的
loader对模块进行翻译,再把翻译后的内容转换成 AST,通过对 AST 的分析找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理- 完成模块编译:在经过第 4 步使用
loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系图- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的
Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
参考答案:
当开启热更新后,页面中会植入一段 websocket 脚本,同时,开发服务器也会和客户端建立 websocket 通信,当源码发生变动时,webpack 会进行以下处理:
- webpack 重新打包
- webpack-dev-server 检测到模块的变化,于是通过 webscoket 告知客户端变化已经发生
- 客户端收到消息后,通过 ajax 发送请求到开发服务器,以过去打包的 hash 值请求服务器的一个 json 文件
- 服务器告诉客户端哪些模块发生了变动,同时告诉客户端这次打包产生的新 hash 值
- 客户端再次用过去的 hash 值,以 JSONP 的方式请求变动的模块
- 服务器响应一个函数调用,用于更新模块的代码
- 此时,模块代码已经完成更新。客户端按照之前的监听配置,执行相应模块变动后的回调函数。
参考答案:
- noParse 很多第三方库本身就是已经打包好的代码,对于这种代码无须再进行解析,可以使用 noParse 配置排除掉这些第三方库
- externals 对于一些知名的第三方库可以使用 CDN,这部分库可以通过 externals 配置不进行打包
- 限制 loader 的范围 在使用 loader 的时候,可以通过 exclude 排除掉一些不必要的编译,比如 babel-loader 对于那些已经完成打包的第三方库没有必要再降级一次,可以排除掉
- 开启 loader 缓存 可以利用
cache-loader缓存 loader 的编译结果,避免在源码没有变动时反复编译- 开启多线程编译 可以利用
thread-loader开启多线程编译,提升编译效率- 动态链接库 对于某些需要打包的第三方库,可以使用 dll 的方式单独对其打包,然后 DLLPlugin 将其整合到当前项目中,这样就避免了在开发中频繁去打包这些库
参考答案:
当遇到代码中包含动态导入语句时,webpack 会将导入的模块及其依赖分配到单独的一个 chunk 中进行打包,形成单独的打包结果。而动态导入的语句会被编译成一个普通的函数调用,该函数在执行时,会使用 JSONP 的方式动态的把分离出去的包加载到模块集合中。
参考答案:
- hash hash 是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的 hash 值都会更改,并且全部文件都共用相同的 hash 值
- chunkhash 每个打包过程单独的 hash 值,如果一个项目有多个 entry,则每个 entry 维护自己的 chunkhash。
- contenthash 每个文件内容单独的 hash 值,它和打包结果文件内容有关,只要文件内容不变,contenthash 不变。
参考答案:
- cache-loader:启用编译缓存
- thread-loader:启用多线程编译
- css-loader:编译 css 代码为 js
- file-loader:保存文件到输出目录,将文件内容转换成文件路径
- postcss-loader:将 css 代码使用 postcss 进行编译
- url-loader:将文件内容转换成 dataurl
- less-loader:将 less 代码转换成 css 代码
- sass-loader:将 sass 代码转换成 css 代码
- vue-loader:编译单文件组件
- babel-loader:对 JS 代码进行降级处理
参考答案:
- clean-webpack-plugin:清除输出目录
- copy-webpack-plugin:复制文件到输出目录
- html-webpack-plugin:生成 HTML 文件
- mini-css-extract-plugin:将 css 打包成单独文件的插件
- HotModuleReplacementPlugin:热更新的插件
- purifycss-webpack:去除无用的 css 代码
- optimize-css-assets-webpack-plugin:优化 css 打包体积
- uglify-js-plugin:对 JS 代码进行压缩、混淆
- compression-webpack-plugin:gzip 压缩
- webpack-bundle-analyzer:分析打包结果
参考答案:
- 如果不做特殊处理,babel-loader 会对所有匹配的模块进行降级,这对于那些已经处理好兼容性问题的第三方库显得多此一举,因此可以使用 exclude 配置排除掉这些第三方库
- 在旧版本的 babel-loader 中,默认开启了对 ESM 的转换,这样会导致 webpack 的 tree shaking 失效,因为 tree shaking 是需要保留 ESM 语法的,所以需要关闭 babel-loader 的 ESM 转换,在其新版本中已经默认关闭了。
参考答案:
本质上就是把 class 语法转换成普通构造函数定义,并做了以下处理:
- 增加了对 this 指向的检测
- 将原型方法和静态方法变为不可枚举
- 将整个代码放到了立即执行函数中,运行后返回构造函数本身
说明:
babel-polyfill 已经是一个非常古老的项目了,babel 从 7.4 版本开始已不再支持它,转而使用更加强大的 core-js,此题也适用于问「core-js 的作用是什么」
参考答案:
&符号后面的内容会和父级选择器合并书写,即中间不加入空格字符
说明:
严格来说,webpack 只是一个打包工具,它并没有 proxy 的功能,甚至连服务器的功能都没有。之所以能够在 webpack 中使用 proxy 配置,是因为它的一个插件,即 webpack-dev-server 的能力。
所以,此题应该问做:「webpack-dev-server 工作原理,为什么能解决跨域?」
参考答案:
实际上就是一个版本管理的问题。
如果此次模块升级只是修复了某一些 bug,作为补丁版本升级即可,不影响主版本和次版本号
如果此次模块升级会新增一些内容,完全兼容之前的 API,作为次版本升级即可
如果此次模块升级会修改之前的 API,则作为主版本升级
在开发项目时,让项目依赖模块的主版本,因此,当模块更新时,只要不是主版本更新,项目都可以非常方便的升级模块版本,无须改动任何代码。但若涉及主版本更新,项目可以完全无视此次版本更新,仍然使用之前的旧版本,无须改动任何代码;当然也可以升级主版本,但就会涉及代码的改动,这就好比跟将 vue2 升级到 vue3 会涉及大量改动一样。
而在开发模块时,在一开始就要精心设计 API,尽量保证 API 的接口稳定,不要经常变动主版本号。如果实在要更新主版本,就需要在一段时间内同时维护两个版本(新的主版本,旧的主版本),给予其他项目一定的升级时间。
- 公共模块 比如某些多页应用会有多个入口,从而形成多个 chunk,而这些 chunk 中用到了一些公共模块,为了减少整体的包体积,可以使用 splitchunksplugin 将公共模块分离出来。 可以配置 minChunks 来指定被多少个 chunk 引用时进行分包
- 并行下载 由于 HTML5 支持 defer 和 async,因此可以同时下载多个 JS 文件以充分利用带宽。如果打包结果是一个很大的文件,就无法利用到这一点。 可以利用 splitchunks 插件将文件进行拆分,通过配置 maxSize 属性指定包体积达到多大时进行拆分
参考答案:
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的
run方法开始执行编译- 确定入口:根据配置中的
entry找出所有的入口文件- 编译模块:从入口文件出发,调用所有配置的
loader对模块进行翻译,再把翻译后的内容转换成 AST,通过对 AST 的分析找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理- 完成模块编译:在经过第 4 步使用
loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系图- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的
Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
参考答案:
本质上,webpack 的插件是一个带有
apply函数的对象。当 webpack 创建好 compiler 对象后,会执行注册插件的 apply 函数,同时将 compiler 对象作为参数传入。在 apply 函数中,开发者可以通过 compiler 对象监听多个钩子函数的执行,不同的钩子函数对应 webpack 编译的不同阶段。当 webpack 进行到一定阶段后,会调用这些监听函数,同时将 compilation 对象传入。开发者可以使用 compilation 对象获取和改变 webpack 的各种信息,从而影响构建过程。
参考答案:
用过 webpack-bundle-analyzer 分析过打包结果,主要用于优化项目打包体积
参考答案:
babel 是一个 JS 编译器,主要用于将下一代的 JS 语言代码编译成兼容性更好的代码。
它其实本身做的事情并不多,它负责将 JS 代码编译成为 AST,然后依托其生态中的各种插件对 AST 中的语法和 API 进行处理
参考答案:
- npm 会检查本地的 node_modules 目录中是否已经安装过该模块,如果已经安装,则不再重新安装
- npm 检查缓存中是否有相同的模块,如果有,直接从缓存中读取安装
- 如果本地和缓存中均不存在,npm 会从 registry 指定的地址下载安装包,然后将其写入到本地的 node_modules 目录中,同时缓存起来。
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。 grunt和gulp是基于任务和流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。 webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。 所以总结一下:
gulp和grunt需要开发者将整个前端构建过程拆分成多个Task,并合理控制所有Task的调用关系 webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工
同样是基于入口的打包工具还有以下几个主流的:
从应用场景上来看:
由于parcel在打包过程中给出的调试信息十分有限,所以一旦打包出错难以调试,所以不建议复杂的项目使用parcel
file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
source-map-loader:加载额外的 Source Map 文件,以方便断点调试
image-loader:加载并且压缩图片文件
babel-loader:把 ES6 转换成 ES5
css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
eslint-loader:通过 ESLint 检查 JavaScript 代码
不同的作用
不同的用法
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
Loader像一个"翻译官"把读到的源文件内容转义成新的文件内容,并且每个Loader通过链式操作,将源文件一步步翻译成想要的样子。 编写Loader时要遵循单一原则,每个Loader只做一种"转义"工作。 每个Loader的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。 还可以通过 this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。 此外webpack还为开发者准备了开发loader的工具函数集——loader-utils。 相对于Loader而言,Plugin的编写就灵活了许多。 webpack在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述 多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。 多页应用中要注意的是:
Npm是目前最大的 JavaScript 模块仓库,里面有来自全世界开发者上传的可复用模块。你可能只是JS模块的使用者,但是有些情况你也会去选择上传自己开发的模块。 关于NPM模块上传的方法可以去官网上进行学习,这里只讲解如何利用webpack来构建。 NPM模块需要注意以下问题:
要支持CommonJS模块化规范,所以要求打包后的最后结果也遵守该规则。
Npm模块使用者的环境是不确定的,很有可能并不支持ES6,所以打包的最后结果应该是采用ES5编写的。并且如果ES5是经过转换的,请最好连同SourceMap一同上传。
Npm包大小应该是尽量小(有些仓库会限制包大小)
发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况。
UI组件类的模块应该将依赖的其它资源文件,例如.css文件也需要包含在发布的模块里。
基于以上需要注意的问题,我们可以对于webpack配置做以下扩展和优化:
Vue UI组件库的按需加载 为了快速开发前端项目,经常会引入现成的UI组件库如ElementUI、iView等,但是他们的体积和他们所提供的功能一样,是很庞大的。 而通常情况下,我们仅仅需要少量的几个组件就足够了,但是我们却将庞大的组件库打包到我们的源码中,造成了不必要的开销。 不过很多组件库已经提供了现成的解决方案,如Element出品的babel-plugin-component和AntDesign出品的babel-plugin-import 安装以上插件后,在.babelrc配置中或babel-loader的参数中进行设置,即可实现组件按需加载了。
单页应用的按需加载 现在很多前端项目都是通过单页应用的方式开发的,但是随着业务的不断扩展,会面临一个严峻的问题——首次加载的代码量会越来越多,影响用户的体验。 通过import()语句来控制加载时机,webpack内置了对于import()的解析,会将import()中引入的模块作为一个新的入口在生成一个chunk。 当代码执行到import()语句时,会去加载Chunk对应生成的文件。import()会返回一个Promise对象,所以为了让浏览器支持,需要事先注入Promise polyfill
逻辑多、文件多、项目的复杂度高了,所以要打包 例如: 让前端代码具有校验能力===>出现了ts css不好用===>出现了sass、less webpack可以解决这些问题
1、commonjs commonjs 是 Node 中的模块规范,通过 require 及 exports 进行导入导出 commonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。 总结:commonjs是用在服务器端的,同步的,如nodejs 2、AMD AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。 总结:amd, cmd是用在浏览器端的,异步的,如requirejs和seajs 3、CMD CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。此规范其实是在sea.js推广过程中产生的。 总结:amd, cmd是用在浏览器端的,异步的,如requirejs和seajs 4、esm esm 是 tc39 对于 ESMAScript 的模块话规范,正因是语言层规范,因此在 Node 及 浏览器中均会支持。 它使用 import/export 进行模块导入导出. 如上例所示,使用import命令的时候,用户需要知道所要加载的变量名或函数名。其实ES6还提供了export default命令,为模块指定默认输出,对应的import语句不需要使用大括号。这也更趋近于ADM的引用写法。 export default命令,为模块指定默认输出
1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
Webpack 的热更新又称热替换(Hot Module Replacement),缩写为** HMR**。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。 HMR的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上 WDS(无线路由)与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该chunk的增量更新。 后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader 和 vue-loader 都是借助这些 API 实现 HMR。
https://juejin.cn/post/6844904180285456398
当刚运行项目的时候,发现刚进入页面,就将所有的js文件和css文件加载了进来,这一进程十分的消耗时间。 如果打开哪个页面就对应的加载响应页面的js文件和css文件,那么页面加载速度会大大提升。
懒加载的好处是什么?
懒加载简单来说就是延迟加载或按需加载,即在需要的时候的时候进行加载。
怎么使用的路由懒加载?
使用到的是es6的import语法,可以实现动态导入
路由懒加载的原理是什么
通过Webpack编译打包后,会把每个路由组件的代码分割成一一个js文件,初始化时不会加载这些js文件,只当激活路由组件才会去加载对应的js文件。
作用就是webpack在打包的时候,对异步引入的库代码进行代码分割时(需要配置webpack的SplitChunkPlugin插件),为分割后的代码块取得名字 Vue中运用import的懒加载语句以及webpack的魔法注释,在项目进行webpack打包的时候,对不同模块进行代码分割,在首屏加载时,用到哪个模块再加载哪个模块,实现懒加载进行页面的优化。
使用npx 命令时,它会首先从本地工程的node_modules/.bin目录中寻找是否有对应的命令
例如:
上面这条命令寻找本地工程的node_modules/.bin/webpack
如果将命令配置到package.json的scripts中,可以省略npx
当执行某个命令时,如果无法从本地工程中找到对应命令,则会把命令对应的包下载到一个临时目录,下载完成后执行,临时目录中的命令会在适当的时候删除
例如:
npx会下载prettyjson包到临时目录,然后运行该命令
如果命令名称和需要下载的包名不一致时,可以手动指定报名
例如@vue/cli是包名,vue是命令名,两者不一致,可以使用下面的命令
npm init通常用于初始化工程的package.json文件
除此之外,有时也可以充当npx的作用
git fetch git fetch origin 分支 git rebase FETCH_HEAD 本地master更新到最新:git fetch origin master; git rebase FETCH_HEAD
git 和 svn 的区别 经常使用的 git 命令? git pull 和 git fetch 的区别 git rebase 和 git merge 的区别 git rebase和merge git分支管理、分支开发 git回退操作 git reset 和 git reverse区别?你还用过什么别的指令? git遇到冲突怎么进行解决
git-flow git reset --hard 版本号 git 如何合并分支; git 中,提交了 a, 然后又提交了 b, 如何撤回 b ? git merge、git rebase的区别 假设 master 分支切出了 test 分支,供所有人合代码测试, 然后我们 从稳定的 master 切出 一个 feature 分支进行开发, 现在 我们开发完了 feature 分支并将其merge 到 test 分支进行测试, 测试过程中出现一些问题,由于疏忽你;忘记切回 feature ,而是直接在 test 分支进行了开发 导致: test 分支优先于你的featuce 分支,此时你应该怎么特 test 分支的修改带入 feature
Workspace:工作区,就是平时进行开发改动的地方,是当前看到最新的内容,在开发的过程也就是对工作区的操作 Index:暂存区,当执行 git add 的命令后,工作区的文件就会被移入暂存区,暂存区标记了当前工作区中那些内容是被 Git 管理的,当完成某个需求或者功能后需要提交代码,第一步就是通过 git add 先提交到暂存区。 Repository:本地仓库,位于自己的电脑上,通过 git commit 提交暂存区的内容,会进入本地仓库。 Remote:远程仓库,用来托管代码的服务器,远程仓库的内容能够被分布在多个地点的处于协作关系的本地仓库修改,本地仓库修改完代码后通过 git push 命令同步代码到远程仓库
name npm 包的名字,必须是一个小写的单词,可以包含连字符-和下划线_,发布时必填。
version npm 包的版本。需要遵循语义化版本格式x.x.x。
description npm 包的描述。
keywords npm 包的关键字。
homepage npm 包的主页地址。
bugs npm 包问题反馈的地址。
license 为 npm 包指定许可证
author npm 包的作者
files npm 包作为依赖安装时要包括的文件,格式是文件正则的数组。也可以使用 npmignore 来忽略个别文件。 以下文件总是被包含的,与配置无关 package.json README.md CHANGES / CHANGELOG / HISTORY LICENCE / LICENSE 以下文件总是被忽略的,与配置无关 .git .DS_Store node_modules .npmrc npm-debug.log package-lock.json ...
main 指定npm包的入口文件。
module 指定 ES 模块的入口文件。
typings 指定类型声明文件。
bin 开发可执行文件时,bin 字段可以帮助你设置链接,不需要手动设置 PATH。
repository npm 包托管的地方
scripts 可执行的命令。
dependencies npm包所依赖的其他npm包
devDependencies npm 包所依赖的构建和测试相关的 npm 包.
peerDependencies 指定 npm 包与主 npm 包的兼容性,当开发插件时是需要的.
engines 指定 npm 包可以使用的 Node 版本.
操作系统:进程线程、死锁条件 Redis的底层数据结构 mongo的实务说一下, redis缓存机制 happypack 原理 【多进程打包 又讲了下进程和线程的区别】
koa洋葱圈模型运行机制 express和koa区别 nginx原理 正向代理与反向代理 手写中间件测试请求时间
mongo的实务
node模块加载机制(require()原理)
路径转变,缓存
koa用的多吗
redis缓存机制是啥
8、 mysql如何创建索引 9、 mysql如何处理博客的点赞问题
完成prosimeFy babel 原理,class 是转换成什么 https://juejin.cn/post/6844904024928419848
进程线程
页面缓存cache-control
node写过东西吗
node模块,path模块
对比一下commonjs模块化和es6 module
两个文件export的时候导出了相同的变量,避免重复--as
exports和module.export的区别
http和https
非对称加密和对称加密
打乱一个数组Math.random()的范围
问的特别全面,基础几乎全问
网站安全,CSRF,XSS,SQL注入攻击
token是做什么的
node koa \4. 由于简历写了了解Nodejs的底层原理。问了Node.js内存泄漏和如何定位内存泄漏 常见的内存场景:1. 闭包 2. http.globalAgent 开启keepAlive的时候,未清除事件监听 定位内存泄漏:1. heapdump 2. 用chromedev 生成三次内存快照 \5. Node.js垃圾回收的原理 \4. 客户端发请求给服务端发生了什么 \6. 你知道MySQL和MongDB区别吗 \7. 你平时怎么使用nodeJS的 \8. restful风格规范是什么 新生代(from空间,to空间),老生代(标记清除,引用计数) NodeJs的EventLoop和V8的有什么区别 Node作为服务端语言,跟其他服务端语言比有什么特性?优势和劣势是什么? Mysql接触过?跨表查询外键索引(就知道个关键字所以没往下问了) 1、介绍一下项目的难点以及怎么解决的 5、移动端的业务有做过吗? 6、介绍一下你对中间件的理解7、怎么保证后端服务稳定性,怎么做容灾8、怎么让数据库查询更快9、数据库是用的什么?10、为什么用mysql1、如何用Node开启一个服务器 2、你的项目当中哪些地方用到了Node,为什么这个地方要用Node处理
Mysql两种引擎
· 数据库表编码格式,UTF8和GBK区别
· 一个表里面姓名和学号两列,一行sql查询姓名重复的信息
· 防范XSS攻击
· 过滤或者编码哪些字符
· Video标签可以播放的视频格式
网易:谈谈同步10和异步10和它们的应用场景。https://zhuanlan.zhihu.com/p/115912936 队列解决:实现bfs返回树中值为value的节点 网易:
你知道MySQL和MongDB区别吗
3 如何判断请求的资源已经全部返回。(从http角度,不要从api角度)
8 异步缓冲队列实现。例如需要发送十个请求,但最多只支持同时发送五个请求,需要等有请求完成后再进行剩余请求的发送。
2.用写websocket进度条以及使用场景
3.写回到上次浏览的位置(我说的记住位置存在sessionstorage/localstorage里),他问窗口大小变动怎么办,我说获取当前窗口大小等比例缩放scolltop。。。
6.正向代理,反向代理以及他们的应用场景
针对博客问了一下session,讲了cookie、session以及jwt,最后问如果不用cookie和localstorage,怎么做校验,比如匿名用户
你觉得你做过最牛逼的事情是什么
如果你有一天成了技术大牛,你最想做的事情是什么
介绍了下简历里各个项目的背景
react native ;node
博客技术文章怎么写的
monrepo技术栈,好处
node 中间件原理
发布订阅once加一个标识
是否能用于生产环境
父子组件执行顺序的原因
10w数据:虚拟滚动,bff(缓存,解决渲染问题)——新元
•如果你的技术方案和一起合作的同学不一致,你该怎么说服他? • 压力 •我觉得你说的这个方案不可行 • 稳定性 • 看到你家在深圳,计划长期在北京发展吗?
egg又不是不可替代的,一个bff而已,egg团队去开发artus了,Artus是更上层的框架,还是基于egg的
node里面for循环处理10亿数据,出现两个问题,js卡死,内存不够
为什么老是有题目处理101数据
我跟他讲:分开循环解决js卡死,把结果用node里面的fs.writefile写入本地文件
解決内存不够
bam就是浏览器上作为一个代理 但实际上mock数据还是在那个「API管理平台上的」
[]==![] 是true
[]== [] 是false
双等号 两边数据类型一样 如果是引用类型,就判断引用地址是否相同,两边数据类型不一样便会隐式转换。
有一点反直觉
如何在一个图片长列表页使用懒加载进行性能优化 ,然后说了图片替换和监听窗口滚动实现图片按需加载,那么如何实现监听呢?,然后扯到节流监听滚动条变化执行函数 输入框要求每次输入都要有联想功能,即随时向后台请求数据,但是频繁输入时,如何防止频繁多次请求 你对操作系统,编译原理的理解
项目权限管理(高低权限怎么区分,如果网络传输中途被人改包,怎么防止这一潜在危险) 在淘宝里面,商品数据量很大,前端怎么优化使加载速度更快、用户体验更好(severless + indexdb) \8. 在7的条件下,已知用户当前在商品列表页,并且下一步操作是点击某个商品进入详情页,怎么加快详情页的渲染速度
- npm 会检查本地的 node_modules 目录中是否已经安装过该模块,如果已经安装,则不再重新安装
- npm 检查缓存中是否有相同的模块,如果有,直接从缓存中读取安装(不需要网络)
- 如果本地和缓存中均不存在,npm 会从 registry 指定的地址下载安装包,然后将其写入到本地的 node_modules 目录中,同时缓存起来。
npm 缓存相关命令