前言
上一篇中,描述了一些關於生產環境的配置:環境變量的使用、配置文件描述、開啓生產模式、環境變量自定義配置等,從這幾個方面入手都可以對生產環境產生一些有利影響。
那麼本篇,從source map和資源壓縮方面入手,繼續深入探究。
1. source map
source map 指的是將編譯、打包、壓縮後的代碼映射回源代碼的過程。
經過webpack打包壓縮後的代碼基本上已經不具備可讀性,此時若是代碼拋出錯誤,想要回溯它的調用棧是非常困難的,而有了source map,加上瀏覽器調試工具(dev tools),要做到這一點就會變得很容易。同時,它對於線上問題的追查也有一定幫助。
1.1 原理
工作原理:webpack對於工程源代碼的每一步處理都有可能會改變代碼的位置、結構、甚至是所處文件,因此每一步都需要生成對應的source map。
如果我們啓用了devtool
配置,那麼source map就會跟隨源代碼一步步被傳遞,直到生成最後的map文件。這個文件默認就是打包後的文件名字上加上後綴[.map],例如bundle.js.map。
在生成map文件的同時,bundle文件中會追加一句註釋來標識map文件的位置,例如:
// bundle.js
(function() {
// bundle的內容
...
})()
// # sourceMappingURL=bundle.js.map
而當我們打開瀏覽器開發者工具後,其實map文件同時也會被加載進來,這時瀏覽器會使用它來對打包後的bundle文件來進行解析,分析出源代碼的目錄結構和內容。
親自嘗試過的朋友可能會發現,打包後,map文件會比較大,甚至超出源文件幾倍的體積大小,不過不用擔心,不打開開發者工具是不會加載這些map文件的,因此對於普通用戶來講沒有什麼影響。但是要注意的是,雖然普通用戶看不到,不過有經驗的“特殊人羣”還是可以通過dev tools看到工程源碼的。因此建議如果是生產環境,還是要解決一下。如何解決呢?下面會提到。
1.2 配置
在webpack.config.js中添加devtool即可完成對source map的配置。
// webpack.config.js
module.exports = {
// ...
devtool: 'source-map'
}
而對於CSS、SCSS及Less來說,則需要添加額外的source map配置項。如:
const path = require('path');
module.exports = {
// ...
devtool: 'source-map',
module: {
rules: [
// scss
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true,
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}
]
}
}
webpack給出多種source map形式:
- source-map
- cheap-source-map
- eval-source-map
- ...
在開發環境中,通常使用module-eval-source-map,因爲在打包速度和源碼信息還原程度都屬於良好程度。
而在生產環境中,通常我們會對代碼進行壓縮,而最常見的壓縮插件UglifyjsWebpackPlugin
目前只支持source-map形式。
1.3 安全
在1.1中我們拋出一個安全問題,就是在開啓source-map的時候任何人都可以通過瀏覽器的開發者工具devtool來看到工程源碼,因此對於安全性來講是一個極大的隱患。那麼如何能在保持其功能的同時又能防止暴漏源碼呢?
webpack提供了兩種安全策略:
- hidden-source-map
- nosources-source-map
hidden-source-map
hidden-source-map意味着Webpack仍然會產出完整的map文件,但是不會在bundle文件中添加對於map文件的引用。這樣當打開瀏覽器開發者工具時,是無法看到map文件的,自然也就無法解析。如果我們自己想要追溯源碼,可使用一些第三方服務,將map文件上傳到第三方服務中。目前比較流行的是Sentry
(錯誤跟蹤平臺),有興趣的可以自行搜索瞭解一下。
nosources-source-map
它對於安全性保護不如hidden-source-map,但是使用方式相對簡單。當打包部署後,我們可以在瀏覽器開發者工具的sources選項卡中看到源碼的目錄結構,但是文件內容會被隱藏起來。這樣,對於錯誤來講,我們仍然可以在console控制檯中查看源代碼的錯誤棧,或者console日誌的準確行數。對於追溯錯誤來說基本上夠使用。
另外的方案則是服務端配合處理,例如正常打包出source map,服務端通過服務器的nginx配置,將.map文件只對固定的白名單(如公司內網)開放,這樣其餘用戶就無法獲取到它們了,也不失爲一個小妙招。
2. 資源壓縮
資源在發佈到生產環境之前,通常會進行代碼壓縮,也叫uglify,意思是移除多餘的空格、換行、執行不到的代碼塊等,同時縮短變量名,在執行結果不變的前提下替換爲更短的形式。
一般工程代碼在被壓縮後整個體積會顯著縮小。
但同時,uglify之後的代碼基本上不具有可讀性,從另一個層面講,一定程度上提高了代碼的安全性。
2.1 壓縮JavaScript
壓縮JS(JavaScript)的工具terser(optomization)
在webpack中已集成(webpack4),並且支持ES6+的代碼壓縮,偏面向未來。
示例:
// webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js'
},
// 壓縮配置
optomization: {
minimize: true
}
}
2.2 壓縮CSS
CSS文件的壓縮前提是使用相關插件處理,先將樣式提取出來,然後進行壓縮。例如常使用extract-text-webpack-plugin或mini-css-extract-plugin將樣式提取,然後使用optimize-css-assets-webpack-plugin來進行壓縮。這個插件本質上使用的是壓縮器cssnano,當然我們可以對其進行配置:
// webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
}
]
},
// css 壓縮
plugins: [new ExtractTextPlugin('style.css')];
optimization: {
minimizer: [new OptimizeCssAssetsPlugin({
// 生效範圍,只壓縮匹配到的資源
assetNameRegExp: /\.optimize\.css$/g,
// 壓縮處理器指定,默認爲 cssnano
cssProcessor: require('cssnano'),
// 壓縮處理器配置
cssProcessorOptions: {
discardComments: {
removeAll: true
}
},
// 是否打印log
canPrint: true
})]
}
}
小結
本篇介紹了關於生產環境配置中比較重量級的兩種配置:source-map和資源壓縮。
開發環境中我們關注打包速度,而在生產環境中我們關心的則是線上錯誤處理、輸出資源的體積以及資源渲染等問題,而比較好的利用source-map和資源壓縮都可以幫助我們處理處理或優化生產環境中的一些問題,因此比較重要,但同時也要注意解決所存在的安全隱患問題。
下一篇則從緩存和bundle體積監控入手繼續描述生產環境配置的其他方面優化問題。