【騰訊Bugly乾貨分享】基於 Webpack & Vue & Vue-Router 的 SPA 初體驗

来源:http://www.cnblogs.com/bugly/archive/2016/09/12/5864305.html
-Advertisement-
Play Games

最近這幾年的前端圈子,由於戲臺一般精彩紛呈,從 MVC 到 MVVM,你剛唱罷我登場。 backbone,angularjs 已成昨日黃花,reactjs 如日中天,同時另一更輕量的 vue 發展勢頭更猛,尤其是即將 release 的2.0版本,號稱兼具了 angularjs 和 reactjs ... ...


本文來自於騰訊bugly開發者社區,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186

導語

最近這幾年的前端圈子,由於戲臺一般精彩紛呈,從 MVC 到 MVVM,你剛唱罷我登場。 backbone,angularjs 已成昨日黃花,reactjs 如日中天,同時另一更輕量的 vue 發展勢頭更猛,尤其是即將 release 的2.0版本,號稱兼具了 angularjs 和 reactjs 的兩者優點。不過現在的官方版本還是1.0 ,下麵就是基於1.0版本的初體驗。

1. 為什麼要 SPA?

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

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

2. 為什麼選擇 vue?

在選擇 vue 之前,使用 reactjs 也做過一個小 Demo,雖然兩者都是面向組件的開發思路,但是 reactjs 的全家桶方式,實在太過強勢,而自己定義的 JSX 規範,揉和在 JS 的組件框架里,導致如果後期發生頁面改版工作,工作量將會巨大。

vue 相對來說,就輕量的多,他的view層,還是原來的 dom 結構,除了一些自定義的 vue 指令作為自定義標簽以外,只要學會寫組件就可以了,學習成本也比較低。

3. 環境配置

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

先創建一個測試目錄,在裡面依次輸入以下命令。

 //初始化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 為模板子目錄。

dist 為構建出的文件目錄。

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')
       }
     }
   }
 }

13. 業界 vue 使用案例

小米移動官網:http://m.mi.com/1/#/index

餓了嗎招聘:https://jobs-mobile.ele.me/#!/


更多精彩內容歡迎關註bugly的微信公眾賬號:

騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合併功能幫助開發同學把每天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精准定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在發佈後快速的瞭解應用的質量情況,適配最新的 iOS, Android 官方操作系統,鵝廠的工程師都在使用,快來加入我們吧!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、【前言】關於tarjan tarjan演算法是由Robert Tarjan提出的求解有向圖強連通分量的演算法。 那麼問題來了找藍翔!(劃掉)什麼是強連通分量? 我們定義:如果兩個頂點互相連通(即存在A到B和B到A的通路),則稱這兩個點強連通。對於一個有向圖G,若是G中任意兩點都強連通,則稱G是一個強 ...
  • 最近大家都在找工作,我好迷茫,覺得自己會的東西太少了。所以決定開始學習SSH三大框架。 首先是struts. struts是基於mvc模式的框架。(struts其實也是servlet封裝,提高開發效率!) Servlet起到控制器作用!主要可以: 》 獲取請求數據封裝 【BeanUtils可以優化, ...
  • 對於自己寫ORM時的感受以及遇到的難點總結,對於自己而言總是會更清晰的理解到不足。 ...
  • 在我們開發很多項目中,數據訪問都是必不可少的,有的需要訪問Oracle、SQLServer、Mysql這些常規的資料庫,也有可能訪問SQLite、Access,或者一些我們可能不常用的PostgreSQL、IBM DB2、或者國產達夢資料庫等等,這些資料庫的共同特點是關係型資料庫,基本上開發的模型都... ...
  • SSH框架: Struts框架, 基於mvc模式的應用層框架技術! Hibernate,基於持久層的框架(數據訪問層使用)! Spring,創建對象處理對象的依賴關係以及框架整合! Dao代碼,如何編寫? 1.操作XML數據 2.使用Jdbc技術 3.原始的jdbc操作, Connection/St ...
  • 我們在學習自定義MVC框架的時候常常會聽到Model1 ,Model2和MVC。那麼什麼是Model1 什麼是Model2什麼又是MVC呢? 什麼是Model1? Model1就是一種純jsp開發技術,將業務邏輯代碼和視圖渲染代碼雜糅在一起。 什麼是Model2? Model2是在Model1的基礎 ...
  • 定義:定義一個用於創建對象的介面,讓子類決定實例化哪一個類,工廠方法使一個類的實例化延遲到其子類。 類型:創建類模式 類圖: ! 工廠模式: 首先需要說一下工廠模式。工廠模式根據抽象程度的不同分為三種:簡單工廠模式(也叫靜態工廠模式)、本文所講述的工廠方法模式、以及抽象工廠模式。工廠模式是編程中經常 ...
  • 1.錯誤描述 error LNK2001: 無法解析的外部符號 "__declspec(dllimport) void __cdecl PadSystem::Private::printQString(class std::basic_ostream<wchar_t,struct std::char ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...