欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-14 11:39:27
vue-cli到多页应用 前言:我有一个cli创建的vue项目,但是我想做成多页应用,怎么办,废话不多说,直接开撸~ 约定:新增代码部分在//add和//end中间 删...

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)
//   }
//  })
// })
//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的服务器
 // 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 代表多页 每页单独一个文件夹 文件夹下建对应文件

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

最后,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'),

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

顺便把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单页到多页应用的方法

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

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

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

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

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