vue 單頁面應用實戰

1. 爲何要 SPA?html

SPA: 就是俗稱的單頁應用(Single Page Web Application)。vue

在移動端,特別是 hybrid 方式的H5應用中,性能問題一直是痛點。 使用 SPA,沒有頁面切換,就沒有白屏阻塞,能夠大大提升 H5 的性能,達到接近原生的流暢體驗。node

2. 爲何選擇 vue?

在選擇 vue 以前,使用 reactjs 也作過一個小 Demo,雖然二者都是面向組件的開發思路,可是 reactjs 的全家桶方式,實在太過強勢,而本身定義的 JSX 規範,揉和在 JS 的組件框架裏,致使若是後期發生頁面改版工做,工做量將會巨大。react

vue 相對來講,就輕量的多,他的view層,仍是原來的 dom 結構,除了一些自定義的 vue 指令做爲自定義標籤之外,只要學會寫組件就能夠了,學習成本也比較低。webpack

3. 環境配置

初始化工程,須要 node 環境使用 npm 安裝相應的依賴包。web

先建立一個測試目錄,在裏面依次輸入如下命令。vue-router

//初始化package.json npm init //安裝vue的依賴 npm install vue --save npm install vue-router --save //安裝webpack的開發依賴 npm install webpack --save-dev //安裝babel的ES6 Loader 的開發依賴 npm install babel --save-dev npm install babel-core --save-dev npm install babel-loader --save-dev npm install babel-preset-es2015 --save-dev //安裝html loacer 的開發依賴 npm install html-loader --save-dev

4. 目錄結構

src 爲開發目錄,其中 components 爲組件子目錄,templates 爲模板子目錄。npm

dist 爲構建出的文件目錄。json

index.html 爲入口文件。瀏覽器

package.json 爲項目描述文件,是剛纔 npm init 所創建。

webpack.config.js 是 webpack 的構建配置文件

5. Webpack 配置

下面是 webpack 的配置文件,如何使用 webpack,請移步 webpack 的官網。

var webpack= require("webpack"); module.exports={ entry:{ bundle:[ "./src/app.js"] }, output:{ path:__dirname, publicPath:"/", filename:"dist/[name].js" }, module:{ loaders:[ {test: /\.html$/, loaders: ['html']}, {test: /(\.js)$/, loader:["babel"] ,exclude:/node_modules/, query:{ presets:["es2015"] } } ] }, resolve:{ }, plugins:[ /* new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) */ ] }

6. 入口文件

index.html

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue Router Demo</title> </head> <body> <div id="app"> <router-view></router-view> </div> <script src="dist/bundle.js"></script> </body> </html>

其中 id 爲 app 的 div 是頁面容器,其中的 router-view 會由 vue-router 去渲染組件,講結果掛載到這個 div 上。

app.js

var Vue = require('vue'); var VueRouter = require('vue-router'); Vue.use(VueRouter); Vue.config.debug = true; Vue.config.delimiters = ['${', '}']; // 把默認的{{ }} 改爲ES6的模板字符串 ${ } Vue.config.devtools = true; var App = Vue.extend({}); var router = new VueRouter({}); router.map(require('./routes')); router.start(App, '#app'); router.go({"path":"/"});

這是 vue 路由的配置。 其中因爲習慣問題,我把 vue 默認的{{ }} 改爲了的 ${ } ,總感受這樣看模板,才順眼一些。

routes.js

module.exports = { '/': { component: require('./components/index') }, '/list': { component: require('./components/list') }, '*': { component: require('./components/notFound') } }

7. 第一個組件

components/index.js

module.exports = { template: require('../templates/index.html'), ready: function () { } };

templates/index.html

<h1>Index</h1> <hr/> <p>Hello World Index!</p>

執行 webpack 構建命令

瀏覽器中訪問:

查看 bundle 源碼:

發現 template 模板文件,已經被 webpack 打成字符串了。這其中,實際上是 webpack 的 html-loader 起的做用

8. 組件之間跳轉

修改剛纔的 index 組件,增長一個跳轉連接,不用 href 了,要用 vue 的指令 v-link。

<h1>Index</h1> <hr/> <p>Hello World Index!</p> <p><a v-link="{path:'/list'}" >List Page</a></p>

添加 list 組件

components/list.js

module.exports = { template: require('../templates/list.html'), data:function(){ return {items:[{"id":1,"name":"hello11"},{"id":2,"name":"hello22"}]}; }, ready: function () { } };

templates/list.html

<h1>List</h1> <hr/> <p>Hello List Page!</p> <ul> <li v-for="(index,item) in items"> ${item.id} : ${item.name} </li> </ul>

v-for 也是 vue 的默認指令,是用來循環數據列表的。

如今開始執行 webpack --watch 命令進行監聽,這樣就不用每次敲 webpack 命令了。只要開發者每次修改 js 點了保存,webpack 都會自動構建最新的 bundle 文件。

瀏覽器裏試試看:

index 頁

點擊 List Page 跳轉到 list 頁

Bingo! 單頁面兩個組件之間跳轉切換成功!

9. 組件生命週期

修改 **componets/list.js **

