原传送门:https://blog.csdn.net/qq_39025670/article/details/106240355
?? ??? ???
一. 深拷贝,浅拷贝
深度复制是针对参考类型的。浅复制只是复制对象的参考。如果复制对象发生变化,原始对象也会发生变化。只有深度复制才是对象的真实复制。
1.浅拷贝
浅拷贝只是复制引用,没有真正复制。在这里,无论我们改变原始值还是克隆值,它们都会相互影响,没有隔离
var arr = [1,2,3]; var obj = {
a:'a', b:[1,2], c:{
cc:'cc'}}; var cloneArr = arr; var cloneObj = obj; cloneArr.push(4); cloneObj.a = 'aaa'; console.log(arr); // [1, 2, 3, 4] console.log(cloneArr); // [1, 2, 3, 4] console.log(obj); // {a:'aaa', b:[1,2], c:{cc:'cc'}} console.log(cloneObj); // {a:'aaa', b:[1,2], c:{cc:'cc'}}
2.深拷贝
深拷贝是完全拷贝,它们相互隔离,不相互影响。
- 利用 JSON 对象中的 parse 和 stringify
- 利用递归实现对象的重建和赋值
var arr = [1,2,3]; var obj =
a:'a', b:[1,2], c:{
cc:'cc'}};
var cloneArr = JSON.parse(JSON.stringify(arr));
var cloneObj = JSON.parse(JSON.stringify(obj));
cloneArr.push(4);
cloneObj.a = 'aaa';
cloneObj.b.push(3);
cloneObj.c = 'ccc';
console.log(arr); // [1, 2, 3]
console.log(cloneArr); // [1, 2, 3, 4]
console.log(obj); // {a:'a', b:[1,2], c:{cc:'cc'}}
console.log(cloneObj); // {a:'aaa', b:[1,2,3], c:{cc:'ccc'}}
JSON 对象中的 parse 和 stringify方法确实实现了深拷贝,也是我们最常用的一种方法,但对于一些复杂的引用类型,就存在问题
var obj = {
name: 'Tom',
sayName: function(){
console.log(this.name)
}
}
var cloneObj = JSON.parse(JSON.stringify(obj));
console.log(obj); // {name: "Tom", sayName: ƒ}
console.log(cloneObj); // {name: "Tom"}
我们发现,它并没有将方法复制下来。原因是: 会在被
利用递归实现深拷贝的思想是
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {
}; // 判断复制的目标是数组还是对象
for(let keys in source){
if(source.hasOwnProperty(keys)){
// 判断属性是否存在于实例中
if(source[keys] && typeof source[keys] === 'object'){
// 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {
};
targetObj[keys] = deepClone(source[keys]);
}else{
// 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
// 测试
var obj = {
name: 'Tom',
sayName: function(){
console.log(this.name)
}
}
var cloneObj = deepClone(obj);
console.log(obj); // {name: "Tom", sayName: ƒ}
console.log(cloneObj); // {name: "Tom", sayName: ƒ}
在来测测最开始的例子
var arr = [1,2,3];
var obj = {
a:'a', b:[1,2], c:{
cc:'cc'}};
var cloneArr = deepClone(arr);
var cloneObj = deepClone(obj);
cloneArr.push(4);
cloneObj.a = 'aaa';
cloneObj.b.push(3);
cloneObj.c = 'ccc';
console.log(arr); // [1, 2, 3]
console.log(cloneArr); // [1, 2, 3, 4]
console.log(obj); // {a:'a', b:[1,2], c:{cc:'cc'}}
console.log(cloneObj); // {a:'aaa', b:[1,2,3], c:{cc:'ccc'}}
JavaScript中的有的方法也能实现拷贝,比如:concat()和slice(),ES6中的Object.assgin()和…展开运算符。这里就不一一测试了,直接给出结论吧。
(1)、concat 只是对数组的第一层进行深拷贝 (2)、slice 只是对数组的第一层进行深拷贝 (3)、Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值 (4)、… 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值
二、性能优化
1、DNS(域名解析系统)将 URL 解析为对应的 IP 地址,建立起 TCP 网络连接(三次握手) 2、发送 HTTP 请求 3、接受 HTTP 响应 4、渲染页面,构建DOM树 5、关闭TCP连接(四次挥手)
1、前端性能优化比较常见的一些点:
- 使用缓存(最重要的一点),cookie与WebStorage
- 减少http请求
- 文件压缩合并:html,js,css压缩。删除一些无用代码:注释
- 图片无损压缩,安卓下可以使用webp格式图片
- 使用字体图标,矢量图svg,雪碧图,base64
- js文件一般放在页面底部,若放在head里一般要在 script 标签上加 async 或者 defer 进行异步加载
- 懒加载,预加载
- 服务端渲染(SSR),增强页面的呈现速度,同时能增强SEO,但页面切换不如单页应用(SPA)流畅
- 减少dom操作,规避重绘与回流;更改css属性较多时,以类名的形式操作dom;有复杂动画的DOM,可以考虑使其脱离文档流,从而减少重绘与回流的范围
- 事件的节流与防抖
- 事件委托。利用事件冒泡,通过指定一个事件处理程序,来管理某一类型的所有事件。这样能减少页面消耗的内存
- 启用DNS预解析。遇到网页中的超链接,DNS prefetching从中提取域名并将其解析为IP地址,这些工作在用户浏览网页时,使用最少的CPU和网络在后台进行解析
- 若使用了闭包,当功能完成后,最好将其置null,以释放内部变量占用的内存
- (1)、利用缓存,精简请求 把基本不会经常改变的数据,比如:用户基本信息等,在进入页面时候就请求,之后都可以在缓存中获取 (2)、合并压缩 压缩资源可以在webpack中设置,在build的时候合并压缩。 (3)、静态资源上传cdn 一些比较大且不会经常变化的静态资源文件,可以上传的CDN上,使用时候,在项目中可以直接下载然后在HTML上引用,具体实现思路是在npm run build之后,执行额外的upload.js,服务器部署的时候只需要部署三个html文件就可以了 (4)、避免高频刷新页面获取数据 可以做了一个限定,比如5秒内刷新页面只获取一次列表数据,避免高频刷新带给服务器的压力
async init() {
try {
const store = JSON.parse(util.getStore('hopoActiveInfo'))
// 避免高频刷新增加服务器压力
if (store && (new Date() - new Date(store.getTime)) < 5000) {
this.basicInfo = store
} else {
this.basicInfo = await getActiveInfo()
this.basicInfo.getTime = new Date()
}
util.setStore(this.basicInfo, 'hopoActiveInfo')
this.btn.noPeopleAndStart.detail[0].text = `${
this.basicInfo.directBuyPrice } 元直接购买`
this.computedStatus()
} catch (error) {
console.log(error)
}
},
(5)、设置响应头cache-control和last-modified 对于所有的数据和接口设置响应头,利用express模拟,如果两次请求间隔小于5秒,直接返回304,不需要服务器进行处理
app.all('*', function(req, res, next){
res.set('Cache-Control','public,max-age=5')
if ((new Date().getTime() - req.headers['if-modified-since'] )< 5000) {
// 检查时间戳
res.statusCode = 304
res.end()
}
else {
var time =(new Date()).getTime().toString()
res.set('Last-Modified', time)
}
next()
})
四.HTTP/HTTPS 网络/通信
1.GET 和 POST 的区别
- GET 在浏览器回退时是无害的,而 POST 会再次提交请求
- GET 产生的 URL 地址可以被收藏,而 POST 不可以
- GET 请求会被浏览器主动缓存,而 POST 不会,除非手动设置
- GET 请求只能进行 url 编码,而 POST 支 持多种编码方式
- GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留
- GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有限制对参数的数据类型
- GET 只接受 ASCII 字符,而 POST 没有限制
- GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息
- GET 参数通过 URL 传递,POST 放在 Request body 中
2.HTTP方法
- GET 获取资源
- POST 传输资源
- PUT 更新资源
- DELETE 删除资源
- HEAD 获得报文首部
3.HTTP状态码
- 1xx:指示信息-表示请求已接收,继续处理
- 2xx:成功-表示请求已被成功接收
- 3xx:重定向-要完成请求必须迸行更迸一步的操作
- 4xx:客戸端错误-请求有语法错误或请求无法实现
- 5xx:服各器错误-服务器未能实现合法的请求
- 200 OK:客户端请求成功
- 206 Partial Content:客户发送了一个带有Range头的GET请求,服务器完成了它
- 301 Moved Permanently:所请求的页面已经转移至新的url
- 302 Found:所请求的页面已经临时转移至新的url
- 304 Not Modified:客户端有缓冲的文档并发出了一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用
- 400 Bad Request:客户端请求有语法错误,不能被服务器所理解
- 401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
- 403 Forbidden:对被请求页面的访问被禁止
- 404 Not Found:请求资源不存在
- 500 Internal Server Error:服务器发生不可预期的错误,原来缓冲的文档还可以继续使用
- 503 Server Unavailable:请求未完成,服务器临时过载或当机,一段时间后可能恢复正常
4.前后端如何通信
- Ajax
- WebSocket
- CORS
5.Ajax
Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
Ajax 常见应用:运用 XMLHttpRequest 或新的 Fetch API 与网页服务器进行异步资料交换。
这里我们手写一个简单的 XHR 请求过程
function request() {
// 1.获取一个 XHR 实例
const xhr = new XMLHttpRequest()
// 2.初始化请求
xhr.open('POST', 'http://192.168.3.195:8088/setUsername', true)
// 3.事件处理器 在每次 readyState 属性变化时被自动调用
xhr.onreadystatechange = function() {
// 请求完成且状态为 200
if (xhr.readyState == 4 && xhr.status == 200) {
// 成功
// 得到服务端返回的数据
let res = JSON.parse(xhr.responseText)
} else {
// 其他情况
}
}
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8')
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*')
// 允许跨域
xhr.withCredentials = true
// 设置发送的数据
const requestData = JSON.stringify({
username: 'Tom'
})
// 4.发送请求
xhr.send(requestData)
}
6.WebSocket
是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
常见应用:客服系统、物联网数据传输系统等。
推荐教程:WebSocket 教程 - 阮一峰
7.CORS
CORS 全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了AJAX只能同源使用的限制。
8.跨域通信及其方式**
协议、IP和端口只要其中之一不同就算跨域。
- Jsonp
- Hash
- postMessage
- WebSocket
- CORS
9.HTTPS
HTTPS 是在 HTTP 上建立 SSL 加密层,并对传输数据进行加密,是 HTTP 协议的安全版。
(1)对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全; (2)对网站服务器进行真实身份认证。
10.HTTPS 与 HTTP 的区别:
- HTTP 是明文传输协议,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全;
- HTTPS 比 HTTP 更加安全,对搜索引擎更友好,利于 SEO,谷歌、百度优先索引 HTTPS 网页;
- HTTPS 需要用到 SSL 证书,而 HTTP 不用;
- HTTPS 标准端口 443,HTTP 标准端口 80;
- HTTPS 基于传输层,HTTP 基于应用层;
- HTTPS 在浏览器显示绿色安全锁,HTTP 没有显示。
参考文章:深入理解HTTPS工作原理
五.前端安全
1.CSRF,跨站请求伪造
- CSRF即Cross-site request forgery(跨站请求伪造),是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
- 添加校验token。系统开发人员可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务端进行token校验,如果请求中没有token或者token内容不正确,则认为是CSRF攻击而拒绝该请求。
- 尽量使用POST,限制GET。POST请求有一定作用,但并不一定完全安全。
- 检查Referer字段。通过验证请求头的来验证来源站点,但请求头很容易伪造。
2.XSS,跨域脚本攻击
跨站脚本攻击是指恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
- 设置httpOnly:很多XSS攻击目标都是窃取用户cookie伪造身份认证,设置此属性可防止JS获取cookie
- 开启CSP,即开启白名单,可阻止白名单以外的资源加载和运行
- 输入检查:对输入内容中 < script>< iframe>等标签进行转义或者过滤
六.浏览器渲染机制
1.浏览器渲染原理与过程
主要包括以下五步:
- 浏览器将获取的HTML文档解析成DOM树
- 处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)
- 将DOM和CSSOM合并为渲染树(rendering tree)
- 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有的元素
- 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制paintin
内容解释 1、HTML parser:HTML解析器,其本质是将HTML文本解释成DOM tree。 2、CSS parser:CSS解析器,其本质是讲DOM中各元素对象加入样式信息。 3、JavaScript引擎:专门处理JavaScript脚本的虚拟机,其本质是解析JS代码并且把逻辑(HTML和CSS的操作)应用到布局中,从而按程序要的要求呈现相应的结果 4、DOM tree:文档对象模型树,也就是浏览器通过HTMLparser解析HTML页面生成的HTML树状结构以及相应的接口。 5、render tree:渲染树,也就是浏览器引擎通过DOM Tree和CSS Rule Tree构建出来的一个树状结构,和dom tree不一样的是,它只有要最终呈现出来的内容,像或者带有display:none的节点是不存在render tree中的。 6、layout:也叫reflow 重排,渲染中的一种行为。当rendertree中任一节点的几何尺寸发生改变了,render tree都会重新布局。 7、repaint:重绘,渲染中的一种行为。render tree中任一元素样式属性(几何尺寸没改变)发生改变了,render tree都会重新画,比如字体颜色、背景等变化。
2.重排 Reflow(也叫回流)
DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为reflow。
3.触发Reflow
- 当你增加、删除、修改DOM结点时,会导致Reflow或Repaint
- 当你移动DOM的位置,或是搞个动画的时候
- 当你修改CSS样式的时候
- 当你Resize窗口的时候(移动端没有这个问题), 或是滚动的时候
- 当你修改网页的默认字体时
4.重绘 Repaint
当我们改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸和位置没有发生改变,这个称为repaint。。
5.回流与重绘的区别:
- 回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流。
- 当页面布局和几何属性改变时就需要回流,比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变。
6.应该尽量减少回流和重绘,那么怎样优化浏览器渲染过程
其实优化就是减少对render tree的操作,并减少对一些style信息的请求,尽量利用好浏览器的优化策略。
- 用 transform 做形变和位移可以减少 reflow
- 避免逐个修改节点样式,尽量一次性修改
- 使用 DocumentFragment 将需要多次修改的 DOM 元素缓存,最后一次性 append 到真实 DOM 中渲染
- 可以将需要多次修改的 DOM 元素设置 display:none,操作完再显示。(因为隐藏元素不在 render 树内,因此修改隐藏元素不会触发回流重绘)
- 避免多次读取某些属性
- 通过绝对位移将复杂的节点元素脱离文档流,形成新的 Render Layer,降低回流成本
- 不要一个一个改变元素的样式属性,最好直接改变className,但className是预先定义好的样式,不是动态的,如果你要动态改变一些样式,则使用cssText来改变
- DOM的增删行为让操作元素离线处理,即使用documentFragment或div等元素进行缓存操作,先把所有要添加到元素添加到1个div,最后才把这个div append到body中
- 先display:none 隐藏元素,然后对该元素进行所有的操作,最后再显示该元素。因对display:none的元素进行操作不会引起回流、重绘。
- 将引起回流的属性赋值给变量,进行缓存,需要用到的时候直接使用变量就行。例如获取一个元素的scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft、offsetWidth、offsetHeight之类的属性,浏览器为了保证值的正确也会回流取得最新的值,所以如果你要多次操作,最取完做个缓存。
- 减少操作影响的节点,影响的节点越多,则越消耗性能。
七.箭头函数与普通函数区别
箭头函数是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几点差异: 1、函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。 2、不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。 3、不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。 4、不可以使用 new 命令,因为:没有自己的 this,无法调用 call,apply;没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 _ proto_
八.webpack
1.webpack是什么?
- 一种前端资源构建工具,一个静态模块打包器(nodule bundle)
- 前端所有资源文件(js/json/css/img…)都会作为模块处理
- 它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)
2. webpack与grunt、gulp的不同?
-
grunt和gulp是基于任务流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程
-
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能
3. 什么是chunk?什么是bundle?
-
首先告诉 Webpack 一个入口文件,如 index.js为起点作为打包,将入口文件的所有依赖项引入进来,这些依赖会跟入口文件形成一个文件(代码块),这个文件(代码块)就是 chunk
-
将这个代码块(chunk)进行处理,比如把 less 文件编译成 css,js 资源编译成浏览器能识别的 js语法等等操作,这些就叫做打包,将打包好的资源再输出出去,这个输出的文件就叫 bundle
4. Webpack 五个核心概念分别是什么?
入口(Entry)指示 Webpack 以哪个文件为入口起点开始打包,分析内部构件依赖图
输出(Output)指示 Webpack 打包后的资源 bundles 输出到哪里去,以及如何命名
Loader 能让 Webpack 处理非 JavaScript/json 文件(Webpack 自身只能处理 JavaScript/json )
插件(Plugins)可以用于执行范围更广的任务,包括从打包优化和压缩到重新定义环境中的变量
模式(Mode)指示 Webpack 使用相应模式的配置,只有development(开发环境)和production(生产环境)两种模式
5. 有哪些常见的Loader?它们是解决什么问题的?
- css-loader:将 css 文件变成 CommonJS 模块加载 js 中,里面内容是样式字符串
- style-loader:创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
- url-loader:在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
- file-loader:打包其他资源(除了css/js/html 资源)
- html-loader:处理 html 文件中的 img
- babel-loader:把 ES6 转换成 ES5
- eslint-loader:通过 ESLint 检查 JavaScript 代码
6. 有哪些常见的Plugin?它们是解决什么问题的?
- html-webpack-plugin:可以复制一个有结构的html文件,并自动引入打包输出的所有资源(JS/CSS)
- clean-webpack-plugin:重新打包自动清空 dist 目录
- mini-css-extract-plugin:提取 js 中的 css 成单独文件
- optimize-css-assets-webpack-plugin:压缩css
- uglifyjs-webpack-plugin:压缩js
- commons-chunk-plugin:提取公共代码
7.webpack的构建流程是什么?
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
- 确定入口:根据配置中的 entry 找出所有的入口文件;
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
8.webpack的热更新是什么?
热更新又称热替换(Hot Module Replacement),缩写为HMR,基于devServer,生产环境不需要devServer,所以生产环境不能用HMR功能
优化打包构建速度,一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
- 样式文件:可以使用HMR功能,因为style-loader内部实现了
- JS文件:默认没有HMR功能,需要修改js代码,添加支持HMR功能。入口文件做不了HMR功能,只能处理非入口js文件
- HTML文件:默认没有HMR功能,同时会导致 html 文件不能热更新(即修改没有任何反应)
修改entry入口,将html文件引入
entry:['./src/js/index.js','./src/index.html']
不用做HMR功能,因为只有一个html文件
9.如何利用webpack来优化前端性能?
- 开启HMR功能,优化打包构建速度
- 配置 devtool: ‘source-map’,优化代码运行的性能
- oneOf 优化 默认情况下,假设设置了7、8个loader,每一个文件都得通过这7、8个loader处理(过一遍),浪费性能,使用 oneOf找到了就能直接用,提升性能
- 开启 babel 缓存 当一个 js 文件发生变化时,其它 js 资源不用变
- code split 分割 将js文件打包分割成多个bundle,避免体积过大
- 懒加载和预加载
- PWA 网站离线访问
- 多进程打包 开启多进程打包,主要处理js文件(babel-loader干的活久),进程启动大概为600ms,只有工作消耗时间比较长,才需要多进程打包,提升打包速度
- dll 打包第三方库
- code split将第三方库都打包成一个bundle,这样体积过大,会造成打包速度慢
- dll 是将第三方库打包成多个bundle,从而进行速度优化
10.npm打包时需要注意哪些?如何利用webpack来更好的构建?
- 要支持CommonJS模块化规范,所以要求打包后的最后结果也遵守该规则
- Npm模块使用者的环境是不确定的,很有可能并不支持ES6,所以打包的最后结果应该是采用ES5编写的。并且如果ES5是经过转换的,请最好连同SourceMap一同上传
- Npm包大小应该是尽量小(有些仓库会限制包大小)
- 发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况
- UI组件类的模块应该将依赖的其它资源文件,例如.css文件也需要包含在发布的模块里
- CommonJS模块化规范的解决方案:设置output.libraryTarget='commonjs2’使输出的代码符合CommonJS2模块化规范,以供给其它模块导入使用
- 输出ES5代码的解决方案:使用babel-loader把 ES6 代码转换成 ES5 的代码。再通过开启devtool: 'source-map’输出SourceMap以发布调试。
- Npm包大小尽量小的解决方案:Babel 在把 ES6 代码转换成 ES5 代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改.babelrc文件,为其加入transform-runtime插件
- 不能将依赖模块打包到NPM模块中的解决方案:使用externals配置项来告诉webpack哪些模块不需要打包。
- 对于依赖的资源文件打包的解决方案:通过css-loader和extract-text-webpack-plugin来实现
11.hash、chunkhash、contenthash三者的区别?
浏览器访问网站后会强缓存资源,第二次刷新就不会请求服务器(一般会定个时间再去请求服务器),假设有了bug改动了文件,但是浏览器又不能及时请求服务器,所以就用到了文件资源缓存(改变文件名的hash值)
- hash:不管文件变不变化,每次wepack构建时都会生成一个唯一的hash值
- chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样问题:js和css同属于一个chunk,修改css,js文件同样会被打爆
- contenthash:根据文件的内容生成hash值。不同文件hash值一定不一样