SPA简介
SPA,即单页面应用(Single Page Application)。单页应用程序 (SPA) 是加载单个 HTML 页面并在用户与应用程序交互时动态更新该页面的 Web 应用程序。仅在该 Web 页面初始化时加载相应的 HTML 、JavaScript 、 CSS 。一旦页面加载完成, SPA 不会因为用户的操作而进行页面的重新加载或跳转,而是利用 JavaScript 动态的变换 HTML。
Vue-router
hash history
hash 模式是一种把前端路由的路径用 # 拼接在真实 url 后面的模式。当 # 后面的路径发生变化时,不会触发页面重新加载(hash的改变是记录在 window.history 中),浏览器并不会重新发起请求,而是会触发 onhashchange 事件,监听hash 的改变。兼容性相对于history来说更好,能兼容到IE8。
模拟实现
文件目录
main.js
class HashRouter {
constructor() {
// 存储hash与callback键值对
this.routes = {};
// 保存当前的hash
this.currentHash = '';
// 绑定事件
const hashChangeUrl = this.hashChangeUrl.bind(this);
// 页面加载事件
window.addEventListener('load', hashChangeUrl, false);
// 监听hashchange事件
window.addEventListener('hashchange', hashChangeUrl, false);
}
// path路径和callback函数对应起来,并且使用 上面的this.routes存储起来
route(path, callback) {
this.routes[path] = callback || function() {};
}
hashChangeUrl() {
/*
获取当前的hash值
location.hash 获取的值为:"#/a, 因此 location.hash.slice(1) = '/a' 这样的
*/
console.log('hashChangeUrl');
this.currentHash = location.hash.slice(1) || '/';
// 执行当前hash对应的callback函数
this.routes[this.currentHash] && this.routes[this.currentHash]();
}
}
// 初始化
const Router = new HashRouter();
const body = document.querySelector('body');
const changeColor = function(color) {
body.style.backgroundColor = color;
};
// 注册函数
Router.route('/', () => {
changeColor('red');
});
Router.route('/a', () => {
changeColor('green');
});
Router.route('/b', () => {
changeColor('#CDDC39');
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hash路由demo</title>
</head>
<body>
<ul>
<li><a href="#/">我是主页</a></li>
<li><a href="#/a">我是a页面</a></li>
<li><a href="#/b">我是b页面</a></li>
</ul>
</body>
</html>
webpack配置
const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
// 清除dist目录下的文件
const ClearWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
// 引入打包html文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入 CommonsChunkPlugin 插件
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
module.exports = {
entry: {
main: './js/main.js'
},
output: {
filename: '[name].js',
// 将输出的文件都放在dist目录下
path: path.resolve(__dirname, 'dist'),
// publicPath: './'
},
module: {
rules: [
{
// 使用正则去匹配
test: /\.styl$/,
use: ExtractTextPlugin.extract({
fallback: {
loader: 'style-loader'
},
use: [
{
loader: 'css-loader',
options: {}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('postcss-cssnext')(),
require('cssnano')(),
require('postcss-pxtorem')({
rootValue: 16,
unitPrecision: 5,
propWhiteList: []
}),
require('postcss-sprites')()
]
}
},
{
loader: 'stylus-loader',
options: {}
}
]
})
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[ext]'
}
},
{
test: /\.js$/,
exclude: /(node_modules)/, // 排除文件
loader: 'babel-loader'
}
]
},
resolve: {
extensions: ['*', '.js', '.json']
},
devtool: 'source-map',
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 8072,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
// hot: true,
inline: true,
// open: true,
overlay: true,
stats: 'errors-only'
},
plugins: [
new ClearWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: './index.html' // 模版文件
}),
new ExtractTextPlugin({
// 从js文件中提取出来的 .css文件的名称
filename: `main.css`
}),
new CommonsChunkPlugin({
name: ['chunk', 'vender'], // 公共代码的chunk命名为 'verder'
filename: '[name].bundle.js' // 生成的文件名为 vender.bundle.js
})
]
};
history
history API 是 H5 提供的新特性,允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求。
- 手动调用 history 的
back、forward和go方法,或者点击后退/前进触发popstate事件,监听进行页面更新; - 调用history.pushState或history.replaceState触发相应的函数后,在后面手动添加回调更新页面;
- 需配置nginx,或者后端需进行配置;
location / {
try_files $uri $uri/ /index.html;
}
模拟实现
main.js
class HistoryRoutes {
constructor() {
// 保存对应键和函数
this.routes = {};
// 监听popstate事件
window.addEventListener('popstate', (e) => {
console.log('popstate');
const path = this.getState();
this.routes[path] && this.routes[path]();
});
}
// 获取路由路径
getState() {
const path = window.location.pathname;
return path ? path : '/';
}
// path路径和callback函数对应起来
route(path, callback) {
this.routes[path] = callback || function() {};
}
// init(path) {
// history.replaceState(null, null, path);
// this.routes[path] && this.routes[path]();
// }
go(path) {
// 压入浏览历史对象
history.pushState(null, null, path);
// 修改页面内容
// this.routes[path] && this.routes[path]();
this.routes[path]();
}
}
window.Router = new HistoryRoutes();
console.log(location.pathname);
// Router.init(location.pathname);
const body = document.querySelector('body');
const changeColor = function(color) {
body.style.backgroundColor = color;
};
// 注册函数
Router.route('/', () => {
changeColor('red');
});
Router.route('/a', () => {
changeColor('green');
});
Router.route('/b', () => {
changeColor('#CDDC39');
});
const ul = document.querySelector('ul');
ul.addEventListener('click', e => {
console.log(e.target);
if (e.target.tagName === 'A') {
e.preventDefault();
Router.go(e.target.getAttribute('href'));
}
});