渲染函數(shù)和jsx
在vue中我們可以不用template來(lái)指定組件的模板疫诽,而是用render函數(shù)來(lái)創(chuàng)建虛擬dom結(jié)構(gòu)舅世,用這種方法優(yōu)點(diǎn)就是性能高,缺點(diǎn)就是使用成本高奇徒,代碼可讀性較低雏亚,可以使用jsx來(lái)在render函數(shù)中創(chuàng)建,這樣既提高了性能摩钙,又減少了成本
但是罢低,我們?cè)谑褂昧藇ue-cli腳手架之后,因?yàn)槟_手架中有對(duì)template標(biāo)簽轉(zhuǎn)換虛擬dom的處理胖笛,所以网持,不需要使用jsx,我們也能高效的轉(zhuǎn)換為createElement形式
Vue里組件的通信
通信:傳參长踊、控制(A操控B做一個(gè)事件)功舀、數(shù)據(jù)共享
模式:父子組件間、非父子組件
-
父組件可以將一條數(shù)據(jù)傳遞給子組件身弊,這條數(shù)據(jù)可以是動(dòng)態(tài)的辟汰,父組件的數(shù)據(jù)更改的時(shí)候列敲,子組件接收的也會(huì)變化
子組件被動(dòng)的接收父組件的數(shù)據(jù),子組件不要再更改這條數(shù)據(jù)了
-
父組件如果將一個(gè)引用類(lèi)型的動(dòng)態(tài)數(shù)據(jù)傳遞給子組價(jià)的時(shí)候,數(shù)據(jù)會(huì)變成雙向控制的莉擒,子組件改數(shù)據(jù)的時(shí)候父組件也能接收到數(shù)據(jù)變化,因?yàn)樽咏M件改的時(shí)候不是在改數(shù)據(jù)(地址)酿炸,而是在改數(shù)據(jù)里的內(nèi)容,也就是說(shuō)引用類(lèi)型數(shù)據(jù)的地址始終沒(méi)有變化涨冀,不算改父組件數(shù)據(jù)
父子間數(shù)據(jù)共享(雙向控制),基本不會(huì)使用填硕,違背了單向數(shù)據(jù)流
-
父組件可以將一個(gè)方法傳遞給子組件,子組件調(diào)用這個(gè)方法的時(shí)候鹿鳖,就可以給父組件傳遞數(shù)據(jù)
父組件被動(dòng)的接收子組件的數(shù)據(jù)
-
父組件可以將一個(gè)事件綁定在子組件的身上隔披,這個(gè)事件的處理程序是父組件某一個(gè)方法帽芽,當(dāng)子組件觸發(fā)自己的這個(gè)被綁定的事件的時(shí)候剑鞍,相當(dāng)于觸發(fā)了父組件的方法
父組件被動(dòng)的接收子組件的數(shù)據(jù)
-
在組件間可以用過(guò)ref形成ref鏈闷煤,組件還擁有一個(gè)關(guān)系鏈(
children,$root),通過(guò)這兩種鏈;理論來(lái)說(shuō)涝滴,任意的兩個(gè)組件都可以互相訪(fǎng)問(wèn)绣版,互相進(jìn)行通信
任意組件通信,用的少...
-
event bus 事件總線(xiàn) 小天使 專(zhuān)注于非父子組件的通信歼疮,其實(shí)父子組件也可以使用杂抽,只是沒(méi)有必要
在B組件的某個(gè)鉤子函數(shù)為event_bus綁定一個(gè)事件,事件的處理程序是B想做的事情
在A組件的某一個(gè)操作里韩脏,觸發(fā)event_bus綁定的事件
大量組件間數(shù)據(jù)共享的時(shí)候 vuex
組件的生命周期
每一個(gè)組件或者實(shí)例都會(huì)經(jīng)歷一個(gè)完整的生命周期缩麸,總共分為三個(gè)階段:初始化、運(yùn)行中赡矢、銷(xiāo)毀
實(shí)例杭朱、組件通過(guò)new Vue() 創(chuàng)建出來(lái)之后會(huì)初始化事件和生命周期,然后就會(huì)執(zhí)行beforeCreate鉤子函數(shù)吹散,這個(gè)時(shí)候弧械,數(shù)據(jù)還沒(méi)有掛載ね,只是一個(gè)空殼空民,無(wú)法訪(fǎng)問(wèn)到數(shù)據(jù)和真實(shí)的dom刃唐,一般不做操作
掛載數(shù)據(jù),綁定事件等等袭景,然后執(zhí)行created函數(shù)唁桩,這個(gè)時(shí)候已經(jīng)可以使用到數(shù)據(jù)闭树,也可以更改數(shù)據(jù),在這里更改數(shù)據(jù)不會(huì)觸發(fā)updated函數(shù)耸棒,在這里可以在渲染前倒數(shù)第二次更改數(shù)據(jù)的機(jī)會(huì),不會(huì)觸發(fā)其他的鉤子函數(shù)报辱,一般可以在這里做初始數(shù)據(jù)的獲取
接下來(lái)開(kāi)始找實(shí)例或者組件對(duì)應(yīng)的模板与殃,編譯模板為虛擬dom放入到render函數(shù)中準(zhǔn)備渲染单山,然后執(zhí)行beforeMount鉤子函數(shù),在這個(gè)函數(shù)中虛擬dom已經(jīng)創(chuàng)建完成幅疼,馬上就要渲染,在這里也可以更改數(shù)據(jù)米奸,不會(huì)觸發(fā)updated,在這里可以在渲染前最后一次更改數(shù)據(jù)的機(jī)會(huì)爽篷,不會(huì)觸發(fā)其他的鉤子函數(shù)悴晰,一般可以在這里做初始數(shù)據(jù)的獲取
接下來(lái)開(kāi)始render,渲染出真實(shí)dom逐工,然后執(zhí)行mounted鉤子函數(shù)铡溪,此時(shí),組件已經(jīng)出現(xiàn)在頁(yè)面中泪喊,數(shù)據(jù)棕硫、真實(shí)dom都已經(jīng)處理好了,事件都已經(jīng)掛載好了,可以在這里操作真實(shí)dom等事情...
當(dāng)組件或?qū)嵗臄?shù)據(jù)更改之后袒啼,會(huì)立即執(zhí)行beforeUpdate哈扮,然后vue的虛擬dom機(jī)制會(huì)重新構(gòu)建虛擬dom與上一次的虛擬dom樹(shù)利用diff算法進(jìn)行對(duì)比之后重新渲染,一般不做什么事兒
當(dāng)更新完成后蚓再,執(zhí)行updated滑肉,數(shù)據(jù)已經(jīng)更改完成,dom也重新render完成对途,可以操作更新后的虛擬dom
當(dāng)經(jīng)過(guò)某種途徑調(diào)用$destroy方法后赦邻,立即執(zhí)行beforeDestroy,一般在這里做一些善后工作实檀,例如清除計(jì)時(shí)器惶洲、清除非指令綁定的事件等等
組件的數(shù)據(jù)綁定、監(jiān)聽(tīng)...去掉后只剩下dom空殼膳犹,這個(gè)時(shí)候恬吕,執(zhí)行destroyed,在這里做善后工作也可以
vue-cli腳手架
現(xiàn)在使用前端工程化開(kāi)發(fā)項(xiàng)目是主流的趨勢(shì)须床,也就是說(shuō)铐料,我們需要使用一些工具來(lái)搭建vue的開(kāi)發(fā)環(huán)境,一般情況下我們使用webpack來(lái)搭建豺旬,在這里我們直接使用vue官方提供的钠惩,基于webpack的腳手架工具:vue-cli
安裝方法:
# 全局安裝 vue-cli
npm install --global vue-cli
# 創(chuàng)建一個(gè)基于 webpack 模板的新項(xiàng)目
vue init webpack my-project
//init之后可以定義模板的類(lèi)型
# 安裝依賴(lài),走你
cd my-project
npm install
npm run dev
模板類(lèi)型:
simple 對(duì)應(yīng)的是一個(gè)超級(jí)簡(jiǎn)單的html文件
webpack 在配置的時(shí)候可以選擇是否需要vue-router
注意的是族阅,模板創(chuàng)建的時(shí)候會(huì)詢(xún)問(wèn)使用需要使用ESLINT來(lái)標(biāo)準(zhǔn)化我們的代碼
在腳手架中篓跛,開(kāi)發(fā)目錄是src文件夾,build負(fù)責(zé)打包的坦刀,config是負(fù)責(zé)配置(內(nèi)置服務(wù)器的端口愧沟、proxy代理)蔬咬,static是靜態(tài)目錄,test是測(cè)試
src中main.js是入口文件沐寺,在里面創(chuàng)建了一個(gè)根實(shí)例林艘,根實(shí)例的模板就是根組件App的模板,其他的組件都在根組件里面進(jìn)行嵌套實(shí)現(xiàn)混坞。
每一個(gè)組件都是一個(gè)單文件組件狐援,這種文件會(huì)被webpack利用vue-loader的工具進(jìn)行編譯
template部分負(fù)責(zé)寫(xiě)組件的模板內(nèi)容,script中創(chuàng)建組件究孕。style里寫(xiě)組件的樣式
assets目錄也是靜態(tài)目錄咕村,在這個(gè)目標(biāo)中的文件我們使用相對(duì)路徑引入,而static目錄中的文件使用絕對(duì)地址來(lái)引入
在style上添加scoped能使這個(gè)style里的樣式只作用于當(dāng)前的組件,不加scoped就是全局樣式
習(xí)慣于在App.vue根組件的style里寫(xiě)全局樣式蚊俺,而每個(gè)組件的style最好都是局部的
配置sass編譯環(huán)境
vue-cli沒(méi)有內(nèi)置sass編譯懈涛,我們需要自己修改配置
下載對(duì)應(yīng)工具:node-sass(4.0.0) sass-loader
在build目錄下的webpack.base.conf.js中的module.rule里添加如下配置
{
test: /\.scss$/,
loader:'style-loader!css-loader!sass-loader'
}
- 在需要使用scss代碼的組件的style標(biāo)簽中添加 lang='scss'
vue-router
現(xiàn)在的應(yīng)用都流行SPA應(yīng)用(single page application)
傳統(tǒng)的項(xiàng)目大多使用多頁(yè)面結(jié)構(gòu),需要切換內(nèi)容的時(shí)候我們往往會(huì)進(jìn)行單個(gè)html文件的跳轉(zhuǎn)泳猬,這個(gè)時(shí)候受網(wǎng)絡(luò)批钠、性能影響,瀏覽器會(huì)出現(xiàn)不定時(shí)間的空白界面得封,用戶(hù)體驗(yàn)不好
單頁(yè)面應(yīng)用就是用戶(hù)通過(guò)某些操作更改地址欄url之后埋心,動(dòng)態(tài)的進(jìn)行不同模板內(nèi)容的無(wú)刷新切換,用戶(hù)體驗(yàn)好忙上。
Vue中會(huì)使用官方提供的vue-router插件來(lái)使用單頁(yè)面拷呆,原理就是通過(guò)檢測(cè)地址欄變化后將對(duì)應(yīng)的路由組件進(jìn)行切換(卸載和安裝)
簡(jiǎn)單路由實(shí)現(xiàn):
- 引入vue-router,如果是在腳手架中疫粥,引入VueRouter之后茬斧,需要通過(guò)Vue.use來(lái)注冊(cè)插件
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
- 創(chuàng)建router路由器
new Router(options)
- 創(chuàng)建路由表并配置在路由器中
var routes = [
{path,component}//path為路徑,component為路徑對(duì)應(yīng)的路由組件
]
new Router({
routes
})
- 在根實(shí)例里注入router,目的是為了讓所有的組件里都能通過(guò)this.
route來(lái)使用路由的相關(guān)功能api
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
利用router-view來(lái)指定路由切換的位置
使用router-link來(lái)創(chuàng)建切換的工具项秉,會(huì)渲染成a標(biāo)簽,添加to屬性來(lái)設(shè)置要更改的path信息慷彤,且會(huì)根據(jù)當(dāng)前路由的變化為a標(biāo)簽添加對(duì)應(yīng)的router-link-active/router-link-exact-active(完全匹配成功)類(lèi)名
<router-link to="main">main</router-link>
<router-link to="news">news</router-link>
.router-link-active{
color:red;
}
多級(jí)路由:
在創(chuàng)建路由表的時(shí)候娄蔼,可以為每一個(gè)路由對(duì)象創(chuàng)建children屬性,值為數(shù)組底哗,在這個(gè)里面又可以配置一些路由對(duì)象來(lái)使用多級(jí)路由岁诉,注意:一級(jí)路由path前加'/'
const routes = [
{path:'/main',component:AppMain},
{path:'/news',component:AppNews,children:[
{path:'inside',component:AppNewsInside},
{path:'outside',component:AppNewsOutside}
]},
]
二級(jí)路由組件的切換位置依然由router-view來(lái)指定(指定在父級(jí)路由組件的模板中)
<router-link to='inside'>inside</router-link>
<router-link to='outside'>outside</router-link>
<router-view></router-view>
默認(rèn)路由和重定向:
當(dāng)我們進(jìn)入應(yīng)用,默認(rèn)像顯示某一個(gè)路由組件跋选,或者當(dāng)我們進(jìn)入某一級(jí)路由組件的時(shí)候想默認(rèn)顯示其某一個(gè)子路由組件涕癣,我們可以配置默認(rèn)路由:
{path:'',component:Main}
當(dāng)我們需要進(jìn)入之后進(jìn)行重定向到其他路由的時(shí)候,或者當(dāng)url與路由表不匹配的時(shí)候:
{path:'',redirect:'/main'}
///...放在最下面
{path:'**',redirect:'/main'},
命名路由
我們可以給路由對(duì)象配置name屬性野建,這樣的話(huà)属划,我們?cè)谔D(zhuǎn)的時(shí)候直接寫(xiě)name:main就會(huì)快速的找到此name屬性對(duì)應(yīng)的路由,不需要寫(xiě)大量的urlpath路徑了
動(dòng)態(tài)路由匹配
有的時(shí)候我們需要在路由跳轉(zhuǎn)的時(shí)候跟上參數(shù)候生,路由傳參的參數(shù)主要有兩種:路徑參數(shù)同眯、queryString參數(shù)
路由參數(shù)需要在路由表里設(shè)置
{path:'/user/:id',component:User}
上面的代碼就是給User路由配置接收id的參數(shù),多個(gè)參數(shù)繼續(xù)在后面設(shè)置
在組件中可以通過(guò)this.$route.params來(lái)使用
queryString參數(shù)不需要在路由表設(shè)置接收唯鸭,直接設(shè)置须蜗?后面的內(nèi)容,在路由組件中通過(guò)this.$route.query接收
router-link
<router-link> 組件支持用戶(hù)在具有路由功能的應(yīng)用中(點(diǎn)擊)導(dǎo)航目溉。 通過(guò) to 屬性指定目標(biāo)地址明肮,默認(rèn)渲染成帶有正確鏈接的 <a> 標(biāo)簽,可以通過(guò)配置 tag 屬性生成別的標(biāo)簽.缭付。另外柿估,當(dāng)目標(biāo)路由成功激活時(shí),鏈接元素自動(dòng)設(shè)置一個(gè)表示激活的 CSS 類(lèi)名陷猫。
router-link的to屬性秫舌,默認(rèn)寫(xiě)的是path(路由的路徑),可以通過(guò)設(shè)置一個(gè)對(duì)象绣檬,來(lái)匹配更多
:to='{name:"detail",params:{id:_new.id},query:{content:_new.content}}'
name是要跳轉(zhuǎn)的路由的名字足陨,也可以寫(xiě)path來(lái)指定路徑,但是用path的時(shí)候就不能使用params傳參娇未,params是傳路徑參數(shù)墨缘,query傳queryString參數(shù)
replace屬性可以控制router-link的跳轉(zhuǎn)不被記錄\
active-class屬性可以控制路徑切換的時(shí)候?qū)?yīng)的router-link渲染的dom添加的類(lèi)名
編程式導(dǎo)航
有的時(shí)候需要在跳轉(zhuǎn)前進(jìn)行一些動(dòng)作,router-link直接跳轉(zhuǎn)零抬,需要在方法里使用$router的方法
router.push = router-link:to
router.replace = router-link:to.replace
router.go() = window.history.go
路由模式
路由有兩種模式:hash镊讼、history,默認(rèn)會(huì)使用hash模式平夜,但是如果url里不想出現(xiàn)丑陋hash值狠毯,在new VueRouter的時(shí)候配置mode值為history來(lái)改變路由模式,本質(zhì)使用H5的histroy.pushState方法來(lái)更改url褥芒,不會(huì)引起刷新嚼松,但是需要后端進(jìn)行路由的配置
路由鉤子
在某些情況下,當(dāng)路由跳轉(zhuǎn)前或跳轉(zhuǎn)后锰扶、進(jìn)入献酗、離開(kāi)某一個(gè)路由前、后坷牛,需要做某些操作罕偎,就可以使用路由鉤子來(lái)監(jiān)聽(tīng)路由的變化
全局路由鉤子:
router.beforeEach((to, from, next) => {
//會(huì)在任意路由跳轉(zhuǎn)前執(zhí)行,next一定要記著執(zhí)行京闰,不然路由不能跳轉(zhuǎn)了
console.log('beforeEach')
console.log(to,from)
//
next()
})
//
router.afterEach((to, from) => {
//會(huì)在任意路由跳轉(zhuǎn)后執(zhí)行
console.log('afterEach')
})
單個(gè)路由鉤子:
只有beforeEnter颜及,在進(jìn)入前執(zhí)行甩苛,to參數(shù)就是當(dāng)前路由
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
路由組件鉤子:
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對(duì)應(yīng)路由被 confirm 前調(diào)用
// 不!能俏站!獲取組件實(shí)例 `this`
// 因?yàn)楫?dāng)守衛(wèi)執(zhí)行前讯蒲,組件實(shí)例還沒(méi)被創(chuàng)建
},
beforeRouteUpdate (to, from, next) {
// 在當(dāng)前路由改變,但是該組件被復(fù)用時(shí)調(diào)用
// 舉例來(lái)說(shuō)肄扎,對(duì)于一個(gè)帶有動(dòng)態(tài)參數(shù)的路徑 /foo/:id墨林,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時(shí)候,
// 由于會(huì)渲染同樣的 Foo 組件犯祠,因此組件實(shí)例會(huì)被復(fù)用旭等。而這個(gè)鉤子就會(huì)在這個(gè)情況下被調(diào)用。
// 可以訪(fǎng)問(wèn)組件實(shí)例 `this`
},
beforeRouteLeave (to, from, next) {
// 導(dǎo)航離開(kāi)該組件的對(duì)應(yīng)路由時(shí)調(diào)用
// 可以訪(fǎng)問(wèn)組件實(shí)例 `this`
}
命名視圖
有時(shí)候想同時(shí)(同級(jí))展示多個(gè)視圖衡载,而不是嵌套展示搔耕,例如創(chuàng)建一個(gè)布局,有 sidebar(側(cè)導(dǎo)航) 和 main(主內(nèi)容) 兩個(gè)視圖痰娱,這個(gè)時(shí)候命名視圖就派上用場(chǎng)了度迂。你可以在界面中擁有多個(gè)單獨(dú)命名的視圖,而不是只有一個(gè)單獨(dú)的出口猜揪。如果 router-view 沒(méi)有設(shè)置名字惭墓,那么默認(rèn)為 default。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
一個(gè)視圖使用一個(gè)組件渲染而姐,因此對(duì)于同個(gè)路由腊凶,多個(gè)視圖就需要多個(gè)組件。確保正確使用 components 配置(帶上 s):
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,//默認(rèn)的拴念,沒(méi)有name的router-view
a: Bar,
b: Baz
}
}
]
})
prop將路由與組件解耦
在組件中接收路由參數(shù)需要this.$route.params.id,代碼冗余钧萍,現(xiàn)在可以在路由表里配置props:true
{path:'detail/:id',component:AppNewsDetail,name:'detail',props:true}
在路由自己中可以通過(guò)props接收id參數(shù)去使用了
props:['id']
Axios 數(shù)據(jù)交互工具
vue官方宣布在2.0版本中不再對(duì)Vue-resource進(jìn)行維護(hù)了,推薦使用axios工具
注意政鼠,axios默認(rèn)配置不會(huì)設(shè)置session-cookie风瘦,需要進(jìn)行配置
axios.defaults.withCredentials = true
詳細(xì)請(qǐng)看文檔
響應(yīng)式原理
因?yàn)関ue是mvvm的框架,所以當(dāng)數(shù)據(jù)變化的時(shí)候公般,視圖會(huì)立即更新万搔,視圖層產(chǎn)生操作后會(huì)自動(dòng)通知vm來(lái)更改model,所以我們可以實(shí)現(xiàn)雙向數(shù)據(jù)綁定官帘,而其中的原理就是實(shí)例會(huì)將設(shè)置的data逐個(gè)遍歷利用Object.defineProperty給數(shù)據(jù)生成getter和setter瞬雹,當(dāng)數(shù)據(jù)變化地方時(shí)候setter會(huì)監(jiān)聽(tīng)到并且通知對(duì)應(yīng)的watcher工具進(jìn)行邏輯運(yùn)算會(huì)更新視圖
vuex借鑒了flux和redux的思想,但是flux和redux是獨(dú)立且完整的架構(gòu)刽虹,vuex是耦合與vue框架的酗捌,所以使用成本要比f(wàn)lux、redux低
聲明式渲染
在vue中,我們可以先在vue實(shí)例中聲明數(shù)據(jù)胖缤,然后通過(guò){{}}等方式渲染在dom中
Vuex
Vuex是vue官方的一款狀態(tài)管理工具尚镰,什么是狀態(tài)呢?我們?cè)谇岸碎_(kāi)發(fā)中有一個(gè)概念:數(shù)據(jù)驅(qū)動(dòng)哪廓,頁(yè)面中任意的顯示不同狗唉,都應(yīng)該有一條數(shù)據(jù)來(lái)控制,而這條數(shù)據(jù)又叫做state撩独,狀態(tài)。
在vue中账月。組件間進(jìn)行數(shù)據(jù)傳遞综膀、通信很頻繁,而父子組件和非父子組件的通信功能也比較完善局齿,但是剧劝,唯一困難的就是多組件間的數(shù)據(jù)共享,這個(gè)問(wèn)題由vuex來(lái)處理
Vuex的使用:
- 創(chuàng)建store:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//可以設(shè)置store管理的state/getter抓歼,mutations,actions
const store = new Vuex.Store({
})
- 設(shè)置state
state就是一個(gè)純對(duì)象讥此,上面有一些狀態(tài)掛載,而且一個(gè)應(yīng)用應(yīng)該只有一個(gè)數(shù)據(jù)源:?jiǎn)我粻顟B(tài)樹(shù)、唯一數(shù)據(jù)源
import state from './modules/state'
//可以設(shè)置store管理的state/getter谣妻,mutations,actions
const store = new Vuex.Store({
state
})
- 在根實(shí)例里配置store
這樣萄喳,我們就可以在任意的組件中通過(guò)this.$store來(lái)使用關(guān)于store的api
import store from './store'
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
- 在組件中使用state
因?yàn)樵诮M件中可以通過(guò)this.store.state來(lái)使用state中管理的數(shù)據(jù)
data(){
return {
num:this.$store.state.num
}
},
但是我們發(fā)現(xiàn),這樣使用的話(huà)蹋半,當(dāng)state的數(shù)據(jù)更改的時(shí)候他巨,vue組件并不會(huì)重新渲染,不會(huì)觸發(fā)組件的相關(guān)生命周期函數(shù)
也就是說(shuō),如果想要在組件中響應(yīng)式的使用的時(shí)候减江,我們需要通過(guò)計(jì)算屬性(computed)來(lái)使用
computed:{
num(){
return this.$store.state.num
}
}
這樣的寫(xiě)法很無(wú)趣染突,而且如果使用的狀態(tài)較多會(huì)產(chǎn)生冗余的感覺(jué),所以vuex提供了mapState輔助函數(shù)辈灼,幫助我們?cè)诮M件中獲取并使用vuex的store中保存的狀態(tài)
所以我們可以這樣寫(xiě):
computed:mapState(['num']),
但是如果組件中已經(jīng)有了num這個(gè)數(shù)據(jù)了份企,而state中的數(shù)據(jù)名字也叫num就會(huì)照成沖突,這個(gè)時(shí)候我們可以在組件使用state的時(shí)候巡莹,給狀態(tài)起個(gè)別名:
computed:mapState({
// _num:'num',//鍵名為別名司志,值字符串代表的是真正的狀態(tài)
_num(state){//方法名為別名,函數(shù)體里還可以對(duì)真正的狀態(tài)做出一些處理
return state.num
}
}),
但是降宅,有的時(shí)候我們?cè)诮M件中還有自己的業(yè)務(wù)邏輯需要用到計(jì)算屬性:
computed:{
a(){
return num+1
},
...mapState({
// _num:'num',//鍵名為別名俐芯,值字符串代表的是真正的狀態(tài)
_num(state){//方法名為別名,函數(shù)體里還可以對(duì)真正的狀態(tài)做出一些處理
return state.num
}
}),
},
- getters
有的時(shí)候钉鸯,我們需要根據(jù)state中的某一個(gè)狀態(tài)派生出一個(gè)新的狀態(tài)吧史,例如,我們state中有一個(gè)num,在某些組件中需要用到是num的二倍的一個(gè)狀態(tài)贸营,我們就可以通過(guò)getters來(lái)創(chuàng)建
const getters = {
doublenum(state){
return state.num*2
}
}
創(chuàng)建了之后吨述,在組件中通過(guò)this.$store.getters來(lái)獲取里面的數(shù)據(jù)
當(dāng)然vuex也提供了mapGetters輔助函數(shù)來(lái)幫助我們?cè)诮M件中使用getters里的狀態(tài),且钞脂,使用的方法和mapState一模一樣
- 使用mutations更改state
我們不能直接在組件中更改state:this.$store.state.num=2,而是需要使用mutations來(lái)更改揣云,mutations也是一個(gè)純對(duì)象,里面包含很多更改state 的方法冰啃,這些方法的形參接收到state邓夕,在函數(shù)體里更改,這時(shí)阎毅,組件用到的數(shù)據(jù)也會(huì)更改焚刚,實(shí)現(xiàn)響應(yīng)式。
但是我們也不能直接調(diào)用mutations 的方法扇调,需要使用this.$store.commit來(lái)調(diào)用矿咕,第一個(gè)參數(shù)為調(diào)用的方法名,第二げ參數(shù)為傳遞參數(shù)
const mutations = {
increment(state){
state.num++
}
}
vuex提供了mapMutations方法來(lái)幫助我們?cè)诮M件中調(diào)用mutations 的方法,使用方法和mapState狼钮、mapGetters一樣
- 使用actions來(lái)處理異步操作
Action 類(lèi)似于 mutation碳柱,不同在于:
Action 提交的是 mutation,而不是直接變更狀態(tài)熬芜。
Action 可以包含任意異步操作莲镣。
也就是說(shuō),如果有這樣的需求:在一個(gè)異步處理之后涎拉,更改狀態(tài)剥悟,我們?cè)诮M件中應(yīng)該先調(diào)用actions,來(lái)進(jìn)行異步動(dòng)作曼库,然后由actions調(diào)用mutation來(lái)更改數(shù)據(jù)
const actions = {
[CHANGE_NUM]({commit}){
alert(1)
setTimeout(() => {
let num = Math.floor(Math.random()*10)
//調(diào)用mitations的方法
commit(CHANGE_NUM,num)
}, 1000);
}
}
``
如上区岗,actions的方法中可以進(jìn)行異步的動(dòng)作,且形參會(huì)接收store毁枯,從中取出commit方法用以調(diào)用mutations的方法
在組件中通過(guò)this.$store.dispatch方法調(diào)用actions的方法
當(dāng)然也可以使用mapMutations來(lái)輔助使用
組件使用數(shù)據(jù)且通過(guò)異步動(dòng)作更改數(shù)據(jù)的一系列事情:
1.生成store,設(shè)置state
2.在根實(shí)例中注入store
3.組件通過(guò)計(jì)算屬性或者mapState來(lái)使用狀態(tài)
4.用戶(hù)產(chǎn)生操作慈缔,調(diào)用actions的方法,然后進(jìn)行異步動(dòng)作
5.異步動(dòng)作之后种玛,通過(guò)commit調(diào)用mutations的方法
6.mutations方法被調(diào)用后藐鹤,更改state
7.state中的數(shù)據(jù)更新之后,計(jì)算屬性重新執(zhí)行來(lái)更改在頁(yè)面中使用的狀態(tài)
8.組件狀態(tài)被更改...創(chuàng)建新的虛擬dom......
9.組件的模板更新之后重新渲染在dom中
vuex的使用:
目前市場(chǎng)上有兩種使用vuex的情況赂韵,
第一種:將需要共享娱节、需要管理的狀態(tài)放入vuex中管理,也就是說(shuō)在必要時(shí)使用
第二種:將所有的數(shù)據(jù)都交由vuex管理祭示,由vuex來(lái)承擔(dān)更多的責(zé)任肄满,組件變得更輕量級(jí),視圖層更輕
---
##### 自定義指令
在實(shí)現(xiàn)回到頂部功能的時(shí)候,我們寫(xiě)了一個(gè)backTop組件稠歉,接下來(lái)需要通過(guò)監(jiān)聽(tīng)window.scroll事件來(lái)控制這個(gè)組件顯示隱藏
因?yàn)榭赡軙?huì)有其他的組件會(huì)用到這樣的邏輯掰担,所以將此功能做成一個(gè)自定義指令:
根據(jù)滾動(dòng)的距離控制一個(gè)數(shù)據(jù)為true還是為false(v-scroll-show)
問(wèn)題:
唯一需要注意的是,在指令的鉤子函數(shù)中我們可以訪(fǎng)問(wèn)到el怒炸,也就是使用指令的標(biāo)簽带饱,但是我們不能直接更改value(指令的值所代表的數(shù)據(jù))
所以我們使用引用類(lèi)型來(lái)進(jìn)行地址的傳遞來(lái)解決這個(gè)問(wèn)題
接下來(lái)有寫(xiě)了一個(gè)v-back-top指令,就是將回到頂部功能做成一個(gè)指令阅羹,哪個(gè)組件或者dom需要使用到回到頂部勺疼,就加上這個(gè)指令就可以,設(shè)置不同的參數(shù)來(lái)控制在不同的情況下觸發(fā)
##### Vue的組件庫(kù)
組件庫(kù)就是通用組件的集合
pc:element-ui iview
mobile: mint-ui
##### nextTick
當(dāng)我們?cè)谑褂靡恍┎寮臅r(shí)候捏鱼,經(jīng)常需要在dom更新完成后進(jìn)行必要操作执庐,但是在vue中提供的api只有updated鉤子函數(shù),而在這個(gè)函數(shù)里穷躁,任意數(shù)據(jù)的變化導(dǎo)致的dom更新完成都會(huì)觸發(fā)耕肩,所以很可能會(huì)造成無(wú)關(guān)數(shù)據(jù)的影響因妇,而使用監(jiān)聽(tīng)的話(huà)只能監(jiān)聽(tīng)到數(shù)據(jù)的變化问潭,此時(shí)dom還沒(méi)有更新,我們只能強(qiáng)行使用setTimeout來(lái)處理
這里推薦大家使用nextTick全局方法:
在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)婚被。在修改數(shù)據(jù)之后立即使用這個(gè)方法狡忙,獲取更新后的 DOM。
eq:
getBillBoards(){
axios.get(this.$root.config.host+'mz/v4/api/billboard/home',{
params:{__t:Date.now()}
}).then(res => {
console.log(res.data.data.billboards)
this.billboards = res.data.data.billboards
//當(dāng)數(shù)據(jù)更新址芯,dom循環(huán)完成后灾茁,執(zhí)行回調(diào)
Vue.nextTick(function () {
new Swiper('.app-home-banner',{
loop:true
})
})
})
}
##### keep-alive
在component組件、router-view外面包裹上keep-alive的話(huà)谷炸,就會(huì)對(duì)組件進(jìn)行緩存北专,當(dāng)切換回來(lái)的時(shí)候,組件會(huì)立即渲染旬陡,理論來(lái)說(shuō)拓颓,切換組件的時(shí)候其實(shí)會(huì)把上一個(gè)組件銷(xiāo)毀,使用了keep-alive則不會(huì)
設(shè)置include描孟、exclude屬性控制有選擇的緩存
include匹配到的組件會(huì)被緩存驶睦,exclude匹配到的不會(huì)被緩存
值可以為逗號(hào)隔開(kāi)的字符串include = 'a,b';正則:include = '/a|b/';數(shù)組:include=['a','b']
使用keep-alive緩存的組件連帶它的子組件們都會(huì)擁有activated、deactivated鉤子函數(shù)匿醒,會(huì)在切換回來(lái)和要切換出去的時(shí)候觸發(fā)
比如场航,main做了緩存,但是main的banner我們希望每次都去重新獲取數(shù)據(jù)廉羔,所以就在banner的activated里獲取數(shù)據(jù)
### Vue試題分析
1. v-for可以實(shí)現(xiàn)數(shù)據(jù)遍歷顯示溉痢,不僅可以遍歷數(shù)組,也可以遍歷對(duì)象,還可以從數(shù)值中取值:
v-for='n in 10' n會(huì)打印1-10
2. vue的生命周期鉤子:
通用:beforeCreate/created/beforeMount/mounted/beforeUpdate/updated/beforeDestroy/destroyed
路由守衛(wèi):beforeRouteEnter/beforeRouteUpdate (2.2 新增)/beforeRouteLeave
keep-alive:activated/deactivated
3. v-if v-show
v-if是真正的條件渲染适室,會(huì)確保在切換中條件塊內(nèi)的事件監(jiān)聽(tīng)嫡意、子組件都會(huì)適當(dāng)?shù)谋讳N(xiāo)毀和重建
v-show總是將節(jié)點(diǎn)渲染在dom中,只是基于css:display來(lái)控制節(jié)點(diǎn)的顯示和隱藏
v-if有更高的切換開(kāi)始捣辆,v-show有更高的初始渲染開(kāi)銷(xiāo)
v-if是惰性的蔬螟,初始條件為假,就不會(huì)渲染
4. axios相關(guān)
axios請(qǐng)求的時(shí)候不會(huì)帶上cookie汽畴,不會(huì)影響帶寬旧巾,可以通過(guò)withCredentials:true來(lái)設(shè)置
對(duì)axios 的請(qǐng)求頭進(jìn)行設(shè)置:
axios.defaults.headers = {'Content-Type':'...'}
vue2.0不在更新維護(hù)vue-resource,官方推薦使用axios
axios攔截器可以攔截請(qǐng)求和響應(yīng)忍些,在then鲁猩、catch之前攔截
6. 組件實(shí)例的作用域是孤立的,意味著不能(不應(yīng)該)在子組件模板里直接引用父組件的數(shù)據(jù)罢坝,要讓子組件使用父組件數(shù)據(jù)的話(huà)廓握,需要通過(guò)props來(lái)將父組件的數(shù)據(jù)傳遞給子組件,子組件不能也不應(yīng)該修改父組件傳入的數(shù)據(jù)嘁酿,但是可以通過(guò)傳入引用類(lèi)型的數(shù)據(jù)來(lái)實(shí)現(xiàn)數(shù)據(jù)共享
7.為了讓組件可以組合隙券,我們需要一種方式來(lái)混合父組件的內(nèi)容與子組件自己的模板。這個(gè)過(guò)程被稱(chēng)為內(nèi)容分發(fā) (即 Angular 用戶(hù)熟知的“transclusion”)闹司。Vue.js 實(shí)現(xiàn)了一個(gè)內(nèi)容分發(fā) API娱仔,參照了當(dāng)前 Web Components 規(guī)范草案,使用特殊的 <slot> 元素作為原始內(nèi)容的插槽游桩。
a-template:
<p>hello world</p>
<b>
<h1>hello world</h1>
</b>
b-template:
<slot></slot>
....
8. 如果把切換出去的組件保存在內(nèi)存中牲迫,保留狀態(tài)避免重新渲染,可以使用keep-alive
include exclude
9. 注冊(cè)方式:
全局:Vue.component(name,Vue.extend({}))
局部:{ components:{name:Vue.extend({})} }
10. 事件總線(xiàn)實(shí)現(xiàn)非父子組件通信
//創(chuàng)建bus
let bus = new Vue()
//a
new Vue({
template:'...',
mounted(){
bus.$on('emit-a',function(){
alert(1)
})
}
})
//b
new Vue({
template:'...',
methods:{
emitA(){
bus.$emit('emit-a')
}
}
})
//當(dāng)b組件的emitA方法被調(diào)用的時(shí)候借卧,A組件就會(huì)執(zhí)行alert(1)
11. methods和計(jì)算屬性的區(qū)別
假設(shè)我們有一個(gè)數(shù)據(jù)為num盹憎,還希望擁有一個(gè)數(shù)據(jù)為doublenum,而且希望doublenum的值永遠(yuǎn)都是num的二倍
方法:
* 因?yàn)槭侵苯语@示在模板中铐刘,也就是說(shuō)陪每,我們可以來(lái)一個(gè)doublenum的方法,這個(gè)方法返回num的二倍滨达,將這個(gè)方法放到模板中的某個(gè)地方執(zhí)行 {{doublenum()}}
但是奶稠,當(dāng)無(wú)關(guān)的例如一個(gè)str的數(shù)據(jù)更改的時(shí)候,組件會(huì)重新創(chuàng)建虛擬dom樹(shù)捡遍,與上一次的虛擬dom樹(shù)對(duì)比之后重新渲染锌订,這個(gè)時(shí)候在重新渲染模板的時(shí)候doublenum函數(shù)會(huì)被再次的調(diào)用,造成不必要的性能浪費(fèi)
* 創(chuàng)建一個(gè)doublenum數(shù)據(jù)画株,使其初始值為num的二倍辆飘,然后利用watch來(lái)監(jiān)聽(tīng)這兩個(gè)數(shù)據(jù)啦辐,在改變的時(shí)候更改對(duì)應(yīng)的數(shù)據(jù),但是需要初始的為doublenum賦值為num的二倍蜈项,如果num是動(dòng)態(tài)獲取到的芹关,doublenun賦值會(huì)更繁瑣
* computed計(jì)算數(shù)據(jù),我們可以利用computed來(lái)創(chuàng)建一條新的doublenum數(shù)據(jù)紧卒。并且設(shè)置它的getter和setter侥衬,并與num建立關(guān)系,且computed會(huì)緩存,在重新渲染的時(shí)候跑芳,不會(huì)重新執(zhí)行g(shù)etter和setter
computed:{
doublenum:{
get(){
return this.num*2
},
set(val){
this.num = val/2
}
}
}
12. 綁定class的對(duì)象語(yǔ)法和數(shù)組語(yǔ)法
<a :class="{a:true,b:false,c:1}"> => </a> => <a class='a c'></a>
data(){
return {
c:'c'
}
}
<a :class = '["a","b",c]'></a> => </a> => <a class='a b c'></a>
13.
new Vue({
el:"#example-3",
methods:{
say(str){
alert(str)
}
}
})
14. 單向數(shù)據(jù)流
prop是單向綁定的轴总,父組件屬性變化,傳遞給子組件博个,但是怀樟,子組件數(shù)據(jù)變化,不能直接傳遞給父組件盆佣,也就是數(shù)據(jù)的流行是從父組件流向子組件的往堡,為了防止子組件修改父組件的數(shù)據(jù)(會(huì)讓?xiě)?yīng)用的數(shù)據(jù)流變的更難開(kāi)發(fā)、更新共耍、維護(hù))
使用了vuex工具的時(shí)候虑灰,store中數(shù)據(jù)在組件中使用的過(guò)程也是單向數(shù)據(jù)流,state->vue component->actions->mutations->state->vue component
15. this.$router.push/replace({name:'user',params:{userId:1})
this.$router.push/replace({path:'/register',query:{plan:private})
##### key相關(guān)
當(dāng)數(shù)據(jù)改變之后征堪,vue會(huì)創(chuàng)建新的虛擬dom來(lái)和原來(lái)的虛擬dom做對(duì)比瘩缆,在創(chuàng)建新的虛擬的dom的時(shí)候关拒,會(huì)根據(jù)key來(lái)查找在原來(lái)的虛擬dom中有沒(méi)有某個(gè)部分佃蚜,如果原來(lái)的有,這次的也需要着绊,就會(huì)實(shí)現(xiàn)復(fù)用谐算,而且在做diff對(duì)比的時(shí)候,如果有key會(huì)加快對(duì)比的查找速度归露,提高性能
盡量循環(huán)的時(shí)候不要將key設(shè)置為數(shù)組的索引洲脂,因?yàn)楫?dāng)刪除某一個(gè)元素的時(shí)候,就會(huì)導(dǎo)致刪除位置下面的所有元素的key值都與上一次虛擬dom的key值不同剧包,導(dǎo)致復(fù)用失敗恐锦,這個(gè)時(shí)候我們最好使用關(guān)鍵的唯一的,例如id這樣的數(shù)據(jù)作為key
如果數(shù)據(jù)變化只是值的變化而不是條數(shù)和位置的變化疆液,可以使用索引作為key
##### Vue.use()
Vue.use會(huì)查找插件對(duì)象里的install方法去執(zhí)行,并且給install方法里傳入Vue對(duì)象
var a = {
install(Vue){
Vue.component("my-a",{...})
}
}
Vue.use(a)
##### 進(jìn)入域后根據(jù)不同的情況顯示不同的頁(yè)面(PC/MOBILE)
很多情況下一铅,一個(gè)應(yīng)用會(huì)有PC和移動(dòng)端兩個(gè)版本,而這兩個(gè)版本因?yàn)椴顒e大堕油,內(nèi)容多潘飘,所以不能用響應(yīng)式開(kāi)發(fā)但是單獨(dú)開(kāi)發(fā)肮之,而域名只有一個(gè),用戶(hù)進(jìn)入域后直接返回對(duì)應(yīng)設(shè)備的應(yīng)用卜录,做法主要有兩種:
1. 前端判斷并跳轉(zhuǎn)
進(jìn)入一個(gè)應(yīng)用或者一個(gè)空白頁(yè)面后戈擒,通過(guò)navigator.userAgent來(lái)判斷用戶(hù)訪(fǎng)問(wèn)的設(shè)備類(lèi)型,進(jìn)行跳轉(zhuǎn)
2. 后端判斷并響應(yīng)對(duì)應(yīng)的應(yīng)用
用戶(hù)地址欄進(jìn)入域的時(shí)候艰毒,服務(wù)器能接收到請(qǐng)求頭上包含的userAgent信息筐高,判斷之后返回對(duì)應(yīng)應(yīng)用
---
function foo(){// 第16行
getName = function(){console.log(1)}
return this
}
foo.getName = function(){console.log(2)}
foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){console.log(5)}
foo.getName()//2
//foo是一個(gè)函數(shù),也可以說(shuō)是一個(gè)對(duì)象丑瞧,所以它也可以?huà)燧d一些屬性和方法凯傲,18行在其上掛載了一個(gè)getName方法
//執(zhí)行的結(jié)果是
getName()//4
//21行有一個(gè)全局函數(shù),全局函數(shù)聲明提前后被20行的getName覆蓋嗦篱,所以輸出4
foo().getName()//1
//foo()執(zhí)行完成后冰单,將全局的getName也就是window.getName給更改后返回this,而在這里this執(zhí)行的就是window灸促,所以最后執(zhí)行的就是window.getName诫欠,所以輸出1
getName()//1
//在上面已經(jīng)更改全局的getName,所以依然是1
new foo.getName()//2
//new 操作符在實(shí)例化構(gòu)造器的時(shí)候浴栽,會(huì)執(zhí)行構(gòu)造器函數(shù)荒叼,也就是說(shuō),foo.getName會(huì)執(zhí)行典鸡,輸出2
new foo().getName()//3
//new操作符的優(yōu)先級(jí)較高被廓,所以會(huì)先new foo()得到一個(gè)實(shí)例,然后再執(zhí)行實(shí)例的getName方法,這個(gè)時(shí)候萝玷,實(shí)例的構(gòu)造器里沒(méi)有g(shù)etName方法嫁乘,就會(huì)執(zhí)行構(gòu)造器原型上的getName方法
new new foo().getName()//3
//先執(zhí)行new foo()得到一個(gè)實(shí)例,然后在new 這個(gè)實(shí)例的getName方法,這個(gè)時(shí)候會(huì)執(zhí)行這個(gè)方法球碉,所以輸出3
//除了本地對(duì)象的方法蜓斧,其他的函數(shù)都能new
---