您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)vue-cli單頁(yè)到多頁(yè)應(yīng)用的示例分析,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
vue-cli到多頁(yè)應(yīng)用
前言:我有一個(gè)cli創(chuàng)建的vue項(xiàng)目,但是我想做成多頁(yè)應(yīng)用,怎么辦,廢話(huà)不多說(shuō),直接開(kāi)擼~
約定:新增代碼部分在//add和//end中間 刪除(注釋)代碼部分在//del和//end中間,很多東西都寫(xiě)在注釋里
第一步:cli一個(gè)vue項(xiàng)目
新建一個(gè)vue項(xiàng)目 官網(wǎng) vue init webpack demo
cli默認(rèn)使用webpack的dev-server服務(wù),這個(gè)服務(wù)是做不了單頁(yè)的,需要手動(dòng)建一個(gè)私服叫啥你隨意 一般叫dev.server或者dev.client
第二步:添加兩個(gè)方法處理出口入口文件(SPA默認(rèn)寫(xiě)死的)
進(jìn)入剛剛創(chuàng)建vue項(xiàng)目 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/'); //頁(yè)面的路徑,比如這里我用的views,那么后面私服加入的文件監(jiān)控器就會(huì)從src下面的views下面開(kāi)始監(jiān)控文件 //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 新增一個(gè)方法處理入口文件(單頁(yè)應(yīng)用的入口都是寫(xiě)死,到時(shí)候替換成這個(gè)方法) 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
第三步:創(chuàng)建私服(不使用dev-server服務(wù),自己建一個(gè))
從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'); //文件監(jiān)控(前面配置了從views下面監(jiān)控) 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的路由控制該頁(yè)面下的路由 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`); })
私服創(chuàng)建好了 安裝下依賴(lài)
有坑。。。
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固定的單頁(yè)出口 末尾動(dòng)態(tài)配上出口 // 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
webpack.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的服務(wù)器 // 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固定的單頁(yè)出口 末尾動(dòng)態(tài)配上出口 // 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環(huán)境一樣
第五步:修改package.json 指令配置
scripts下面'dev':
這樣執(zhí)行的時(shí)候就不會(huì)走默認(rèn)的dev-server而走你的私服了
"scripts": { "dev": "node build/webpack.dev.client.js", "start": "npm run dev", "build": "node build/build.js" },
第六步:創(chuàng)建測(cè)試文件
src目錄下新建 views文件夾 (代碼注釋里有 當(dāng)時(shí)配的目錄跟這個(gè)一致就可以 隨便你命名 遵循命名規(guī)范就行)
views 文件夾下新建兩個(gè)文件夾index和home 代表多頁(yè) 每頁(yè)單獨(dú)一個(gè)文件夾 文件夾下建對(duì)應(yīng)文件
最后,npm run dev
這個(gè)時(shí)候你會(huì)發(fā)現(xiàn),特么的什么鬼文章 報(bào)錯(cuò)了啊
稍安勿躁~
兩個(gè)地方,
1.webpack.dev.client.js
//雙路由 私服一層控制私服路由 vue的路由控制該頁(yè)面下的路由 app.use(router) app.use('/static', express.static(path.join(assetsRoot, 'static')));
這個(gè)assetsRoot cli創(chuàng)建的時(shí)候是沒(méi)有的 在config/index.js 下面找到dev加上
assetsRoot: path.resolve(__dirname, '../dist'),
順便把dev和build的assetsPublicPath 路徑都改成相對(duì)路徑'./'
2.還是版本問(wèn)題
webpack-dev-middleware 默認(rèn)是3.1.3版本但是會(huì)報(bào)錯(cuò)
具體哪個(gè)版本不報(bào)錯(cuò)我也不知道
context.compiler.hooks.invalid.tap('WebpackDevMiddleware', invalid);
找不到invalid 源碼里面是有的
卸載webpack-dev-middleware
npm uninstall webpack-dev-middleware
使用dev-server自帶的webpack-dev-middleware (cli單頁(yè)應(yīng)用是有熱加載的)
重新install dev-server
npm install webpack-dev-server@2.10.0 --save-dev
npm run dev
關(guān)于“vue-cli單頁(yè)到多頁(yè)應(yīng)用的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guā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)容。