解决办法:添加package.config.js配置文件中,添加本文章的红色部分代码vue
顺利安装完成并按 changelog 作了修改以后,启动项目也正常,当我兴致勃勃地打开 Browser,得心应手地输入 localhost,并天然而然地按下 Enter,一切水到渠成。webpack
然而,迎接个人竟是一片白板,控制台里赫然映着一串红字。git
[Vue warn] : You are using the runtime-only build of Vue where the template option is not available. Either pre-compile the templates into render functions, or use the compiler-included build. (found in root instance)github
What? template 选项不能用了,changelog 没提到啊?但 vue-router 的例子中都在用啊,什么鬼?甚至我将代码所有替换成例子中的代码依旧没法运行,但在 vue-router 项目里就能跑,什么鬼啊!web
可是,我并不妥协,分别打断点运行,发现二者居然跑的不是同一段代码,纳尼!vue-router
import vue from 'vue'
一样的 import
语句,却有不同的结果,vue-router 中引的是 vue.js,而在个人项目中引的居然是 vue.common.js...common...mon...n...json
为何会引 vue.common.js,from 'vue'
不应引的是 vue.js 么?这就要引入另外一个知识点:package.json。segmentfault
package.json 中的 main
属性决定了,当项目被引入时,输出的是哪一个文件,而 vue 的 package.json 中的 main
指向的是 dist/vue.common.js
。api
福利时间:推荐一个网站 json.is,它对 package.json 里的每条属性都有详细的解释。网站
找到了问题产生的缘由,那么解决也就垂手可得了。
import vue from 'vue/dist/vue.js'
每次引用 vue 的时候都要写那么长,一点都不优雅,并且为何 vue-router 的例子能够用啊?
我要一探究竟。确认了 vue-router 中依赖的 vue 的 package.json 文件中的 main
字段指向的也是 dist/vue.common.js
。那就只有一个可能了,webpack 对引入作了处理,查看 webpack.config.js
module.exports = { // 省略... resolve: { alias: { 'vue': 'vue/dist/vue.js' } }, ...
果真啊~他用 webpack 的别名功能把 vue/dist/vue.js
命名成了 vue,防不胜防。
在本身项目的 wepack.config.js 里一样给 vue 起别名,这样就又能愉快地使用 import vue from 'vue'
了。
你是否是觉得这样就结束了?不,对待一个问题要刨根问底,不能不求甚解。
为何 vue 默认导出的是 vue.common.js,它和 vue.js 的区别在哪里,又有什么关系?
这个问题在囧克斯的博客中有提到。
Vue 最先会打包生成三个文件,一个是 runtime only 的文件 vue.common.js,一个是 compiler only 的文件 compiler.js,一个是 runtime + compiler 的文件 vue.js。
也就是说,vue.js = vue.common.js + compiler.js
,而若是要使用 template
这个属性的话就必定要用 compiler.js,那么,引入 vue.js 是最恰当的。
vue-router 的升级并不困难,参照 Releases Note 上的注释修改应该没有什么大问题,主要的变化有两点:
路由配置从一系列的方法调用,变成了传递一个配置对象
原先的 v-link
指令,变成了 router-link
Component,路径指向用 to
属性
正当你觉得会一帆风顺顺水,轻松升级路由完成的时候,现实总会给你当头一棒。
以前博客的 vue-router 中使用了 beforeEach
和 afterEach
方法,根据 Release Note
router.beforeEach (replaced by the beforeEach option)
router.afterEach (replaced by the afterEach option)
行,那我把它改到配置里
const ROUTER_SETTING = { routes: [ // 省略... ], beforeEach: () => { /* some function */ }, afterEach: () => { /* some function */ } }
But, not work. What's wrong?
难道我哪里写错了?又通过我一番谷哥和查阅文档以后,发如今下一个版本的 Release Note 中有这么一段
beforeEach and afterEach are reverted as router instance methods (options removed). This makes it more convenient for plugins/modules to add hooks after the router instance has been created.
好吧,它又被恢复回路由实例的方法了。那么,改回去
const router = new VueRouter(ROUTER_SETTING); router .beforeEach(() => { /* some function */ }) .afterEach(() => { /* some function */ });
OK,这样总好了吧。然而,并无...console 中报出没法从 undefined
中读取 afterEach
,好吧,我猜这应该是 beforeEach
中没有像以前同样返回路由对象,因此不能链式调用。
class VueRouter { // 省略... beforeEach (fn: Function) { this.beforeHooks.push(fn) } afterEach (fn: Function) { this.afterHooks.push(fn) } // 省略... }
看一眼源码,果真如此。
那再将以前的代码稍做修改就能够了。
const router = new VueRouter(ROUTER_SETTING); router.beforeEach(() => { /* some function */ }); router.afterEach(() => { /* some function */ });
不过,不能链式调用彷佛没以前的优雅了哪~
最后,提一下 vue-router 2.0 里全部的 hook(就像以前的 beforeEach
, afterEach
,以及每一个路由状态中的 beforeEnter
, beforeRouteLeave
等)都具备相同的参数签名,这在 Release Note 中也有提到。
fn (toRoute, redirect, next) {
// toRoute: {Object} 当前路由对象 // redirect: {Function} 调用跳转至另外一路由 // next: {Function} 调用继续当前路由跳转 // 什么都不作,则取消当前跳转 }
路由升级完成后,若是控制台没有什么报错,那么,路由能够相互切换了,那些不依赖数据读取的组件已经能够正常显示了。
那些依赖数据读取的组件哪?
这就要提到组件的生命周期钩子(即 lifecycle hooks)。
生命周期钩子应该算 vue 此次升级中 broken changes 最多的一部分了,对照 1.0 的文档和 release note,做了下面这张表
vue 1.0+ | vue 2.0 | Description |
---|---|---|
init | beforeCreate | 组件实例刚被建立,组件属性计算以前,如 data 属性等 |
created | created | 组件实例建立完成,属性已绑定,但 DOM 还未生成,$el 属性还不存在 |
beforeCompile | beforeMount | 模板编译/挂载以前 |
compiled | mounted | 模板编译/挂载以后 |
ready | mounted | 模板编译/挂载以后(不保证组件已在 document 中) |
- | beforeUpdate | 组件更新以前 |
- | updated | 组件更新以后 |
- | activated | for keep-alive ,组件被激活时调用 |
- | deactivated | for keep-alive ,组件被移除时调用 |
attached | - | 不用了还说啥哪... |
detached | - | 那就不说了吧... |
beforeDestory | beforeDestory | 组件销毁前调用 |
destoryed | destoryed | 组件销毁后调用 |
知道了 hooks 升级先后的对应关系,那么升级起来就垂手可得了,改改组件的属性名就能够了。
那么,改完属性名是否是就完成了?然而并无。
由于,在 vue 1.0+ 中,若是一个组件和路由相关,那么,它就可能不仅仅有本身组件的 lifecycle hooks,它还会有基于 vue-router 的 lifecycle hooks。
而在 vue 2.0 中,router lifecycle hooks 所有被移除了,由于,这些 hooks 能够经过其余的方式来代替,这样不但简化了配置,还不用在组件中去处理路由相关的业务,下降了耦合。那这些 hooks 该如何替换,咱们接下来就来看一下。
activate
& deactivate
:使用组件自身的 lifecycle hook 替代
data
:经过组件 watch
属性来监听当前路由 $route
的变化
canActivate
:由路由属性 beforeEnter
来代替
canDeactivate
:由路由属性 beforeRouteLeave
来代替
canReuse
:去除
那个这个是否是也直接改改属性名就行了哪?
恩,差很少。不过须要注意的是,若是原先 hooks 中使用了有关路由信息的 transition
参数是确定不能用了。好比,根据路由参数来进行查询,原先经过 transition.to.params
获取路由参数,如今就要经过刚刚提到的当前路由对象 this.$route.params
来获取。
在升级这里的过程当中,还遇到一个问题:当用户输入的 URL 知足路由匹配,但根据路由参数没法得到正确的文章时,我想让路由直接跳转到首页。
在 1.0 版本中,我经过 transition.redirect('/');
就轻松的回到了首页,因为 2.0 中没有 transition
参数,而 $route
只包含当前路由的信息,并不包换路由切换的操做。那该怎么作哪?再一次谷哥和查阅文档,然而一无所得。
最后在 vue-router 的例子中找到了解决问题的钥匙——$router
。
$router
返回的是整个项目路由的实例,它是只读的。因而,刚刚那个问题就能够经过 this.$router.replace('/');
来解决。
这里还有一点,在 1.0 版本中组件配置 route 属性时还能够设置一个叫 waitForData
的属性。这个在 2.0 中,我尚未找到直接的替换方式,不过,我在整个组件上添加 v-if
来处理。从理论和效果的角度上讲,v-if
是能够替代原先的 waitForData
属性,就彷佛不那么优雅。