Vue路由和组件异步加载

928 阅读2分钟

SPA简介

SPA,即单页面应用(Single Page Application)。单页应用程序 (SPA) 是加载单个 HTML 页面并在用户与应用程序交互时动态更新该页面的 Web 应用程序。仅在该 Web 页面初始化时加载相应的 HTMLJavaScriptCSS 。一旦页面加载完成, SPA 不会因为用户的操作而进行页面的重新加载或跳转,而是利用 JavaScript 动态的变换 HTML

Vue-router

hash history

hash 模式是一种把前端路由的路径用 # 拼接在真实 url 后面的模式。当 # 后面的路径发生变化时,不会触发页面重新加载(hash的改变是记录在 window.history 中),浏览器并不会重新发起请求,而是会触发 onhashchange 事件,监听hash 的改变。兼容性相对于history来说更好,能兼容到IE8

模拟实现

文件目录

图片.png

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 的 backforwardgo 方法,或者点击后退/前进触发 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'));
  }
});

页面效果图

图片.png

图片.png