vue- Vue-Cli腳手架工具安裝 -創建項目-頁面開發流程-組件生命周期- -03

  • 2019 年 10 月 11 日
  • 筆記

本部落格環境

如果這些環境過時了,那就不用浪費時間看這部落格了

Vue-Cli 項目環境搭建

npm (就類似於手機的應用商城,python 中的 pip 也一樣)

與 python 基礎環境對比

  1. node ~ python 解釋器
    • node 是 js 的解釋器,用 c++ 寫的
  2. npm ~ pip python 的庫管理工具(第三方模組下載的應用商店)
    • npm 就類似於 pip ,也要換源(常用淘寶源 cnpm),下載才快
  3. vue ~ django
    • 類似於 django 是一個大框架

環境搭建

  • 安裝 node
node 官網下載安裝包,傻瓜式安裝:https://nodejs.org/zh-cn/

下面的命令都是在 命令行中 執行-g 參數代表全局(global),會自動配置環境變數)

  • 安裝 cnpm (使用淘寶鏡像源)
npm install -g cnpm --registry=https://registry.npm.taobao.org
  • 安裝腳手架工具 vue/cli
cnpm install -g @vue/cli
  • 清空快取處理(前兩步出錯了再執行這個(把那些下載錯誤的文件刪除掉))
npm cache clean --force

創建啟動 vue 項目

一定要注意當前命令行的目錄位置

可以用 webstorm 來編寫 vue 程式碼, 專門用來開發前端的,pycharm 的提示及便捷性不如它(自行了解)

命令創建項目(步驟小多)

先進入到存放項目的目錄下

cd /d E:PyCharm 2019.1.3ProjectFileday010day066 (項目將會放在 day66 這個文件夾下)

創建項目

vue create v-proj(項目名) ,輸入完後會進入到下面的介面

介面操作方式:鍵盤 上下(↑↓)箭頭 選擇要用那個模板,回車選中(進入下面的介面同樣用 上下箭頭 移動游標,按空格選取,回車確認)

我們選擇下面這個 Manually select features

來到下面這個頁面,來看看這些選項都啥意思

各選項

  • Babel
    • 把 ES6 語法轉換成 ES5 讓瀏覽器可以解析
  • TypeScript
    • 是 JavaScript 的超集(學起來會比 JavaScript 難一些)
  • Progressive Web App (PWA) Support
    • 有很多優化前台項目的組件(後期再用到)
  • Router
    • vue 前台的路由管理
  • Vuex
    • 相當於一個全局 單例,頁面未刷新有效,一刷新就沒了
  • CSS Pre-processors
    • less、sass –> 預編譯語言,寫的也是 css,但它可以寫邏輯,必須瀏覽器解析成原生 css 才有用,相對來說 less 會好學一點,兩個小時左右就能學會了
  • Linter / Formatter
    • 限制(團隊)程式碼風格(縮進空格個數之類的) ESLint 更嚴格
  • Unit Testing
    • 測試用的
  • E2E Testing
    • 測試用的

我們選擇下面的這幾個(空格選取)

回車下一步(再一路選大寫的就行)

history 讓 vue 的當頁面應用可以至此跳轉(其他的暫時默認即可)

默認配置如下

然後就會自動進行安裝

至此,vue 項目創建完成

啟動 vue 項目(命令行方式)

在命令行下,進入項目根目錄,執行 npm run serve 啟動項目

等待載入後,出現下面的頁面即可在瀏覽器上輸入 localhost:8080 訪問(vue 項目默認埠是 8080)

在瀏覽器上訪問

啟動 vue 項目(pycharm 方式)

命令行方式啟動有著諸多不便,所以我們還是選擇採用 pycharm 來啟動吧(其實 webstorm 對 vue 的支援會更好,但不是這裡的重點),下面是 pycharm 啟動需要做的一些配置

先右鍵項目文件夾,用 pycharm 打開

配置 pycharm 啟動

用 pycharm 的綠色的啟動按鈕啟動 vue 項目

頁面效果(至此,pycharm 即可啟動項目)

Vue 項目目錄結構分析

