前言:在看本文前,建議你看下,下面這兩篇文章 順便給個贊和
github
的贊哦~
若是你對webpack
不是很瞭解,請你關注我以前的文章,都是百星以上star
的高質量文javascript
《前端進階》
專欄 一塊兒突破學習文章內容都會不按期更新 記得必定要收藏
webpack
webpack
用了會上癮,它也是突破你技術瓶頸的好方向,如今基本上任何東西都離不開webpack
,webpack
用得好,什麼next nuxt
隨便上手(本人體會很深),本人蔘考了Vue
腳手架,京東的webpack
優化方案,以及本人的其餘方面優化,着重在生產模式
下的構建速度優化提高很是明顯(固然開發環境下也是~),性能提高很明顯哦~.Vue
文件和template模板
tree shaking
搖樹優化 刪除掉無用代碼babel polifill
而且按需加載,識別一切代碼async / await
和 箭頭函數 PWA
功能,熱刷新,安裝後當即接管瀏覽器 離線後仍讓能夠訪問網站 還能夠在手機上添加網站到桌面使用preload
預加載資源 prefetch
按需請求資源 ,這裏除了dns
預解析外,建議其餘的使用按需加載組件,順便代碼分割,這也是京東的優化方案nginx
,攔截非預期請求(京東的方案)CSS
模塊化,不怕命名衝突base64
處理sx js json
等VueRouter
路由懶加載,按需加載 , 代碼分割 指定多個路由同個chunkName
而且打包到同個chunk
中 實現代碼精確分割less sass stylus
等預處理code spliting
優化首屏加載時間 不讓一個文件體積過大chunkhash
,每一個文件有對應的contenthash
,方便瀏覽器區別緩存CSS
壓縮CSS
前綴 兼容各類瀏覽器code spliting
CSS
文件單獨抽取出來每隔三天,技術就會進步一次
正式開始吧,假設你已經懂什麼是
entry output loader plugin
,若是不懂,看我上面的文章哦~
// 入口文件 entry: { app: './src/js/index.js', }, // 輸出文件 output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), publicPath: '/' //確保文件資源可以在 http://localhost:3000 下正確訪問 }, // 開發者工具 source-map devtool: 'inline-source-map', // 建立開發者服務器 devServer: { contentBase: './dist', hot: true // 熱更新 }, plugins: [ // 刪除dist目錄 new CleanWebpackPlugin(['dist']), // 從新穿件html文件 new HtmlWebpackPlugin({ title: 'Output Management' }), // 以便更容易查看要修補(patch)的依賴 new webpack.NamedModulesPlugin(), // 熱更新模塊 new webpack.HotModuleReplacementPlugin() ], // 環境 mode: "development", // loader配置 module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] } ] }
這裏面咱們重點關注module
和plugins
屬性,由於今天的重點是編寫loader
和plugin
,須要配置這兩個屬性。
webpack
啓動後,在讀取配置的過程當中會先執行 new MyPlugin(options)
初始化一個 MyPlugin
得到其實例。在初始化 compiler
對象後,再調用 myPlugin.apply(compiler)
給插件實例傳入 compiler
對象。插件實例在獲取到 compiler
對象後,就能夠經過 compiler.plugin
(事件名稱, 回調函數) 監聽到 Webpack
廣播出來的事件。
而且能夠經過 compiler 對象去操做 webpack。css
Compiler
對象包含了 Webpack
環境全部的的配置信息,包含 options,loaders,plugins
這些信息,這個對象在 Webpack
啓動時候被實例化,它是全局惟一的,能夠簡單地把它理解爲 Webpack
實例;html
Compilation
對象包含了當前的模塊資源、編譯生成資源、變化的文件等。當 Webpack 以開發模式運行時,每當檢測到一個文件變化,一次新的
Compilation 將被建立。
Compilation 對象也提供了不少事件回調供插件作擴展。經過
Compilation 也能讀取到
Compiler` 對象。Compiler 和 Compilation
的區別在於:Compiler
表明了整個 Webpack
從啓動到關閉的生命週期,而 Compilation
只是表明了一次新的編譯。 webpack
經過 Tapable
來組織這條複雜的生產線。 webpack
的事件流機制保證了插件的有序性,使得整個系統擴展性很好。webpack
的事件流機制應用了觀察者模式,和 Node.js 中的 EventEmitter
很是類似。Commonjs、amd
或者es6
的import,webpack
都會對其進行分析。來獲取代碼的依賴)webpack
作的就是分析代碼。轉換代碼,編譯代碼,輸出代碼webpack
的一些基礎知識,對於理解webpack的工做機制頗有幫助。commonjs
模塊化方案,若是你不是很懂,那麼看起來很費勁,我寫的腳手架,就不使用模塊化方案了,簡單粗
暴yarn
不解釋 就用yarn
webpack.dev.js
開發模式下的配置yarn init -y
yarn add webpack webpack-cli
(yarn
會自動添加依賴是線上依賴仍是開發環境的依賴)entry: path.resolve(__dirname, '../src/main.js')}
output: { filename: 'js/[name].[hash:5].js', path: path.resolve(__dirname, '../dist'), },
Vue
腳手架裏基本配置的loader
,後面的loader
都是往rules
數組裏加就好了~module: { rules: [ { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use: [{ loader: 'url-loader', options: { limit: 10000, name: 'img/[name]-[hash:5].[ext]', } } ] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'fonts/[name]-[hash:5].[ext]', } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use: [ { loader: 'url-loader', options: { limit: 4096, name: 'media/[name]-[hash:5].[ext]', } } ] } ] },
有人會問 這麼多我怎麼看啊 別急 第一個url-loader
是處理base64
圖片的,讓低於limit
大小的文件以base64
形式使用,後面兩個同樣的套路,只是換了文件類型而已 ,不會的話,先複製過去跑一把?
.vue
文件和tempalte
模板 , yarn add vue vue-loader vue-template-compiler
加入loader { test:/\.vue$/, loader:"vue-loader" } 加入plugin const vueplugin = require('vue-loader/lib/plugin') 在webpack的plugin中 new vueplugin()便可
babel-polifill
,vendor
代碼分割公共模塊,打包後這些代碼都會在一個公共模塊app: ['babel-polyfill', './src/index.js', './src/pages/home/index.js', './src/pages/home/categorys/index.jsx'], vendor: ['vuex', 'better-scroll', 'mint-ui', 'element-ui']
html
文件爲模板打包輸出,自動引入打包後的js
文件const HtmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname,'../index.html'), filename: 'index.html' }), ]
.vue
的後綴 ,直接配置在module.exports
對象中,跟entry
同級resolve: { extensions: ['.js','.json','.vue'], }
html
文件的loader
{ test: /\.(html)$/, loader: 'html-loader' }
const os = require('os') { loader: 'thread-loader', options: { workers: os.cpus().length } }
babel-loader
加入 babel-loader 還有 解析JSX ES6語法的 babel preset@babel/preset-env解析es6語法 @babel/plugin-syntax-dynamic-import解析vue的 import按需加載,附帶code spliting功能 { loader: 'babel-loader', options: { //jsx語法 presets: ["@babel/preset-react", //tree shaking 按需加載babel-polifill ["@babel/preset-env", { "modules": false, "useBuiltIns": "false", "corejs": 2 }]], plugins: [ //支持import 懶加載 "@babel/plugin-syntax-dynamic-import", //andt-mobile按需加載 true是less,若是不用less style的值能夠寫'css' ["import", { libraryName: "antd-mobile", style: true }], //識別class組件 ["@babel/plugin-proposal-class-properties", { "loose": true }], ], cacheDirectory: true }, }
babel
配置後 咱們躺着就能夠用vueRouter
的路由懶加載了第二,在 Webpack 中,咱們可使用動態 import語法來定義代碼分塊點 (split point):前端
import('./Foo.vue') // 返回 Promise
注意
const Foo = () => import('./Foo.vue') 在路由配置中什麼都不須要改變,只須要像往常同樣使用 Foo: const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] }) # 把組件按組分塊 有時候咱們想把某個路由下的全部組件都打包在同個異步塊 (chunk) 中。只須要使用 命名 chunk,一個特殊的註釋語法來提供 chunk name (須要 Webpack > 2.4)。 const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') Webpack 會將任何一個異步模塊與相同的塊名稱組合到相同的異步塊中。
const HtmlWebpackPlugin = require('html-webpack-plugin') const webpack = require('webpack') new HtmlWebpackPlugin({ template: './src/index.html' }), new webpack.HotModuleReplacementPlugin(), devServer: { contentBase: '../build', open: true, port: 5000, hot: true },
less-css
識別的模塊{ test: /\.(less|css)$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' , options: { modules: false, //不建議開啓css模塊化,某些ui組件庫可能會按需加載失敗 localIdentName: '[local]--[hash:base64:5]' } }, { loader: 'less-loader', options: { javascriptEnabled: true } } ] },
下面正式開始生產環境
html
殺掉無效的代碼new HtmlWebpackPlugin({ template: './src/index.html', minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, } }),
{ test: /\.(jpg|jpeg|bmp|svg|png|webp|gif)$/, use:[ {loader: 'url-loader', options: { limit: 8 * 1024, name: '[name].[hash:8].[ext]', outputPath:'/img' }}, { loader: 'img-loader', options: { plugins: [ require('imagemin-gifsicle')({ interlaced: false }), require('imagemin-mozjpeg')({ progressive: true, arithmetic: false }), require('imagemin-pngquant')({ floyd: 0.5, speed: 2 }), require('imagemin-svgo')({ plugins: [ { removeTitle: true }, { convertPathData: false } ] }) ] } } ] }
{ exclude: /\.(js|json|less|css|jsx)$/, loader: 'file-loader', options: { outputPath: 'media/', name: '[name].[contenthash:8].[ext]' } }
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') new OptimizeCssAssetsWebpackPlugin({ cssProcessPluginOptions:{ preset:['default',{discardComments: {removeAll:true} }] } }),
vue
腳手架是同步異步分開割,我是直接一塊兒割optimization: { runtimeChunk:true, //設置爲 true, 一個chunk打包後就是一個文件,一個chunk對應`一些js css 圖片`等 splitChunks: { chunks: 'all' // 默認 entry 的 chunk 不會被拆分, 配置成 all, 就能夠了拆分了,一個入口`JS`, //打包後就生成一個單獨的文件 } }
pwa這個技術其實要想真正用好,仍是須要下點功夫,它有它的生命週期,以及它在瀏覽器中熱更新帶來的反作用等,須要認真研究。能夠參考百度的lavas框架發展歷史~ const WorkboxPlugin = require('workbox-webpack-plugin') new WorkboxPlugin.GenerateSW({ clientsClaim: true, //讓瀏覽器當即servece worker被接管 skipWaiting: true, // 更新sw文件後,當即插隊到最前面 importWorkboxFrom: 'local', include: [/\.js$/, /\.css$/, /\.html$/,/\.jpg/,/\.jpeg/,/\.svg/,/\.webp/,/\.png/], }),
VUE
首選nuxt
框架,也可使用它的腳手架next nuxt
和pwa
的使用~
腳手架的搭建過程不少坑,可是卻能大大提高你的技術天花板,跟着做者一塊兒踩坑吧,別忘了來個人
github
點贊哦~
倉庫源碼地址~歡迎star