後端小白的VUE入門筆記, 進階篇
使用 vue-cli( 腳手架) 搭建專案
基於vue-cli 建立一個模板專案
通過 npm root -g 可以檢視vue全域性安裝目錄,進而知道自己有沒有安裝vue-cli
如果沒有安裝的話,使用如下命令全域性安裝
cnpm install -g vue-cli
建立一個基於webpack的新專案,在這過程中, 會安裝依賴
vue init webpack 專案名
啟動
cd vue-router-demo
npm start
常用的目錄結構
如果我們的專案是通過腳手架搭建的,這已經是一個比較完善的種子專案了
|-- build : webpack 相關的配置資料夾(基本不需要修改) |-- config: webpack 相關的配置資料夾(基本不需要修改) |-- index.js: 指定的後臺服務的埠號和靜態資原始檔夾 |-- node_modules: 在上面安裝的依賴,都存放在這個資料夾下 |-- src : 原始碼資料夾,我們後續開發的元件和js分門別類的放在這裡面 |-- main.js: 應用入口 js |-- static: 靜態資原始檔夾 |-- .babelrc: babel 的配置檔案 |-- .editorconfig: 通過編輯器的編碼/格式進行一定的配置 |-- .eslintignore: eslint 檢查忽略的配置 |-- .eslintrc.js: eslint 檢查的配置 |-- .gitignore: git 版本管制忽略的配置 |-- index.html: 主頁面檔案 |-- package.json: 他就相當於maven的pom.xml, 裡面存放著相關的依賴資訊和專案的版本資訊 |-- README.md: 應用描述說明的 readme 檔案
配置config/index.js
可以在config/index.js中做一下的常用配置
- 新增跨域的配置
- 配置專案的主機名,埠號
- 配置是否開啟瀏覽器
- 程式碼檢查工具eslint
在開發的時候我們主要還是關注src檔案, 後來需要的路由,store,ajaxApi,以及其他元件全部在建立在這個資料夾下
const path = require('path') module.exports = { dev: { // Paths assetsSubDirectory: 'static', assetsPublicPath: '/', proxyTable: {}, //新增跨域的配置 // Various Dev Server settings host: 'localhost', // can be overwritten by process.env.HOST port: 9528, // 配置是否開啟瀏覽器 autoOpenBrowser: true, //配置是否開啟瀏覽器 errorOverlay: true, notifyOnErrors: false, poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- // Use Eslint Loader? // If true, your code will be linted during bundling and // linting errors and warnings will be shown in the console. useEslint: false, // If true, eslint errors and warnings will also be shown in the error overlay // in the browser. showEslintErrorsInOverlay: false,
入口js檔案 main.js的主要作用
- 建立vue例項, 關聯index.html中id為app的div程式碼塊
- 新增專案中的路由模組 router
- 新增store模組---vuex
一般做好這不配置之後main就不用再改動了
import App from './App' import router from './router' import store from './store' Vue.use(ElementUI, { locale }) new Vue({ el: '#app', router, store, template: '<App/>', components: { App } })
根元件App.vue
其實每一個元件都可以完整的擁有下面三部分, 當然如果哪個元件中不需要新增css樣式,可以把最後一個style或者script標籤去掉
<template>
<div>
<!-- 這裡存放 -->
</div>
</template>
<//script>
export default {
name: 'app'
}
<///script>
<style>
</style>
元件間的相互呼叫
比如根元件想使用hello.vue元件,怎麼做呢?
像下面這樣,三步走
- 第一步:引入元件
- 第二步:將元件對映成target標籤
- 第三步使用標籤
<template>
<div>
<!--第三步使用標籤-->
<hello/>
<div/>
<template/>
<script>
import hello form './XXX/hello.vue'
export default{
// 將元件對映成標籤
components:{
hello
}
}
<style>
</style>
第二步中引入標籤時也可以去掉.vue字尾
或者直接這樣寫,是從@/ 代表的是 src/
import hello form '@/XXX/hello'
打包與釋出
打包
- 打包的命令:
npm run build
專案經過打包,產出是一個dist檔案,裡面分別是index.html 和 靜態資原始檔夾, 這也是前後端分離開發的特色,後端想控制view層,也難了,只有一張index.html
釋出方法1-靜態伺服器工具包
命令:
npm install -g serve // 安裝工具
serve dist
釋出方法2-使用tomcat伺服器
注意點,使用tomcat當伺服器,要求資料夾的名字和專案的名字要一樣,修改的步驟如下:
- 修改
/build/webpack_prod.conf.js
檔案
output:{
...
pathPath:'專案名稱'
}
- 編譯重新打包
npm run build
- 把打包得到的dist資料夾改名,改成專案名
- 將改名完事後的檔案拷貝到tomcat的webapps目錄下,執行tomcat
eslint的編碼規範檢查
好的習慣就是使用它,規範自己的程式碼風格, 但是也得說一下怎麼禁用eslint'
- 方法一: 通過如果是webstorm編譯器的話,點選file/settings/ , 搜尋eslint,可以enable掉
- 方法二: 編輯.eslintignore檔案,新增自己想被忽略的檔案
*.js
*.vue
一般我們就寫這兩部分,這一下子全忽略了
因為eslint有個莫名其妙的要求,程式碼最後一行要求是空行,可以通過下面的方法三取消掉
- 方法三: 編輯.eslintrc.js
rules:{
...
// 新增
'indent':0
}
父子元件之間資料互動
在差分元件的時候,本著多個元件共享的資料放在根元件的原則, 於是我們把共用的資料放在根元件,於此同時操作這些資料的方法也被我們定義在根元件,子元件想要使用這些資料,想要操作這些陣列怎麼辦呢? 想下面那樣,進行元件之間的資料傳遞
- 在父元件中給子元件傳遞方法或資料
使用:強制資料繫結的方法, ChildTarget是我們在components模組將子元件對映得來的子元件標籤, name可以是vue中data的方法,也可以是方法
<template>
<ChildTarget :name="name"/>
</template>
- 子元件取出父元件傳遞過來的值
export default{
props:['name','name2']
}
資料的互動@click
最常用的就是使用@click="方法名"
, 或者@click="value = !value"
或者@click="value = true
如果我們向上面那樣, 把公共的資料放在父元件中, 那麼事件的觸發一定是發生在子元件中, 子元件一般通過@click
給模板中的元素繫結上指定的動作,進而呼叫父元件的傳遞進來的方法,操作父元件傳遞進來的值
此外,在所有的元件中,vue的data部分都向下面這樣寫,是規定
data(){
return{
name:''
}
}
- 常用的監視watch模組
watch:{
監視的data中的物件
name:{
deep:true, // 深度監視
handler: function(value){ // value就是變化後的新的值
// todo
}
}
}
- 快取模組
從快取中把去出來的字串轉換成json串
JSON.parse(window.localStorage.getItem('')||'預設值');
把物件,儲存進瀏覽器的快取
window.localStorage.setItem('自定義的key',JSON.stringfy(value))
訊息訂閱,打破父子元件資訊傳遞的約束
像上面那樣,如果不存在父子元件的關係,父元件不引入子元件,也就沒辦法把他對映成標籤, 既然對映不成標籤也就沒法像上面那樣,通過 : 冒號 強制進行資料的繫結達到傳遞值的效果,於是有了訊息訂閱
元件之間的通訊方式: 釋出/訂閱
繫結監聽: 訂閱事件
觸發事件: 釋出事件
藉助外掛-public.js
安裝命令:
npm install --save pubsub-js
場景: 我們給模板上的按鈕繫結點選事件,一旦被點解他就釋出事件
- 在使用前需要匯入PubSub物件
import PubSub from 'public-js'
使用:訊息的釋出
<button @click="search">Search</botton>
export default{
methods:{
search(name){
// search是方法名
// name 是攜帶的引數,沒引數就不用寫
Publish.publish('search',name)
}
}
}
訊息的訂閱:
- 依然是第一步:引入PubSub物件
- 編碼實現:
mounted: {
PubSub.subscribe("search",(name)=>{
// todo with name
});
非同步請求
安裝外掛axios
npm install axios --save
- 在使用之間同樣是需要引入:
import axios from 'axios'
傳送一個get請求
axios.get(url)
.then(res=>{
// todo with res
})
.catch(error){
// todo
}
路由:
vue是如何做到使後端乖乖交出view層的控制權的?,難道是直接使用window.location.href = url嗎?
其實學過路由才知道, 使用的是vue-router,一個官方提供的路由框架,可以使用我通過組合元件來組成應用程式,仰仗它的路由外掛vue-router,我們輕鬆控制頁面的切換
我們要做的就是將元件components對映到routers,然後告訴vue-router到哪裡去渲染他們
定義路由器
安裝外掛
npm install vue-router --save
編碼,其實大家都會把關於路由的編碼單獨放到一個叫router的資料夾中,而且,它的編碼基本上是機械化的編碼,分為如下幾步
- 引入Vue,VueRouter
- 宣告Vue.use(VueRouter)
- 引入路由元件
- 對外暴露路由器物件,並且把路由元件配置進路由器物件
注意點 下面的配置部分, routes 不寫亂寫!!!
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
import Home from './Home.vue'
import About from './About.vue'
import Me from './Me.vue'
export default new VueRouter({
// 新增路由
routes:[
{
path:'/home',
component:Home,
meta:{
// 新增自定義的欄位,可以當成flag,也可以文字
}
},
{
path:'/about',
component:About,
meta:{
// 新增自定義的欄位,可以當成flag,也可以文字
},
childred:[ // 巢狀路由
{
path:'/about',
component:About,
meta:{
// 新增自定義的欄位,可以當成flag,也可以文字
}
}
]
}
},
{
path:'', // 預設訪問空的話,重定向到/home
redirect:'/home'
}
]
})
使用路由進行頁面的跳轉
原來進行頁面的跳轉我們通常使用a標籤,(一般把a標籤設計成按鈕,或者導航欄的樣子,點擊發送請求,進而跳轉頁面了), 而vue的路由其實和a標籤差不多,我們使用vue的 router-link標籤替換a標籤
<router-link to:'/about' class="可以讓我看起來像按鈕的css樣式"> </router-link>
<router-link to:'/home' class="可以讓我看起來像按鈕的css樣式"> </router-link>
<router-view ></router-view>
這樣使用者點選 router-link,就會把相應的子元件移植到
補充:
屬性 | 型別 | 含義 |
---|---|---|
to | string | Location | 表示目標路由的連結。當被點選後,內部會立刻把 to 的值傳到 router.push() ,所以這個值可以是一個字串或者是描述目標位置的物件。 |
replace | boolean | 設定 replace 屬性的話,當點選時,會呼叫 router.replace() 而不是 router.push() ,於是導航後不會留下 history 記錄。 |
append | boolean | 設定 append 屬性後,則在當前(相對)路徑前新增基路徑。例如,我們從 /a 導航到一個相對路徑 b ,如果沒有配置 append ,則路徑為 /b ,如果配了,則為 /a/b |
回退到上一個路由
我們可以在按鈕上新增下面的動作,是路由回退一級
<button @click="$router.back()"></button>
快取路由元件
使用如下標籤包裹我們的router-view,這樣當我們再回退到上一個路由時,使用者加進去的狀態依然存在
<keep-alive>
<router-view ></router-view>
</keep-alive>
$router與$route
$router是路由器物件,說白了就是用它去跳轉頁面,美其名曰:程式設計式路由導航
$route是路由物件,說白了就是某一個路由物件,既然是某一個,就不能進行頁面的跳轉,相反是可以獲取出當前路由元件的屬性,它的結構圖如下:
$route的組成圖
向路由元件傳遞值 一
需求: 我們想傳送這樣的請求 http:localhost:8080/home/1/羊肉串,在路徑上攜帶著引數1
路由怎麼接收引數呢?--> 使用:佔位
export default new VueRouter({
// 新增路由
routes:[
{
path:'/home/:id/:type', // 如果想在路徑上傳遞值進來,就使用:佔位
component:Home,
meta:{
// 新增自定義的欄位,可以當成flag,也可以文字
flag:true
}
},
當我們添加了/:之後,它的組成結構就變成了這個樣子
像下面這樣傳遞值進去,發起請求
<router-link to:`/home/${id}/${type}` class="可以讓我看起來像按鈕的css樣式"> </router-link>
同時,我們也可以向下面這樣使用$route. 在對應不同的路由元件中,把裡面的屬性取出來, 注意啊,這樣取值,前提是我們前面使用 /:id佔位,並且也整整傳遞值進去了
<h1>id= {{$route.params.id}}</h1>
向路由元件傳遞值 二
使用<router-view >
標籤傳遞值
<router-view msg='abc'></router-view>
在路由元件中通過props取出值,然後可以直接使用
export default{
props:[
msg:String
]
}
程式設計式的路由導航
程式設計式的路由導航說白了就是,不用router-link標籤轉而使用程式碼路由的跳轉唄, 舉個例子,我們使用手機qq,最下面有幾個導航欄,點選不同的按鈕轉換到不同的頁面去,如果用程式設計式的路由導航就很好做
- 第一步就是將需要的路由元件配置進路由器
- 給按鈕繫結上點選事件
- 點選事件觸發我們所謂的程式設計式路由導航
vue提供了兩種程式設計式的路由導航實現
- 第一種:
這種常用的一種
this.$router.replace(`/home/${id}`)
- 第二種:
這種具有棧的特性,也就是說,使用者點選返回鍵,會返回到上一級路由
this.$router.push(`/home/${id}`)
slot標籤
它是個和 rout-view 和像的標籤,都是用來佔位的,它可以接受父元件傳遞給他的一段html
舉個例子: 有四張路由元件,他們共用一個叫header的元件當作自己的頭部, 但是他們需要傳遞進去屬於自己的不同的值, 下面使用slot實現
在 MyHeader.vue中
<!--首頁頭部-->
<header class="header">
<!-- 這裡使用插槽佔位-->
<slot name="left"></slot>
<span">
<span >我是header</span>
</span>
<!-- 這裡使用插槽佔位-->
<slot name="right"></slot>
</header>
在父元件中使用:注意啊,下面的元件想往MyHeader.vue中的插槽中,傳遞進去程式碼片段,前提是他要把MyHeader.vue對映成標籤,成為他的父元件
<div>
<MyHeader>
<span class="header_search" slot="left">
<i class="iconfont icon-sousuo"></i> /*在插槽的左邊植入一個icon*/
</span>
<!-- 給右邊的插槽傳遞模板 -->
<span class="header_login" slot="right">
<a href="" >登入|註冊</a> /* 在插槽的右邊植入一個連結 */
</span>
</MyHeader>
</div>
Vuex
官方的解釋: vuex是專門為Vue.js應用程式開發的狀態管理模式,它採用集中式的儲存應用中所有元件的狀態,並以相應的規則保證狀態以一種可預期的方式發生變化
說白了: 當我們劃分元件之後,每一個元件都有自己的屬性,但是不同的元件的資料是不能共享的,於是我們可以使用從父元件往子元件傳播資料的模式, 而且完全不相干的兩個元件可能需要對方data裡的資料,又怎麼傳遞呢? vuex 就應對 迎戰這個問題
vuex就是一個單獨儲存的區域,用於存放公共的屬性
安裝命令:
npm install --save vuex
建立vuex的四個元件物件,如上圖
vuex的元件物件一: state.js
狀態物件,存放一系列的狀態,其實就是把子元件中data裡面的欄位賦複製過來
state.js檔案
export default {
arr: []
}
vuex的元件物件二: actions.js
超級重要的元件, 在這個元件中我們可以提交非同步事件, 最常用的就是使用者直接通過$store.dispatch('action中的方法名'), action會觸發 mutation的呼叫, 間接更新狀態
action.js
// add方法的方法第一個引數是不變的{commit}, 其實他就是 $store 物件
// 通過這個commit方法, 把資料包裝成物件傳遞給 mutations
// 第二個引數的可選的,可以是呼叫者傳遞進來的引數,也可以是state物件
export default {
add({commit},item){
// 提交mutation請求
commit(ADD_TODO,{item}); // 把資料包裝成物件傳遞給 mutations
},
vuex的元件物件三: mutations.js
真正的去執行action傳進來,更新state中資料的操作
mutations.js
export default {
add(state,{item}){
state.arr.unshift(item);
}
}
vuex的元件物件四: getters.js
包含了所有的基於state的 get計算屬性, 這一點也很好用,他是一種雙向的資料繫結
getters.js
export default {
// 計算屬性
totalCount (state) {
return state.arr.length
},
}
把四個元件拼裝成store物件
- 在src下建立store資料夾,在改資料夾下建立store.js
- 匯入Vue , Vuex
- 宣告Vue使用Vuex
- 將上面的四個元件註冊進來store.js
state: 狀態物件,存放的是需要共享資料的欄位
actions: 包含多個事件回撥函式的物件
mutations: 包含真正去更新state中欄位的函式
getter: 計算屬性的方法 - 對外暴露匿名store物件
- 將store配置進main.js vue的入口js中
編碼實現: store.js
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import actions from './actions'
import mutations from './mutations'
import getters from './getter2'
Vue.use(Vuex)
// 對外暴露你匿名 store物件
export default new Vuex.Store({
state,
actions,
mutations,
getters
})
把store物件,註冊進main.js
更全面的資料處理流程圖
獲取state中的值
做好了上面的配置,在任何地方都能用下面的方式獲取出store裡面的資料
this.$store.state.屬性
使用vuex,改變狀態值
新增上字首,再使用
this.$store.commit('matations中的方法名','可選的引數')
// 注意哦, action中是可以提交非同步函式的
this.$store.dispach('action中的方法名','可選的引數')
也可以像下面這樣,先進行對映就可以不再新增任何字首,直接使用他們
// 從vuex中引入對映
import {mapState,mapGetters,mapActions} from 'vuex'
export default {
computed:{
...mapState(['state中的屬性值'])
...mapGetters(['getters.js中的方法名'])
},
methods:{
...mapActions(['actions.js中的方法名'])
}
}