前端工程化以前的時代略過css
自從xmlhttprequest被挖掘出來,網頁可以和服務端通信,js能作的事愈來愈多,文件體積愈來愈大,互相引用原來越多。
然而網速只有幾兆的帶寬。因而想到,要把js文件狠狠的壓縮,能合併的就給合併起來html
主要有的js壓縮工具:前端
JSMin 使用簡單、靈活,對不一樣語言、環境支持好。通常配合不一樣的環境、語言用命令行執行壓縮
YUI Compressor 雅虎推出。有JAVA版本和.NET版本,需二者環境配合
UglifyJS 基於nodejs,壓縮策略較爲安全,因此不會對源代碼進行大幅度的改造。
Closure Compiler 谷歌出品
在我理解,壓縮主要作了局部變量命名簡化、空格/換行/註釋消除、自動優化可簡化的語法等工做。vue
使用壓縮工具用es6語法寫的js在壓縮測試比較中:node
壓縮有了,可是當a文件和b文件都引用了c文件的方法時,若是把c文件分別和a、b合併,這樣就只有兩個文件了。這就是最開始的合併方式。
通常是經過在windows上用bat腳本或者mac/linux上的shell腳原本決定合併哪些文件、用什麼工具壓縮、怎麼壓縮linux
經過腳本構建的項目裏的文件相互依賴複雜,命名什麼的徹底有可能一不當心就衝突了,並且依賴多是一層一層依賴下去的,維護起來要命呀。
因此先解決js相互依賴的問題webpack
CommonJS規範解決了js模塊化依賴的問題
既然CommonJS提供了模塊化的思路,也已經在服務端(nodejs)裏大展手腳。那麼瀏覽器裏可不可行?
瀏覽器不兼容CommonJS的緣由首先是,缺乏了4個NodeJs環境變量:git
那麼只須要提供了這些個環境變量就好了吧。Browerify就是作這的es6
Browserify 是目前最經常使用的 CommonJS 格式轉換的工具
Browserify的核心思路是講module暴露出的模塊放入一個數組,require時根據模塊id找到相應的module執行,總之就是給上面缺乏的變量寫成可執行的es5的策略github
那麼是否是這樣就能在瀏覽器上愉快使用CommonJS?
CommoJS是同步require的方式獲取js模塊,在瀏覽器上會阻斷主線程。頁面會因加載js可能卡住
這確定是不能容忍的
因而AMD(異步模塊定義)誕生
AMD也採用require()語句加載模塊,可是要傳兩個參數require([module], callback)。是的,回調思路。
(1)實現js文件的異步加載,避免網頁失去響應
(2)管理模塊之間的依賴性,便於代碼的編寫和維護
CMD規範 和AMD大同小異,具體實現是seajs。沒用過,應該都差很少吧,啊哈哈
less,sass,stylus 的 css 預處理器簡化css語法
ejs,jade 等html的模板語法
這些真的是前端狗的福音,很少說,css-next來了,繼續啃咯。
這樣html/css/js 就都有了適合自動構建的擴展結構。可是這時候寫一個構建這些依賴的命令太長太複雜,因此打包工具開始流行:
grunt 寫法簡單,插件還賊多
gulp 效率更高,可擴展性更強
nodejs配合這倆大佬作web項目的自動化構建用着都挺爽的
var gulp = require('gulp') var nodemon = require('gulp-nodemon') var browserSync = require('browser-sync').create() gulp.task('nodemon', function(cb) { var started = false return nodemon({ script: 'mswadmin.js' , ext: 'js' , env: { 'NODE_ENV': 'default' } }).on('start', function() { if (!started) { cb(); started = true; } }) }); gulp.task('serve', function(){ browserSync.init({ proxy: 'http://10.3.10.27:18282', browser: 'chrome', port: 18282 }) gulp.watch('static/**/*.+(scss|jade|ls)', ['inject']) .on('change', browserSync.reload); }) gulp.task('default', ['nodemon','serve']);
上面是一個用nodemon監控本地服務+watch代碼熱更新的配置。能夠看出,以流任務的方式一個個執行。用起來也簡單
js 對應的 AMD 模塊,而後該 AMD 模塊渲染對應的 html 到容器內
這樣網頁再也不是傳統的文檔一類的頁面了。而是更像一個完整的程序。一個主入口,js完成的前端路由,AMD模塊完成頁面內從新渲染。
雖然是作出來這個SPA了,可是小問題多:
首先,webpack是靜態模塊打包器(bundler),grunt/gulp是流任務執行器。
區分二者能夠用grunt-webpack形象說明:你能夠將 webpack 或 webpack-dev-server 做爲一項任務(task)執行
這兩點是我以爲最突出的地方,詳細對比請參考對比
瀏覽器環境下,用了ES6規範的話,你應該不想用其餘的了
打包的規則爲:一個入口文件對應一個bundle。該bundle包括入口文件模塊和其依賴的模塊。按需加載的模塊或需單獨加載的模塊則分開打包成其餘的bundle。
除了這些bundle外,還有一個特別重要的bundle,就是manifest.bundle.js文件,即webpackBootstrap。這個manifest文件是最早加載的,負責解析webpack打包的其餘bundle文件,使其按要求進行加載和執行。
不管你選擇哪一種模塊語法,那些 import 或 require 語句如今都已經轉換爲 webpack_require 方法,此方法指向模塊標識符(module identifier)。經過使用 manifest 中的數據,runtime 將可以查詢模塊標識符,檢索出背後對應的模塊。
雖然網上有不少 十分鐘入門webpack 的教程。但仍是推薦去擼一遍webpack官方指南
我的以爲指南里你要注意的細節:
- webpack 不會更改代碼中除 import 和 export 語句之外的部分。若是你在使用其它 ES2015 特性,請確保你在 webpack 的 loader 系統中使用了一個像是 Babel 或 Bublé 的轉譯器
- npm腳本運行時默承認以使用npx命令
- source map要合理使用
- 留意webpack-dev-middleware,配合express作服務端渲染要用到哦
- HMR(模塊熱替代)通常用你選用的框架自帶的loader(vue-loader)
- 用UglifyJsPlugin插件自動移除 JavaScript 上下文中的未引用代碼(dead-code)。webpack4裏使用 mode=production 替代。要結合SideEffects使用,webpack4又提供了SideEffects插件使用的方式
- process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js' 這樣的條件語句在配置文件裏沒法使用,用if/else
- splitChunks優化,webpack4已經移除了CommonsChunkPlugin。下文會詳細解釋
- dynamic imports(動態導入)優化,chunkFilename決定非入口 chunk 的名稱,vue裏的運用實例就是路由懶加載(vue-lazyload),生成了新的bundle
webpack 可使用dynamic imports的方式引用模塊,咱們使用 async/ await 和 dynamic import 來實現。每個dynamic import都將做爲一個單獨的chunk打包。在vue中的一個例子就是路由懶加載+babel-plugin-dynamic-import-node的構建方案。使用babel-plugin-dynamic-import-node是由於開發環境下觸發熱更新很慢,這個插件講import異步所有改爲require同步
通常來講咱們在dist生成了一下三種bundle
main bundle 會隨着自身的新增內容的修改,而發生變化。
vendor bundle 會隨着自身的 module.id 的修改,而發生變化。
manifest bundle 會由於當前包含一個新模塊的引用,而發生變化。
然而咱們並不但願vendor每次構建都生成新的hash,畢竟咱們但願用到緩存的。解決方法官方有兩個插件NamedModulesPlugin和HashedModuleIdsPlugin
vue裏使用的是HashedModuleIdsPlugin
相信不少人從webpack3升級到4會碰到問題,接下來
### 5. 升級到webpack4你該搞明白
主要使用了模式的概念。
development 模式下,默認開啓了NamedChunksPlugin 和NamedModulesPlugin方便調試,提供了更完整的錯誤信息,更快的從新編譯的速度
production 模式下,因爲提供了splitChunks和minimizer,因此基本零配置,代碼就會自動分割、壓縮、優化,同時 webpack 也會自動幫你 Scope hoisting(做用域提高) 和 Tree-shaking
至關於把一些基本的配置當成默認配置。只須要在命令行運行時帶上mode參數就搞定
#### 2. 一些插件的廢除和替換
廢棄了 | 頂替者(用optimization屬性) | 變化 |
---|---|---|
uglifyjs-webpack-plugin | minimizer | 壓縮優化 |
CommonsChunkPlugin | splitChunks | 代碼分割,下面詳解 |
還有一些新的插件:Tree Shaking,SideEffects。我還不知道怎麼用--
它與extract-text-webpack-plugin最大的區別是:它在code spliting的時候會將原先內聯寫在每個 js chunk bundle的 css,單獨拆成了一個個 css 文件。js變得更乾淨了,css是根據optimization.splitChunks的配置自動拆分css文件爲單獨的模塊的規則拆分的,不用擔憂過多的httlp資源請求問題
這是爲了解決當css與js文件有依賴時,二者有相同的chunkhash。這樣js修改了,css沒改的狀況下chunkhash頁被修改了,無法緩存了呀
contenthash 你能夠簡單理解爲是 moduleId + content 所生成的 hash
相關issue
在optimization.minimizer裏推薦使用optimize-css-assets-webpack-plugin直接配置。可是vue-cli3裏的配置本身配的。嗯...反正也不想看那些配置,就這樣吧~~~
上面多處提到了這個optimization.splitChunks
Webpack 4 最大的改進即是Code Splitting chunk。webpack3是經過CommonsChunkPlugin拆分的。而後如今直接被廢棄了,我能怎麼辦?,跟着學唄。
開啓Code Splitting很簡單,使用production的mode就行,會自動開啓。並有一個設置好了的一個很合理的配置
若是同時知足下列條件,chunk 就會被拆分:
默認配置已經很合理了,然而當出現以下狀況:
已vue-cli建立的項目爲例。項目用到了第三方的UI組件庫,在main.js入口處依賴了第三方庫。
由於在入口引入了,因此第三方庫會被打包進app.js。這樣,只要我修改了app.js裏的其餘代碼,打出來的包的hash就變了。瀏覽器又得再次緩存app.js。第三庫至關於又被緩存了一次,這顯然不是咱們想要的。
看一下花褲衩的配置
splitChunks: { chunks: "all", cacheGroups: { libs: { name: "chunk-libs", test: /[\\/]node_modules[\\/]/, priority: 10, chunks: "initial" // 只打包初始時依賴的第三方 }, elementUI: { name: "chunk-elementUI", // 單獨將 elementUI 拆包 priority: 20, // 權重要大於 libs 和 app 否則會被打包進 libs 或者 app test: /[\\/]node_modules[\\/]element-ui[\\/]/ }, commons: { name: "chunk-commons", test: resolve("src/components"), // 可自定義拓展你的規則 minChunks: 2, // 最小共用次數 priority: 5, reuseExistingChunk: true } } };
主要思路就是
提醒: 代碼的拆分必定要結合項目的實際狀況,好比你就用到element裏的一兩個組件,徹底能夠按需加載在main.js,而後直接打包進app.js。因此沒有最合理的拆分規則,只有最適合你的。
支持了Prefetching/Preloading瀏覽器資源加載優化
核心思想是減小JS下載時間
學不動了學不動了,先緩緩