首页 > 编程 > JavaScript > 正文

手把手教你vue-cli单页到多页应用的方法

2019-11-19 13:44:34
字体:
来源:转载
供稿:网友

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')//addconst glob = require('glob');const HtmlWebpackPlugin = require('html-webpack-plugin');  //功能:生成html文件及js文件并把js引入htmlconst pagePath = path.resolve(__dirname, '../src/views/'); //页面的路径,比如这里我用的views,那么后面私服加入的文件监控器就会从src下面的views下面开始监控文件//endexports.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路由) * */// faviconrouter.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/homerouter.get('/:home', (req, res, next) => { sendFile(pathJoin(req.params.home + '.html'), res, next);});// http://localhost:8080/indexrouter.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-devnpm install webpack-dev-middleware --save-devnpm install webpack-hot-middleware@2.21.0 --save-devnpm 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.HOSTconst 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)//   }//  })// })//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.HOSTconst 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)//   }//  })// })//endmodule.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

总结:核心点就在创建并配置私服和修改出口入口配置,坑就在版本不兼容

建议:cli一个vue的demo项目 从头撸一遍 再在实际项目里使用,而不是copy一下运行没问题搞定~

建议而已,你怎么打人,呜呜呜~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表