介紹 vue+webpack 手動搭建過程,熟悉 webpack 的配置,loader&插件的使用css
目標:熟悉 webpack 的配置,loader&插件的使用html
一般新建 vue 項目都使用 vue-cli 腳手架,其實對於 webpack 的配置是隻知其一;不知其二,當須要升級 webpack 或者優化項目配置時,就顯得很無力。經過手動搭建 webpack 能夠對 webpack 有更深刻的瞭解,當咱們使用其餘模塊管理器時(eg:rollup,gulp),也不會那麼生疏。vue
經過 NODE_ENV=developement 或 NODE_ENV=production 指定開發仍是生產環境node
devDependencies 是隻會在開發環境下依賴的模塊,生產環境不會被打入包內。react
而 dependencies 依賴的包不只開發環境能使用,生產環境也能使用。其實這句話是重點,按照這個觀念很容易決定安裝模塊時是使用--save 仍是--save-dev。webpack
項目地址nginx
"dependencies": { "@xunlei/vue-lazy-component": "^1.1.3", "echarts": "^4.2.1", "element-resize-event": "^3.0.3", "element-ui": "^2.7.2", "file-loader": "^4.0.0", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "intersection-observer": "^0.7.0", "lodash": "^4.17.11", "url-loader": "^2.0.0", "vue": "^2.6.10", "webpack": "^4.34.0" }
解決辦法:Vue-loader 在 15.*以後的版本都是 vue-loader 的使用都是須要伴生 VueLoaderPlugin 的git
解決辦法:webpcak.config.js 中github
new HTMLPlugin({ template: 'index.html' }),
解決辦法:webpcak.config.js 中web
resolve: { alias: { vue: 'vue/dist/vue.js' } },
這裏涉及到一個小知識點 編譯時&運行時
https://cn.vuejs.org/v2/guide...
由於在 Vue.js 2.0 中,最終渲染都是經過 render 函數,若是寫 template 屬性,則須要編譯成 render 函數,那麼這個編譯過程會發生運行時,因此須要帶有編譯器的版本。很顯然,這個編譯過程對性能會有必定損耗,因此一般咱們更推薦使用 Runtime-Only 的 Vue.js。
只有如下狀況會用到 compiler: 1.有指定 template; 2.沒指定 template,也沒指定 render(這時候使用的就是被掛載元素的 outerHtml)。
因此,沒有使用到 compiler 的狀況只有:沒有指定 template,但指定了 render。
有時會遇到這樣的錯誤:[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
以上提到,解決這個問題有兩種方式,但大多會選擇後者,也就是使用全功能的 vue(runtime+compiler),這個版本的 vue 文件相比僅包含 runtime 的版本體積要大,並且運行時的 compiler 轉換會消耗性能,compiler 過程其實能夠放到構建過程當中進行。總結就是,若是能夠的話,儘可能使用 runtime 版的 vue 文件。
Webpack 新版本要求配置 module 中的 loader 不能縮寫,也就是
loader:"json-loader"
中的-loader 必需要寫。
(在網上找配置時須要注意)
npm init
npm i webpack vue vue-loader npm i css-loader vue-template-compiler
<template> <div id="test">{{text}}</div> </template> <script> export default { data() { return { text: 'abc' } } } </script> <style> #test { color: red; } </style>
import Vue from 'vue' import App from './app.vue' // Runtime Only // new Vue({ // render: h => h(App) //h就是vue中的createApp參數 // }).$mount('#app') //將app掛載到body下的div上 // 會用到 compiler 因此使用全功能的 vue new Vue({ el: '#app', components: { App }, template: '<App/>' })
const path = require('path') //nodeJs的基本包 module.exports = { //path.join(__dirname, 'src/index.js')中__dirname表示當前文件的路徑,path.join就是將當前文件的路徑跟'src/index.js'拼接起來,造成一個絕對路徑 entry: path.join(__dirname, 'src/index.js'), //輸出文件,取名爲bundle.js,路徑爲dist文件夾 output: { filename: 'bundle.js', path: path.join(__dirname, 'dist') }, module: { rules: [ { test: /.vue$/, loader: 'vue-loader' } ] } }
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.config.js" }
其中 index.js 入口文件以下:
import './assets/css/global.css'
其中 webpack.config.js 以下:
module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.html$/i, loader: 'html-loader' }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.scss$/, loaders: ['style-loader', 'css-loader', 'sass-loader'] }, { test: /\.(gif|jpg|jpeg|png|svg)$/, use: [ { loader: 'url-loader', //可以將圖片轉成base64代碼直接寫在js裏面,依賴file-loader,因此在安裝的時候不只要裝url-loader還要裝file-loader options: { limit: 1024, //若是文件大小小於1024字節,就會轉義成base64,不然仍然是圖片 name: '[name]-aaa.[ext]' //輸出文件的名字,name就是原先圖片的名字,-aaa是本身家的字段 } } ] } ] },
"scripts": { "build": "cross-env NODE_ENV=production webpack --config webpack.config.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js" }
cross-env 能跨平臺地設置及使用環境變量
npm 安裝方式
npm i --save-dev cross-env
在 npm 腳本(可能是 package.json)裏這麼配置
"scripts": { "build": "cross-env NODE_ENV=production webpack --config webpack.config.js", }
運行 npm run build,這樣 NODE_ENV 便設置成功,無需擔憂跨平臺問題
因此,整個 webpack 的代碼爲:
const path = require('path') const HTMLPlugin = require('html-webpack-plugin') const VueLoaderPlugin = require('vue-loader/lib/plugin') const webpack = require('webpack') const isDev = process.env.NODE_ENV === 'development' const config = { target: 'web', entry: path.join(__dirname, 'src/index.js'), output: { filename: 'dist/bundle.js', path: path.join(__dirname, 'dist') }, resolve: { alias: { vue$: 'vue/dist/vue.esm.js' // 用 webpack 1 時需用 'vue/dist/vue.common.js' } }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.html$/i, loader: 'html-loader' }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.scss$/, loaders: ['style-loader', 'css-loader', 'sass-loader'] }, { test: /\.(gif|jpg|jpeg|png|svg)$/, use: [ { loader: 'url-loader', //可以將圖片轉成base64代碼直接寫在js裏面,依賴file-loader,因此在安裝的時候不只要裝url-loader還要裝file-loader options: { limit: 1024, //若是文件大小小於1024字節,就會轉義成base64,不然仍然是圖片 name: '[name]-aaa.[ext]' //輸出文件的名字,name就是原先圖片的名字,-aaa是本身家的字段 } } ] } ] }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: isDev ? '"development"' : '"production"' } }), //通常vue、react等框架都要用到這個插件。 //在這裏定義了,在咱們的js代碼中是能夠引用到的。 //如今,veu/react這類框架會根據環境去區分打包,打包後的dist在開發環境中是比較大的,由於有不少相似錯誤的信息們能夠幫助咱們開發人員開發,而生產環境是比較小的,沒有繁多的錯誤信息,咱們也不但願錯誤信息給用戶看,因此就不必把錯誤信息打包進去了 //爲何單引號裏面還要雙引號?由於若是沒有的話,調用的時候,就成了process.env.NODE_ENV = development,這時候development就成了一個變量,因此須要寫上雙引號 new HTMLPlugin({ template: 'index.html' }), new VueLoaderPlugin() ] } if (isDev) { config.devtool = '#cheap-module-eval-source-map' //幫助咱們在頁面上調試咱們的代碼的,而且有不少種source-map的映射方式,不一樣映射方式有不一樣的優缺點,這裏寫的只是其中一種,這個值,可讓你在瀏覽器看到源碼 config.devServer = { port: 8088, host: 'localhost', //能夠經過localhost,127.0.0.1,本機的內網IP進行訪問(IP的話,就能夠在別人的電腦上訪問) overlay: { error: true //若是編譯有錯誤,就直接顯示在網頁上 }, open: true, hot: true //熱加載,不須要刷新頁面就能加載出來 } config.plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() //減小咱們不須要的信息的展現 ) } module.exports = config
這部分厲害了,看完文檔就知道爲何使用 vue-cli 腳手架 vue init webpack [項目名]生成的 webpack 配置文件包含
;-build | -webpack.base.conf.js | -webpack.base.dev.js | -webpack.base.prod.js
development(開發環境) 和 production(生產環境) 這兩個環境下的構建目標存在着巨大差別。
咱們先從安裝 webpack-merge 開始,並將已經成型的那些代碼進行分離:
npm install --save-dev webpack-merge
- |- webpack.config.js + |- webpack.common.js + |- webpack.dev.js + |- webpack.prod.js
具體使用方式查看文檔webpack 指定 mode
該插件將爲你生成一個 HTML5 文件, 其中包括使用 script 標籤的 body 中的全部 webpack 包。
new HTMLPlugin({ template: 'index.html', minify: { collapseWhitespace: true, removeComments: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true } }),
針對生成的 html 移除註釋、刪除空行、html 壓縮等操做
查看文件中 webpack.config.js 配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { plugins: [ new MiniCssExtractPlugin({ // 相似 webpackOptions.output裏面的配置 能夠忽略 filename: '[name].css', chunkFilename: '[id].css', }), ], module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ isDev ? 'sass-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ] }, ] } }
測試 css 被單獨提取,文件大小爲 main.css 264K
webpack5 可能會內置 CSS 壓縮器,webpack4 須要本身使用壓縮器,可使用 optimize-css-assets-webpack-plugin 插件。 設置 optimization.minimizer 覆蓋 webpack 默認提供的,確保也指定一個 JS 壓縮器
optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourcMap: true }), new OptimizeCSSAssetsPlugin({}), ], }
測試 css 被單獨提取,文件大小爲 main.css 219K 比壓縮前減小 45K
ERROR in bundle.js from UglifyJs
Unexpected token: punc «(» src/app.vue:21,0
解決方法: 配置 babel
未使用前 bundle.js 1937KB 使用後壓縮到 752KB
當咱們的項目愈來愈龐大是時候 會發現 即便作了 code split 代碼壓縮 動態加載 等等一系列優化以後 頁面的響應速度依舊很慢
這個時候時候可使用 compression-webpack-plugin 這個插件
new CompressionPlugin({ "filename": "[path].gz[query]", "test": new RegExp( "\\.(js|css)$" //壓縮 js 與 css ), "threshold": 500, // 當文件超過限制大小 使用gzip "minRatio": 0.8, "algorithm": "gzip" })
該插件的做用是 在超過限定的文件大小的 時候會生成一個跟文件同名的 gz 包,這個時候咱們須要在改下 nginx 的配置 啓用 gzip 壓縮並 開啓 gzip_static
vendors~main.772d21ef3139973e4cab.bundle.js.gz 能夠將原來 819KB 壓縮到 220KB 壓縮力度仍是很是大的