module.exports = { template: require('../templates/list.html'), data:function(){ return {items:[{"id":1,"name":"hello11"},{"id":2,"name":"hello22"}]}; }, //在實例開始初始化時同步調用。此時數據觀測、事件和 watcher 都還沒有初始化 init:function(){ console.log("init.."); }, //在實例建立以後同步調用。此時實例已經結束解析選項,這意味着已創建:數據綁定,計算屬性,方法,watcher/事件回調。可是尚未開始 DOM 編譯,$el 還不存在。 created:function(){ console.log("created.."); }, //在編譯開始前調用。 beforeCompile:function(){ console.log("beforeCompile.."); }, //在編譯結束後調用。此時全部的指令已生效,於是數據的變化將觸發 DOM 更新。可是不擔保 $el 已插入文檔。 compiled:function(){ console.log("compiled.."); }, //在編譯結束和 $el 第一次插入文檔以後調用,如在第一次 attached 鉤子以後調用。注意必須是由 Vue 插入(如 vm.$appendTo() 等方法或指令更新)才觸發 ready 鉤子。 ready: function () { console.log("ready.."); }, //在 vm.$el 插入 DOM 時調用。必須是由指令或實例方法(如 $appendTo())插入,直接操做 vm.$el 不會 觸發這個鉤子。 attached:function(){ console.log("attached.."); }, //在 vm.$el 從 DOM 中刪除時調用。必須是由指令或實例方法刪除,直接操做 vm.$el 不會 觸發這個鉤子。 detached:function(){ console.log("detached.."); }, //在開始銷燬實例時調用。此時實例仍然有功能。 beforeDestroy:function(){ console.log("beforeDestroy.."); }, //在實例被銷燬以後調用。此時全部的綁定和實例的指令已經解綁,全部的子實例也已經被銷燬。若是有離開過渡,destroyed 鉤子在過渡完成以後調用。 destroyed:function(){ console.log("destroyed.."); } };

在瀏覽器裏執行了看看:

首次進入 List 頁面的執行順序以下:

此時點一下瀏覽器的後退,List Component 會被銷燬,執行順序以下:

這是官方的生命週期的圖:

10. 父組件與子組件

在不少狀況下,組件是有父子關係的,好比 list 列表組件有個子組件 item

components/item.js

module.exports = { template: require('../templates/item.html'), props:["id","name"], ready: function () { } };

templates/item.html

<p>我是subitem: ${id} - ${name}</p>

修改 list 組件,添加 item 的引用

components/list.js

//引用item組件 import item from "./item"; module.exports = { template: require('../templates/list.html'), data:function(){ return {items:[{"id":1,"name":"hello11"},{"id":2,"name":"hello22"}]}; }, //定義item組件爲子組件 components:{ "item":item }, ready: function () { } };

templates/list.html

<h1>List</h1> <hr/> <p>Hello List Page!</p> <ul> <li v-for="(index,item) in items"> <!--使用item子組件,同時把id,name使用props傳值給item子組件--> <item v-bind:id="item.id" v-bind:name="item.name"></item> </li> </ul>

瀏覽器裏試試看:

子組件成功被調用了

11. 組件跳轉傳參

組件之間的跳轉傳參,也是一種很是常見的狀況。下面爲列表頁,增長跳轉到詳情頁的跳轉,並傳參 id 給詳情頁

修改路由 routes.js

module.exports = { '/': { component: require('./components/index') }, '/list': { component: require('./components/list') }, //增長詳情頁的跳轉路由,並在路徑上加上id傳參,具名爲name:show '/show/:id': { name:"show", component: require('./components/show') }, '*': { component: require('./components/notFound') } }

添加組件 show

components/show.js

module.exports = { template: require('../templates/show.html'), data:function(){ return {}; }, created:function(){ //獲取params的參數ID var id=this.$route.params.id; //根據獲取的參數ID,返回不一樣的data對象(真實業務中,這裏應該是Ajax獲取數據) if (id==1){ this.$data={"id":id,"name":"hello111","age":24}; }else{ this.$data={"id":id,"name":"hello222","age":28}; } }, ready: function () { console.log(this.$data); } };

templates/show.html

<h1>Show</h1> <hr/> <p>Hello show page!</p> <p>id:${id}</p> <p>name:${name}</p> <p>age:${age}</p>

修改 templates/item.html

<p>我是subitem: <a v-link="{name:'show',params: { 'id': id } }"> ${id} : ${name}</a> </p>

這裏 name:‘show’ 表示具名路由路徑,params 就是傳參。

繼續瀏覽器裏點到詳情頁試試:

點擊「hello11」,跳轉到詳情頁:

傳參邏輯成功。

12. 嵌套路由

僅有路由跳轉是遠遠不夠的,不少狀況下,咱們還有同一個頁面上,多標籤頁的切換,在 vue 中,用嵌套路由,也能夠很是方便的實現。

添加兩個小組件

components/tab1.js

module.exports = { template: "<p>Tab1 content</p>" };

components/tab2.js

module.exports = { template: "<p>Tab2 content</p>" };

修改 components/index.js 組件,掛載這兩個子組件

import tab1 from "./tab1"; import tab2 from "./tab2"; module.exports = { template: require('../templates/index.html'), components:{ "tab1":tab1, "tab2":tab2 }, ready: function () { } };

在路由里加上子路由

module.exports = { '/': { component: require('./components/index'), //子路由 subRoutes:{ "/tab1":{ component:require('./components/tab1') }, "/tab2":{ component:require('./components/tab2') } } }, '/list': { component: require('./components/list') }, '/show/:id': { name:"show", component: require('./components/show') }, '*': { component: require('./components/notFound') } }

好了,在瀏覽器裏試一下:

初始狀態:

點了 tab1,tab2:

Tab 切換沒問題,但是,初始狀態顯示是空的,能不能默認顯示 Tab1 Content 呢?很簡單,調整下路由就能夠了:

module.exports = { '/': { component: require('./components/index'), //子路由 subRoutes:{ //默認顯示Tab1 "/":{ component:require('./components/tab1') }, "/tab1":{ component:require('./components/tab1') }, "/tab2":{ component:require('./components/tab2') } } } }
相關文章
相關標籤/搜索