在這一期的學習進度中,咱們會開始學習在咱們工做開發中真正要使用的開發模式,vue-cli3腳手架的運用以及Vue-router和狀態管理倉Vuexjavascript
安裝node環境css
由於Vue-cli3也是基於webpack搭建的,因此咱們仍是必需要用到node,若是你的電腦尚未安裝node的話,能夠到node官網自行下載安裝,https://nodejs.org/zh-cn/ 推薦你們下載10.14.0版本。 安裝事後 到命令行執行 node -v 檢查版本,若是彈出版本 v10.14.0的話 恭喜你 安裝成功,咱們開始進行下面的步驟了。html
全局安裝vue-cli3
Vue CLI 的包名稱由 vue-cli 改爲了 @vue/cli。 若是你已經全局安裝了舊版本的 vue-cli (1.x 或 2.x),你須要先經過 npm uninstall vue-cli -g 卸載它。vue
官方要求安裝的node版本是大於8.9的,咱們安裝的node爲10.14.0,若是你自己的版本低於8.9 那麼能夠用nvm進行升級java
而後咱們到命令行執行node
npm install -g @vue/cli
等待運行完畢,咱們命令行執行react
vue --version
若是版本在3.0以上,恭喜你安裝成功了webpack
接下來咱們在命令行運行git
vue create hello-world
這時候它會提醒咱們來選擇須要安裝的選項es6
Vue CLI v3.1.3 ┌───────────────────────────┐ │ Update available: 3.2.1 │ └───────────────────────────┘ ? Please pick a preset: (Use arrow keys) > my (vue-router, vuex, less, babel, eslint) default (babel, eslint) Manually select features
第一個是我已經保存過的了,咱們初次搭建,選擇 Manually select features
Vue CLI v3.1.3 ┌───────────────────────────┐ │ Update available: 3.2.1 │ └───────────────────────────┘ ? Please pick a preset: Manually select features ? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection) >( ) Babel ( ) TypeScript ( ) Progressive Web App (PWA) Support ( ) Router ( ) Vuex ( ) CSS Pre-processors ( ) Linter / Formatter ( ) Unit Testing ( ) E2E Testing
咱們看到如上界面之後,選擇 Babel Router Vuex CSS Pre-processors (使用空格鍵選中) 進行下一步
Vue CLI v3.1.3 ┌───────────────────────────┐ │ Update available: 3.2.1 │ └───────────────────────────┘ ? Please pick a preset: Manually select features ? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json ? Save this as a preset for future projects? Yes ? Save preset as: MyTest
接下來你們按照如上配置,基本能夠完成項目搭建。等待安裝完畢後執行
cd helloworld npm run serve
進入瀏覽器 打開 localhost:8080 若是看到Vue的歡迎界面,恭喜你,咱們已經邁出成功第一步了
在Vue-cli3 中 爲你們提供了圖形化配置界面 執行
vue ui
會開到提示在 localhost:8000打開了UI配置界面,因爲我的感受圖形化配置仍是不靈活的,這裏就先不給你們作太多演示了,要想作好一個VUE項目,須要作太多的搭配,我這裏會配置一套基本可使用的VUE框架放到我的github供你們使用。
<template> <!-- 這裏面寫html --> </template> <script> // 若是要引入外部的文件 請在 export default外面引入 // 例如 // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' //這裏引入了 HelloWorld組件 export default { name: 'home', components: { HelloWorld //使用組件 } } export default { name:'vue', //這裏面就寫VUE組件內的屬性便可 } </script> <style> /* 這裏寫css 固然你也能夠寫 lass scss 須要babel支持而且在 style上添加 lang屬性 例如 <style lang="scss"> */ </style>
咱們使用VUE通常是用來開發單頁程序,那麼在單頁程序內的跳轉就要用到路由的形式,因爲這裏課程是要帶你們快速熟悉VUE而且使用VUE,因此原理這裏暫且跳過,直接告訴你們使用方法。
咱們在開始選擇框架要用到的東西的時候已經選擇了Vue-Router,這裏咱們直接上代碼使用便可。咱們這裏就搭配着Vue-Cli3的腳手架來說解,這裏順便爲你們講解一下Vue-cli3腳手架爲咱們建立的目錄結構
│ .gitignore git屏蔽提交文件
│ babel.config.js 自定義babel的地方
│ package.json 你的全部依賴啥的
│ README.md 不介紹
│ yarn.lock yarn 若是你是 npm 就是 package-lock.json
│
├─public 經過執行 npm run build 產生
│ favicon.ico
│ index.html
│
└─src 主要咱們要修改的地方
│ App.vue App主組件 │ main.js 入口JS │ router.js 路由文件 │ store.js vueX store 文件 │ ├─assets 靜態文件存放處 │ logo.png │ ├─components 咱們本身寫的組件 │ HelloWorld.vue │ └─views VUE頁面,這麼安排爲了方便區分是組件仍是用於路由跳轉的頁面 About.vue Home.vue
咱們直接看到入口文件
// main.js import Vue from 'vue' import App from './App.vue' import router from './router' //在入口文件引用了router 而且 在newVue的時候 把router掛在到了router屬性上 import store from './store' Vue.config.productionTip = false new Vue({ router, // 掛載 store, render: h => h(App) }).$mount('#app')
接着咱們看到 router.js文件
//router.js import Vue from 'vue' import Router from 'vue-router' //這裏引用了vue-router 而且用Vue.use來使用Router import Home from './views/Home.vue' Vue.use(Router) // Router的內置屬性 export default new Router({ mode: 'history', //要使用hash模式仍是 history模式 咱們通常狀況下仍是用 hash模式 history在後臺支持的狀況下能夠開啓 base: process.env.BASE_URL, // 應用的基路徑。例如,若是整個單頁應用服務在 /app/ 下,而後 base 就應該設爲 "/app/" routes: [ //這就是真正寫路由的地方了 { path: '/', // 當路由是/的時候 咱們匹配哪一個組件(這裏是Home.vue) name: 'home', component: Home }, { path: '/about', // 當路由是/about的時候 咱們匹配哪一個組件(About.vue) 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') //路遊懶加載,推薦使用這種模式,不過咱們須要改寫一下 } ] })
稍微作一下懶加載模式的改寫
//router.js import Vue from 'vue' import Router from 'vue-router' //這裏引用了vue-router 而且用Vue.use來使用Router Vue.use(Router) const Home = ()=> import('./views/Home.vue') //使用常量模式 提早聲明 而且在component中使用 這樣咱們就完成了一個能夠簡單實用的路由了 const About = ()=> import('./views/About.vue') // Router的內置屬性 export default new Router({ mode: 'history', //要使用hash模式仍是 history模式 咱們通常狀況下仍是用 hash模式 history在後臺支持的狀況下能夠開啓 base: process.env.BASE_URL, // 應用的基路徑。例如,若是整個單頁應用服務在 /app/ 下,而後 base 就應該設爲 "/app/" routes: [ //這就是真正寫路由的地方了 { path: '/', // 當路由是/的時候 咱們匹配哪一個組件(這裏是Home.vue) name: 'home', component: Home }, { path: '/about', // 當路由是/about的時候 咱們匹配哪一個組件(About.vue) 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: About } ] })
那麼 這些路由組件怎麼展現呢?在哪裏展現呢?
這裏咱們要看到 App.vue文件
<template> <div id="app"> <div id="nav"> <router-link to="/">Home</router-link> <!-- 使用router-link標籤 添加 to屬性決定咱們要路由到什麼地方 --> <router-link to="/about">About</router-link> </div> <router-view/> <!-- 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>
到這裏 咱們一個簡單的基礎的路由已經寫好了 可使用了
如何建立子路由呢?其實也是至關簡單的
//router.js import Vue from 'vue' import Router from 'vue-router' //這裏引用了vue-router 而且用Vue.use來使用Router Vue.use(Router) const Home = ()=> import('./views/Home.vue') //使用常量模式 提早聲明 而且在component中使用 這樣咱們就完成了一個能夠簡單實用的路由了 const About = ()=> import('./views/About.vue') const Children = ()=> import('./views/Children.vue') //咱們引入這個頁面 頁面內容 // Router的內置屬性 export default new Router({ // mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home, children:[ { path: 'about', name: 'about', component: About, }, { path:'children', name:'children', component:Children }, //這時候咱們訪問 訪問about 其實是home的子路由了 // 它是home的子路由,因此我們須要在home裏面再書寫一個路由顯示的組件 <router-view/> 子路由的內容就會顯示在其 router-view下面 ] } ] })
這裏我把改進後的 app.vue 和 about.vue 等代碼貼出。
<!-- app --> <template> <div id="app"> <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>
<!-- about --> <template> <div class="about"> <h1>This is an about page</h1> </div> </template>
<template> <!-- children --> <div class="children"> <h1>This is an children page</h1> </div> </template>
<!-- home --> <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <div> <!-- <router-link to="/">Home</router-link> | --> <router-link to="/about">About</router-link> | <router-link to="/children">children</router-link> </div> <router-view></router-view> </div> </template> <script> // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script>
編程式的導航 router.push
編程式的導航傳參咱們須要使用 this.$router.push() 這個方法 咱們有三種傳參方式
<script> // 字符串 // 這種方式能夠實現路由跳轉 可是沒有辦法獲取到參數 沒有辦法傳參 this.$router.push("children"); // 對象傳參 // 這裏面咱們就要用到路由裏面的name屬性了 // 命名路由傳遞參數須要使用params來傳遞,目標頁面接收傳遞參數時使用this.$route.params // 特別注意:命名路由這種方式傳遞的參數,若是在目標頁面刷新是會出錯的 this.$router.push({ name: 'children', params: { userId: 'qm' }}) // 想要傳遞參數主要就是以對象的方式來寫,分爲兩種方式:命名路由、查詢參數,下面分別說明兩種方式的用法和注意事項。 // 查詢參數 this.$router.push({ path: '/children', query: { userId: 'qm' }}); // 這種方式參數是暴露在地址欄上面的 //查詢參數其實就是在路由地址後面帶上參數和傳統的url參數一致的,傳遞參數使用query並且必須配合path來傳遞參數而不能用name,目標頁面接收傳遞的參數使用query。 //注意:和name配對的是params,和path配對的是query </script>
聲明式的導航 <router-link>
聲明式的導航也分爲三種方式
<!-- 字符串 --> <router-link to="children">click to news page</router-link> <!-- 命名路由 --> <router-link :to="{ name: 'children', params: { userId: 'qm'}}">click to news page</router-link> <!-- 查詢參數 --> <router-link :to="{ path: '/children', query: { userId: 'qm'}}">click to news page</router-link>
1.命名路由搭配params,刷新頁面參數會丟失
2.查詢參數搭配query,刷新頁面數據不會丟失
3.接受參數使用this.$router後面就是搭配路由的名稱就能獲取到參數的值
url 傳參
這種傳參方法咱們須要在書寫路由的時候作一個小小的改動
//router.js import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) const Home = ()=> import('./views/Home.vue') const About = ()=> import('./views/About.vue') const Children = ()=> import('./views/Children.vue') // Router的內置屬性 export default new Router({ // mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home, children:[ { path: 'about/:id', // 咱們在後面加入/:id 這樣咱們在url輸入的地址相應就變成了 /about/3 咱們在about組件內 經過 this.$route.params 如何動態監聽路由的變化呢?要知道當咱們僅僅參數變化組件但是沒有刷新的,提示一下 watch但是能夠監聽某些數據哦~實戰課程我會帶你們瞭解一下這個該如何運用 name: 'about', component: About, }, { path:'children', name:'children', component:Children }, ] } ] })
相應頁面改動以下 home.vue about.vue
<!-- about --> <template> <div class="about"> <h1>This is an about page</h1> {{this.$route.params.id}} </div> </template>
<!-- home --> <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <div> <router-link to="/about/3">About</router-link> | <router-link to="/children">children</router-link> </div> <router-view></router-view> </div> </template> <script> // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld }, } </script>
404的製做
其實vue-router裏面還有不少細節內容,這裏因爲咱們是基礎課程因此不作太詳細的介紹,相信你們在瀏覽網頁的時候常常會看到404頁面,那麼用咱們的vue-router如何去實現一個404呢?其實是很是簡單的
咱們須要在router.js裏面書寫一個通配路徑放置在最後位置,當全部的路徑都不匹配的時候,就會去通配這樣一個404頁面提示你們頁面丟失了,下面我詳細給你們書寫一下
// router.js import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) const Home = ()=> import('./views/Home.vue') const About = ()=> import('./views/About.vue') const Children = ()=> import('./views/Children.vue') const NotFound = ()=> import('./views/notFound.vue') // Router的內置屬性 export default new Router({ // mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home, children:[ { path: 'about/:id', name: 'about', component: About, }, { path:'children', name:'children', component:Children }, ] }, { path: '*', name: '404', component: NotFound, } ] })
學完這些,你對於vue-router的基本運用已經能夠算是合格了,工做中的大部分用法也都接觸到了,咱們接下來說解vuex。
首先來一個小demo展現一下vuex的具體用途
// store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { number:0, }, mutations: { ADD(state){ state.number++ }, SUB(state){ state.number-- } }, actions: { } })
<!-- children.vue --> <template> <div class="children"> <h1>This is an children page</h1> <button @click="add"> + </button> {{$store.state.number}} <button @click="sub"> - </button> </div> </template> <script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, sub() { this.$store.commit('SUB') } } } </script>
這樣一個小小的demo實際上已經詮釋了咱們vuex的做用,咱們須要在刷新以前永久保留的狀態,而且想要遠距離傳參而且及時作出響應,那麼均可以使用vuex來進行。它就是一個狀態管理和加工的倉庫,一共有五個重要屬性,state,mutations,actions , getter , module 這麼五個小玩意 我會帶你們一個一個認識他們的做用,而且教會你們基本用法,固然了,它也是有較爲高級的小用法的。咱們實戰課也會使用稍微高級的用法來說解。
咱們用VUE文件來類比講解store
state就至關於vue中的data屬性,全部的狀態或者說是數據都存儲在這個state裏面,咱們在別的地方獲取須要使用 this.$store.state.屬性名稱 來獲取相應的值,而且咱們能夠經過 mutations 和 actions 來改變state的值,從而觸發全部使用到state的地方刷新。state裏能夠存儲各類數據類型,data裏面能夠用的數據類型,state裏面一樣可使用。
getters咱們類比到vue中,那麼它應該是 computed了 咱們在使用的時候 要使用 this.$store.getters.屬性名 用法也和computed相似,它其實是調用一個方法,而後獲取到的數據是通過一系列處理後而且return回來的數據,它的具體寫法是。
// store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { number:0, }, mutations: { ADD(state){ state.number++ }, SUB(state){ state.number-- } }, actions: { }, getters:{ getNumber(state){ //getter的書寫方法 return state.number + 100 } } })
<!-- children.vue --> <template> <div class="children"> <h1>This is an children page</h1> <button @click="add"> + </button> {{$store.state.number}} {{$store.getters.getNumber}} <!-- getters的基礎調用方法,固然還有更高級的 實戰課會講解 --> <button @click="sub"> - </button> </div> </template> <script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, sub() { this.$store.commit('SUB') } } } </script>
這就是getters的簡單實用
mutations類比到vuex中應該是 methods
它是更改 Vuex 的 store 中的狀態的惟一方法是提交 mutation。Vuex 中的 mutation 很是相似於事件:每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是咱們實際進行狀態更改的地方,而且它會接受 state 做爲第一個參數:
// store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { number:0, }, mutations: { ADD(state){ //咱們把state傳入 就能夠對 state內的數據進行咱們想要的變化,它必須按照咱們想要獲得的格式去變化, 想要直接提交mutations 須要使用 this.$stote.commit('mutations的方法名',參數) 若是想要在mutations方法中傳參,寫法就要變成 ADD(state,形參){ ... } 這樣一種形式了 我舉個例子 state.number++ }, SUB(state){ state.number-- }, // ADDPARAM(state,param){ // if (typeof param !== 'number'){ // param = 0 // } // state.number = state.number + param // } 這個方法就能夠傳入咱們想用的參數了,相應調用的地方也須要改變 }, actions: { }, getters:{ getNumber(state){ //getter的書寫方法 return state.number + 100 } } })
帶參數的commit如何使用
<!-- children.vue --> <template> <div class="children"> <h1>This is an children page</h1> <button @click="add"> + </button> {{$store.state.number}} {{$store.getters.getNumber}} <!-- getters的基礎調用方法,固然還有更高級的 實戰課會講解 --> <button @click="sub"> - </button> </div> </template> <script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, // addparam() { // this.$store.commit('ADDPARAM',5) // }, // this.$store.commit('要提交的mutations名字',要傳入的參數) sub() { this.$store.commit('SUB') } } } </script>
actions屬性用法和mutations相似,可是actions咱們是不能夠修改state的 須要在actions經過commit來調用mutations來修改數據,那麼action的意義何在呢?處理異步事件就要用action來作了呀。調用方法是,this.$store.dispatch("action的名字",參數)
書寫的方法呢 我給你們展現一下
// store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { number:0, }, mutations: { ADD(state){ state.number++ }, SUB(state){ state.number-- }, ADDPARAM(state,param){ if (typeof param !== 'number'){ param = 0 } state.number = state.number + param } }, actions: { ASYNCADD(context,param){ //這裏咱們傳入context上下文,裏面包含 commit, state ,getters 這三個屬性均可以經過context來調用到而且觸發內部方法 setTimeout(function(){ context.commit('ADDPARAM',param) },1000) } }, getters:{ getNumber(state){ //getter的書寫方法 return state.number + 100 } } })
action的使用
<!-- children.vue --> <template> <div class="children"> <h1>This is an children page</h1> <button @click="add"> + </button> {{$store.state.number}} {{$store.getters.getNumber}} <button @click="sub"> - </button> <button @click="actAdd">action</button> </div> </template> <script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, actAdd(){ // 咱們經過 dispatch來調用action 而後由action去提交咱們的 mutations來達到異步更改狀態的目的 this.$store.dispatch('ASYNCADD',50) }, sub() { this.$store.commit('SUB') } } } </script>
vuex是否是很是簡單呢,到這裏你們可能在想,若是個人項目很大 我有不少的這些個方法啊,狀態啊,我都寫在這裏不就亂了嗎?沒錯,確定亂了,因此vuex還提供了modules 方便咱們分塊管理
因爲使用單一狀態樹,應用的全部狀態會集中到一個比較大的對象。當應用變得很是複雜時,store 對象就有可能變得至關臃腫。
爲了解決以上問題,Vuex 容許咱們將 store 分割成模塊(module)。每一個模塊擁有本身的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行一樣方式的分割
咱們把上面的這些個代碼分割出去
// store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const children = { // 由於咱們是寫在了children這個組件裏面 這個命名規則也好讓咱們知道它存在於哪裏 state: { number:0, }, mutations: { ADD(state){ state.number++ }, SUB(state){ state.number-- }, ADDPARAM(state,param){ if (typeof param !== 'number'){ param = 0 } state.number = state.number + param } }, actions: { ASYNCADD(context,param){ setTimeout(function(){ context.commit('ADDPARAM',param) },1000) } }, getters:{ getNumber(state){ return state.number + 100 } } } export default new Vuex.Store({ modules:{ children, // 這裏咱們把children傳入之後 仍是同樣可使用咱們的哪些方法,不過咱們是state要加上 modules名字 因此咱們的children.vue 要相應修改 } })
<!-- children.vue --> <template> <div class="children"> <h1>This is an children page</h1> <button @click="add"> + </button> {{$store.state.children.number}} <!-- 你們能夠看到,這裏須要加上modules名字才能夠獲取到number 可是其他的不須要處理,因此咱們開發中通常想要獲取到 state 最好是經過getter來獲取 --> {{$store.getters.getNumber}} <button @click="sub"> - </button> <button @click="actAdd">action</button> </div> </template> <script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, actAdd(){ // 咱們經過 dispatch來調用action 而後由action去提交咱們的 mutations來達到異步更改狀態的目的 this.$store.dispatch('ASYNCADD',50) }, sub() { this.$store.commit('SUB') } }, } </script>
不少時候,咱們在正式使用中都會加上命名空間,也就是 modules裏面的 namespaced 屬性咱們讓它變爲 namespaced: true, 若是你們想要學習,能夠去vuex文檔學習,得益於es6 vuex給咱們提供了
mapState, mapGetters, mapActions 和 mapMutations 這四巨頭 咱們能夠輕鬆使用命名空間 這裏不作過多講解,實戰課程中咱們邊用邊說。