Vue-router原理:5、initComponents方法实现
initComponents
方法,主要作用是用来创建router-link
和router-view
这两个组件的。
下面先在这个方法中创建router-link
这个组件。
先来看一下router-link
这个组件的基本使用
<router-link to="/users"> 用户管理</router-link>
我们知道,router-link
这个组件最终会被渲染成a
标签,同时to
作为一个属性,其值会作为a
标签中的href
属性的值。同时还要获取<router-link>
这个组件中的文本,作为最终超链接的文本。
initComponents(Vue) {
Vue.component("router-link", {
props: {
to: String,
},
template: '<a :href="to"><slot></slot></a>',
});
}
在上面的代码中,我们通过Vue.component
来创建router-link
这个组件,同时通过props
接收to
属性传递过来的值,并且对应的类型为字符串。
最终渲染的模板是一个a
标签,href
属性绑定了to
属性的值,同时使用<slot>
插槽作为占位符,用具体的文字内容填充该占位符。
现在已经将router-link
这个组件创建好了。
下面我们需要对我们写的这些代码进行测试。
要进行测试应该先将createRouteMap
方法与initComponents
方法都调用一次,那么问题是
在什么时候调用这两个方法呢?
我们可以在VueRoute对象创建成功后,并且将VueRouter
对象注册到Vue
的实例上的时候,调用这两个方法。
也就是在beforeCreate
这个钩子函数中。
当然为了调用这两个方便,在这里我们又定义了init
方法,来做了一次封装处理。
init() {
this.createRouteMap();
this.initComponents(_Vue);
}
对init
方法的调用如下:
beforeCreate() {
//在创建Vue实例的时候
// 也就是new Vue()的时候,才会有$options这个属性,
//组件中是没有$options这个属性的。
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router;
//调用init
this.$options.router.init();
}
},
this.$options.router.init();
这句代码的含义:this
表示的就是Vue实例,$options
表示的就是在创建Vue
的实例的时候传递的选项,如下所示:
const vm = new Vue({
el: "#app",
router,
});
通过上面的代码,我们可以看到,传递过来的选项中是有router
.
而这个router
是什么呢?
const router = new VueRouter({})
就是VueRouter
这个类的实例。而我们当前自己所模拟的路由,所创建的类就叫做VueRouter
(也就是以后在创建路由实例的时候,使用我们自己创建的VueRouter
这个类来完成).
而init
方法就是VueRouter
这个类的实例方法。所以可以通过this.$options.router.init()
的方式来调用。
下面我们来测试一下。
在vue_router_app
项目的src
目录下面,创建router.js
文件,文件定义路由规则.
如下代码所示:
import Vue from "vue";
// import Router from "vue-router";
import Router from "./vuerouter";//注意:这里导入的是自己定义的路由规则
import Login from "./components/Login.vue";
import Home from "./components/Home.vue";
Vue.use(Router);
export default new Router({
model: "history",
routes: [
{ path: "/", component: Home },
{ path: "/login", component: Login },
],
});
在components
目录下面分别创建Home.vue
与Login.vue
.
Home.vue
的代码如下:
<template>
<router-link to="/login">登录</router-link>
</template>
<script>
export default {};
</script>
<style></style>
Login.vue
的代码如下
<template>
<div>
登录页面
</div>
</template>
<script>
export default {};
</script>
App.vue
组件的内容如下:
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link>
<router-link to="/login">Login</router-link>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {};
</script>
<style></style>
在main.js
中完成路由的注册。
import Vue from "vue";
import App from "./App.vue";
//导入router.js
import router from "./router";
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
运行上面的代码会出现如下的错误:
第二个错误是我们还没有创建router-view
这个组件,所以才会出现该错误。这里暂时可以先不用考虑。
主要是第一个错误,该错误的含义是,目前我们使用的是运行时版本的Vue
, 模板编译器不可用。
你可以使用预编译把模板编译成render
函数,或者是使用包含编译版本的Vue
.
以上错误说明了Vue
的构建版本有两个,分别是“运行时版”和"完整版".
运行时版:不支持template
模板,需要打包的时候提前编译。
完整版:包含运行时和编译器,体积比运行时版大10k
左右,程序运行的时候把模板转换成render
函数。性能低于运行时版本。
使用vue-cli
创建的项目默认为运行时版本
,而我们创建的VueRouter
类中有template
模板,所以才会出现第一个错误。
官方文档:https://cn.vuejs.org/v2/guide/installation.html
下面我们看一下解决方案:
在前面我们已经提到过,使用vue-cli
创建的项目是运行时项目,所以没有编译器,如果我们将其修改成完整版,就有编译器,对模板进行编译。
解决的方案:在项目的根目录创建vue.config.js
文件,在该文件中添加runtimeCompiler
配置项,该配置项表示的是,是否使用包含运行时编译器的Vue
构建
版本(完整版)。设置为true
后你就可以在Vue
组件中使用template
选项了,但是这会让你的应用额外增加10kb
左右。默认该选项的取值为false
.
vue.config.js
文件配置如下
module.exports = {
runtimeCompiler: true,
};
表示使用的是完整版,这时编译器会将template
选项转换成render
函数。
注意:要想以上配置内容起作用,必须重新启动服务器。
npm run serve