前端面试中,会问到很是多的知识点。框架,几乎是必问的问题之一。Vue 做为目前最流行的 SPA 框架之一,是面试过程当中的重头戏。Vue Router 做为 Vue 生态中极其重要的角色,也是咱们必须掌握的一项技能。javascript
这篇文章将介绍 Vue Router 的使用,而且本身动手实现一个简易版的 Vue Router。css
首先使用 vue cli 建立一个 Vue 项目来回顾一下 vue router 的使用。html
全局安装 vue cli。前端
npm i -g @vue/cli
复制代码
安装完成后检查版本是否正常。vue
vue --version
复制代码
而后建立一个演示项目。java
vue create vue-router-demo
复制代码
首先使用自定义选择。Manually select features。node
vue cli 会询问一些问题。只须要选择三项 Babel, Router, Linter。webpack
这样 vue cli 会帮咱们建立 vue router 的基本代码结构。nginx
进入项目并启动项目。web
npm run serve
复制代码
而后就能够在浏览器中看到路由的效果了。
在 vue 中使用 vue router 的步骤大体有以下几步。
建立路由对应的页面。
默认在 views 文件夹中。
使用 Vue.use(VueRouter)来注册路由插件。Vue.use 方法是专门用来注册插件的,若是传入的是函数,会直接调用。若是传入的是对象,会调用对象的 install 方法。
默认在 router/index.js 中。
首先定义一套路由规则,路由规则是一个数组,数组中包含不少对象,每个对象都是一个规则。对象上面会有 path 和 component 等属性,path 表明着路径,compoent 表明着渲染的组件。当浏览器中 path 发生变化时,会渲染对应的 component 到页面中。
经过 new VueRouter 的方式建立对象,VueRouter 的构造函数是一个对象。要把这个对象的 routes 属性设置为刚刚定义的路由规则。
默认在 router/index.js 中。
在 new Vue 时,配置对象中的 router 选项设置为上面建立的路由对象。
在 Vue 实例指定的 el 选项对应的元素中,使用 router-view 标签建立路由组件的占位。路由组件每次都会渲染到这个位置。
使用 router-link 建立连接,经过 router-link 来改变路由。
当 Vue 实例开启 router 选项后,实例对象会多出两个属性,分别是 $route 和 $router。
$route 是当前的路由规则对象,里面存储了路径、参数等信息。
$router 是路由实例对象,里面存储了不少路由的方法,好比 push、replace、go 等。还存储了路由的信息,好比 mode 和 currentRoute。
假设有一个用户详情页面。咱们不会给每个用户都建立一个详情页面,由于这个页面是通用的,变化的只是用户的 id。
首先添加一个路由。
:id 前面的路径是固定的,:id 自己的意思就是接收一个 id 参数。
component 返回的是一个函数,这就是路由懒加载的写法。
也就是当这个路由被触发时,才会渲染这个组件。当这个路由没有被触发时,不会渲染组件。能够提升性能。
// ... other code
const routes = [ // ... other code { path: "/detail/:id", name: "Detail", component: () => import("../views/Detail.vue"), }, ]; 复制代码
有了路由以后,再建立一个用户页面。
<template>
<div>当前用户ID:{{ $route.params.id }}</div> </template> <script> export default { name: "Detail", }; </script> 复制代码
这就是第一种获取动态路由参数的方式,经过路由规则,获取参数。
可是这种方式有一个缺点,就是强制依赖 $route 才能正常工做。
可使用另外一种方式来下降这种依赖。
在路由规则中开启 props 属性。
// ... other code
const routes = [ // ... other code { path: "/detail/:id", name: "Detail", props: true, component: () => import("../views/Detail.vue"), }, ]; 复制代码
props 属性的做用是将路由中的参数以 props 的形式传入到组件中,这样在组件内就能够经过 props 获取到参数。
<template>
<div>当前用户ID:{{ id }}</div> </template> <script> export default { name: "Detail", props: ["id"], }; </script> 复制代码
这样 Detail 这个组件就不是必须在路由中才可使用,只要传递一个 id 属性,它就能够被应用到任何位置。
因此更加推荐使用 props 的方式传递路由参数。
当多个路由组件具备相同的内容,能够把多个路由组件相同的内容提取到一个公共的组件中。
假设首页和详情页具备相同的头部和尾部。能够提取一个 layout 组件,把头部和尾部抽取到 layout 组件中,并在发生变化的位置放置一个 router view。当访问对应的路由时,会把路由组件和 layout 组件的内容合并输出。
假设还有一个登陆页面,它不是须要 layout 的,因此它也不须要嵌套路由。
编写 layout 组件。
<template>
<div> <div> <header>header</header> </div> <div> <router-view /> </div> <div> <footer>footer</footer> </div> </div> </template> <script> export default { name: "login", }; </script> <style> header { width: 100%; background: #65b687; color: #39495c; } footer { width: 100%; background: #39495c; color: #65b687; } </style> 复制代码
建立 Login.vue 组件。
<template>
<div>登录页</div> </template> <script> export default { name: "login", }; </script> 复制代码
修改 app.vue 中 template 代码块中的内容。
<template>
<div id="app"> <router-view /> </div> </template> 复制代码
修改 routes 配置。
const routes = [
{ path: "/login", name: "login", component: Login, }, { path: "/", component: Layout, children: [ { name: "home", path: "", component: Home, }, { name: "detail", path: "detail:id", props: true, component: () => import("../views/Detail.vue"), }, ], }, ]; 复制代码
这样当访问http://localhost:8080/login时,会正常进入Login组件。
访问http://localhost:8080/时,会首先加载/对应的Layout组件,而后再加载Home组件,并把Layout组件和Home组件的内容进行合并。
访问http://localhost:8080/detail/id时,也会和Home加载方式同样,先加载Layout,再加载Detail,并把id传递进去。最后把两个组件的内容合并。
除了使用 router-link 进行导航之外,咱们还可使用 js 代码的方式进行导航。
这种需求很是常见,好比点击一个按钮,进行逻辑判断后再进行导航。
经常使用的编程式导航 API 有 4 个。分别是 $router.push、$router.replace、$router.back 和 $router.go
改造一下上面的三个页面,来体验一下这 4 个 API。
登录页经过点击登录按钮跳转到首页。
<template>
<div> <div>登录页</div> <button @click="push">登录</button> </div> </template> <script> export default { name: "login", methods: { push() { this.$router.push("/"); // this.$router.push({ name: 'home' }) }, }, }; </script> <style> button { background: #39495c; color: #65b687; border-radius: 8px; padding: 5px 10px; border: none; outline: none; } </style> 复制代码
首页能够跳转到用户详情页,也能够退出,退出的话跳转到登录页,而且在浏览器的浏览历史中不保存当前页。
<template>
<div class="home"> <div>Home Page.</div> <button @click="goToDetail">查看用户8的资料</button> <button @click="exit">退出</button> </div> </template> <script> export default { name: "Home", methods: { goToDetail() { this.$router.push("/detail/8"); // this.$router.push({ name: 'detail', params: { id: 8 } }) }, exit() { // this.$router.replace('/login') this.$router.replace({ name: "login" }); }, }, }; </script> 复制代码
用户详情页中能够回退到上一页,也能够回退两页。
<template>
<div> <div>当前用户ID:{{ id }}</div> <button @click="back">返回</button> <button @click="backTwo">回退两页</button> </div> </template> <script> export default { name: "Detail", props: ["id"], methods: { back() { this.$router.back(); }, backTwo() { this.$router.go(-2); }, }, }; </script> 复制代码
其中 push 方法和 replace 方法的用法基本上是一致的,均可以经过传递一个字符串或者传递一个对象来实现页面导航。若是传递字符串的话,就表示页面的路径。传递对象的话,会根据对象的 name 属性去寻找对应的页面组件。若是须要传递参数,能够拼接字符串,也能够在对象中设置 params 属性。二者不一样之处在于 replace 方法不会在浏览器中记录当前页面的浏览历史,而 push 方法会记录。
back 方法是回到上一页,它的用法最简单,不须要传递参数。
go 方法能够传递一个 number 类型的参数,表示是前进仍是后退。负数表示后退,正数表示前进,0 的话刷新当前页面。
Vue Router 中的路由模式有两种,分别是 hash 模式和 history 模式,hash 模式会在导航栏地址中具备一个井号(#),history 模式则没有。
两种模式都是由客户端来处理的,使用 JavaScript 来监听路由的变化,根据不一样的 URL 渲染不一样的内容。若是须要服务端内容的话,使用 Ajax 来获取。
从美观上来看,history 模式更加美观。
hash 模式的 URL。会附带一个井号(#),若是传递参数的话,还须要问号(?)。
http://localhost:8080/#/user?id=15753140
复制代码
history 模式的连接。
http://localhost:8080/user/15753140
复制代码
可是 history 不能够直接使用,须要服务端配置支持。
Hash 模式是基于锚点以及 onhashchange 事件。
History 模式是基于 HTML5 中的 History API。history 对象具备 pushState 和 replaceState 两个方法。可是须要注意 pushState 方法须要 IE10 之后才能够支持。在 IE10 以前的浏览器,只能使用 Hash 模式。
history 对象还有一个 push 方法,能够改变导航栏的地址,并向服务器发送请求。pushState 方法能够只改变导航栏地址,而不向服务器发送请求。
History 须要服务器的支持。
缘由是单页面应用中,只有一个 index.html。而在单页面应用正常经过点击进入 http://localhost:8080/login 不会有问题。可是当刷新浏览器时,就会请求服务器,而服务器上不存在这个 URL 对应的资源,就会返回 404。
因此在服务器上应该配置除了静态资源之外的全部请求都返回 index.html。
下面演示一下页面匹配不到的效果。
在 views 目录下建立 404.vue。
<template>
<div class="about"> <h1>404</h1> </div> </template> 复制代码
在 routes 中添加 404 的路由。
const routes = [
// other code { path: "*", name: "404", component: () => import("../views/404.vue"), }, ]; 复制代码
在 Home.vue 中添加一个不存在的连接。
<router-link to="/video">video</router-link>
复制代码
而后启动服务器,进入首页,点击 video 连接,就会跳转到 404 页面。
这是一个咱们预期想要的效果。在 vue cli 默认的服务器中,已经帮咱们配置好了。可是在咱们实际部署的时候,仍然须要本身去配置服务器。
首先使用 nodejs 开发一个服务器。
建立一个 server 文件夹,并初始化项目。
npm init -y
复制代码
安装项目的依赖,这里使用 express 和 connect-history-api-fallback。
express 是一个 nodejs 著名的 web 开发服务器框架。
connect-history-api-fallback 是一个处理 history 模式的模块。
npm i express connect-history-api-fallback
复制代码
建立并编写 server.js 文件。
const path = require("path");
// 处理 history 模式的模块 const history = require("connect-history-api-fallback"); const express = require("express"); const app = express(); // 注册处理 history 模式的中间件 app.use(history()); // 注册处理静态资源的中间件 app.use(express.static(path.join(__dirname, "./web"))); app.listen(4000, () => { console.log(` App running at: - Local: http://localhost:4000/ `); }); 复制代码
这里把 server 项目下根目录的 web 文件夹设置为网站的根路径。
当启动 server.js 后,请求http://localhost:4000/的URL都会去web文件夹下找到相应的资源。
如今打包原来的 vue 项目。
回到 vue 项目中,运行打包命令。
npm run build
复制代码
能够获得 dist 文件夹。
将 dist 目录中的全部内容复制到 server 项目的 web 目录中,就完成了项目的部署。
接下来运行 server.js。
node server.js
复制代码
打开浏览器,进入 detail 页面(http://localhost:4000/detail/8)。刷新浏览器,一切正常。
若是不处理 history,就会出现问题。
尝试把 app.use(history())
注释掉,从新启动服务器。
一样进入 detail 页面,刷新浏览器,就会进入 express 默认的 404 页面。缘由就是刷新浏览器,会请求服务器。服务器在 web 目录下找不到 detail/8 资源。若是开启了 history 处理,服务器找不到 detail/8,就会返回 index.html,客户端会根据当前路径渲染组件。
首先安装 nginx。
能够在 nginx 官网下载 nginx 的压缩包。
http://nginx.org/en/download.html
把压缩包解压到不附带中文的目录下。
或者借助某些工具安装,好比 brew。
brew install nginx
复制代码
nginx 的命令比较简单,经常使用的命令以下。
启动
nginx
复制代码
重启
nginx -s reload
复制代码
中止
nginx -s stop
复制代码
压缩包的方式安装,nginx 的默认端口是 80,若是 80 未被占用,会正常启动。启动后在浏览器访问http://localhost便可访问。
brew 方式安装的 nginx 默认端口是 8080。
把 vue 项目 dist 文件夹中的内容拷贝到 nginx 文件夹中的 html 文件夹中。html 文件夹就是 nginx 的默认文件夹。
部署成功后,在浏览器中访问项目,发现会存在一样的刷新 404 问题。
这时就须要在 nginx 的配置文件中添加对应的配置。
nginx 的默认配置在 conf/nginx.conf 中。
在 nginx.conf 中找到监听 80 的那个 server 模块,在从中找到 location /的位置。
添加 try_files 配置。
location / {
root html;
index index.html index.htm;
# $uri 是 nginx 的变量,就是当前此次请求的路径
# try files 会尝试在这个路径下寻找资源,若是找不到,会继续朝下一个寻找
# $uri/ 的意思是在路径目录下寻找 index.html 或 index.htm
# 最后都找不到的话,返回 index.html
try_files $uri $uri/ /index.html;
}
复制代码
修改完配置文件后,nginx 须要重启。
nginx -s reload
复制代码
重启后在浏览器中操做,一切正常。
因为 history 和 hash 模式的实现很像,这里直接使用 history 模式进行模拟。
如今再次回顾一下 vue router 的工做原理。
vue router 是前端路由,当路径切换时,在浏览器端判断当前路径并加载当前路径对应的组件。
hash 模式:
history 模式:
经过观察 vue router 的使用,能够快速推断出 vue router 是如何实现的。
下面是一个简单的使用流程。
// 注册插件
Vue.use(VueRouter); // 建立路由对象 const router = new VueRouter({ routes: [{ name: "home", path: "/", component: homeComponent }], }); // 建立 Vue 实例,注册 router 对象 new Vue({ router, render: (h) => h(App), }).$mount("#app"); 复制代码
首先是执行 Vue.use 注册 VueRouter。
Vue.use 方法是用于注册插件的,Vue 的强大,得益于它的插件机制。像 VueRouter、Vuex 和一些组件,都是使用插件机制实现的。
Vue.use 方法能够接受 1 个函数或者 1 个对象,若是是函数,则直接调用该函数,若是是对象,则调用对象上的 install 方法。这里的 VueRouter 是一个对象。
接下来建立了一个 router 实例,那么 VueRouter 应该是一个构造函数或者是一个类。
结合上面的分析,能够得知,VueRouter 是一个具备 install 方法的类。
VueRouter 的构造函数是一个对象,构造参数对象会有一个 routes 属性,记录了路由的配置信息。
最后在建立 Vue 实例的构造参数对象中传入了 router 对象。
能够经过 UML 类图来描述 VueRouter 这个类。
UML 类图包含 3 个部分。
最上面是类的名字,第二部分是类的实例属性,第三部分是类的方法,其中加号(+)表示原型方法、下划线(_)表示静态方法。
使用 vue cli 建立一个新的项目,配置选项中选择 babel、vue router、eslint,以便用于咱们测试。
当使用 Vue.use()时,会首先调用 install,因此先实现 install。
首先要分析,install 中要实现哪几件事情。
在 src 目录下建立 vue-router 目录,并在其中建立 index.js 文件。
let _Vue = null;
export default class VueRouter { static install(Vue) { // 1. 判断当前插件是否已安装 if (VueRouter.install.installed) { return; } VueRouter.install.installed = true; // 2. 把 Vue 构造函数存储到全局变量中 _Vue = Vue; // 3. 把建立 Vue 实例时传入的 router 对象注入到全部 Vue 实例上 // 混入 _Vue.mixin({ beforeCreate() { if (this.$options.router) { _Vue.prototype.$router = this.$options.router; } }, }); } } 复制代码
第一步比较简单,记录一个是否被插件,相比于全局变量,更好的方式就是在插件自己的 install 方法上添加一个 installed 属性。若是已安装,直接返回。未安装,把 installed 设置为 true,继续执行逻辑。
第二步很是简单,只须要给全局的_Vue 赋值就能够了。
第三步比较难,由于在这里咱们并不知道何时会调用 new Vue,因此也获取不到构造参数中的 router 对象。这时能够借助混入来解决这个问题。在 mixin 方法中传入的对象具备 beforeCreate 方法,这个是 new Vue 时的钩子函数,该函数中的 this 指向的就是 Vue 实例,因此在这里能够将 VueRouter 实例注入到全部的 Vue 实例上。因为每一个组件也是一个 Vue 实例,因此还须要区分是 Vue 实例仍是组件,否则原型扩展的逻辑会被执行不少次。具体经过 this.$options 是否具有 router 属性来判断,由于只有 Vue 实例才会具备 router 属性,组件是没有的。
接下来实现构造函数,构造函数的逻辑比较简单。
建立了三个实例属性。options 用来存储构造参数;routerMap 就是一个键值对对象,属性名就是路由地址,属性值就是组件;data 是一个响应式对象,具备一个 current 属性,用于记录当前的路由地址。能够经过_Vue.observable 来建立响应式对象。
export default class VueRouter {
// other code constructor(options) { this.options = options; this.routeMap = {}; this.data = _Vue.observable({ current: "/", }); } } 复制代码
该函数的做用是将构造函数参数 options 中的 routes 属性转换为键值对的形式存储到 routeMap 上。
export default class VueRouter {
// other code initRouteMap() { this.options.routes.forEach((route) => { this.routeMap[route.path] = route.component; }); } } 复制代码
接下来实现 initComponents,这个方法主要是注册 router-link 和 router-view 这两个组件。
initComponents 方法接收 1 个 Vue 构造方法做为参数,传入参数的目的是为了减小方法和外部的依赖。
router-link 组件会接收一个字符串类型的参数 to,就是一个连接。router-link 自己会转换成 a 标签,而 router-link 的内容也会被渲染到 a 标签内。
export default class VueRouter {
// other code initComponents(Vue) { Vue.component("router-link", { props: { to: String, }, template: `<a :href="to"><slot></slot></a>`, }); } } 复制代码
建立 init 函数,这个函数将 initRouteMap 和 initComponents 包装起来,方便使用。
而后在建立 Vue 实例时调用 init 方法,建立 router-link 组件。
export default class VueRouter {
// other code static install(Vue) { if (VueRouter.install.installed) { return; } VueRouter.install.installed = true; _Vue = Vue; _Vue.mixin({ beforeCreate() { if (this.$options.router) { _Vue.prototype.$router = this.$options.router; // 在这里调用 init this.$options.router.init(); } }, }); } init() { initRouteMap(); initComponents(); } } 复制代码
如今就能够去测试了。
将 src/router/index.js 的 vue-router 替换为咱们本身写的 vue router。
// import VueRouter from 'vue-router'
import VueRouter from "../../vue-router/index"; 复制代码
启动项目。
npm run serve
复制代码
打开浏览器,会发现页面上一片空白,可是控制台会获得两个错误。
第一个错误是:
vue.runtime.esm.js?2b0e:619 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
复制代码
这个错误的意思是目前使用的是运行时版本的 Vue,模板编译器不可用。可使用预编译将模板编译成渲染函数,或者使用编译版本的 Vue。
Vue 的构建版本:
运行时版:不支持 template 模板,须要打包的时候提早编译。
完整版:包含运行时和编译器,体积比运行时版本大 10k 左右,程序运行的时候把模板转换成 render 函数。
第二个错误是 router-view 组件未定义,由于如今尚未处理 router-view,能够忽略。
Vue Cli 建立的项目,默认使用运行时版本的 Vue,由于它的效率更高。
若是要修改 Vue Cli 项目的配置,须要在项目根目录下建立 vue.config.js 文件,这个文件使用 CommonJS 规范导出一个模块。
将 runtimeCompiler 设置为 true 就可使用完整版 Vue,默认状况下这个选项是 false。
module.exports = {
runtimeCompiler: true, }; 复制代码
而后从新启动项目,以前碰到的第一个问题就获得了解决。
可是完整版本的 Vue 体积会大 10k,并且是运行时编译,消耗性能,不建议使用。
运行时版本的 Vue 不包含编译器,因此也不支持 template 选项。而编译器的做用就是将 template 选项转换为 render 函数。
咱们在编写.vue 文件时,在不开启 runtimeCompiler 时也不会编写 render 函数。这时由于 Vue Cli 中配置的 webpack 会在代码编译打包阶段将 vue 文件中的 template 转换为 render 函数,也就是预编译。而咱们编写的 js 文件,是没有进行这种预编译的。因此要在运行时版本的 Vue 中须要使用 render 函数。
首先删除掉 vue.config.js。
修改 initComponents 函数。
export default class VueRouter {
// other code initComponents(Vue) { Vue.component("router-link", { props: { to: String, }, // template: `<a :href="to"><slot></slot></a>` render(h) { return h( "a", { attrs: { href: this.to, }, }, [this.$slots.default] ); }, }); } } 复制代码
render 函数接收一个 h 函数,h 函数的做用是建立虚拟 DOM,最终 render 将返回虚拟 DOM。
h 函数的用法有不少种,具体可参考官方文档:https://cn.vuejs.org/v2/guide/render-function.html
从新启动项目,符合预期。
router-view 组件相似于 slot 组件,提供一个占位符的做用。根据不一样的路由地址,获取到不一样的路由组件,并渲染到 router-view 的位置。
export default class VueRouter {
// other code initComponents(Vue) { // other code const self = this; Vue.component("router-view", { render(h) { const component = self.routeMap[self.data.current]; return h(component); }, }); } } 复制代码
这样就完成了 router-view 组件。
可是如今去尝试点击超连接,发现并不能正常跳转。缘由是由于 a 标签会默认请求服务器,致使页面刷新。
因此须要阻止 a 标签默认请求服务器的行为,并使用 histor.pushState 方法改变导航栏的 URL,改变的 URL 要保存到 this.data.current 中。由于 this.data 是响应式数据。
修改 router-link 组件的逻辑。
export default class VueRouter {
// other code initComponents(Vue) { // other code Vue.component("router-link", { props: { to: String, }, render(h) { return h( "a", { attrs: { href: this.to, }, on: { click: this.clickHandler, }, }, [this.$slots.default] ); }, methods: { clickHandler(e) { history.pushState({}, "", this.to); this.$router.data.current = this.to; e.preventDefault(); }, }, }); } } 复制代码
再次回到项目中,运行项目。点击 a 标签,就能够正常刷新页面内容了。
虽然上面已经实现了全部的功能,可是还存在一个小问题。
点击浏览器左上角的前进、后退按钮时,只是修改了地址栏的 URL,页面并无随之发生改变。
解决这个问题也很简单。
实现思路是监听 popstate 方法,并在其中将 this.data.current 的值设置为当前导航栏的 URL。因为 this.data 是响应式的数据,因此当 this.data 发生变化时,全部用到 this.data 的组件都会被从新渲染。
export default class VueRouter {
// other code init() { // other code this.initEvent(); } initEvent(Vue) { window.addEventListener("popstate", () => { this.data.current = window.location.pathname; }); } } 复制代码
这样就解决了导航栏前进后退不刷新组件的小问题。
至此,history 模式的 vue router 简单实现已经完成。
附所有源码:
let _Vue = null;
export default class VueRouter { static install(Vue) { // 1. 判断当前插件是否已经被安装 if (VueRouter.install.installed) { return; } VueRouter.install.installed = true; // 2. 把 Vue 构造函数记录到全局变量 _Vue = Vue; // 3. 把建立的 Vue 实例时所传入的 router 对象注入到 Vue 实例上 // 混入 _Vue.mixin({ beforeCreate() { if (this.$options.router) { _Vue.prototype.$router = this.$options.router; this.$options.router.init(); } }, }); } constructor(options) { this.options = options; this.routeMap = {}; this.data = _Vue.observable({ current: "/", }); } init() { this.initRouterMap(); this.initComponents(_Vue); this.initEvent(); } initRouterMap() { // 遍历全部的路由规则,把路由规则解析成键值对的形式 存储到 routerMap 中 this.options.routes.forEach((route) => { this.routeMap[route.path] = route.component; }); } initComponents(Vue) { Vue.component("router-link", { props: { to: String, }, // template: ` // <a :href="to"> // <slot></slot> // </a> // `, render(h) { return h( "a", { attrs: { href: this.to, }, on: { click: this.clickHandler, }, }, [this.$slots.default] ); }, methods: { clickHandler(e) { history.pushState({}, "", this.to); this.$router.data.current = this.to; e.preventDefault(); }, }, }); const self = this; Vue.component("router-view", { render(h) { console.log(self); const component = self.routeMap[self.data.current]; return h(component); }, }); } initEvent() { window.addEventListener("popstate", () => { this.data.current = window.location.pathname; }); } } 复制代码
过去已通过去,将来即将美好。
这不是一篇面经,除了标题之外,内容都是干货。
可是,下面我要写几句软文了。
当还有不少人惧怕甚至畏惧面试,而疯狂刷面经时。
你应该明白,并非会用、会实现、看过源码的人才能经过面试。懂得创造、思考和改革的人一样能够。
你能够结交志同道合的朋友,一块儿讨论框架、工具的意义。
你也能够发现发掘业务中的痛点和本身的兴趣,实现本身的想法,不断从兴趣中获得知足,不断进步。
你还能够博览群书,从书中寻求软件的奥义。
总之,方法有不少不少,路子很广很广,要看本身如何践行。
不要由于本身愚笨而放弃本身。小厂同样能够过得很快乐,也能够实现你的价值,大厂并非惟一。
你要知道你能作什么,而不是想要什么。
你是否有自我提升的能力?你可否承受得了 996?要选择最适合本身的那种生活方式。
别为了钱,而委屈了本身。
但是没钱,生活会委屈你。
本文使用 mdnice 排版