您好,登錄后才能下訂單哦!
這篇文章主要介紹webpack4如何打包vue前端多頁(yè)面項(xiàng)目,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
1.用法
項(xiàng)目結(jié)構(gòu)如下:
project |- bulid <!-- 這個(gè)目錄是自動(dòng)生成的--> |- public |- css |- js |- page1.html <!-- 插件生成的html文件--> |- page2.html <!-- 插件生成的html文件--> ... |- public/ <!-- 存放字體、圖片、網(wǎng)頁(yè)模板等靜態(tài)資源--> |- src <!-- 源碼文件夾--> |- components/ |- css/ |- js/ |- page1.js <!-- 每個(gè)頁(yè)面唯一的VUE實(shí)例,需綁定到#app--> |- page2.js <!-- 每個(gè)頁(yè)面唯一的VUE實(shí)例,需綁定到#app--> ... |- package.json |- package-lock.json |- README.md
public文件夾存放一些靜態(tài)文件,src文件夾存放源碼。每個(gè)頁(yè)面通過(guò)一個(gè)入口文件(page1.js,page2.js,..)生成vue實(shí)例,掛載到插件生成的html文件的#app元素上。
安裝依賴
$ npm install
進(jìn)入開發(fā)模式
$ npm run start
瀏覽器會(huì)打開 http://localhost:3000 ,這時(shí)頁(yè)面一片空白,顯示 cannot get幾個(gè)字。不要慌,在url后面加上 /page1.html ,回車,便可看見我們的頁(yè)面。 這是因?yàn)槲野验_發(fā)服務(wù)器的主頁(yè)設(shè)置為 index.html ,而本例中頁(yè)面為 page1.html,page2.html,因此會(huì)顯示一片空白。
開發(fā)完成了,構(gòu)建生產(chǎn)版本:
$ npm run build
這會(huì)產(chǎn)生一個(gè)build/文件夾,里面的文件都經(jīng)過(guò)優(yōu)化,服務(wù)器響應(yīng)的資源,就是來(lái)自于這個(gè)文件夾。
2.介紹
2.1 webpack基礎(chǔ)配置
我們的開發(fā)分為生產(chǎn)環(huán)境和開發(fā)環(huán)境,因此需要有2份webpack的配置文件(可能你會(huì)想用env環(huán)境變量,然后用3目運(yùn)算符根據(jù)env的值返回不同值。然而這種方法在webpack導(dǎo)出模塊的屬性中無(wú)效,我試過(guò)~~~)。這里我們拆分成3個(gè)文件,其中 webpack.common.js 是常規(guī)的配置,在兩種環(huán)境下都會(huì)用到, webpack.dev.js 和 webpack.prod.js 則是在2種環(huán)境下的特有配置。這里用到 webpack-merge 這個(gè)包,將公共配置和特有配置進(jìn)行合成。
webpack.common.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const devMode = process.env.NODE_ENV !=='production'; // 需要被打包入口文件數(shù)組 // 數(shù)組元素類型 {string|object} // string:將以默認(rèn)規(guī)則生成bundle // object{filename|title|template} 生成的bundle.html的文件名|title標(biāo)簽內(nèi)容|路徑 /public 下的模板文件(需指定文件后綴) const entryList = [ 'page1', 'page2', ]; /** * @param {array} entryList * @param {object} option:可選 要手動(dòng)配置的內(nèi)容 */ const createEntry = (list = [], option = {}) => { const obj = {}; list.forEach((item) => { const name = item.filename ? `./js/${item.filename}` : `./js/${item}`; obj[name] = path.resolve(__dirname, './src', `./${item}.js`); }); return Object.assign(obj, option); }; module.exports = { entry: createEntry(entryList), output: { path: path.resolve(__dirname, './build'), }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], }, }, }, { test: /\.vue$/, use: 'vue-loader', }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: { loader: 'file-loader', options: { name: 'public/fonts/[name].[ext]', }, }, }, { test: /\.(png|svg|jpg|gif)$/, use: { loader: 'file-loader', options: { name: 'public/images/[name].[ext]', }, }, }, ], }, plugins: createPluginInstance(entryList).concat([ // vue SFCs單文件支持 new VueLoaderPlugin(), ]), };
這里我們沒有進(jìn)行css文件的配置,是因?yàn)樯a(chǎn)環(huán)境下需要優(yōu)化、提取,所以在另外2個(gè)文件分別配置。
webpack.dev.js const webpack = require('webpack'); const path = require('path'); const merge = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'development', devtool: 'inline-source-map', output: { filename: '[name].js', chunkFilename: '[name].js', }, module: { rules: [ { test: /\.(css|less)$/, use: [ 'vue-style-loader', 'css-loader', 'postcss-loader', 'less-loader' ], }, ], }, resolve: { alias: { vue: 'vue/dist/vue.js' } }, });
vue分為開發(fā)版本和生產(chǎn)版本,最后一行是根據(jù)路徑指定使用哪個(gè)版本。
webpack.prod.js
const webpack = require('webpack'); const merge = require('webpack-merge'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'production', output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js', }, resolve: { alias: { vue: 'vue/dist/vue.min.js' } }, module: { rules: [ { test: /\.(css|less)$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader' ], }, ], },
在production環(huán)境下,我們使用了哈希值便于緩存,以后往生產(chǎn)環(huán)境下添加其他資源都會(huì)如此。
2.2 解決文件輸出目錄
我們期待的build文件夾具有如下結(jié)構(gòu):
build
|- css/
|- js/
|- page1.html
|- page2.html
...
即文件按照類型放在一起,html文件直接放在該目錄下,可是我們上面的配置的輸出結(jié)果是混合在一起的。由于name屬性既可以是文件名,也可以是 /dir/a 之類帶有路徑的文件名,我們根據(jù)這個(gè)特點(diǎn)做出一些修改。
直接對(duì)output的輸出路徑更改
比如改為 build/js ,其他資源利用相對(duì)路徑比如 ../page1.html 進(jìn)行修改。我一開始就這樣做的,但最終會(huì)導(dǎo)致開發(fā)服務(wù)器無(wú)法響應(yīng)文件的變化,因?yàn)樗荒茚槍?duì)輸出目錄下的文件進(jìn)行監(jiān)聽,該目錄之上的文件變化無(wú)能為力。
修改入口名稱
這也是我們的最終解決方案。將原來(lái)的文件名 page1 修改為 /js/page1 ,最終輸出的js文件便都會(huì)放在js文件夾里。在生產(chǎn)環(huán)境下我們通過(guò) MiniCssExtractPlugin 這個(gè)插件提取js文件中的css,這是該插件的配置:
new MiniCssExtractPlugin({ filename:'[name].[contenthash].css' })
這里的name就是當(dāng)初入口的名字,受到入口名稱更改的影響,上面最終會(huì)變成 js/page1.131de8553ft82.css ,并且該占位符[name]只在編譯時(shí)有效,這意味著無(wú)法用函數(shù)對(duì)該值進(jìn)行處理。因此不能使用[name]占位符達(dá)到想要的目的,干脆只用[id]。
new MiniCssExtractPlugin({ filename:'/css/[id].[contenthash].css' })
3.代碼分割
在webpack4中使用optimization.splitChunks進(jìn)行分割.
//webpack.common.js const path = require('path'); module.exports = { // ... 省略其他內(nèi)容 optimization:{ runtimeChunk:{ name:'./js/runtime' }, splitChunks:{ // 避免過(guò)度分割,設(shè)置尺寸不小于30kb //cacheGroups會(huì)繼承這個(gè)值 minSize:30000, cacheGroups:{ //vue相關(guān)框架 main:{ test: /[\\/]node_modules[\\/]vue[\\/]/, name: './js/main', chunks:'all' }, //除Vue之外其他框架 vendors:{ test:/[\\/]node_modules[\\/]?!(vue)[\\/]/, name: './js/vendors', chunks:'all' }, //業(yè)務(wù)中可復(fù)用的js extractedJS:{ test:/[\\/]src[\\/].+\.js$/, name:'./js/extractedJS', chunks:'all' } } } } };
runtimeChunk包含了一些webapck的樣板文件,使得你在不改變?cè)次募?nèi)容的情況下打包,哈希值仍然改變,因此我們把他單獨(dú)提取出來(lái),點(diǎn)這兒了解更多。 cacheGroups用于提取復(fù)用的模塊,test會(huì)嘗試匹配( 模塊的絕對(duì)路徑||模塊名 ),返回值為true且滿足條件的模塊會(huì)被分割。滿足的條件可自定義,比如模塊最小應(yīng)該多大尺寸、至少被導(dǎo)入進(jìn)多少個(gè)chunk(即復(fù)用的次數(shù))等。默認(rèn)在打包前模塊不小于30kb才被會(huì)分割。
4.樹抖動(dòng)
在package.json里加入
"sideEffects":["*.css","*.less","*.sass"]
該數(shù)組之外的文件將會(huì)受到樹抖動(dòng)的影響——未使用的代碼將會(huì)從export導(dǎo)出對(duì)象中剔除。這將大大減少無(wú)用代碼。如果樹抖動(dòng)對(duì)某些文件具有副作用,就把這些文件名放進(jìn)數(shù)組以跳過(guò)此操作。css文件(包括.less,.sass)都必須放進(jìn)來(lái),否則會(huì)出現(xiàn)樣式丟失。
5. 插件的使用
5.1 clean-webpack-plugin
每次打包后都會(huì)生成新的文件,這可能會(huì)導(dǎo)致無(wú)用的舊文件堆積,對(duì)于這些無(wú)用文件自己一個(gè)個(gè)刪太麻煩,這個(gè)插件會(huì)在每次打包前自動(dòng)清理。實(shí)際中,我們不想在開發(fā)環(huán)境下清理掉build命令生成的文件,因此只在生產(chǎn)環(huán)境使用了這個(gè)插件。
5.2 html-Webpack-plugin
我們的源碼目錄中并沒有html文件,打包后的多個(gè)html文件,就是我們用這個(gè)插件生成的。
//webpack.common.js // ...省略上面已經(jīng)出現(xiàn)過(guò)的內(nèi)容 //每個(gè)html需要一個(gè)插件實(shí)例 //批量生成html文件 const createPluginInstance = (list = []) => ( list.map((item) => { return new HtmlWebpackPlugin({ filename: item.filename ? `${item.filename}.html` : `${item}.html`, template: item.template ? `./public/${item.template}` : './public/template.html', title: item.title ? item.title : item, chunks: [ `./js/${item.filename ? item.filename : item}`, './js/extractedJS', './js/vendors', './js/main', './js/runtime', './css/styles.css', devMode ? './css/[id].css' : './css/[id].[contenthash].css', ], }); }) );
默認(rèn)會(huì)將所有的入口文件,代碼分割后的文件打包進(jìn)一個(gè)html文件里,通過(guò)指定 chunks 屬性來(lái)告訴插件 只包含 哪些塊,或者exludeChunks指定不應(yīng)包含那些chunks。這里有個(gè)小問(wèn)題,我們無(wú)法讓文件剛好只包含他需要的塊。若想不包含未使用的chunks,只能根據(jù)實(shí)際情況手動(dòng)配置,用這個(gè)函數(shù)批量生成的文件,總會(huì)包含所有的公共打包文件。
5.3 mini-css-extract-plugin (prooduction)
該插件用于提取js文件中的css到單獨(dú)的css文件中。
//webpack.prod.js //...省略其他內(nèi)容 plugins:[ new CleanWebpackPlugin('build'), // 提取css new MiniCssExtractPlugin({ filename:'./css/[id].[contenthash].css' }), //優(yōu)化緩存 new webpack.HashedModuleIdsPlugin() ]
5.4 optimize-css-assets-webpack-plugin (production)
用于精簡(jiǎn)打包后的css代碼,設(shè)置在配置optimization的minimizer屬性中,這將會(huì)覆蓋webpack默認(rèn)設(shè)置,因此也要同時(shí)設(shè)置js的精簡(jiǎn)工具(這里我們用uglifyplugin插件):
optimization: { minimizer:[ new UglifyJsPlugin({ cache: true, parallel: true }), new OptimizeCSSAssetsPlugin() ] }
6.開發(fā)服務(wù)器、熱模塊替換 (development)
webpack.dev.js中增加如下內(nèi)容即可:
//...省略其他內(nèi)容 devServer:{ index:'index.html', hot:true, contentBase:path.resolve(__dirname,'./build'), port:3000, noInfo:true }, plugins:[ new webpack.HotModuleReplacementPlugin() ]
使用開發(fā)服務(wù)器可以在我們修改了源文件后自動(dòng)刷新,因?yàn)槭菍?shù)據(jù)放在內(nèi)存中,因此不會(huì)影響硬盤中build文件夾。熱模塊替換還需要在源文件做相應(yīng)修改。我們也為動(dòng)態(tài)導(dǎo)入語(yǔ)法進(jìn)行了相應(yīng)配置。
7.其他
public用于存放靜態(tài)資源,打包后也會(huì)在build/下創(chuàng)建一個(gè)同名文件夾,里面存放的是public會(huì)被使用到的資源。如果在.css文件里引用了public里的資源,如圖片,添加url的時(shí)候要使用絕對(duì)路徑:
<!-- src/css/page1.css --> .bg-img { background-image:url(/public/images/1.jpg) }
這樣通過(guò) http/https 打開的時(shí)候就能正常使用,如果是以文件形式打開(比如打包后雙擊page1.html),會(huì)發(fā)現(xiàn)瀏覽器顯示無(wú)法找到資源。通過(guò)導(dǎo)入圖片作為變量引用( import name from path ),既可使用絕對(duì)路徑,也可使用相對(duì)路徑。
以上是“webpack4如何打包vue前端多頁(yè)面項(xiàng)目”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。