├── v-proj  |   ├── node_modules    // 當前項目所有依賴,一般不可以移植給其他電腦環境(版本控制、備份程式碼 等等,這個文件一般都排除在外),在新環境下執行 cnpm install 即可重新安裝下載這個文件里的內容  |   ├── public  |   |   ├── favicon.ico // 瀏覽器標籤頁圖標(public/index.html 中的 <link rel="icon" href="<%= BASE_URL %>favicon.ico">)  |   |   └── index.html  // 當前項目唯一的頁面  |   ├── src  |   |   ├── assets      // 用來存放靜態資源,如:img、css、js  |   |   ├── components  // 存放小組件(再老一些的版本 components 和 views 是一個文件夾)  |   |   ├── views       // 存放頁面組件(一般是路由跳轉的組件)  |   |   ├── App.vue     // 根組件  |   |   ├── main.js     // 全局腳本文件(項目的入口)  |   |   ├── router.js   // 路由腳本文件(配置路由 url鏈接 與 頁面組件的映射關係)  |   |   └── store.js    // 倉庫腳本文件(vuex插件的配置文件,數據倉庫)  |   ├── README.md  └   └── **配置文件...    // "├" 輸入法 "v9" 可以找到這個符號

pycharm 支援 vue 語法

安裝 vue 插件

雙擊 App.vue 可以看到,沒有任何語法高亮,.vue 文件被識別成了普通文件,那麼我們可以通過安裝 vue 插件來解決這個問題

可以點圖中的 install plugins 來安裝 vue 插件(按提示安裝即可)

也可以在 pycharm 的 settings 里下載,下載完成重啟 pycharm 即可

重啟 pycharm 後, pycharm 就能識別 .vue 文件了,並且能夠為我們提供語法高亮(眼前又瞬間充滿了色彩)

部分 vue 文件剖析

自定義組件並渲染到頁面上

組件文件放在 components 下面,通常首字母大寫

組件通常由以下三部分組成

  1. template
    • 裡面有且只有一個根標籤
  2. script
    • 必須將 {} 導出(導出了外界才能導入) export default
    • 外界導入時,將 {} 賦值給前面的變數(進行關聯)
  3. style
    • style 標籤必須明確 scoped 屬性,代表該樣式只在組件內部起作用(樣式的組件化)

組件的導入與導出

寫程式碼的時候有些地方紅色波浪線可能是 ESLint 報錯,某個變數未被使用就會這樣,接著寫下去就好,不要太緊張

將組件導出(暴露出來)

組件需要將 實例?(內容豐富了長得很像 vue 實例)導出,外界才能使用

src/components/Test.vue

<template>  <!--    template 里只能有一個根標籤(作為一個組件)-->      <div class="test">          <p>Test 組件</p>      </div>  </template>    <script>      // 需要將其導出,外界才能導入,這個括弧相當於一個 vue 實例      export default {          name: "Test"      }  </script>    <style scoped>      /* style 裡面的這個 scoped 可以用來限制樣式的控制範圍(僅對這個組件生效)(讓樣式局部化,避免造成 css 衝突)*/      p {          color: red      }  </style>

父級組件導入(暴露出來的)組件

src/views/TestViews.vue

<template>      <div class="home">          <!-- 3. 直接在頁面上渲染自定義標籤(在 .vue 結尾的文件中標籤可以區分大小寫(不像 html 文件,不能區分標籤的大小寫))-->          <T></T>  <!-- 到時候子組件會直接把這個替換掉 -->      </div>  </template>    <script>      // 1. 將組件導入,並賦值給 T (把子組件 export default 那個實例? 跟 變數T ? 關聯起來,在本(父)組件中就可以直接用 T 來當 Test 組件操作了)      import T from '@/components/Test'      // 這個 @ 就等價於 src 文件夾的絕對路徑        export default {          // 2.註冊組件          components: {              T, // 註冊 T 組件          }      }  </script>

在 routers.js 里配置路由

src/router.js

沒有 history 地址欄就會有 # 標識(localhost:8080/#/路由

import Vue from 'vue'  import Router from 'vue-router'  import Home from './views/Home.vue'  // ****** 1. 先導入 ******  import Test from './views/TestViews'    Vue.use(Router)    export default new Router({    mode: 'history',  // 讓 vue 這種單頁面應用也支援 瀏覽器的前進後退(← →) (可以展開搜索下)    base: process.env.BASE_URL,    routes: [      {        path: '/',        name: 'home',        component: Home      },      {        path: '/about',        name: 'about',        // route level code-splitting        // this generates a separate chunk (about.[hash].js) for this route        // which is lazy-loaded when the route is visited.        component: () => import(/* webpackChunkName: "about" */ './views/About.vue')      },      // ****** 2. 註冊一條路由 ******      {        path: '/test',  // ****** 3. 一會兒直接訪問這個路由即可 localhost:8080/test ******        name: 'test',        component: Test      }    ]  })  

瀏覽器訪問

localhost:8080/test

