詳解Webpack4多頁應(yīng)用打包方案
前言
學(xué)習(xí)了 webpack 之后,將自己的博客的前端進行重構(gòu),由于自己的博客是多頁應(yīng)用,所以研究了下多頁應(yīng)用的打包方案。在這里把最后配置的成果分享下,不足之處,請指正。(文字不多,全是代碼,不是配置教程,所以沒有特別詳細的寫,只是一個參考)
項目地址: https://github.com/Ray-daydayup/MPA-webpack
文件目錄結(jié)構(gòu)
項目目錄結(jié)構(gòu)
首先先看下我的項目的目錄結(jié)構(gòu)
myblog-webpack ├── dist // 打包輸出文件夾 ├── src // 源代碼 │ ├── api // 請求文件夾,存放封裝的請求方法 │ ├── assets // 靜態(tài)資源文件夾 │ ├── lib // 一些庫 │ ├── pages // 頁面 │ │ ├── about // 頁面名稱 │ │ │ ├── index.html // html模板 │ │ │ └── index.js // 入口js │ │ ├── category │ │ │ ├── index.html │ │ │ └── index.js │ │ ├── detail │ │ │ ├── index.html │ │ │ └── index.js │ │ ├── index │ │ │ ├── index.html │ │ │ └── index.js │ │ └── tag │ │ ├── index.html │ │ └── index.js │ ├── styles // 樣式文件 │ └── utils // 工具函數(shù) ├── package.json ├── README.md ├── package-lock.json ├── webpack.base.js // 公共配置 ├── webpack.dev.js // 開發(fā)環(huán)境配置 └── webpack.prod.js // 生產(chǎn)環(huán)境配置
打包輸出文件目錄結(jié)構(gòu)
最終打包輸出的目錄如下
dist ├── assets ├── css │ ├── commons672a1e57.css │ └── index6085d612.css ├── js │ ├── aboutfc723f0e.js │ ├── categorye4be3bd6.js │ ├── commons78f1dd3f.js │ ├── detail0df434c5.js │ ├── indexe1e985d9.js │ ├── markdown-it │ │ ├── highlight.vendors.js │ │ ├── markdown-it-integrated.js │ │ ├── markdown-it-integrated.min.js │ │ ├── markdown-it-integrated.min.js.LICENSE.txt │ │ └── markdown-it.vendors.js │ └── tagf1c1035c.js ├── about.html ├── category.html ├── detail.html ├── favicon.ico ├── index.html └── tag.html
webpack 配置文件
注意:文中各部分所需的包名,并沒有書寫,請在附錄中查找?。?!相關(guān)配置的意義可以查找官方文檔
公共配置
動態(tài)獲取 entry 和設(shè)置 html-webpack-plugin
多頁應(yīng)用的打包思路是 每個頁面對應(yīng)一個 entry ,一個 html-webpack-plugin ,但每次新增或刪除頁面需要改 webpack 配置,所以需要根據(jù)文件目錄動態(tài)獲取入口,來設(shè)置 html-webpack-plugin 。
利用一個庫 glob 來實現(xiàn)對文件目錄的讀取 具體代碼如下,相關(guān)配置的意義可以查找官方文檔
const path = require('path') const glob = require('glob') const HtmlWebpackPlugin = require('html-webpack-plugin') const setMPA = () => { const entry = {} const htmlWebpackPlugins = [] const entryFiles = glob.sync(path.join(__dirname, './src/pages/*/index.js')) // 匹配各頁面的對應(yīng)入口文件 entryFiles.forEach((item) => { const pageName = item.match(/pages\/(.*)\/index.js/)[1] // 獲取文件夾名,即頁面名稱 entry[pageName] = item // 設(shè)置入口文件路徑 // 設(shè)置html-webpack-plugin數(shù)組 htmlWebpackPlugins.push( new HtmlWebpackPlugin({ template: path.join(__dirname, `./src/pages/${pageName}/index.html`),//模板地址 filename: `${pageName}.html`, //輸出文件名 chunks: [pageName], // 插入的js chunk名稱,和output有關(guān) inject: true, favicon: path.join(__dirname, './src/assets/favicon.ico'), // 圖標(biāo) minify: { //壓縮代碼 html5: true, collapseWhitespace: true, preserveLineBreaks: false, minifyCSS: true, minifyJS: true, removeComments: false } }) ) }) return { entry, htmlWebpackPlugins } } const { entry, htmlWebpackPlugins } = setMPA()
使用 ES6 和 async、await ,以及 eslint
使用 ES6 和 async、await 以及 eslint ,需要借助 babel-loader 和 eslint-loader ,其具體配置很簡單
babel 相關(guān)的庫: @babel/core 、 @babel/preset-env 、 @babel/plugin-transform-regenerator 、 @babel/plugin-transform-runtime 、 eslint 相關(guān)的庫: babel-eslint 、 eslint
module.rules 的配置
{ test: /\.js$/, use: ['babel-loader', 'eslint-loader'] }
.babelrc 文件的配置
{ "presets": [ "@babel/preset-env" ], "plugins": [ //設(shè)置支持async和await "@babel/plugin-transform-runtime", "@babel/plugin-transform-regenerator" ] }
.eslintrc.js 的配置
module.exports = { parser: 'babel-eslint', extends: ['alloy'], // eslint標(biāo)準(zhǔn)請自行選擇下載 env: { browser: true, node: true }, rules: { 'no-new': 'off', 'no-proto': 'off' // 'no-console': 'error' } }
加載圖片
利用 url-loader ,具體 module.rules 配置如下
{ test: /\.(png|svg|jpg|gif)$/, use: [ { loader: 'url-loader', options: { esModule: false, name: '[name][hash:8].[ext]', //文件指紋 limit: 10240, // 轉(zhuǎn)base64 outputPath: './assets/images/' // 圖片文件輸出目錄和publicPath有關(guān) } } ] }
清理打包目錄和復(fù)制文件到打包目錄
自動清理打包目錄利用 clean-webpack-plugin 插件,復(fù)制文件利用 copy-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') const basePlugins = [ ...htmlWebpackPlugins, //之前配置的htmlWebpackPlugin數(shù)組 new CleanWebpackPlugin(), // 自動清理構(gòu)建目錄 new CopyWebpackPlugin({ patterns: [ { from: path.join(__dirname, 'src/lib/markdown-it'), // 源 to: path.join(__dirname, 'dist/js/markdown-it') // 目標(biāo)位置 } ] }) ]
.browserslistrc 配置
# Browsers that we support last 2 version > 1% iOS 7
開發(fā)環(huán)境配置
熱更新以及 devServer
具體配置如下:
plugins: [...basePlugins, new webpack.HotModuleReplacementPlugin()], //熱更新插件 devServer: { contentBase: 'dist', // 開啟服務(wù)的目錄 hot: true, //熱更新開啟 proxy: { '/api': { target: 'http://raydaydayup.cn:3000', //代理 pathRewrite: { '^/api': '' } } } }
CSS 相關(guān)配置
module.rules 的配置如下
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }
出口
output: { filename: '[name].js', path: path.join(__dirname, 'dist') }
生產(chǎn)環(huán)境配置
出口
output: { filename: 'js/[name][chunkhash:8].js', // 統(tǒng)一輸出到j(luò)s文件夾 path: path.join(__dirname, 'dist'), publicPath: '/' // 服務(wù)路徑 }
js 代碼壓縮
利用 terser-webpack-plugin 插件,配置 optimization
const TerserPlugin = require('terser-webpack-plugin') optimization: { minimize: true, minimizer: [ new TerserPlugin({ include: [/\.js$/] }) ] }
CSS 相關(guān)配置
CSS 代碼壓縮,利用 optimize-css-assets-webpack-plugin 和 cssnano
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') plugins: [ new OptimizeCSSAssetsPlugin({ assetNameRegExp: /\.css$/g, cssProcessor: require('cssnano') }) ]
CSS 分離單獨文件,利用 mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin') module: { rules: [ ...baseModuleRules, { test: /\.less$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { esModule: true } }, 'css-loader', 'less-loader' ] }, { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { esModule: true } }, 'css-loader' ] } ] }, plugins: [ ...basePlugins, new MiniCssExtractPlugin({ filename: 'css/[name][contenthash:8].css' // 輸出文件名和地址 }), ],
PostCSS 插件 autoprefixer 自動補齊 CSS3 前綴
module: { rules: [ ...baseModuleRules, { test: /\.less$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { esModule: true } }, 'css-loader', { loader: 'postcss-loader', options: { plugins: () => [require('autoprefixer')] } }, 'less-loader' ] }, ] },
分離公共文件(CSS和JS)
配置 optimization.splitChunks
optimization: { splitChunks: { cacheGroups: { commons: { name: 'commons', chunks: 'all', minChunks: 2 // 最少引用兩次 } } } }
使用相關(guān)注意
html-webpack-plugin 模板使用以及 html-loader
導(dǎo)入 html 片段,在相應(yīng)位置寫下面的代碼就像,需要注意的是, html-loader 加載的 html 片段內(nèi)部 <%%> 語法會失效
<%= require("html-loader!@/components/recent.html") %>
html 中圖片的加載。在 html 中相關(guān)的 img 標(biāo)簽的圖片, html-loader 加載時會自動調(diào)用 url-loader ,但是存在問題,加載出來的路徑?jīng)]有 "" 。為了避免這個問題,我就沒有再 html-loader 加載的 html 片段中使用圖片,而是直接再 html 模
板中直接用 require
<img src="<%= require('@/assets/logo362x82.png') %>" alt="" />
附錄
配置文件完整版
webpack.base.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin') const path = require('path') const glob = require('glob') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') const setMPA = () => { const entry = {} const htmlWebpackPlugins = [] const entryFiles = glob.sync(path.join(__dirname, './src/pages/*/index.js')) entryFiles.forEach((item) => { const pageName = item.match(/pages\/(.*)\/index.js/)[1] entry[pageName] = item htmlWebpackPlugins.push( new HtmlWebpackPlugin({ template: path.join(__dirname, `./src/pages/${pageName}/index.html`), filename: `${pageName}.html`, chunks: [pageName], inject: true, favicon: path.join(__dirname, './src/assets/favicon.ico'), minify: { html5: true, collapseWhitespace: true, preserveLineBreaks: false, minifyCSS: true, minifyJS: true, removeComments: false } }) ) }) return { entry, htmlWebpackPlugins } } const { entry, htmlWebpackPlugins } = setMPA() const baseModuleRules = [ { test: /\.js$/, use: ['babel-loader', 'eslint-loader'] }, { test: /\.(png|svg|jpg|gif)$/, use: [ { loader: 'url-loader', options: { esModule: false, name: '[name][hash:8].[ext]', limit: 10240, outputPath: './assets/images/' } } ] } ] const basePlugins = [ ...htmlWebpackPlugins, new CleanWebpackPlugin(), new CopyWebpackPlugin({ patterns: [ { from: path.join(__dirname, 'src/lib/markdown-it'), to: path.join(__dirname, 'dist/js/markdown-it') } ] }) ] module.exports = { entry, baseModuleRules, basePlugins }
webpack.dev.js
const webpack = require('webpack') const path = require('path') const { entry, baseModuleRules, basePlugins } = require('./webpack.base.js') module.exports = { mode: 'development', entry, output: { filename: '[name].js', path: path.join(__dirname, 'dist') }, module: { rules: [ ...baseModuleRules, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, resolve: { // 設(shè)置別名 alias: { '@': path.join(__dirname, 'src') // 這樣配置后 @ 可以指向 src 目錄 } }, plugins: [...basePlugins, new webpack.HotModuleReplacementPlugin()], devServer: { contentBase: 'dist', hot: true, proxy: { '/api': { target: 'http://raydaydayup.cn:3000', pathRewrite: { '^/api': '' } } } } }
webpack.prod.js
const { entry, baseModuleRules, basePlugins } = require('./webpack.base.js') const path = require('path') const TerserPlugin = require('terser-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') module.exports = { mode: 'production', entry, output: { filename: 'js/[name][chunkhash:8].js', path: path.join(__dirname, 'dist'), publicPath: '/' }, resolve: { // 設(shè)置別名 alias: { '@': path.resolve('src') // 這樣配置后 @ 可以指向 src 目錄 } }, module: { rules: [ ...baseModuleRules, { test: /\.less$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { esModule: true } }, 'css-loader', { loader: 'postcss-loader', options: { plugins: () => [require('autoprefixer')] } }, 'less-loader' ] }, { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { esModule: true } }, 'css-loader' ] } ] }, plugins: [ ...basePlugins, new MiniCssExtractPlugin({ filename: 'css/[name][contenthash:8].css' }), new OptimizeCSSAssetsPlugin({ assetNameRegExp: /\.css$/g, cssProcessor: require('cssnano') }) ], optimization: { minimize: true, minimizer: [ new TerserPlugin({ include: [/\.js$/] }) ], splitChunks: { cacheGroups: { commons: { name: 'commons', chunks: 'all', minChunks: 2 } } } } }
package.json文件
{ "name": "myblog-webpack", "version": "1.0.0", "description": "", "main": ".eslintrc.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.prod.js", "dev": "webpack-dev-server --config webpack.dev.js --open" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.10.2", "@babel/plugin-transform-regenerator": "^7.10.3", "@babel/plugin-transform-runtime": "^7.10.3", "@babel/preset-env": "^7.10.2", "autoprefixer": "^9.8.4", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^6.0.3", "css-loader": "^3.6.0", "cssnano": "^4.1.10", "eslint": "^7.2.0", "eslint-config-alloy": "^3.7.2", "eslint-loader": "^4.0.2", "file-loader": "^6.0.0", "glob": "^7.1.6", "html-loader": "^1.1.0", "html-webpack-plugin": "^4.3.0", "less-loader": "^6.1.2", "mini-css-extract-plugin": "^0.9.0", "optimize-css-assets-webpack-plugin": "^5.0.3", "postcss-loader": "^3.0.0", "style-loader": "^1.2.1", "terser-webpack-plugin": "^3.0.5", "url-loader": "^4.1.0", "webpack": "^4.43.0", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.11.0" }, "dependencies": { "axios": "^0.19.2" } }
到此這篇關(guān)于詳解Webpack4多頁應(yīng)用打包方案的文章就介紹到這了,更多相關(guān)Webpack4多應(yīng)用打包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Bootstrap typeahead插件實現(xiàn)搜索框自動補全的方法
這篇文章主要介紹了使用Bootstrap typeahead插件實現(xiàn)搜索框自動補全的方法的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07- 在一些電商網(wǎng)站上,經(jīng)常看到有商品圖片被放大查看的功能,所以本文將使用前端技術(shù)實現(xiàn)一個簡單的圖片放大鏡功能,希望能給大家?guī)硪欢ǖ膸椭?/div> 2023-06-06
最新評論