前引
今天是2018年12月30,雖不是2018年的最后一天,但是卻是自己在2018年寫的最后一篇博客了,昨天下班在地鐵上閑來無事,翻起了關注的一些公眾號發的技術博文,里面就提到寫博客的重要性,其實這樣的內容看了N多了,但是,里面有一句感覺說的很對,現在的博客變了味了,因為我們可以Ctrl+c、Ctrl+v,短短幾秒鍾就是一遍幾千字的博文,真正自己寫出的博文需要花費很長的時間。說心里話,自己的博客中也有,大概有三分之一吧,突然莫名的恐慌,因為很可能就是這三分之一的博客,就會掩蓋自己花費精力完成的其他的博客,被別人冠上這么一個標簽,這人也是一個充數的、裝大半蒜的家伙。其實內心已經一萬個草泥馬了,因為我們都是人,說話說的好,一個人一直做好事,但是突然有一天做了一件壞事,大家就定義這個人是壞人,好人難做啊,同樣的,寫博客亦如此。
不感慨了,看到的小伙伴們支持一下,當然歡迎轉載,歡迎Ctrl+c,但是有一個請求,在文章的最上方請注明文章來源,非常感謝。
接下來,說今天的正事,本篇博客的正事,Vue的進階篇,進階,談不上,也算是一點稍微不那么基礎的基礎吧,更基礎的歡迎瀏覽本人的另一篇博客Vue基礎篇
老習慣,開始正文之前,先來了解一些必知必會的小知識點
前言
1,組件和插件的區別(組件是一個項目的必須組成部分,而插件是不必須的,動態的,即插即用)
2,在一個項目中,一個文件以.開頭表示電腦系統的隱藏文件。
3,在前端中,一個項目中的index文件或main文件,默認為一個項目的入口
備注:在vue-cli項目中會有一個包含很多包的文件,這個文件的文件名就是node_modules,還有一個.gitignore的文件,這個隱藏文件的意思就是git忽視的一個文件,打開這個文件,里面的有這些內容:
.DS_Store
node_modules
/dist
在這個文件中,我們可以配置當前項目一些忽略的文件,支持部分正則匹配。比如,node_modules項,如果項目上傳git,或者GitHub時,會自動忽略掉這個文件,所以我們上傳到GitHub上的項目中是不包含這個node_modules文件的,這也是GitHub的一個局限性,大的文件無法上傳,導致我們從GitHub上下載的一些開源的項目,並不能直接使用,對於vue的項目,還需要使用一個命令:npm install 將包含很多包的這個文件下載下來。
因此,我們從GitHub上下載下來的vue的開源代碼,第一步就是運行一個命令:npm install (一般在當前目錄下)
GitHub中,每個開源文件都有一個文件READMI.md(markdown文件),這個文件中,會告訴我們怎么使用這個項目。
贈送的小技巧:另外的我們在一個項目名中,看到帶有awesome的,表示一個生態系統,里面在這個基礎上封裝了很多插件和模塊。
vue-cli開始
在vue中,一個.vue文件就是一個vue的組件。
一個標准的可重用的vue組件的格式:
<!--一個組件包含html、css、js--> <template> <!--組件的結構--> </template> <script> // 處理業務邏輯 export default {
name: "Vheader",
} </script> <style> /*頁面的樣式*/ </style>
備注:
1,在template模板中,必須有一個包裹的標簽。 需要注意的是:template不是一個標簽,是vue中的一個模板
2,在vue中,一個組件的命名規范是:首字母必須大寫,比如:所有的組件以“V”開頭。一個組件中的data(數據屬性)必須是函數,這個函數必須return一個對象。
3,在一個項目中的MP3文件,由於webpack的打包編譯,是無法直接引入到src中,所以導入MP3文件時,必須當做模塊來引入
import song1 from './assets/薛之謙 - 一半 [mqms2].mp3'
<audio src="song1" autoplay controls></audio>
總之,所有的靜態資源都可以作為模塊導入 。
組件的引入
組件的引入很簡單 ,分三步:
1,導入組件 import Vheader from './components/Vheader.vue'
2,掛載組件 在export default 中 定義一個key為components,值為一個對象,這個對象中放着導入鍵值對;鍵為變量名,值為組件名,在es6中,鍵值相同時,可以縮寫為一個單一的值。
比如:components:{ Vheader }
3,在template模板中使用組件: <Vheader> </Vheader>
備注:在父組件中定義的樣式類,默認為全局的樣式類,所以如果要保證自身元素的樣式,在子組件的style標簽中加入:scoped即可 <style scoped><style>
父子傳值
父子組件的傳值:
父組件向子組件傳值
1,綁定自定義的屬性 : 在父組件中對應的子組件的模板上綁定屬性。
比如:<Vcontent :menu=“menus”><Vcontent> #menu為定義的屬性名 menus為數據屬性
2,在子組件中接收這個自定義的屬性,並且屬性必須要驗證 ,接收的屬性放在props中
比如:props:{ menu:Array } 備注:接收的變量名必須與自定義的變量名保持一致
3,這時子組件中的數據值就是這個對應的menu變量,我們也可以在數據屬性中重新賦值
比如:data(){ return { menus:this.menu } } #這時我們在子組件用的值就是menus了
備注:這里的this是指當前的子組件的Vue對象,Vue創建組件時:Vue.component()
子組件向父組件傳值
1,在子組件中定義一個事件: 比如:在Vheader中定義一個點擊事件。
<template> <div class="header"> // 定義一個點擊事件 addOne <button @click="addOne">添加一個菜單</button> </div> </template> <script> export default { name: "Vheader", data(){ return { } }, methods:{ // 事件觸發自定義的事件 addOne(){ this.$emit('addMenu','酸菜魚'); #this.$emit(“自定義事件名”,“往父組件傳的值”) } } } </script> <style scoped> </style>
2,觸發自定義的事件,this.$emit("自定義的事件名","要向父組件傳的值") #備注:固定寫法,而且自定義的事件名必須與父組件中自定義的事件名一致
3,在父組件中對應的子組件模板中,定義好這個自定義事件,比如:<Vheader @addMenu="addHand"></Vheader>
並在對應的事件中,接收傳的值,並做相應處理。
methods:{
addHand(value){
this.menus.push(value); #接收傳的值,並將值添加到父組件的menus數組中
}
}
vue的優勢和用途
vue主做單頁面應用
比如:餓了么、掘金、網易雲音樂、豆瓣、知乎等等
在vue中,可以給每一個單頁面(組件)配置一個路由,這個是由vue-router來控制的
vue-router
首先,要在我們的項目中下載安裝這個vue-router
npm install vue-router
如果在一個模塊化工程中使用它,必須要通過 Vue.use() 明確地安裝路由功能:
在項目的入口 main.js中
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
接着就可以在這個main.js中配置組件
1. 定義 (路由) 組件。
// 比如:
const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' }
// 也可以從其他文件 import 進來
import Vhome from './components/Vhome.vue' import Vuser from './components/Vuser.vue'
2. 定義路由每個路由應該映射一個組件。 其中"component" 可以是通過 Vue.extend() 創建的組件構造器,或者,只是一個組件配置對象。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
3. 創建 router 實例,然后傳 `routes` 配置
// 你還可以傳別的配置參數, 不過先這么簡單着吧。
const router = new VueRouter({
routes // (縮寫) 相當於 routes: routes
}
4. 創建和掛載根實例。
// 記得要通過 router 配置參數注入路由,
// 從而讓整個應用都有路由功能
const app = new Vue({
router
}).$mount('#app')
現在,應用可以啟動了!
Tips: 通過注入路由器,我們可以在任何組件內通過 this.$router 訪問路由器,也可以通過 this.$route 訪問當前路由:
console.log(this.$router) #打印$router的結果:
[]
app:Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
apps:[Vue]
beforeHooks:[]
fallback:false
history:HashHistory {router: VueRouter, base: "", current: {…}, pending: null, ready: true, …}
matcher:{match: ƒ, addRoutes: ƒ}
mode:"hash"
options:{routes: Array(2)}
resolveHooks:[]
currentRoute:(...)
__proto__:Object
點擊打開__proto__(父級中聲明的方法,在子級中都可以使用):
addRoutes:ƒ addRoutes(routes)
afterEach:ƒ afterEach(fn)
back:ƒ back()
beforeEach:ƒ beforeEach(fn)
beforeResolve:ƒ beforeResolve(fn)
forward:ƒ forward()
getMatchedComponents:ƒ getMatchedComponents(to)
go:ƒ go(n)
init:ƒ init(app /* Vue component instance */)
match:ƒ match( raw, current, redirectedFrom )
onError:ƒ onError(errorCb)
onReady:ƒ onReady(cb, errorCb)
push:ƒ push(location, onComplete, onAbort)
replace:ƒ replace(location, onComplete, onAbort)
resolve:ƒ resolve( to, current, append )
constructor:ƒ VueRouter(options)
currentRoute:(...)
get currentRoute:ƒ ()
__proto__:Object
console.log(this.$route) 打印$route的結果:
備注:$router中,push方法常用,在$route中,path(獲取當前訪問的路徑)和fullpath(獲取當前訪問的全路徑)方法常用
配置好了路由,接下來就該在HTML中使用了
<div id="app"> <h1>Hello App!</h1> <p> <!-- 使用 router-link 組件來導航. --> <!-- 通過傳入 `to` 屬性指定鏈接. --> <!-- <router-link> 默認會被渲染成一個 `<a>` 標簽 --> <router-link to="/foo">Go to Foo</router-link> <router-link to="/bar">Go to Bar</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的組件將渲染在這里 --> <router-view></router-view> </div>
備注:路由出口 --> 路由匹配到的組件將渲染在這里 --> <router-view></router-view> 這是路由的出口
使用 router-link 組件來導航. -->通過傳入 `to` 屬性指定鏈接. --><router-link> 默認會被渲染成一個 `<a>` 標簽
注意:router-link和router-view也可以放置在不同的子組件中
<router-view></router-view> 可以簡寫成 <router-view/>
動態路由匹配:
一個“路徑參數”使用冒號 : 標記。當匹配到一個路由時,參數值會被設置到 this.$route.params,可以在每個組件內使用。於是,我們可以更新 User 的模板,輸出當前用戶的 ID:
const User = { template: '<div>User {{ $route.params.id }}</div>' }
你可以在一個路由中設置多段“路徑參數”,對應的值都會設置到 $route.params 中。例如:
| 模式 | 匹配路徑 | $route.params |
|---|---|---|
| /user/:username | /user/evan | { username: 'evan' } |
| /user/:username/post/:post_id | /user/evan/post/123 | { username: 'evan', post_id: 123 } |
除了 $route.params 外,$route 對象還提供了其它有用的信息,例如,$route.query (如果 URL 中有查詢參數)、$route.hash
編程式的導航
除了使用 <router-link> 創建 a 標簽來定義導航鏈接,我們還可以借助 router 的實例方法,通過編寫代碼來實現。
router.push(location, onComplete?, onAbort?)
注意:在 Vue 實例內部,你可以通過 $router 訪問路由實例。因此你可以調用 this.$router.push。
想要導航到不同的 URL,則使用 router.push 方法。這個方法會向 history 棧添加一個新的記錄,所以,當用戶點擊瀏覽器后退按鈕時,則回到之前的 URL。
當你點擊 <router-link> 時,這個方法會在內部調用,所以說,點擊 <router-link :to="..."> 等同於調用 router.push(...)。所以通過這個方法我們可以實現重定向的效果,
比如:登錄后跳轉到首頁,可以把首頁的路徑push到$router中即可
| 聲明式 | 編程式 |
|---|---|
<router-link :to="..."> |
router.push(...) |
該方法的參數可以是一個字符串路徑,或者一個描述地址的對象。例如:
// 字符串
router.push('home')
// 對象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
// 帶查詢參數,變成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
在使用動態路由或者編程式的路由時,會有這樣一個問題,路由切換后,頁面不切換,解決方法:
使用watch監聽路由的變化,並做數據的覆蓋處理
<template> // 兩種方式觸發切換
<!--方式一:通過點擊事件的切換--> <div v-for="(item,index) in theshow.recommend_courses" @click="toaimcourse(item)"><a href="javascript:vild(0);">{{item.title}}</a></div> <!--方式二:通過動態路由的切換--> <router-link v-for="(item,index) in theshow.recommend_courses" :to="{ name: 'coursedetail', params: { id:item.id }}">{{item.title}}</router-link> </template> <script> export default { name: "vcoursedetail", data(){ return { showdetail:this.$store.state.allCourseDetail, theshow:'', currcourse:'', courseall:this.$store.state.allCourse, descurl:'', charperurl:'' } }, created(){ this.courseshowdetail(this.$route.params.id); this.getcurrentcourse(this.$route.params.id); }, methods:{ courseshowdetail(nid){ for (var i=0;i<this.showdetail.length;i++){ if (this.showdetail[i].course.id === nid){ this.theshow = this.showdetail[i]; return this.showdetail[i] } } }, getcurrentcourse(nid){ for (var i=0;i<this.courseall.length;i++){ if (this.courseall[i].id === nid){ this.currcourse = this.courseall[i].title; this.descurl = "/course/coursedetail/"+nid; this.charperurl = '/course/coursedetail/'+nid+'/charper'; console.log(this.descurl,'文章摘要的url'); return this.courseall[i] } } }, toaimcourse(item){ this.$router.push({ name: 'coursedetail', params: { id:item.id }}) } }, computed:{ }, watch:{ "$route"(to,from){ this.courseshowdetail(to.params.id); this.getcurrentcourse(to.params.id); } } } </script>
我們也可以做一個全局的路由操作,比如:在每個組件加載或者路由切換時,判斷有沒有登錄
詳情參考:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
完整的導航解析流程
- 導航被觸發。
- 在失活的組件里調用離開守衛。
- 調用全局的
beforeEach守衛。 - 在重用的組件里調用
beforeRouteUpdate守衛 (2.2+)。 - 在路由配置里調用
beforeEnter。 - 解析異步路由組件。
- 在被激活的組件里調用
beforeRouteEnter。 - 調用全局的
beforeResolve守衛 (2.5+)。 - 導航被確認。
- 調用全局的
afterEach鈎子。 - 觸發 DOM 更新。
- 用創建好的實例調用
beforeRouteEnter守衛中傳給next的回調函數。
攔截過濾認證
單個組件:
可以在mounted方法中做攔截,通過判斷全局變量中的token值,來判斷有無登錄。
多個組件:可以在全局做攔截
# 在main.js中配置
router.beforeEach(function (to,from,next) {
// meta 用於設置一個標識 所以必須在需要驗證是否登錄的組件中加一個meta參數 meta:{requireAuth:true}
if (to.meta.requireAuth){
// 表示此時路徑是要判斷是否登錄的
if (store.state.token){
next()
}else{
// query 用於給這個重定向路由設置一個參數 用於在登錄成功后重定向的路徑
// 設置此參數后需要在登錄成功后做一個判斷 this.$route.query.backUrl 獲取設置的參數
// 有值,表示是需要跳轉回的,然后通過重定向將取的值,定位即可
next({path:'/login',query:{backUrl:to.fullPath}})
}
}else{
next()
}
});
bootstrap的使用
在項目中,使用bootstrap時,先在package.json中查看開發環境依賴:
"dependencies": {
"bootstrap": "^3.3.7",
"marked": "^0.4.0",
"vue": "^2.5.16",
"vue-router": "^3.0.1"
},
如果沒有,先在當前項目中 npm install xxx
配置好依賴后,再在需要的地方導入: 導入時,對於node_modules文件下的文件,直接導入即可。比如:import 'bootstrap/dist/css/bootstrap.min.css' 在vue-cli中,已經配置了路徑,所以直接導入即可。
備注:使用vue-router時,路由的切換會在路徑前默認加一個#/,顯得url很不好看,所以,可以在實例router對象時加一個mode:"history"
const router = new VueRouter({
mode: 'history',
routes: [...]
})
在使用a標簽時,會有一個默認的跳轉事件,在vue中,我們可以在相應的事件后加一個.native用來阻止原生的默認事件
@click.native = "xxxx"
參考Vue提供的生命周期圖示
下圖展示了實例的生命周期。你不需要立馬弄明白所有的東西,不過隨着你的不斷學習和使用,它的參考價值會越來越高。

在vue的實例的生命周期中,vue為我們提供了八個方法:
beforeCreate(在創建之前)、created(創建時的初始化操作,創鍵的是虛擬DOM)、beforeMount、mounted(掛載,將虛擬DOM轉化為真實DOM)、beforeUpdate、updated、beforeDestroy、destroyed(銷毀)
我們常用的是 created和mounted這兩個方法,其中created方法適用於頁面的初始化操作,mounted適用於發送ajax請求數據的操作。
比如:切換路由,頁面樣式在在此刷新時會出錯,可以使用created方法進行一些初始化操作
created(){ for (var i=0;i<this.routes.length;i++){ if (this.routes[i].src === this.$route.path){ this.current=i; return; } } },
vue-cli中2x版本和3x版本修改端口的方法:
在2x版本中:

在3x版本中:

在3x版本中,vue-cli不推薦我們直接在模塊中修改配置
推薦這樣:在當前項目的目錄下直接創建一個js文件 ,文件名必須是 vue.config.js 的形式
在這個js文件中寫入:
module.exports = {
devServer: { open: process.platform === 'darwin', host: '0.0.0.0', port: 8088 , https: false, hotOnly: false, proxy: null, // 設置代理 before: app => {} } }
修改完成后重啟項目即可。
使用vue-cli打包文件
生產環境中,我們都會講項目中的文件打包壓縮再上線,vue-cli中提供了一種快捷的打包方式。
在我們項目的根目錄下運行 npm run build 就會將項目打包,打包后會在項目中多處一個文件夾dist,打開這個dist文件夾

備注:在項目中替換文件時,最好是先將原文件刪除,在拖進去,不要直接替換,放置文件合並
在打包時,一定要配置壓縮文件中的路徑參數。比如:前綴加/static/
vue中的全家桶:vue、vue-router、vuex構成了vue的全家桶,vue是MVVM的架構,vuex--->M vue--->V vue-router--->VM
vue全家桶之vuex
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。
補充:在vue中專門用來發送ajax請求的是axios,詳細用法參考:https://www.kancloud.cn/yunye/axios/234845
我們可以在全局的main.js或者store.js中定義一個axios ,將這個axios掛到vue的實例對象中,這樣,在vue的子對象中都可以通過this.$axios調用到axios對象。使用axios時,可以通過返回的參數response.data取得返回的數據
import axios from 'axios'
import Vue from 'vue'
Vue.prototype.axios = axios
特別注意:使用jQuery的$.ajax發送post請求發送數據時,內部會自動轉化為json字符串的形式發送到后端,而使用vue中的axios時,則不行,axios post提交數據時,發送的是普通的值,所有在使用axios發送數據時,如果后端需要json形式的字符串,則需要借助一個模塊 qs (npm install qs) 轉化或者,JSON.stringfity
還有一個值得注意的地方,在vue的中,我們可以通過this拿到vue的實例對象,但是在axios中由於作用域的原因則不行,console.log(this)得到的是一個undefined,所以我們要在axios的外面聲明一個變量接收這個this var _this = this
vue中的mutation:更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。
使用vue-cli時,會在src文件夾下默認創建views、components的兩個文件夾,views中存放每一個vue-router(路由)對應的視圖,components中存放父子嵌套的組件。
獲取v-html指令對應的文本值
使用v-html指令時,如果我們要獲取渲染的文本內容,可以給當前標簽綁定一個屬性ref="變量名",然后在vue的實例中可以通過this.$refs.變量名取得這個標簽,在通過DOM方法.innerText取得內容,this.$refs是一個對象
<div id="show" v-html="currentmsg" ref="mark"> // 這個div的文本內容為: var content = this.$refs.mark.innerText;
備注:不要濫用ref,對性能有損耗,多個標簽時,可以使用js方法或者jQuery方法獲取文本值 $("show").text()
Action 類似於 mutation,不同在於:
- Action 提交的是 mutation,而不是直接變更狀態。
- Action 可以包含任意異步操作。
備注:mutation是同步的,action是異步的, action是用來提交的
用法:
mutation的用法:接收一個state參數作為第一個參數,有其他參數可以直接跟在后面,在mutation的方法中可以直接操作state中的數據,有多個參數時,最好可以將其余的參數作為一個對象,作為方法的第二個參數傳值。
在vue的視圖中可以直接this.$store.commit("方法名"),喚醒這個方法的執行
action的用法:接收一個context參數作為第一個參數,同樣的,后可以跟其他參數,多個參數時,合並為一個object對象。通過context.commit("mutation方法名") 提交, 可以喚醒mutation中的方法
在vue的視圖中可以通過this.$store.dispatch('action方法名'),觸發action中的對應方法執行
mutation和action的關系圖如下:

過濾器(格式化)
1.價格后面加上符號‘元’。




2.價格后面的單位也要動態的傳值。(如:元、¥、$)




代碼:
<!DOCTYPE html> <html > <head> <meta charset="UTF-8"> <title>過濾器</title> <script src="bli/vue.js"></script> </head> <body> <div id="app"> <input type="text" v-model="price"> <!-- {{ price | currency }} --> {{ price | currency('美元') }} </div> </body> <script src="js/14.main.js"></script> </html>
// 過濾器 Vue.filter('currency',function(data,unit){ data = data || 0; // data有值就等於data,沒值就為0. unit = unit || '元'; // unit有值就等於unit,沒值就為'元'. return data + unit; // return data + '元'; }); new Vue({ el:'#app', data:{ price:10, }, });
3.毫米與米的轉換。





4.毫米與米的轉換,保留兩位小數。




自定義指令-基礎配置
1.自定義指令



2.給只為true的元素定位(固定定位)


3.加一個按鈕,切換是否定位。
默認都是沒有定住的。點擊之后定住,再點擊之后就是取消定住。


默認、取消定住

定住

4.可以給很多按鈕就加上。

默認、取消定位

定位

自定義指令-配置傳參及修飾符
以上例子只能定位到左上角,不夠靈活。位置應該動態傳參。
1.定位到右下角:


打印 var position = binding.modifiers;
console.log('position',position) // position {bottom: true, right: true}


運行結果:已定位到右下角

2.定位到左下角,只要改一個值即可。


3.讓一些卡片樣式有所不同,突出。

獲取該值(:true),設置樣式。



混合 mixins
1.點擊切換顯示隱藏。


默認不顯示div.

點擊后顯示div,再次點擊有隱藏div.

2.鼠標移入、移出切換顯示隱藏。


默認、移出

移入:

3.點擊顯示的div應該有個可以關閉的按鈕。
注意:這兩個組件有好多重復的代碼!

點擊后隱藏div

混合 mixins(相同的代表放在一起)



功能是一樣的。注意:自己寫的會覆蓋mixins的。

插槽 slots
1.定義一個樣式



2.內容都是相同的。動態傳參(插槽)


3.如果頭部、底部都要動態傳參呢???定義name!!!


4.指定默認值。


在Vue的項目中使用了Vue-Router,當某個路由有子級路由時,如下寫法:
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
children:[
{
path:'/',
name: 'console',
component: Console,
}
]
}
]
})
解決辦法
因為當某個路由有子級路由的時候,這時候父級路由需要一個默認的路由,所以父級路由不能定義name屬性,SO解決辦法是:即去除父級的name屬性即可。