為什麼頁面上會有 Home | About 呢?我們剛剛又沒寫

為什麼頁面上會有 Home | About 呢? – 其實是根組件 App.vue 裡面寫了

src/App.vue (看裡面的 Home、| 、 About)

<template>    <div id="app">      <div id="nav">        <router-link to="/">Home</router-link> |        <router-link to="/about">About</router-link>      </div>      <router-view/>    </div>  </template>    <style>  #app {    font-family: 'Avenir', Helvetica, Arial, sans-serif;    -webkit-font-smoothing: antialiased;    -moz-osx-font-smoothing: grayscale;    text-align: center;    color: #2c3e50;  }  #nav {    padding: 30px;  }    #nav a {    font-weight: bold;    color: #2c3e50;  }    #nav a.router-link-exact-active {    color: #42b983;  }  </style>  

全局腳本文件 main.js 解析(項目入口)

一般導入的時候會省略後綴,所以 同一文件夾下面盡量不要重名(導入可以忽略後綴)

默認寫法(創建 vue 項目自動生成的)

import Vue from 'vue'  import App from './App.vue'  import router from './router'  import store from './store'    // 新手教程的 使用提示 (要點 next next 的動畫)  Vue.config.productionTip = false    new Vue({      router,      store,      render: h => h(App)  }).$mount('#app')  // 等同於 el 掛載  

解釋性寫法 我們比較能理解的寫法(只是相當於對上面的解釋)

import Vue from 'vue'  // 載入vue環境  import App from './App.vue'  // 載入根組件  import router from './router'  // 載入路由環境  import store from './store'  // 載入數據倉庫環境    Vue.config.productionTip = false    new Vue({      el: '#app',      router,      store,      render: function (readFn) {          return readFn(App);      },  });  

vue 項目啟動生命周期

載入 mian.js 啟動項目

  • import Vue from 'vue' 為項目載入vue環境
  • import App from './App.vue' 載入根組件用於渲染替換掛載點
  • import router from './router' 載入路由腳本文件,進入路由相關配置

載入 router.js 文件

為項目提供路由服務,並載入已配置的路由(鏈接與頁面組件的映射關係)

註:不管當前渲染的是什麼路由,頁面渲染的一定是根組件,鏈接匹配到的頁面組件只是替換根組件中的 <router-view></router-view>

監測路由變化來做處理

vue 發生頁面跳轉的原理

如果請求鏈接改變(路由改變),router 里匹配到了,會把路由對應的 組件 拿出來,然後把根組件里的 <router-view></router-view> 標籤替換成 該組件

  • 每次路由跳轉都會走一次組件的生命周期

參與文件

main.js 入口文件

該文件內容不變

import Vue from 'vue'  import App from './App.vue'  import router from './router'  import store from './store'    Vue.config.productionTip = false    new Vue({      router,      store,      render: h => h(App)  }).$mount('#app')  

App.vue 項目根組件

常見項目的根組件( App.vue)都只寫下面這幾行程式碼

<template>      <div id="app">          <!-- url路徑會載入不同的頁面組件              eg:/red => RegPage  | /blue => BluePage           來替換router-view標籤,完成頁面的切換           -->          <router-view></router-view>      </div>  </template>

views/RedPage.vue 自定義頁面組件

<template>      <div class="red-page">        </div>  </template>  <script>        export default {          name: "RedPage",          components: {            },      }  </script>  <style scoped>      .red-page {          width: 100vw;          height: 100vh;          background-color: red;      }  </style>

views/BluePage.vue

<template>      <div class="blue-page">        </div>  </template>  <script>        export default {          name: "BluePage",          components: {            }      }  </script>  <style scoped>      .blue-page {          width: 100vw;          height: 100vh;          background-color: blue;      }  </style>

router.js 路由文件

import Vue from 'vue'  import Router from 'vue-router'  import Home from './views/Home.vue'  import RedPage from "./views/RedPage";  import BluePage from "./views/BluePage";    Vue.use(Router);    export default new Router({      mode: 'history',      base: process.env.BASE_URL,      routes: [          {              path: '/',              name: 'home',              component: Home          },          {              path: '/red',              name: 'red',              component: RedPage          },          {              path: '/blue',              name: 'blue',              component: BluePage          }      ]  })  

全局樣式文件配置與應用

jQuery、BootStrap 這些外部環境,都需要在 main.js 里配

後期可能把路徑配置這些寫成一個配置文件

assets/css/global.css

/*html, body, h1, h2, ul, p {*/  *{      margin: 0;      padding: 0;  }  ul {      list-style: none;  }  a {      color: black;      text-decoration: none;  }

