這篇文章將為大家詳細講解有關vue-cli單頁到多頁應用的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
vue-cli到多頁應用
前言:我有一個cli創建的vue項目,但是我想做成多頁應用,怎么辦,廢話不多說,直接開擼~
約定:新增代碼部分在//add和//end中間 刪除(注釋)代碼部分在//del和//end中間,很多東西都寫在注釋里
第一步:cli一個vue項目
新建一個vue項目 官網 vue init webpack demo
cli默認使用webpack的dev-server服務,這個服務是做不了單頁的,需要手動建一個私服叫啥你隨意 一般叫dev.server或者dev.client
第二步:添加兩個方法處理出口入口文件(SPA默認寫死的)
進入剛剛創建vue項目 cd demo
在目錄下面找到build/utils.js文件
修改部分:
utils.js
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
//add
const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //功能:生成html文件及js文件并把js引入html
const pagePath = path.resolve(__dirname, '../src/views/'); //頁面的路徑,比如這里我用的views,那么后面私服加入的文件監控器就會從src下面的views下面開始監控文件
//end
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}
//add 新增一個方法處理入口文件(單頁應用的入口都是寫死,到時候替換成這個方法)
exports.createEntry = () => {
let files = glob.sync(pagePath + '/**/*.js');
let entries = {};
let basename;
let foldername;
files.forEach(entry => {
// Filter the router.js
basename = path.basename(entry, path.extname(entry), 'router.js');
foldername = path.dirname(entry).split('/').splice(-1)[0];
// If foldername not equal basename, doing nothing
// The folder maybe contain more js files, but only the same name is main
if (basename === foldername) {
entries[basename] = [
'webpack-hot-middleware/client?noInfo=true&reload=true&path=/__webpack_hmr&timeout=20000',
entry];
}
});
return entries;
};
//end
//add 新增出口文件
exports.createHtmlWebpackPlugin = () => {
let files = glob.sync(pagePath + '/**/*.html', {matchBase: true});
let entries = exports.createEntry();
let plugins = [];
let conf;
let basename;
let foldername;
files.forEach(file => {
basename = path.basename(file, path.extname(file));
foldername = path.dirname(file).split('/').splice(-1).join('');
if (basename === foldername) {
conf = {
template: file,
filename: basename + '.html',
inject: true,
chunks: entries[basename] ? [basename] : []
};
if (process.env.NODE_ENV !== 'development') {
conf.chunksSortMode = 'dependency';
conf.minify = {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
};
}
plugins.push(new HtmlWebpackPlugin(conf));
}
});
return plugins;
};
//end第三步:創建私服(不使用dev-server服務,自己建一個)
從express新建私服并配置(build文件夾下新建 我這里叫webpack.dev.client.js)
webpack.dev.client.js
/**
* created by qbyu2 on 2018-05-30
* express 私服
* */
'use strict';
const fs = require('fs');
const path = require('path');
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware'); //文件監控(前面配置了從views下面監控)
const webpackHotMiddleware = require('webpack-hot-middleware'); //熱加載
const config = require('../config');
const devWebpackConfig = require('./webpack.dev.conf');
const proxyMiddleware = require('http-proxy-middleware'); //跨域
const proxyTable = config.dev.proxyTable;
const PORT = config.dev.port;
const HOST = config.dev.host;
const assetsRoot = config.dev.assetsRoot;
const app = express();
const router = express.Router();
const compiler = webpack(devWebpackConfig);
let devMiddleware = webpackDevMiddleware(compiler, {
publicPath: devWebpackConfig.output.publicPath,
quiet: true,
stats: {
colors: true,
chunks: false
}
});
let hotMiddleware = webpackHotMiddleware(compiler, {
path: '/__webpack_hmr',
heartbeat: 2000
});
app.use(hotMiddleware);
app.use(devMiddleware);
Object.keys(proxyTable).forEach(function (context) {
let options = proxyTable[context];
if (typeof options === 'string') {
options = {
target: options
};
}
app.use(proxyMiddleware(context, options));
});
//雙路由 私服一層控制私服路由 vue的路由控制該頁面下的路由
app.use(router)
app.use('/static', express.static(path.join(assetsRoot, 'static')));
let sendFile = (viewname, response, next) => {
compiler.outputFileSystem.readFile(viewname, (err, result) => {
if (err) {
return (next(err));
}
response.set('content-type', 'text/html');
response.send(result);
response.end();
});
};
//拼接方法
function pathJoin(patz) {
return path.join(assetsRoot, patz);
}
/**
* 定義路由(私服路由 非vue路由)
* */
// favicon
router.get('/favicon.ico', (req, res, next) => {
res.end();
});
// http://localhost:8080/
router.get('/', (req, res, next)=>{
sendFile(pathJoin('index.html'), res, next);
});
// http://localhost:8080/home
router.get('/:home', (req, res, next) => {
sendFile(pathJoin(req.params.home + '.html'), res, next);
});
// http://localhost:8080/index
router.get('/:index', (req, res, next) => {
sendFile(pathJoin(req.params.index + '.html'), res, next);
});
module.exports = app.listen(PORT, err => {
if (err){
return
}
console.log(`Listening at http://${HOST}:${PORT}\n`);
})私服創建好了 安裝下依賴
有坑。。。
webpack和熱加載版本太高太低都不行
npm install webpack@3.10.0 --save-dev npm install webpack-dev-middleware --save-dev npm install webpack-hot-middleware@2.21.0 --save-dev npm install http-proxy-middleware --save-dev
第四步:修改配置webpack.base.conf.js
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
//del 注釋掉spa固定的單頁出口 末尾動態配上出口
// new HtmlWebpackPlugin({
// filename: 'index.html',
// template: 'index.html',
// inject: true
// }),
//end
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
//add
.concat(utils.createHtmlWebpackPlugin())
//end
})
//del
// module.exports = new Promise((resolve, reject) => {
// portfinder.basePort = process.env.PORT || config.dev.port
// portfinder.getPort((err, port) => {
// if (err) {
// reject(err)
// } else {
// // publish the new Port, necessary for e2e tests
// process.env.PORT = port
// // add port to devServer config
// devWebpackConfig.devServer.port = port
//
// // Add FriendlyErrorsPlugin
// devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
// compilationSuccessInfo: {
// messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
// },
// onErrors: config.dev.notifyOnErrors
// ? utils.createNotifierCallback()
// : undefined
// }))
//
// resolve(devWebpackConfig)
// }
// })
// })
//endwebpack.dev.conf.js
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
//del 注掉SPA的服務器
// devServer: {
// clientLogLevel: 'warning',
// historyApiFallback: {
// rewrites: [
// { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
// ],
// },
// hot: true,
// contentBase: false, // since we use CopyWebpackPlugin.
// compress: true,
// host: HOST || config.dev.host,
// port: PORT || config.dev.port,
// open: config.dev.autoOpenBrowser,
// overlay: config.dev.errorOverlay
// ? { warnings: false, errors: true }
// : false,
// publicPath: config.dev.assetsPublicPath,
// proxy: config.dev.proxyTable,
// quiet: true, // necessary for FriendlyErrorsPlugin
// watchOptions: {
// poll: config.dev.poll,
// }
// },
//end
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
//del 注釋掉spa固定的單頁出口 末尾動態配上出口
// new HtmlWebpackPlugin({
// filename: 'index.html',
// template: 'index.html',
// inject: true
// }),
//end
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
//add
.concat(utils.createHtmlWebpackPlugin())
//end
})
//del
// module.exports = new Promise((resolve, reject) => {
// portfinder.basePort = process.env.PORT || config.dev.port
// portfinder.getPort((err, port) => {
// if (err) {
// reject(err)
// } else {
// // publish the new Port, necessary for e2e tests
// process.env.PORT = port
// // add port to devServer config
// devWebpackConfig.devServer.port = port
//
// // Add FriendlyErrorsPlugin
// devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
// compilationSuccessInfo: {
// messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
// },
// onErrors: config.dev.notifyOnErrors
// ? utils.createNotifierCallback()
// : undefined
// }))
//
// resolve(devWebpackConfig)
// }
// })
// })
//end
module.exports = devWebpackConfig;webpack.prod.conf.js
plugins最后加上.concat(utils.createHtmlWebpackPlugin())
test環境一樣
第五步:修改package.json 指令配置
scripts下面'dev':
這樣執行的時候就不會走默認的dev-server而走你的私服了
"scripts": {
"dev": "node build/webpack.dev.client.js",
"start": "npm run dev",
"build": "node build/build.js"
},第六步:創建測試文件
src目錄下新建 views文件夾 (代碼注釋里有 當時配的目錄跟這個一致就可以 隨便你命名 遵循命名規范就行)
views 文件夾下新建兩個文件夾index和home 代表多頁 每頁單獨一個文件夾 文件夾下建對應文件

最后,npm run dev
這個時候你會發現,特么的什么鬼文章 報錯了啊
稍安勿躁~
兩個地方,
1.webpack.dev.client.js
//雙路由 私服一層控制私服路由 vue的路由控制該頁面下的路由
app.use(router)
app.use('/static', express.static(path.join(assetsRoot, 'static')));這個assetsRoot cli創建的時候是沒有的 在config/index.js 下面找到dev加上
assetsRoot: path.resolve(__dirname, '../dist'),

順便把dev和build的assetsPublicPath 路徑都改成相對路徑'./'
2.還是版本問題
webpack-dev-middleware 默認是3.1.3版本但是會報錯
具體哪個版本不報錯我也不知道
context.compiler.hooks.invalid.tap('WebpackDevMiddleware', invalid);找不到invalid 源碼里面是有的
卸載webpack-dev-middleware
npm uninstall webpack-dev-middleware
使用dev-server自帶的webpack-dev-middleware (cli單頁應用是有熱加載的)
重新install dev-server
npm install webpack-dev-server@2.10.0 --save-dev
npm run dev


關于“vue-cli單頁到多頁應用的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。