webpack学习笔记
manifest
记录各个文件的identify id,在浏览器端控制页面资源加载,import和require都转换为_webpack_require_
,通过使用 manifest 中的数据,runtime 将能够查询模块标识符,检索出背后对应的模块。
使用<script>
标签在Index.html
文件中引入包的缺点
- 无法立即体现,脚本的执行依赖于外部扩展库(external library)。
- 如果依赖不存在,或者引入顺序错误,应用程序将无法正常运行。
- 如果依赖被引入但是并没有使用,浏览器将被迫下载无用代码。
注意,webpack 不会更改代码中除 import
和 export
语句以外的部分
通过 package.json 的 "sideEffects"
属性,来实现项目文件是否包含为引用函数(副作用)。告知webpack是否在打包时安全删除这些函数
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js",
"*.css"
]
}
任何导入的文件都会受到 tree shaking 的影响。这意味着,如果在项目中使用类似
css-loader
并导入 CSS 文件,则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除
import()
调用会在内部用到 promises。
性能优化
把项目中的第三方库在打包时提取到一个单独的js中,方便客户端缓存,减少下次请求
shimming全局变量
const path = require('path');
+ const webpack = require('webpack');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
- }
+ },
+ plugins: [
+ new webpack.ProvidePlugin({
+ _: 'lodash'
+ })
+ ]
};
构建性能
- 保持webpack,node,npm或yarn的最新版本
- 将Loader应用于最少数的必要模块中
- 每个额外的 loader/plugin 都有启动时间。尽量少使用不同的工具。
- 使用
DllPlugin
将更改不频繁的代码进行单独编译。 - 减少编译的整体大小,以提高构建性能。尽量保持 chunks 小巧。
thread-loader
可以将非常消耗资源的 loaders 转存到 worker pool 中- 使用
cache-loader
启用持久化缓存。使用package.json
中的"postinstall"
清除缓存目录。
对development特别有用的方案
- 增量编译。使用监听模式
- 在内存中编译。
- devtools选用
- 避免在生产环境下才会用到的工具
- 最小化chunk入口
- 避免额外的优化步骤
- ts-loader 开启transpileOnly
对poduction特别有用的方案
优化代码质量在大多数情况下比构建性能更重要。
- Source maps 真的很消耗资源。你真的需要他们?
内容安全策略
目的
CSP 的主要目标是减少和报告 XSS 攻击 。一个CSP兼容的浏览器将会仅执行从白名单域获取到的脚本文件,忽略所有的其他脚本 (包括内联脚本和HTML的事件处理属性)。
用法
为使CSP可用,你需要配置你的网络服务器返回Content-Security-Policy
<meta>
元素也可用来配置该策略
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
Content-Security-Policy-Report-Only不强制执行策略,只发送报告
nonce
-
后台生成nonce,至少128位数据的随机base64编码字符串
-
设置在对应的script/style nonce属性上
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">…</script>
-
将第一步后台生成的nonce增加一个
nonce-
前缀,然后附加在后端生成的CSP 标头上Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
webpack能够为所有的脚本添加nonce,启用此功能,需要在引入的入口脚本中设置一个 __webpack_nonce__
变量。
在 entry 文件中:
// ...
__webpack_nonce__ = 'c29tZSBjb29sIHN0cmluZyB3aWxsIHBvcCB1cCAxMjM=';
// ...
公共路径
publicPath能帮助你为项目中的所有资源指定一个基础路径
Service Worker
什么是Service Worker
Service Worker 是浏览器在后台独立于网页运行的脚本,它打开了通向不需要网页或用户交互的功能的大门。
service worker生命周期独立于网页
- 在页面javaScript中注册
- 浏览器在后台启动服务工作线程安装步骤。在安装过程中,您通常需要缓存某些静态资产。如果所有文件缓存成功,则安装成功。
- 激活。这是管理旧缓存的绝佳机会
- Service Worker 将会对其作用域内的所有页面实施控制,不过,首次注册该 Service Worker 的页面需要再次加载才会受其控制。
使用条件
- 浏览器支持
- 在开发过程中,可以通过
localhost
使用 Service Worker,但如果要在网站上部署 Service Worker,则需要在服务器上设置 HTTPS。
注册
if ('serviceWorker' in navigator)
window.addEventListener('load', function () {
navigator.serviceWorker.register('/sw.js').then(function (registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function (err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
})
})
sw.js文件位于根网域,Service Worker 将接收此网域上所有事项的
fetch
事件。如果我们在/example/sw.js
处注册 Service Worker 文件,则 Service Worker 将只能看到网址以/example/
开头(即/example/page1/
、/example/page2/
)的页面的fetch
事件。
安装
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
event.waitUtil(
caches.open(CACHE_NAME).then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
})
缓存和返回请求
// 缓存请求
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) {
return response;
}
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(function(response) {
// 确保响应有效, 确保响应状态为200, 确保响应类型为basic, 即自身发起的请求。这意味着,对第三方资产的请求也不会添加到缓存
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
//通过检查,则克隆响应。该响应是数据流,主体只能使用一次,所以先克隆
var responseToCache = response.clone();
caches.open(CACHE_NAME).then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
})
return fetch(event.request);
})
)
})
更新service worker
self.addEventListener('activate', function(event) {
var cacheWhitelist = ['page-cache-v1', 'blog-posts-cache-v1'];
event.waitUtil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
)
})
)
})