main.js 中導入,讓它生效

import Vue from 'vue'  import App from './App.vue'  import router from './router'  import store from './store'    Vue.config.productionTip = false;    // ********* 配置全局樣式 *********  import '@/assets/css/global.css'      // new Vue({  //     router,  //     store,  //     render: h => h(App)  // }).$mount("#app");    new Vue({      el: '#app',      router,      store,      render: function (readFn) {          return readFn(App);      },  });  

小案例 – 封裝 Nav 導航欄組件

components/Nav.vue 新建子組件

採用 a 標籤會發生頁面跳轉刷新,重新載入了一次項目介面

–> 而 vue 是單頁面應用,通常採用 <router-link to="/路由"> </router-link> 處理跳轉

  • 每次路由跳轉都會走一次組件的生命周期
<template>      <div class="nav">          <!--採用 vue-router 完成頁面跳轉,不能採用 a 標籤(會發生頁面刷新,本質就是重新載入了一次項目介面)-->          <ul>              <li>                  <!--<a href="/">主頁</a>-->                  <router-link to="/">主頁</router-link>              </li>              <li>                  <router-link to="/red">紅頁</router-link>              </li>              <li>                  <router-link to="/blue">藍頁</router-link>              </li>          </ul>      </div>  </template>    <script>      export default {          name: "Nav",      }  </script>    <style scoped>      .nav {          width: 100%;          height: 60px;          background-color: orange;      }      .nav li {          float: left;          font: normal 20px/60px '微軟雅黑';          padding: 0 30px;      }      .nav li:hover {          cursor: pointer;          background-color: aquamarine;      }      .nav li.active {          cursor: pointer;          background-color: aquamarine;      }  </style>

views/HomePage.vue 新建視圖頁面

RedPage.vue與BluePage都是添加下方三個步驟程式碼

<template>      <div class="home">          <!-- 3)使用Nav組件 -->          <Nav></Nav>      </div>  </template>    <script>      // 1)導入Nav組件      import Nav from '@/components/Nav'      export default {          // 2)註冊Nav組件          components: {              Nav,          }      }  </script>  

新增頁面三步驟

  1. 在views文件夾中創建視圖組件(.vue 文件)
  2. 在router.js文件中配置路由
  3. 設置路由跳轉,在指定路由下渲染該頁面組件(替換根組件中的router-view標籤)
    • 渲染誰,(router-view)就替換成誰

案例

增加一個 tan 頁面 案例程式碼

views/TanPage.vue

<template>      <div class="tan-page">          <Nav></Nav>      </div>  </template>    <script>      import Nav from '@/components/Nav'      export default {          name: "TanPage",          components: {              Nav          }      }  </script>    <style scoped>      .tan-page {          width: 100vw;          height: 100vh;          background-color: tan;      }  </style>

router.js

// ...  import TanPage from "./views/TanPage";  export default new Router({      mode: 'history',      base: process.env.BASE_URL,      routes: [          // ...          {              path: '/tan',              name: 'tan',              component: TanPage          }      ]  })

components/Nav.vue

...  <li>      <router-link to="/tan">土頁</router-link>  </li>  ...

組件生命周期(鉤子函數剖析)*****

基本概念

詳細的可以看 vue 官方 API (推薦好好看看官方文檔這塊的介紹)

組件的生命周期:一個組件從創建到銷毀的過程,就稱之為組件的生命周期

組件創建到銷毀的過程中,會出現眾多關鍵的時間節點,如:

  • 組件要創建了
  • 組件創建完畢了
  • 組件數據渲染完畢了
  • 組件要被銷毀了
  • 組件銷毀完畢了
  • …等等 時間節點

