vue 路由的实现原理
路由这个概念最初是由后端提出来的, 在我们没有 SPA 单页面应用之前, 使用的一直都是后端路由, 根据不同的路由返回不同的页面, 后来随着单页面应用的诞生, 开始有了前端路由, 实现不刷新但是更新页面的效果
vue-router 是专为 Vue 打造的路由管理工具
vue-router 提供三种路由模式
hash 模式
默认模式, 通过路径中的 hash 值来控制路由跳转, 不存在兼容问题
history 模式
H5 新增的 history API, 相对 hash 而言, 不会显示 #号, 但是需要服务器端配置
abstract 模式
支持 JavaScript 的所有运行环境, 常指 Node.JS 服务器环境
hash 模式实现原理
示例: http://www.haochenguang.cn/#/home
在正常路径后跟一个 # 号, 匹配 # 后边的路径为前端路由, 通过 Windows.onhashchange 方法来操控路由改变的时候切换内容
onhashchange 方法的触发时机
直接更改浏览器地址, 在最后面增加或改变 #hash;
通过改变 location.href 或 location.hash 的值;
通过触发点击带锚点的链接;
浏览器前进后退可能导致 hash 的变化, 前提是两个网页地址中的 hash 值不同.
示例代码
- <body>
- <ul>
- <li>
- <a href="#/home">home</a>
- </li>
- <li>
- <a href="#/list">list</a>
- </li>
- <li>
- <a href="#/detail">detail</a>
- </li>
- </ul>
- </body>
- <script>
- Windows.onhashchange = function() {
- // 做页面切换渲染等
- console.log(location.href);
- console.log(location.hash);
- }
- </script>
通过点击 a 标签, 传递一个 hash 值, 然后通过 Windows.onhashchange 方法来监听 hash 的变化, 然后在这个事件触发的时候, 根据 location.hash 来动态的修改单页面应用的内容即可
history 模式实现原理
示例: http://www.haochenguang.cn/home
看起来与后端路由没有任何区别, 在 Windows.history 这个对象中, 包含浏览器的历史, 而在 html5 中, 新增了 pushState 和 replaceState, 通过这两个 API 可以改变 url 地址且不会发送请求, 同时还有 popstate 事件, 实现原理与 hash 相似, 只不过因为没有了 # 号, 所以刷新页面还是会向服务器发送请求, 而后端没有对应的处理措施的话, 会返回 404, 所以需要后端配合
popstate 方法的触发时机
仅仅调用 pushState 方法或 replaceState 方法 , 并不会触发该事件;
只有用户点击浏览器倒退按钮和前进按钮, 或者使用 JavaScript 调用 back,forward,go 方法时才会触发.
另外, 该事件只针对同一个文档, 如果浏览历史的切换, 导致加载不同的文档, 该事件也不会触发.
示例代码
- <body>
- <ul>
- <li>
- <a href="/home">home</a>
- </li>
- <li>
- <a href="/list">list</a>
- </li>
- <li>
- <a href="/detail">detail</a>
- </li>
- </ul>
- </body>
- <script>
- document.querySelectorAll('a').forEach(item => {
- item.onclick = function (e) {
- e.preventDefault();
- let link = item.getAttribute('href');
- Windows.history.pushState({link}, link, link);
- };
- });
- Windows.addEventListener('popstate', function (e) {
- console.log(e.state);
- console.log(location.href);
- })
- </script>
当点击浏览器的后退或前进按钮, 或者调用 history 上的 go,back 方法时, 就会触发事件, 打印出对应的数据, e.state 中就存放着 pushState 方法中的 state 对象
通过在修改路由信息的同时执行切换 DOM 的操作, 来实现了路由切换
abstract 模式实现原理
abstract 模式是不依赖于浏览器环境, 所以没有使用 hash 或者 history 等浏览器才会提供的 API, 而是 VueRouter 内部使用数组进行模拟了路由管理, 在 node 环境, 或者原生 App 环境下, 都会默认使用 abstract 模式, VueRouter 内部会根据所处的环境自行判断, 默认使用 hash 模式, 如果检测到没有浏览器 API 的时候, 就会使用 abstract 模式.
SPA 路由 history 模式上线后刷新 404
出现问题: 在使用 history 模式的时候, 由于浏览器路径与后端路径类似, 所以当刷新页面的时候, 浏览器还是会向服务器发送请求, 但是由于服务器并没有对应的路由页面, 所以会导致 404 空白
解决方案: 在服务端增加一个覆盖所有情况的候选资源: 如果 URL 匹配不到任何静态资源, 则应该返回同一个 index.HTML 页面, 这个页面就是你 App 依赖的页面. 同时这么做以后, 服务器就不再返回 404 错误页面, 因为对于所有路径都会返回 index.HTML 文件. 为了避免这种情况, 在 Vue 应用里面覆盖所有的路由情况, 然后在给出一个 404 页面. 或者, 如果是用 Node.JS 作后台, 可以使用服务端的路由来匹配 URL, 当没有匹配到路由的时候返回 404, 从而实现后退.
node 服务器
简单粗暴的一种方法, 如果没有读取到静态资源就直接返回 HTML 页面, 可以用于前端调试
- const http = require('http');
- const fs = require('fs');
- http.createServer((req,res) => {
- fs.readFile(__dirname + req.url, (err, data) => {
- if(!err) {
- res.end(data);
- }else {
- fs.readFile('index.html', (e, HTML) => {
- if(!e) {
- res.end(HTML);
- }
- })
- }
- })
- }).listen(8080, err => {
- !err&&(console.log('http://localhost:8080')
- })
nginx 静态服务器
常用方式, 直接使用 80 端口
- server {
- listen 80;
- server_name localhost;
- location / {
- root HTML;
- index index.HTML;
- try_files $uri $uri/ /index.HTML;
- }
- }
在 nginx 安装目录下, 将打包后的文件放置在安装目录下的 HTML 目录中, 接着打开 conf 目录中的 nginx.conf 文件, 修改 server 部分如上, 然后在 nginx 根目录使用 nginx -t 测试配置文件修改后的语法是否正确(如果有问题会报错), 如果没有错误的话, 使用命令 nginx -s reload 命令重启让配置文件生效, 打开浏览器, 输入 localhost 即可看到项目部署完成
常用方式, 同时运行多个程序时, 80 端口被占用时
- server {
- listen 8000;
- server_name localhost;
- location / {
- root HTML/project;
- index index.HTML;
- try_files $uri $uri/ /index.HTML;
- }
- }
在 HTML 目录下新建文件夹, 例如 project, 将打包后的文件放置在 project 文件夹下, 在 conf/nginx.conf 文件中, 原 80 端口的下方新加入上述代码, 接着重复 nginx -t 与 nginx -s reload 再次打开浏览器, 输入 localhost:8000 就可以看到项目部署完成
$route 和 $router 的区别
$route 是路由信息对象, 包括 path,params,hash,query,fullPath,meta,name 等路由信息参数, 可以通过 $route 来获取当前路由的各种参数
path String 保存当前绝对路径信息
params Object 保存当前的 params 参数对象
hash String 保存当前的 hash 信息, 不带 #号, 如果没有 hash 为空字符串
query Object 保存当前的 query 参数对象
meta Object 保存当前路由的源信息
fullPath String 保存完成解析后的路径信息, 包含 hash,query 等参数
name String 保存路由 name 值
$router 是路由实例对象, 包含的是 VueRouter 实例上的方法以及配置属性, 常用于编程式导航;
- push
- Function(Object|String)
- this.$router.push('/home?tab=all')
- this.$router.push({
- path: '/home', query:{
- tab:'all'
- }
- })
- replace
- Function(Object|String)
- this.$router.replace('/home?tab=all')
- this.$router.replace({
- path: '/home', query:{
- tab:'all'
- }
- })
- go Function(Number)
this.$router.go(-1) 操作 history 的 API 进行后退
this.$router.go(1) 操作 history 的 API 进行前进
......
自定义过滤器详解
在 Vue1.x 的版本中, 是有内置过滤器的, 而到了 2.0 以后, 废除了内置过滤器, 允许我们自定义过滤器
自定义过滤器的使用方法
在插值表达式 {{}} 以及 v-bind: 指令绑定的属性的冒号内, 可以使用自定义过滤器
使用时, 在 JS 表达式的结尾使用管道符来指示过滤器
示例:
{{date| formatDate}}
或者
<input v-bind:value="date | formatDate" />
自定义过滤器的使用场景
在我们前端拿到后台的数据时, 经常有些数据是不能直接展示出来的, 需要经过处理之后再展示, 在 Vue 中, 我们可以通过 methods 来做, 也可以通过 computed 计算属性来做, 但是最好还是通过过滤器 filters 来做, 因为它是在原数据基础上进行过滤, 不会修改原有数据, 使用 computed 以及 methods 属于大材小用了
过滤器的定义
在 Vue 中分为全局过滤器和局部过滤器
- // 全局过滤器
- Vue.filter(过滤器名称, function(value) {
- return value; // 处理 value,vlaue 就是需要过滤的数据
- })
- // 局部过滤器
- new Vue({
- el: '#app',
- filters: {
- '过滤器名称'(value) {
- return value; // 处理 value,vlaue 就是需要过滤的数据
- }
- }
- })
实现自定义过滤器
无参数过滤器
- Vue.filter('formatDate', function(value) {
- if(!value) return;
- return new Date(value).toLocaleString();
- })
- // HTML
- <div>{{ 1562731217574 | formatDate }}</div> // 2019/7/10 下午 12:00:17
有参数过滤器
- Vue.filter('formatPrice', function(value, num) {
- if(!value) return;
- return value.toFixed(num);
- })
- // HTML
- <div>{{ 15.6264 | formatPrice(2)}}</div> // 15.63
多个参数的过滤器
- Vue.filter('formatText', function(value, start, end) {
- if(!value) return;
- return start + '-' + value + '-' + end;
- })
- // HTML
- <div>{{ '中间' | formatText('开始','结束')}}</div> // 开始 - 中间 - 结束
过滤器串联, 可以多个串联
<div>{{ '中间' | formatText('开始','结束') | formatText('串联 start','串联 end') }}</div>
自定义指令详解
见前端常见面试题(三)@郝晨光
assets 和 static/public 的区别
在 vue-cli 工具中, 有两个存放静态资源的目录, 一个是 src 目录下的 assets, 另一个在 vue-cli2 下是 static 目录, 在 vue-cli3 下是 public 目录, 那这两个目录的区别是什么呢?
首先需要知道: 在 *.vue 组件中, 所有的 templates 和 CSS 都会被 vue-HTML-loader 和 CSS-loader 解析, 寻找资源的 URL.
例如: <img src="./assets/logo.png">或者 background: url('./assets/logo.png'), 这两个 url 路径都会被 webpack 解析成资源模块依赖, 而由于这些静态文件大都不是 JS 文件, 而 vue-cli 工具利用各种 loader 帮助我们解析了这些资源
资源处理规则
相对路径, ./assets/logo.PNG 将会被解析成一个模块依赖.
没有前缀的路径, assets/logo.PNG 将会被看成./assets/logo.PNG 进行解析模块依赖
前缀带~ 的 URL 会被当成模块请求, 例如:
<img src="~assets/logo.png">
相对根目录的 URL/assets/logo.PNG 是不会被处理的
JavaScript 中使用静态资源
例如:
imgUrl: require('./assets/logo.png')
assets 和 static/public 的区别
在 assets 目录下的静态资源会被 webpack 解析成资源模块依赖, 在项目打包的时候会随着项目一起打包
而在 static/public 目录下的静态资源不会被解析, 是真正的静态资源
结言
感谢您的查阅, 代码冗余或者有错误的地方望不吝赐教; 菜鸟一枚, 请多关照
来源: http://www.jianshu.com/p/0f1d3e173a7c