Webpack

1. 简介
  • 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle.
2. 核心概念
  • 入口(entry)

1.指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

2.可以通过在 webpack 配置中配置 entry 属性,来指定一个入口起点(或多个入口起点)。默认值为./src

  • 输出(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程

  • 加载(loader)

  • loaderwebpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块

  • 在更高层面,在 webpack 的配置中 loader 有两个目标:

1.test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。

2.use 属性,表示进行转换时,应该使用哪个 loader

注意:Webpack选择了compose方式,即从右到左执行loader

  • 插件(plugins)

1.插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

2.plugins需要暴露出一个class, 在new WebpackPlugin()的时候通过构造函数传入这个插件需要的参数,在webpack启动的时候会先实例化plugin再调用plugin.apply()方法,插件需要在apply函数里监听webpack生命周期里的事件,做相应的处理

  • 模式(mode)

通过选择 developmentproduction 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化


// 多个入口

module.exports = {

  mode: 'production',

  entry: {

index: ["./src/index.js"],

main: ["./src/main.js"]

  },

  output: {

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

filename: 'js/[name].[hash:8].js'

  },

  module: {

rules: [{

  test: /\.js$/, // 正则匹配文件名

  exclude: '/node_modules/', // 排除

  use: ['babel-loader']

}

  },

  plugins: [ // 插件

new copyWebpackPlugin([{

  from: path.resolve(__dirname, 'public/static'),

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

  ignore: ['index.html']

  }])

}

3. 基本流程
  • 解析shellconfig中的配置项,用于激活webpack的加载项和插件

  • webpack初始化工作,包括构建compiler对象,初始化compiler的上下文,loaderfile的输入输出环境

  • 解析入口js文件,通过对应的工厂方法创建模块,使用acron生成AST树并且遍历AST,处理requiremodule,如果依赖中包含依赖则遍历build module,在遍历过程中会根据文件类型和loader配置找出合适的loader用来对文件进行转换

  • 调用seal方法,封装,逐次对每一个modulechunk进行整理,生成编辑后的代码

4. 模块打包
  • 通过fs将模块读取成字符串,然后用warp包裹一下,使之成为一个字符串形式的的函数然后调用 vm.runInNewContext这样类型的方法,这个字符串会变成一个函数。

  • 这些模块的函数会被存放在数组里,然后进行解析执行。moduleexport都是传入的对象,webpack会实现require函数,去加载其他模块。

  • 如果是异步模块,则会通过jsonp的形式去加载该模块打包好生成的chunk。异步加载模块可以使用importrequire.ensure函数,函数将会返回一个promise

  • 上面方法都是公共的,可以抽离成模板的js文件,webpack负责做依赖分析,并将模块读成函数填充入数组。(这里说的只是js的模块)


<!-- 同步模块 -->

var moduleDepList = [

  {'./moduleA': 1}, // module[0] 的依赖 他依赖moduleA 且 moduleA的下标在moduleList 中 为 1

  {}

]



function require(id, parentId) {

  var currentModlueId = parentId !== undefined ? moduleDepList[parentId][id] : id

  var module = {exports: {}}

  var moduleFunc = moduleList[currentModlueId]

  moduleFunc(id => require(id, currentModlueId), module, module.exports)

  return module.exports

}


<!-- 异步模块 -->

var cache = {}

window.__jsonp = function(chunkId, moduleFunc) {

  var chunk = cache[chunkId]

  var resolve = chunk[0]

  var module = {exports: {}}

  moduleFunc(require, module, module.exports)

  resolve(module.exports)

}

require.ensure = function(chunkId, parentId) {

  var currentModlueId = parentId !== undefined ? moduleDepList[parentId][chunkId] : chunkId

  var currentChunk = cache[currentModlueId]

  if (currentChunk === undefined) {

var $script = document.createElement('script')

$script.src = `chunk.${chunkId}.js`

document.body.appendChild($script)

var promise = new Promise(function(resolve) {

  var chunkCache = [resolve] // 数组形式是为了保存promise

  chunkCache.status = true // 异步模块加载中 如果有别的包 在 异步加载在模块 那么下面的

  cache[chunkId] = chunkCache

})

cache[chunkId].push(promise)

return promise

  }

  if (currentChunk.status) {

return currentChunk[1] // 这里的promise 这里的就直接返回promise 这样模块只会加载一次

  }

  return currentChunk

}

5. 热更新
  • clientserver 建立一个 websocket 通信

  • 当有文件发生变动(如fs.watchFile)的时候,webpack编译文件,并通过 websocketclient发送一条更新消息

  • client 根据收到的hash值,通过ajax获取一个 manifest 描述文件

  • client 根据manifest 获取新的JS模块的代码

  • 当取到新的JS代码之后,会更新 modules tree,(installedModules)调用之前通过 module.hot.accept注册好的回调,可能是loader提供的,也可能是你自己写的

  • manifest: 描述资源文件对应关系如下,打包后的文件拥有了hash值,所以需要进行映射。


{

  "a.js": "a.41231243.js"

}

6. 如何开发一个plugin
  • 一个 JavaScript 命名函数。

  • 在插件函数的 prototype 上定义一个 apply 方法。

  • 指定一个绑定到 webpack 自身的事件钩子。

  • 处理 webpack 内部实例的特定数据。

  • 功能完成后调用 webpack 提供的回调。

tapable 工具,它提供了 webpack 插件接口的支柱


// 一个 JavaScript 命名函数。

function plugin() {};

// 在插件函数的 prototype 上定义一个 `apply` 方法。

plugin.prototype.apply = function(compiler) {

  // 指定一个挂载到 webpack 自身的事件钩子。

  compiler.plugin('webpacksEventHook', function(compilation, callback) {

callback();

  });



  // 使用taptable的写法

  //基本写法

  compiler.hooks.someHook.tap(...)

  //如果希望在entry配置完毕后执行某个功能

  compiler.hooks.entryOption.tap(...)

  //如果希望在生成的资源输出到output指定目录之前执行某个功能

  compiler.hooks.emit.tap(...)

};

7. Compiler和Compliation 对象和钩子
  • 对象

1.compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。

2.compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用

  • 钩子:总体分成两大类:Compiler和Compliation

1.Compiler暴露了和webpack整个生命周期相关的钩子

2.Compilation暴露了与模块和依赖有关的粒度更小的事件钩子,官方文档中的说法是模块会经历加载(loaded),封存(sealed),优化(optimized),分块(chunked),哈希(hashed)和重新创建(restored)这几个典型步骤,从上面的示例可以看到,compilation是Compiler生命周期中的一个步骤,使用compilation相关钩子的通用写法为:


compiler.hooks.compilation.tap('SomePlugin',function(compilation, callback){

compilation.hooks.someOtherHook.tap('SomeOtherPlugin',function(){

....

})

});

  • 钩子的类型

1.同步钩子

(1)syncHook: 不关心返回值

(2)syncBailHook: 有一个返回值不为null就跳过剩下的逻辑

(3)SyncWaterfallHook: 下一个任务要拿到上一个任务的返回值

(4)SyncLoopHook: 监听函数返回true表示继续循环,返回undefine表示结束循环

2.异步钩子

(1)AsyncParallelHook: 异步并发执行,仍是单线程

(2)AsyncParallelBailHook: 异步并发执行,有一个失败了,其他的都不用走了

(3)AsyncSeriesHook: 异步串行执行

(4)AsyncSeriesBailHook: 异步串行执行,有一个返回值不为null就跳过剩下的逻辑

(5)AsyncSeriesWaterfallHook: 异步串行执行,下一个任务要拿到上一个任务的返回值

8. 常见plugin
  • clean-webpack-plugin: 在构建之前删除上一次build的文件夹

  • copy-webpack-plugin: 复制文件或文件夹到生成后的目录

  • extract-text-webpack | mini-css-extract-plugin: 将所有入口的chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件

  • html-webpack-plugin: 将build后生成的资源以标签的形式嵌入到HTML模板内

  • hot-module-replacement: 模块热更新

9. 常见loader
  • babel-loader: 语法,源码转换以便能够运行在当前和旧版本的浏览器或其他环境中

  • css-loader: 配合style-loader可以解析在js中引入的css文件,并以<style>便签将css-loader内部样式注入到我们的HTML页面

  • file-loader: 可以解析js中require的文件,输出到输出目录并返回 public URL

  • html-loader: 可以对HTML模板中指定哪个标签属性组合(tag-attribute combination)元素应该被此 loader 处理

  • less-loader: 依赖less,可以将less编译成css

  • postcss-loader: 配合一些plugin如cssnano,autoprefixer可以对css进行压缩,优化,自动补足前缀等

  • scss-loader: 配合node-scss,可以将scss编译成css

  • style-loader: 配合css-loader可以解析在js中引入的css文件,并以<style>便签将css-loader内部样式注入到我们的HTML页面

  • url-loader: url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL(base64)

10. 常见打包优化
  • 使用dll

  • 移除prefetch, preload,关闭sourceMap

  • webpack-bundle-analyzer打包分析,将大的模块可能的移至CDN。打包时间分析使用speed-measure-webpack-plugin

  • 开启gzip,服务器需要支持

  • 使用多线程:thread-loader或HappyPack

  • webpack4内置的terser启动多线程压缩

  • 对项目进行拆分

11. 性能优化
  • webapck优化与开启gzip压缩

1.babel-loaderincludeexclude 来帮我们避免不必要的转译,不转译node_moudules中的js文件,其次在缓存当前转译的js文件,设置loader: 'babel-loader?cacheDirectory=true'

2.文件采用按需加载等等

3.具体的做法非常简单,只需要你在你的 request headers 中加上这么一句:

accept-encoding:gzip,该功能需要服务器支持才能正常显示页面。

4.图片优化,采用svg图片或者字体图标

5.浏览器缓存机制,它又分为强缓存和协商缓存

  • 本地存储——从 CookieWeb StorageIndexedDB

说明一下SessionStoragelocalStorage还有cookie的区别和优缺点

  • 代码优化

1.事件代理

2.事件的节流和防抖

3.页面的回流和重绘

4.EventLoop事件循环机制

5.代码优化等等

概念

1. MVVM
  • View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。

  • ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

2. 组件化思想
  • 简单的说组件就是:将一段UI样式和其对应的功能作为独立的整体去看待,无论这个整体放在哪里去使用,它都具有一样的功能和样式,从而实现复用,这种整体化的思想就是组件化。

  • 组件化设计就是为了增加复用性,灵活性,提高系统设计,从而提高开发效率。

3. 虚拟DOM
  • 使用Javascript来操纵DOM,操作效率往往很低,由于DOM被表示为树结构,每次DOM中的某些内容都会发生变化,因此对DOM的更改非常快,但更改后的元素,并且它的子项必须经过Reflow / Layout阶段,然后浏览器必须重新绘制更改,这很慢的。

  • 因此,回流/重绘的次数越多,您的应用程序就越卡顿。但是,Javascript运行速度很快,虚拟DOM是放在JS 和 HTML中间的一个层。它可以通过新旧DOM的对比,来获取对比之后的差异对象,然后有针对性的把差异部分真正地渲染到页面上,从而减少实际DOM操作,最终达到性能优化的目的。

4. SPA 和 多页面应用
  • 单页面应用: 仅仅在web页面初始化时加载相应的HTML、JavaScript、CSS,一旦页面加载完成了,SPA不会因为用户的操作而进行页面的重新加载或跳转,而是利用 JavaScript 动态的变换HTML的内容,从而实现UI与用户的交互。

  • 多页面应用: 多页面跳转刷新所有资源,每个公共资源(js、css等)需选择性重新加载,常用于 app 或 客户端

5. CDN
  • CDN的全称是Content Delivery Network,即内容分发网络。基本原理是在用户和服务器之间增加Cache层,主要是通过接管DNS实现,将用户的请求引导到Cache上获得源服务器的数据,从而降低网络的访问时间。CDN的关键技术主要有负载均衡,内容存储和分发技术。

  • 负载均衡:使用整体性的网络负载均衡技术,通过内容路由器中的重定向(DNS)机制,在多个远程POP上均衡用户的请求,以使用户请求得到最近内容源的响应。

  • 内容分发:借助于建立索引、缓存、流分裂、组播(Multicast)等技术,将内容发布或投递到距离用户最近的远程服务点(POP)处。

  • 内容存储:在功能上包括对各种内容格式的支持,对部分缓存的支持;在性能上包括支持的容量、多文件吞吐率、可靠性、稳定性,都是存储需要考虑的问题。

6. 函数式编程
  • 函数式编程是种编程方式,它将电脑运算视为函数的计算。在函数编程中,函数是第一等公民,且该函数应该是一个纯函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。

  • 列如含有:

1.log

2.http请求

3.可变数据如new Date()

4.DOM操作

  • 纯函数带来的好处就是:更好的进行单元测试和调试,一对一的数据关系可以便于缓存。函数式编程还有其他特性:

1.闭包和高阶函数

2.惰性计算

3.递归

  • 函数式编程有两个最基本的运算:合成compose和柯里化curry。

结束语

2020前端面试就分享到这里,前端就是一个大杂烩,乱炖,需要会的、了解的东西太多了,学无止境,如果发现问题,欢迎评论区指正。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,828评论 6 526
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,669评论 3 412
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 174,467评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,238评论 1 306
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,039评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,561评论 1 319
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,658评论 3 433
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,806评论 0 285
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,316评论 1 329
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,293评论 3 353
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,443评论 1 364
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,998评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,698评论 3 342
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,097评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,333评论 1 281
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,012评论 3 385
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,434评论 2 370