每一個時間節點,vue 都為其提供了一個回調函數(在該組件到達該時間節點時,就會觸發對應的回調函數,在函數中就可以完成該節點需要完成的業務邏輯

生命周期鉤子函數也是 vue 的實例成員

生命周期鉤子函數

vue 官方提供的生命周期鉤子函數

beforeCreate  created  beforeMount  mounted  beforeUpdate  updated  activated  deactivated  beforeDestroy  destroyed  errorCaptured

在 vue 組件的 script 的 export default 導出字典中直接寫鉤子函數

重點鉤子函數:created(其他函數根據需求來用)

一般該組件請求後台的數據,都是在該鉤子中完成

  1. 請求來的數據可以給頁面變數進行賦值(此時實例成員已經載入了)
  2. 該節點還只停留在虛擬 DOM 範疇,如果數據還需要做二次修改再渲染到頁面
  3. 可以在 beforeMount、mounted 鉤子中添加邏輯處理
export default {      // ...      beforeCreate() {          console.log('組件創建了,但數據和方法還未提供');          // console.log(this.$data);          // console.log(this.$options.methods);          console.log(this.title);          console.log(this.alterTitle);      },      // 該鉤子需要掌握,一般該組件請求後台的數據,都是在該鉤子中完成      // 1)請求來的數據可以給頁面變數進行賦值      // 2)該節點還只停留在虛擬 DOM 範疇,如果數據還需要做二次修改再渲染到頁面,      //  可以在beforeMount、mounted鉤子中添加邏輯處理      created() {          console.log('組件創建了,數據和方法已提供');          // console.log(this.$data);          // console.log(this.$options.methods);          console.log(this.title);          console.log(this.alterTitle);          console.log(this.$options.name);      },      destroyed() {          console.log('組件銷毀完畢')      }  }
  • this.$options 可以拿到所有實例成員,包括自定義成員(好像會讓 vue 提前載入實例成員這些),一堆的屬性這些都在裡面(可以用它來取自定義屬性)
  • vue 實例可以直接 this.屬性/方法 取到 實例成員 data、methods 的內容(做了封裝,可以直接拿到)

  • vue 沒有 this.$method 這個成員屬性

根據請求路徑高亮路由標籤案例

<router-link to="/">主頁</router-link>

  • router-link 最終會被解析為 a 標籤,用 to 完成指定路徑跳轉,但是不能添加系統事件(因為是組件標籤)
  • 在 js 方法中可以用 this.$router.push('路徑') 完成 邏輯跳轉
  • 在 js 方法中可以用 this.$route.path 拿到當前請求的頁面路由

components/Nav.vue

this.$route、this.$router 可以好好了解一下(搜一下),一個管理路由數據,一個管理路由跳轉

<template>      <div class="nav">          <!--採用vue-router完成頁面跳轉,不能採用a標籤(會發生頁面刷新,本質就是重新載入了一次項目介面)-->          <ul>              <li @click="changePage('/')" :class="{active: currentPage === '/'}">                  <!--<a href="/">主頁</a>-->                  <!--<router-link to="/">主頁</router-link>-->                  主頁              </li>              <li @click="changePage('/red')" :class="{active: currentPage === '/red'}">                  <!--<router-link to="/red">紅頁</router-link>-->                  紅頁              </li>              <li @click="changePage('/blue')" :class="{active: currentPage === '/blue'}">                  <!--<router-link to="/blue">藍頁</router-link>-->                  藍頁              </li>              <li @click="changePage('/tan')" :class="{active: currentPage === '/tan'}">                  <!--<router-link to="/tan">土頁</router-link>-->                  土頁              </li>          </ul>      </div>  </template>    <script>      export default {          name: "Nav",          data() {              return {                  // 每渲染一個頁面,都會出現載入 Nav 組件,currentPage 就會被重置,                  // 1)在點擊跳轉事件中,將跳轉的頁面用 資料庫 保存,在鉤子函數中對 currentPage 進行數據更新                  // currentPage: localStorage.currentPage ? localStorage.currentPage: ''                  // 2)直接在 created 鉤子函數中,獲取當前的 url 路徑,根據路徑更新 currentPage                  currentPage: ''              }          },          methods: {              changePage(page) {                  // console.log(page);                  // 當 Nav 出現渲染,該語句就無意義,因為在 data 中將 currentPage 重置為空                  // this.currentPage = page;                    // 有 bug,用戶不通過點擊,直接修改請求路徑完成頁面跳轉,資料庫就不會更新數據                  // localStorage.currentPage = page;                    // 任何一個標籤的事件中,都可以通過 router 完成邏輯條件                  // console.log(this.$route);  // 管理路由數據                  // console.log(this.$router);  // 管理路由跳轉                  this.$router.push(page);  // 路由的邏輯跳轉              }          },          // 當前組件載入成功,要根據當前實際所在的路徑,判斷單選激活標籤          created() {              // console.log(this.$route.path);              this.currentPage = this.$route.path;          }      }  </script>    <style scoped>      .nav {          width: 100%;          height: 60px;          background-color: orange;      }      .nav li {          float: left;          font: normal 20px/60px '微軟雅黑';          padding: 0 30px;      }      .nav li:hover {          cursor: pointer;          background-color: aquamarine;      }      .nav li.active {          cursor: pointer;          background-color: aquamarine;      }  </style>

vue 官方提供的組件生命周期圖