Vue template compiler 的問題

[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.

轉載自:http://www.javashuo.com/article/p-nwhqnjto-a.html

解決辦法:添加package.config.js配置文件中,添加本文章的紅色部分代碼vue

import 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.jsapi

福利時間:推薦一個網站 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 上的註釋修改應該沒有什麼大問題,主要的變化有兩點:

  1. 路由配置從一系列的方法調用,變成了傳遞一個配置對象

  2. 原先的 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(就像以前的 beforeEachafterEach,以及每一個路由狀態中的 beforeEnterbeforeRouteLeave等)都具備相同的參數簽名,這在 Release Note 中也有提到。

fn (toRoute, redirect, next) {
    // toRoute: {Object} 當前路由對象
    // redirect: {Function} 調用跳轉至另外一路由
    // next: {Function} 調用繼續當前路由跳轉
    // 什麼都不作,則取消當前跳轉
}

路由升級完成後,若是控制檯沒有什麼報錯,那麼,路由能夠相互切換了,那些不依賴數據讀取的組件已經能夠正常顯示了。

那些依賴數據讀取的組件哪?

這就要提到組件的生命週期鉤子(即 lifecycle hooks)

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 屬性,就彷佛不那麼優雅。

相關文章
相關標籤/搜索