JavaScript 和 ES6
在这个过程你会发现,有很多 JS 知识点你并不能更好的理解为什么这么设计,以及这样设计的好处是什么,这就逼着让你去学习这单个知识点的来龙去脉,去哪学?第一,书籍,我知道你不喜欢看,我最近通过刷大厂面试题整理了一份前端核心知识笔记,比较书籍更精简,一句废话都没有,这份笔记也让我通过跳槽从8k涨成20k。
如果你觉得对你有帮助,可以戳这里获取:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
2
3
4
5
6
7
8
workbox.precaching.preacheAndRoute([
‘/styles/index.0c9a31.css’,
‘/scripts/main.0d5770.js’,
{
url: ‘/index.html’,
revision: ‘383676’
},
]);
以上这段代码会在 Service Worker 安装成功的时候下载 /styles/index.0c9a31.css
, /scripts/main.0d5770.js
, /index.html
文件,并且会以构造路由的方式将这些文件都存储到 Cache Storage 中。这个传入的数组其实就是预缓存内容列表,有两种形式,一种是直接写文件名的字符串(带 Hash 的),一种是如下所示带有 url 和 revesion 值的对象:
1 |
2
3
4
{
url: '将要预缓存的文件 URL,
revision: ‘文件内容的 Hash 值’
}
这里需要注意的是这个 revision 的值,当预缓存的文件就任何变动的时候就会被更新,如果 revision 没有更新,那当你更新 Service Worker 的时候,被缓存的文件也不会被更新。
我们接下来介绍一些帮助我们生成预缓存内容列表的 precaching.precacheAndRoute
API 的配置选项。
处理 /
和 /index.html
通常当用户访问 /
时,对应的访问的页面 HTML 文件是 /index.html
,默认情况下,precache 路由机制会在任何 URL 的结尾的 /
后加上 index.html
,这就以为着你预缓存的任何 index.html
都可以通过 /index.html
或者 /
访问到。
当然,你也可以通过 directoryIndex
参数禁用掉这个默认行为:
1 |
2
3
4
5
6
7
8
9
10
11
12
13
workbox.precaching.preacheAndRoute(
[
‘/styles/index.0c9a31.css’,
‘/scripts/main.0d5770.js’,
{
url: ‘/index.html’,
revision: ‘383676’
},
],
{
directoryIndex: null
}
);
忽略请求参数
例如,如果我么想要让 key1
参数不是 /example.html?key1=1&key2=2
的一部分,你只需要设置 ignoreURLParametersMatching
参数把它排除掉:
1 |
2
3
4
5
6
7
8
9
10
11
workbox.precaching.precacheAndRoute(
[
{
url: ‘/example.html?key2=2’,
revision: ‘6821e0’
}
],
{
ignoreUrlParametersMatching:[/key1/],
}
);
这样 /example.html?key1=1&key2=2
这个路由对应的内容就可以被预缓存了。
生成预缓存列表
靠手动维护 precache.precacheAndRoute
API 中的预缓存内容列表是不可能的,revision 我们无法手动维护,所以我们肯定是要借助一些工具来干这个事情,好在 workbox 提供了多种方式让我们选择:
-
workbox 命令行
-
workbox-build npm 包
-
workbox-webpack-plugin
在使用以上三种方式生成预缓存内容列表之前,我们先预设一下应用场景:假设你的项目在目录 /app
下,必须保证在你的项目根目录下有一个 app/sw.js
包含以下内容:
1 |
2
// 通常项目中的 sw.js 源文件都是通过这样预留一个空数组的方式来预缓存内容列表的
workbox.precaching.precacheAndRoute([]);
这样才能保证能将生成的预缓存内容列表内容注入到 Service Worker 文件中。
workbox 命令行
workbox 提供了一套命令行,专门来帮助我们注入预缓存内容列表,可以帮助我们生成注入预缓存内容列表所需要的 workbox-cli-config.js
配置文件,然后通过命令行使用配置文件就可以生成一个预缓存列表的代码并注入到之前自定义的 app/sw.js
文件中,最终编译成线上所需要的 Service Worker 文件:dist/sw.js
。
安装命令行:
1 |
npm install -g workbox-cli
所以我们先要有一个 workbox-cli-config.js
文件,然后根据这个文件结合 /app/sw.js
源文件来生成一个含有预缓存列表的 dist/sw.js
文件。接下来我们可以看下具体这一系列流程需要怎么操作。
首先执行 workbox generate:sw
命令生成一个配置文件 workbox-cli-config.js
,这个命令执行之后将会弹出一些问题让你选择,如下所示:
1 |
2
3
4
5
>$ workbox generate:sw
? What is the root of your web app? app
? Which file types would you like to cache? (Press <space> to select, <a> to toggle all, <i> to inverse selection)html,ico,svg,png,js,css
? What should the path of your new service worker file be (i.e. ‘./build/sw.js’)? dist/sw.js
? Last Question - Would you like to save these settings to a config file? Yes
回答完这些问题之后你就可以在你的项目里导出一个新的 workbox-cli-config.js
配置文件了大概长以下这个样子:
1 |
2
3
4
5
6
7
8
9
// workbox-config.js
module.exports = {
“globDirectory”: “app/”,
“globPatterns”: [
“**/*.{html,ico,svg,png,js,css}”
],
“swDest”: “dist/sw.js”,
“swSrc”: “app/sw.js”
};
拿到了 workbox-cli-config.js
配置文件之后可以执行 workbox inject:manifest workbox-cli-config.js
命令生成编译后的 dist/sw.js
文件了,这一步干的事情就是把预缓存内容列表注入到 app/sw.js
中,一般只有在上线前才用命令行注入预缓存内容列表,通常我们都不会手动去执行这些命令的,比较合理的做法是实现生成好 workbox-cli-config.js
文件,然后在构建脚本中配置上自动执行 workbox inject:manifest
命令。
workbox inject:manifest
命令的做法就是去匹配/app/sw.js
中的workbox.precaching.precacheAndRoute([])
方法的正则,然后通过 replace 内容注入的,可以参考下面的 workbox-build 的介绍。
workbox-build
使用命令行总感觉太傻了,操作步骤也比较繁琐,为了使得预缓存工作更加简便灵活,workbox 也提供了一个 NPM 包 – workbox-build,你可以在任何构建工具中都使用。
可以在你的工程根目录中执行以下命令安装 workbox-build 包:
1 |
npm install --save-dev workbox-build
然后直接可以在你想要处理 Service Worker 预缓存的地方引入 workbox-build 库,并且调用其 injectManifest
方法:
1 |
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const workboxBuild = require(‘workbox-build’);
workboxBuild.injectManifest({
swSrc: path.join(__dirname, ‘app’, ‘sw.js’),
swDest: path.join(__dirname, ‘dist’, ‘sw.js’),
globDirectory: ‘./dist/’,
globPatterns: [‘**/*.{html,js,css}’],
globIgnores: [‘admin.html’],
templatedUrls: {
‘/shell’: [‘dev/templates/app-shell.html’, ‘dev/**/*.css’],
},
// 要替换的预留代码区正则
injectionPointRegexp: /(.precacheAndRoute()\s*[\s*]\s*())/,
})
.catch(err => {
console.error(Unable to inject the precache manifest into sw.js
);
throw err;
});
在构建文件中执行这段代码就会读取 app/sw.js
文件然后生成一个 dist/sw.js
文件含有注入的预缓存内容列表。
关于如何使用
injectManifest
方法可以查看 workbox-build 的injectManifest
方法 全部参数
workbox-webpack-plugin
有了 workbox-build 可想而知就能搞出很多实用的预缓存插件方案,比如 Webpack、Gulp 等插件,workbox 官方也提供了一个插件 workbox-webpack-plugin
,只需要通过以下方式,就可以将插件安装到你的 webpack 项目中:
1 |
npm install --save-dev workbox-webpack-plugin
然后就只需要将插件添加到 Webpack 配置中就可以正常使用了,插件参数和 workbox-build 的 injectManifest
方法保持一致:
1 |
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const workboxPlugin = require(‘workbox-webpack-plugin’);
// …
webpack({
plugins: [
// …
new workboxPlugin({
swSrc: ‘./src/sw.js’,
swDest: ‘./dist/sw.js’,
globDirectory: ‘./dist/’,
globPatterns: [‘**/*.{html,js,css}’],
})
]
// …
});
这里有个地方和之前提到的替换预留的 app/sw.js
不一样:使用 workbox 提供的 Webpack 插件必须在 app/sw.js
中包含以下代码才能完成预缓存内容列表注入工作
1 |
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
当插件跑起来之后,会在 /dist/sw.js
中增加一段 importScripts()
代码用来引入一个模块,这个模块的内容就是 self.__precacheManifest
,也就是预缓存内容列表的内容,具体的效果可以在项目中使用 workbox-webpack-plugin
尝试一下,看看 build 后的 dist/sw.js
结果就会比较清楚了。
路由请求缓存
路由请求缓存是通过文件路由匹配的模式分别对制定的路由文件做不同策略缓存的方式,这部分工作可以在 app/sw.js
中直接使用 workbox 提供的 workbox.routing.registerRoute
API 完成,这个 API 可以理解为干了两件事情,一、通过请求路由配置匹配到指定待缓存文件或请求内容,二、通过第二个参数的处理回调函数决定用何种策略来缓存匹配上的文件。有 三种 方法可以通过 workbox-route 来匹配一个请求 URL
- 字符串方式
1 |
2
3
4
5
6
7
8
9
10
11
// 可以直接是当前项目下的绝对路径
workbox.routing.registerRoute(
‘/logo.png’,
handler // handler 是做缓存策略的回调函数,通常指后面所会降到的 ‘缓存策略函数’
);
// 也可以是完整的带完整 host 的 URL 路径,这里的 URL 必须是 https 的
workbox.routing.registerRoute(
‘https://some-host/some-path/logo.png’,
handler
);
- 正则表达式方式
1 |
2
3
4
workbox.routing.registerRoute(
new RegExp(‘.*.js’), // 这里是任何正则都行,只要能匹配得上的请求路由地址
handler
);
- 回调函数方式
1 |
2
3
4
5
6
7
8
9
10
// 通过回调函数来匹配请求路由将会让策略更加灵活
const matchFunction = ({url, event}) => {
// 如果请求路由匹配了就返回 true,也可以返回一个参数对象以供 handler 接收处理
return false;
};
workbox.routing.registerRoute(
matchFunction,
handler
);
上面讲到了匹配请求路由的三种方式,接下来可以讲讲如何处理匹配上的请求所返回的内容,也就是上面三种路由匹配方式的 handler,通常有两种做法:
-
使用一种 workbox 通过
workbox.strategies
API 提供的 缓存策略。 -
提供一个自定义返回带有返回结果的 Promise 的回调方法。
路由请求缓存策略
下面介绍一下 workbox 默认提供的几种缓存策略 API,这些 API 可以被当成 handler 使用。
Stale While Revalidate
这种策略的意思是当请求的路由有对应的 Cache 缓存结果就直接返回,在返回 Cache 缓存结果的同时会在后台发起网络请求拿到请求结果并更新 Cache 缓存,如果本来就没有 Cache 缓存的话,直接就发起网络请求并返回结果,这对用户来说是一种非常安全的策略,能保证用户最快速的拿到请求的结果,但是也有一定的缺点,就是还是会有网络请求占用了用户的网络带宽。可以像如下的方式使用 State While Revalidate
策略:
1 |
2
3
4
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.staleWhileRevalidate()
);
Network First
这种策略就是当请求路由是被匹配的,就采用网络优先的策略,也就是优先尝试拿到网络请求的返回结果,如果拿到网络请求的结果,就将结果返回给客户端并且写入 Cache 缓存,如果网络请求失败,那最后被缓存的 Cache 缓存结果就会被返回到客户端,这种策略一般适用于返回结果不太固定或对实时性有要求的请求,为网络请求失败进行兜底。可以像如下方式使用 Network First
策略:
1 |
2
3
4
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.networkFirst()
);
Cache First
这个策略的意思就是当匹配到请求之后直接从 Cache 缓存中取得结果,如果 Cache 缓存中没有结果,那就会发起网络请求,拿到网络请求结果并将结果更新至 Cache 缓存,并将结果返回给客户端。这种策略比较适合结果不怎么变动且对实时性要求不高的请求。可以像如下方式使用 Cache First
策略:
1 |
2
3
4
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.cacheFirst()
);
Network Only
比较直接的策略,直接强制使用正常的网络请求,并将结果返回给客户端,这种策略比较适合对实时性要求非常高的请求。可以像如下方式使用 Network Only
策略:
1 |
2
3
4
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.networkOnly()
);
Cache Only
这个策略也比较直接,直接使用 Cache 缓存的结果,并将结果返回给客户端,这种策略比较适合一上线就不会变的静态资源请求。可以像如下方式使用 Cache Only
策略:
1 |
2
3
4
workbox.routing.registerRoute(
match, // 匹配的路由
workbox.strategies.networkOnly()
);
无论使用何种策略,你都可以通过自定义一个缓存来使用或添加插件(后面我们会介绍 workbox 插件)来定制路由的行为(以何种方式返回结果)。
1 |
2
3
4
5
6
7
8
9
workbox.strategies.staleWhileRevalidate({
// 使用用户自定义的缓存名称
cacheName: ‘my-cache-name’,
// 使用 workbox 插件
plugins: [
// …
]
});
当然,这些配置通常需要在缓存请求时更安全,也就是说,需要限制缓存的时间或者确保设备上用的数据是被限制的。
自定义策略
如果以上的那些策略都不太能满足你的请求的缓存需求,那就得想想办法自己定制一个合适的策略,甚至是不同情况下返回不同的请求结果,workbox 也考虑到了这种场景(这也是为什么我会极力推荐 workbox 的原因),当然,最简单的方法是直接在 Service Worker 文件里通过最原始的 fetch
事件控制缓存策略。也可以使用 workbox 提供的另一种方式:传入一个带有对象参数的回调函数,对象中包含匹配的 url
以及请求的 fetchEvent
参数,回调函数返回的就是一个 response 对象,具体用法如下所示:
1 |
2
3
4
5
6
7
8
9
10
11
12
13
14
workbox.routing.registerRoute(
({url, event}) => {
return {
name: ‘workbox’,
type: ‘guide’,
};
},
({url, event, params}) => {
// 返回的结果是:A guide on workbox
return new Response(
A ${params.type} on ${params.name}
);
}
);
第三方请求的缓存
如果有些请求的域和当前 Web 站点不一致,那可以被认为是第三方资源或请求,针对第三方请求的缓存,因为 Workbox 无法获取第三方路由请求的状态,当请求失败的情况下 workbox 也只能选择缓存错误的结果,所以 workbox 3 原则上默认不会缓存第三方请求的返回结果。也就是说,默认情况下如下的缓存策略是不会生效的:
1 |
2
3
4
workbox.routing.registerRoute(
‘https://notzoumiaojiang.com/example-script.min.js’,
workbox.strategies.cacheFirst(),
前端资料汇总
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
我一直觉得技术面试不是考试,考前背背题,发给你一张考卷,答完交卷等通知。
首先,技术面试是一个 认识自己 的过程,知道自己和外面世界的差距。
更重要的是,技术面试是一个双向了解的过程,要让对方发现你的闪光点,同时也要 试图去找到对方的闪光点,因为他以后可能就是你的同事或者领导,所以,面试官问你有什么问题的时候,不要说没有了,要去试图了解他的工作内容、了解这个团队的氛围。
找工作无非就是看三点:和什么人、做什么事、给多少钱,要给这三者在自己的心里划分一个比例。
最后,祝愿大家在这并不友好的环境下都能找到自己心仪的归宿。