前言:我有一個 cli 創建的 vue 項目,但是我想做成多頁應用,怎麼辦,廢話不多說,直接開擼~_
約定:新增代碼部分在 //add 和 //end 中間 刪除 (註釋) 代碼部分在 //del 和 //end 中間,很多東西都寫在註釋裏_
新建一個 vue 項目 官網:
vue init webpack demo
cli 默認使用 webpack 的 dev-server 服務,這個服務是做不了單頁的,需要手動建一個私服叫啥你隨意 一般叫 dev.server 或者 dev.client
進入剛剛創建 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] = process.env.NODE_ENV === 'development' ?
[
'webpack-hot-middleware/client?noInfo=true&reload=true&path=/__webpack_hmr&timeout=20000',
entry
]: [entry];
}
});
return entries;
};
//end
//add 新增出口文件
exports.createHtmlWebpackPlugin = (publicModule) => {
let files = glob.sync(pagePath + '/**/*.html', {matchBase: true});
let entries = exports.createEntry();
let plugins = [];
let conf;
let basename;
let foldername;
publicModule = publicModule || [];
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
};
// 在構建生產環境時,需要指定共用模塊
conf.chunks = [...publicModule, ...conf.chunks];
}
plugins.push(new HtmlWebpackPlugin(conf));
}
});
return plugins;
};
//end
從 express 新建私服並配置 (build 文件夾下新建 我這裏叫 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(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(<