_______________________________________________________________________________________________
4、兄弟組件通信
兄弟組件的通信,借助于一個Vue的實例(eventBus 事件總線)作為一個中介,組件通過監(jiān)聽該實例去完成事件的綁定和觸發(fā)(僅)
varbus= new Vue(); ? ? ? ? ?//新建一個Vue實例,并接收這個實例,當作事件總線
Vue.component("發(fā)送方組件",{
? methods:{
? ? handleClick: function(){
? ? ?bus.$emit('event自定義事件名','arg1','arg2'...)
? ?}? ? ? ? ?//觸發(fā)當前實例上的事件,第二個參數(shù)是觸發(fā)時附加的參數(shù),會傳給監(jiān)聽器回調(diào)
? },
? template: `<button @click="handleClick">觸發(fā)事件,進行通信</button>`
})
Vue.component("接收方組件",{
? created:function(){ ? ? ? ? ? ? ?//放在'組件實例創(chuàng)建完成后'的函數(shù)中,可以自動接收
? ?bus.$on('event自定義事件名',function(arg1,arg2...){ }
? } ? ? ? ? ? ?//綁定監(jiān)聽當前實例上的自定義事件,事件由.emit觸發(fā)
? ? ? ? ? ? ? ? //第二個參數(shù)是觸發(fā)事件成功的回調(diào)函數(shù),會接收所有傳入'事件觸發(fā)函數(shù)'的額外參數(shù)
? template: `<div>接收通信</div>`
})
_______________________________________________________________________________________________
路由
使用 vueRouter 插件可以實現(xiàn)SPA
單文件: 在webpack環(huán)境下使用路由模塊: ?或者在項目初始化時提前設置自動安裝
? ? ? ? var Vue=require('vue')
? ? ? ? var VueRouter=require('vue-router')
? ? ? ? Vue.use(VueRouter)
VueRouter
① 引入 vue-router.js 文件
② 指定容器
<div id="example">
? <router-view></router-view> ? ? ? ?//指定組件插入的容器
</div>
③ 創(chuàng)建各個子組件
var Test01Component=Vue.component("test01",{ ? ?//組件定義在最上面,才能加載路由
? template:`<div>
? <h1>這是子組件test01</h1>
? </div>`
});
④ 配置路由地址(分為三段)
constmyRoutes= [ ? ? ?? //① 配置路由地址: 創(chuàng)建一個對象數(shù)組,每個對象指定path/component
? { path:'/myPath1',component:Test01Component?},
? { path:'/myPath2',component:Test01Component},
? { path: "/",component: Test01Component?} ? ? ?? //沒有path,只有一個/時跳轉(zhuǎn)的地址
];
constmyRouter= new VueRouter({ ? ? ? ?//② 創(chuàng)建一個VueRouter的實例,并接收
? routes:myRoutes? ? ? ?//指定 routes 屬性為配置的路由地址,與①可合為一個
});
new Vue({
? router:myRouter? ? ? ?//③ 在vue的實例中指定router屬性為路由 VueRouter 的實例
? ? ? ? ? <=> new VueRouter({routes:myRoutes}); ? ?//但不建議這樣寫,跳轉(zhuǎn)不便
})
跳轉(zhuǎn):
html的方式? <router-link to="/myPath1">跳轉(zhuǎn)到myPath1</router-link>? //渲染出來是一個 a 標簽
js的方式:
jump:function(){?myRouterVueRouter的實例.push("/myPath1");?}
<button @click="jump">跳轉(zhuǎn)到myPath1</button>
傳參:
① 明確發(fā)送方、接收方
② 配置接收方的路由地址path
? {path: "/myPath1/:arg",component:Test01Component?}
③ 接收方取得傳遞過來的參數(shù)
? created:function(){
? ? this.resArgdata數(shù)據(jù)=this.$route.params.arg;
? }
④ 發(fā)送方發(fā)送參數(shù)
<router-link to='/myPath1/20'> ?? <router-link v-bind:to="'/myPath1/'+value"></router-link>
<a href='#/myPath1/20'></a> ? ? ? <a v-bind:href="'#/myPath1/'+value"></a>
jump:function(){?myRouter.push('/myPath1/20'); }
jump:function(){?myRouter.push("/myPath1"+this.value);?} ? ?//通過'該組件的數(shù)據(jù)'傳參
jump:function(key){?myRouter.push("/myPath1/"+key);} ? ? ?? //通過'方法傳來的參數(shù)'傳參
觸發(fā)事件綁定的方法: ?<any @click="jump(key)"><any>
嵌套路由:
var MyMail = Vue.component("mail",{
? template: `<div>
? <h1>這是郵箱主頁面</h1>
? <router-link to="/inbox">收件箱</router-link>
? <router-link to="/outbox">發(fā)件箱</router-link>
? <router-view></router-view> ? ? ? ? ? ? ? ? ? ? ? //① 為子路由指定容器
? </div>`
? });
② 在配置路由地址時,為子路由指定path/component
constmyRoutes=[
? { path:'/myPath1',component:Test01Component?},
?{path:"/myMail", component:MyMail,?children:[
? ? { path:"/inbox",component:inBox },
? ? { path:"/outbox",component:outBox}]?},
? { path: "/",component: Test01Component?}
];
注意: 子路由的跳轉(zhuǎn)鏈接不是必須加在父路由中。可以加在項目的任一頁面,都可跳向子路由,同時也會顯示其父路由的內(nèi)容
_______________________________________________________________________________________________
三、網(wǎng)絡請求(使用vue-resource或者axios插件)
① 引入vue-resource.js文件
Vue.component('test',{
? data: function(){
? ? return { List:[] }
? },
? methods:{
? ? loadData: function(){ ?? //this.$http.get()第一個參數(shù)是請求的url,第二個參數(shù)是傳遞的參數(shù)
? ? ?this.$http.get('data/stu.json請求url',{"id":123}) ? ? ?? //② 發(fā)起網(wǎng)絡請求 get/post
? ? ? ?.then(function(response){? ? ? ? ? ? ? ?? //響應成功的回調(diào)函數(shù),參數(shù)是返回的數(shù)據(jù)
? ? ? ? ? this.List = response.data; ? ? ? ? ? ? ?? //③ 對響應到的數(shù)據(jù)進行操作
? ? ?})
? ? }
? },
? template:`<div>
? <ul><li v-for="tmp in List">{{tmp.name}}</li></ul> ? ? ?? //將返回的數(shù)據(jù)顯示在視圖上
? <button @click="loadData">加載數(shù)據(jù)</button> ? ? ? ? ? ? ? //綁定請求事件
? </div>`
})
axios(愛可西柚子)******************************************************************************
特色:?① 瀏覽器端發(fā)起XMLHttpRequests請求? ② node端發(fā)起http請求? ③ 支持Promise API
? ? ? ④ 監(jiān)聽請求和返回? ⑤ 轉(zhuǎn)化請求和返回? ⑥ 取消請求? ⑦ 自動轉(zhuǎn)化json數(shù)據(jù)? ⑧ 客戶端支持抵御
安裝使用:
npm:? npm install axios --save-dev
cdn:? <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
src/main.js: 引入axios
import axios from 'axios'
Vue.prototype.$axios = axios
src/App.vue: 攔截器,在請求或者返回被then或catch處理之前對它們進行攔截
created(){
? var loading = null;
? this.$axios.interceptors.request.use(config => {? ? //發(fā)出請求之前進行攔截
? ? if(store.state.token){? //判斷是否存在token,如果存在的話,則每個http header都加上token
? ? ? config.headers.Authorization = `token ${store.state.token}`;
? ? }
? ? loading = this.$loading({?text:'請求中...'?});
? ? return config;
? }, err => {
? ? this.$message({?message:'請求接口失敗...',?type:'warning',?duration: 1333?});
? ? return Promise.reject(err);
? })
??this.$axios.interceptors.response.use(res => {? ? ? //對返回的數(shù)據(jù)進行攔截
? ? loading.close();
? ? return res;
? }, err => {
? ? loading.close();
? ? if(err.response){
? ? ? switch (err.response.status) {
? ? ? ? case 401:
store.commit(types.LOGOUT);//返回401(未授權(quán)),清除token信息并跳轉(zhuǎn)到登錄頁面
? ? ? ? ? router.replace({?path: 'login',
? ? ? ? ? ? ? ? ? ? ? ? ? ?query: {redirect: router.currentRoute.fullPath}
? ? ? ? ? })
? ? ? }
? ? }
return Promise.reject(err);//返回接口返回的錯誤信息
? })
}
跨域請求:
config/index.js:
proxyTable:{
? '/api':{? ? ? ? ? ? ? ? ? ? ? ? ?? // '/api'文件中的匹配項
? ? target: 'http://erpserver.mgupiao.com', ? ? ?? //請求接口的地址
? ? secure: false, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //如果是https接口,需要配置此參數(shù)
? ? changeOrigin: true, ? ? ? ? ? ? ? ? ? ? ? ? ?? //如果需要跨域,需要配置此參數(shù)
? ? pathRewrite:{ '^/api':'' } ? ? ?? // 因為在ajax的url中加了前綴 '/api'用于匹配,而原本的接口是沒有這個前綴的,所以需要通過pathRewrite來重寫地址,將前綴'/api'轉(zhuǎn)為'/'
?},
? '/station': {
? ? target: 'http://erpserver.mgupiao.com',
? ? changeOrigin: true,
? ? pathRewrite: { '^/station': 'station' }
? }
},
axios的功能特性:
在瀏覽器中發(fā)送XMLHttpRequests請求
支持Promise API
攔截請求和響應
轉(zhuǎn)換請求和響應數(shù)據(jù)
自動轉(zhuǎn)換JSON數(shù)據(jù)
客戶端支持保護安全免受XSRF攻擊
請求方式:
var axios = this.$axios
axios(config)
axios.request(config)
axios.get(url[,config])
axios.delete(url[,config])
axios.head(url[,config])
axios.post(url[,data[,config]])
axios.put(url[,data[,config]])
axios.patch(url[,data[,config]])
get請求:
axios.get('/user',{param:{id:1}})? ? ? ? //可以取param參數(shù)
? ? ?.then(res=>{console.log(res)})
? ? ?.catch(err=>{console.log(err)})
post請求:
axios.post('/user',{id:2})? ? ? ? ? ? ? ?//會轉(zhuǎn)換為json格式
? ? ?.then(res=>{console.log(res)})
? ? ?.catch(err=>{console.log(err)})
發(fā)送并發(fā)請求:
axios.all([axios.get('/profile'), axios.post('/user')])
? ? ?.then(axios.spread((res1,res2)=>{
? ? ? ?console.log(res1); console.log(res2);
? ? ?}
))
*.vue單文件************************************************************************************
每一個*.vue文件都是組件使用大駝峰命名法
每個組件分為3個部分: <template></template>、<script></script>、<style></style>
template中只能有一個根標簽
*.vue中的data是一個方法,該方法需要返回一個對象,在對象中初始化數(shù)據(jù)
<template>
? <divclass="hello"> ? ? ?? //只能有一個根標簽(如div/section)
? ? <head-top></head-top> ? ?//③ 使用組件
? ? ...
? </div>
</template>
<script>
? import headTop from './Header' ? ? ?? //① 引入組件
? export default{
? ? name: 'SelectBall', ? ?? //導出組件名
? ? data () {
? ? ? return {}
? ? },
? ? components: { ? ? ? ? ?? //②?注冊組件(使用components屬性)
? ? ? headTop? ? ? ? ? ? ? ? //headTop: ES6的簡寫,相當于ES5: headTop:headTop
? ? }
? }
</script>
<style lang="less" type="text/less" scoped> ? ?//若不寫type="text/less",WebStorm可能會報錯
? ? ? ? ? ? ? //scoped: 使style中的樣式(如less)只作用于當前組件(若不寫,則樣式會作用于全局)
? @import 'other.less'; ? ? ?? //導入的也遵守此規(guī)則
? .tilte{
? ? font-size: 1.2rem;
? }
</style>
src/router/index.js: ?每一個*.vue文件都需要在此文件中引入才能使用(配置路由)
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld' ? ?//引入組件
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //@: 在build/webpack.base.conf.js中的alias配置的別名
Vue.use(Router)? ? ? ? ? ? ? ? ? ? ? ? ?//使用路由
constrouter=?new Router({
? mode:'history', ? ? ? ?//配置此參數(shù)后,路徑不會帶#(可使用pushState/replaceState來管理記錄)
? ? ? ? ? ? ? ? ? ? ? ? ?//否則需要在跳轉(zhuǎn)地址前加 /#
? routes: [{path: '/helloWorld', ? ? ? ? ?? //配置路由地址
? ? ? ? ? ? ?name: 'helloWorld',? ? ? ? ? ? ?//name和path使用小駝峰命名法
? ? ? ? ? ?? component:?HelloWorld,
? ? ? ? ? ?? children:[{ path:"/inbox",component:InBox },
? ? ? ? ? ? ? ? ? ? ? ?? { path:"/outbox",component:OutBox}]},
? ? ? ? ? ?{path: '/xiaoliang',
? ? ? ? ? ? ?name: 'xiaoliang',
? ? ? ? ? ?? component: Xiaoliang,
? ? ? ? ? ? ?meta: {
? ? ? ? ? ? ? ?title: '標題',
? ? ? ? ? ? ? ?requireAuth: true,? ? //自定義字段,表示進入這個路由需要登錄
? ? ? ? ? ? ?},?}]
})
利用vue-router提供的鉤子函數(shù)beforeEach對路由進行判斷,進行登錄驗證、修改title等操作
router.beforeEach((to, from, next) => {
? if(to.meta.requireAuth){? ? ? ? //判斷該路由是否需要登錄權(quán)限
? ? if(store.state.token){? ? ? ? //通過vuex state獲取當前的token是否存在
? ? ? next();
? ? }else{
? ? ??next({?path: '/login',
? ? ? ? ? ? ?query: {redirect: to.fullPath} //將跳轉(zhuǎn)的路由path作為參數(shù),登錄成功后跳回該路由
? ? ? })
? ? }
? }else{?next();?}
? if(to.meta.title){
? ? document.title = to.meta.title;? ? //設置網(wǎng)頁標題
? }
? next();
})
export default?router
跳轉(zhuǎn):?使用路由跳轉(zhuǎn),可跳到子組件或兄弟組件
注意:
① 若不在域名的根目錄下時,需要在routes中的每個路由下添加 /目錄名
② 應使用<router-link to="/home">Home</router-link>跳轉(zhuǎn),若用a跳轉(zhuǎn)則會刷新頁面
③?若使用mode: 'history',應將服務期所有404頁面重定向回index.html,使用前端路由跳轉(zhuǎn)
? ?如nginx:?error_page? 404? ? ? ? /index.html;
④?因為所有的404都會重定向到index.html,所以可以在Vue應用里覆蓋所有的路由,給出一個404頁面
? ?{?path: '*',?component: NotFoundComponent }
⑤?在IE中,頁面大小<1024b會被認為不友好,會將該頁面替換成自己的錯誤提示頁面,所以應避免index.html小于1024b
⑥?登錄應做雙重判定,首先應由前端路由控制(router.beforeEach),為確保token失效后確保無法訪問需要登錄的頁面,應使用http攔截器+后臺接口返回的http狀態(tài)碼來判斷(axios攔截器)
vue技巧*****************************************************************************************
路由動畫效果:
src/App.Vue:
<div id="app">
? <transition :name="transitionName">
? ? <router-view class="child-view"></router-view>
? </transition>
</div>
export default {
? name: 'App',
? data(){
? ? return {
? ? ? transitionName: 'slide-left'
? ? }
? },
? watch: {
'$route'(to, from){//監(jiān)聽路由的路徑,可以通過不同的路徑去選擇不同的切換效果
? ? ? const toDepth = to.path.split('/').length;
? ? ? const fromDepth = from.path.split('/').length;
? ? ? this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left';
? ? }
? }
}
#app,.child-view{? ? ? ? //防止切換路由時出現(xiàn)滾動條
? position: absolute;
? top: 0;
? left: 0;
? right: 0;
? bottom: 0;
}
#app{
? overflow-x: hidden;
}
.child-view{
? transition: all .8s cubic-bezier(.55, 0, .1, 1);
}
.slide-left-enter, .slide-right-leave-active{
? opacity: 0;
? -webkit-transform: translate(100%, 0);
? transform: translate(100%, 0);
}
.slide-left-leave-active, .slide-right-enter{
? opacity: 0;
? -webkit-transform: translate(-100%, 0);
? transform: translate(-100%, 0);
}
配置資源路徑:
方法①
config/index.js:
assetsPublicPath:'/', ? ? ? ? ? ? //可發(fā)布到網(wǎng)絡上,資源使用的是根路徑
assetsPublicPath:?'/dist/', ? ? ??//在dist中創(chuàng)建一個WebStorm項目即可查看(根路徑)
assetsPublicPath:'', ? ? ? ? ? ? ?//輸出相對路徑,不用服務器也能打開(引用直接寫的文件名)
assetsPublicPath:'./', ? ? ? ? ? ?//輸出相對路徑,不用服務器也能打開(引用寫的./文件名)
方法②
build/webpack.prod.conf.js:
output: { publicPath:'./', }, ? ?? //添加此語句,調(diào)試打包時,都輸出相對路徑(引用使用./文件名)
基于vue的ui庫***********************************************************************************
PC端:
① element? ? ? 餓了么前端出品的基于Vue 2.0的桌面端組件庫
? ?https://github.com/ElemeFE/element? ? ? ? ? ? ? ? ?//github倉庫地址
② iview? ? ? ? 基于Vuejs的開源UI組件庫
? ?https://github.com/iview/iview? ? ? ? ? ? ? ? ? ? ?//github倉庫地址
③ muse-ui? ? ? 三端樣式一致的響應式UI庫
? ?https://github.com/museui/muse-ui? ? ? ? ? ? ? ? ? //github倉庫地址
移動端:
① Vux? ? ? ? ? 基于Vue和微信官方WeUI的組件庫
? ?https://github.com/airyland/vux? ? ? ? ? ? ? ? ? ? //github倉庫地址
② mint-ui? ? ? 餓了么前端推出的基于Vue.js的移動端組件庫
? ?https://github.com/ElemeFE/mint-ui? ? ? ? ? ? ? ? ?//github倉庫地址
? ?http://elemefe.github.io/mint-ui/#/? ? ? ? ? ? ? ? //demo地址
③ vue-material 基于Vue Material和Vue 2精美的app應用(英文文檔)
? ?https://github.com/marcosmoura/vue-material? ? ? ? //github倉庫地址