0. 基於vue ui創建vue cli3.0項目:參考:https://blog.csdn.net/qq_42231156/article/details/82343793。
1.項目文件vueCli3.0下:
pulic文件下:favicon.ico是網址上方標題的的小圖標。
index.html:是入口文件。
src:項目的主文件:
assets:是存放靜態資源(如,圖片,css等)的文件。
img:存放圖片文件。
iconfont:圖標字體文件。
css:存放公共css文件。
js:存放第三方js文件。
components:可複用的組件。
views:頁面組件。
api:項目的ajax請求請求。
config:項目的配置文件。
index.js:配置地址,在需要的地方import config from "./config/index.js"引入配置對象即可。
directive:存放vue的自定義指令。
index.js:
lib:
tools.js:存放與業務無關的,純粹的工具方法,如封裝的通用js方法。
util.js:存放與業務結合有關的,如定義的路徑變量。
router:存放路由有關的,將router.js移入該文件內。
index.js::存放引入文件,配置路由實例。如下圖:
import Vue from 'vue'
import Router from 'vue-router'
import routes from "./router.js"
import {setTitle} from "@/lib/util.js"
Vue.use(Router)
const router=new Router({
routes
})
const IS_LOGIN=true //根據存儲在cookie的登錄信息判斷是否登錄的判斷
router.beforeEach((to,form,next)=>{ //router實例的beforeEach方法是註冊一個全局前置守衛,從from路由對象到to路由對象,即禁止在沒有登錄情況下,在網址欄輸入admin會跳轉到admin頁面。
to.meta && setTitle(to.meta.title) //設置頁面網址上面的標題
if(to.name !=="login"){ //如果即將跳轉的頁面不是登錄頁面,如跳轉的是admin頁面
if(IS_LOGIN) { //根據是否已經登錄,判斷是否可以跳轉到adminy頁面
next() //如果已即登錄,就直接跳轉
}else{
next({name:'login'}) //如果沒有登錄,就跳轉到登錄頁面
}
}else{ //如果即將跳轉的頁面是登錄頁面
next()
}
})
router.beforeResolve((to,form,next)=>{ //router實例的beforeResolve方法是註冊一個全局守衛,從from路由對象到to路由對象,即頁面跳轉前所有鉤子執行完最後執行該函數 ,
})
router.afterEach((to,form)=>{ //router實例的afterEach方法是註冊一個全局後置守衛,從from路由對象到to路由對象,即頁面跳轉之後執行,
//loading=false
})
export default router
/**
* 1. 導航被觸發
* 2. 在失活的組件(即將離開的頁面組件)裏調用離開守衛 beforeRouteLeave
* 3. 調用全局的前置守衛 beforeEach
* 4. 在重用的組件裏調用 beforeRouteUpdate
* 5. 調用路由獨享的守衛 beforeEnter
* 6. 解析異步路由組件
* 7. 在被激活的組件(即將進入的頁面組件)裏調用 beforeRouteEnter
* 8. 調用全局的解析守衛 beforeResolve
* 9. 導航被確認
* 10. 調用全局的後置守衛 afterEach
* 11. 觸發DOM更新
* 12. 用創建好的實例調用beforeRouterEnter守衛裏傳給next的回調函數
*/
router.js::單獨存放處理路由文件,配置路由列表。如下圖:
import Home from './views/Home.vue'
export default [
{
path: '/',
alias:'/home_page', //爲路由取別名,即訪問"/home_page"和訪問"/"和通過name:"home",訪問的效果是一樣的
name: 'home',
component: Home, //普通寫法
beforEnter:(to,from,next)=>{ //該組件獨有的守衛
if(from.name="login"){
alert("這是從登陸頁面跳轉過來的")
}else{
alert("這不是從登陸頁面跳轉過來的")
}
next() //一定要在最後執行next(),否則不會跳轉
}
},
{
path: '/about',
name: 'about',
component: () => import(./views/About.vue') //這樣寫,有懶加載的作用,即該頁面顯示時纔會加載。
},
{
path: '/argu/:name', //動態路由傳參:高級寫法,動態加載路由,name是參數
props:true, //表示允許組件props:{}中接受name參數值,然後可以直接渲染在頁面{{name}}
component: () => import('./views/Argu.vue')
},
{
path: '/parent', //嵌套路由
component: () => import( './views/Argu.vue'),
children:[
{
path:"child1", //注意只有父級需要'/'
component: () => import('./views/child1.vue'),
},
{
path:"child2",
component: () => import('./views/child2.vue'),
},
]
},
{
path: '/name_router', //命名視圖
components: {
default:() => import('./views/about.vue') , //如果<router-view/> 沒有name屬性值,默認對應該路由
email:() => import('./views/parent.vue') , //<router-view name="email"/> 對應該路由
tel:() => import('./views/argu.vue') //<router-view tel="tel"/> 對應該路由
}
},
{
path: '/home', //重定向路由
redirect:"/", //3種重定向的方法
// redirect:{
// name:"home1"
// },
// redirect:to =>{ //如根據參數確定跳轉到哪個頁面
//// console.log(to)
//// return "/"
// return {
// name:"home1"
// }
// }
},
{
path: '/login',
component: () => import('./views/login.vue')
},
{
path:"*", //404頁面一定要寫在最後,因爲是從上到下匹配路由。
component: () => import('./views/error404.vue'),
}
]
store::存放狀態管理的文件。store.js移入到該文件內,修改爲index.js。記得修改main.js的入口文件
index.js:
mutations.js:
getters.js: 相當於計算屬性,對state參數的計算
state.js:
actions.js:
mudels:存放模塊文件。
user.js:如存放用戶的登錄信息
mock: 模擬返回的數據,在後臺接口還麼有完成,前端通過模擬數據。參考:http://mockjs.com/
vue.config.js:自定義設置文件的配置,如跨域請求地址,配置後臺(相當於vue-cli3.0以下版本的build/serve-dev-serve.js文件),路徑簡寫src爲@,設置項目的基本路徑。
2. vscode編譯器的:添加配置編譯器的文件:
在src同級下.editorconfig:
root=true
[*] //表示該編譯器配置針對所有文件
charset=utf-8
indent_style=tabs 縮進鍵
indent-size=2 縮進的大小
然後在編譯器中安裝editorConfig for VS Code,然後就可運行添加的配置編譯器的文件了
3. router-link與router-view:router-link是封裝了的a標籤。router-view渲染路由視圖,兩者效果一樣。
命名路由:即路由有name屬性name: 'home',<router-link to="/about"></router-link> 相當於命名路由 <router-link :to="{name:'about'}"></router-link>。
3.1 配置路由列表的5種方法:
a.爲路由取別名:
import Home from './views/Home.vue' //如果有 name: 'home',表示是命名路由。
{
path: '/',
alias:'/home_page', //取別名,即訪問"/home_page"和訪問"/"和通過name:"home",訪問的效果是一樣的
name: 'home', //命名路由
component: Home //普通加載頁面模塊
},
b.頁面懶加載頁面模塊:即該頁面顯示時纔會加載。不需要像import Home from './views/Home.vue'一樣提前加載。
{
path: '/about',
name: 'about',
component: () => import('./views/About.vue')
},
c.動態路由加載傳參:動態加載路由,name是參數,{{$route.params.name}}調用參數name的值。
{
path: '/argu/:name',
props:true, //表示允許組件props:{}中接受name參數值,並直接渲染在頁面{{name}}
name: 'argu',
component: () => import( './views/argu.vue')
},
d.嵌套路由: <div class="parent"> <router-view/> </div> 。
{
path: '/parent', //嵌套路由
component: () => import('./views/Argu.vue'),
children:[
{
path:"child1", //注意只有父級需要'/'
component: () => import( './views/child1.vue'),
},
{
path:"child2",
component: () => import('./views/child2.vue'),
},
]
}
e. 命名視圖:同時存在多個路由跳轉時<router-view/> <router-view name="email"/> <router-view name="tel"/> 。
{
path: '/name_router',
components: {
default:() => import('./views/about.vue') , //如果<router-view/> 沒有name屬性值,默認對應該路由。
email:() => import('./views/parent.vue') , //<router-view name="email"/> 對應該路由。
tel:() => import('./views/argu.vue') //<router-view name="tel"/> 對應該路由。
}
},
f. 重定向:重新定義跳轉的路徑,如本來是訪問 '/home',重新綁定跳轉到"/"。
{
path: '/home',
redirect:"/", //3種重定向的方法
// redirect:{
// name:"home1"
// },
// redirect:to =>{ //如根據參數確定跳轉到哪個頁面
//// return "/"
// return {
// name:"home1"
// }
// }
}
4. js操作路由(即編程式的導航)進行頁面跳轉:
a. 返回/前進一頁:返回:this.$router.go(-1)、this.$router.back()。前進:this.$router.go(1)。
b. 跳轉到其他頁:
this.$router.push("/parent")。
this.$router.push({name:"parent",query:{name:"ace"}),即瀏覽歷史紀錄保存着,query是參數。
this.$router.push({path:`/argu/${name}`}) ,es6帶參數跳轉,針對 path: '/argu/:name',該路由。
this.$router.push({path:"/parent",params:{name:"ace"}) , 帶參數跳轉。
c. 用其他頁替換本頁:this.$router.replace("/about")或this.$router.replace({name:"parent"}),即瀏覽歷史紀錄沒有了。
5. 路由傳值:
5.1 基於動態路由的頁面(path: '/argu/:name')傳值。
{
path: '/argu/:name',
props:true, //表示允許Argu.vue組件中props:{}中接受name參數值,然後可以直接渲染在頁面{{name}}
component: () => import( './views/argu.vue' )
},
5.2 基於普通頁面傳參,對象模式傳參。
{
path: '/about',
props:{
food:"香蕉"
}, //表示允許about.vue組件中props:{}中接受food參數值,然後可以直接渲染在頁面{{food}}
component: () => import( './views/argu.vue')
},
5.3 基於普通頁面傳參,函數模式傳參。
{
path: '/parent',
props: route=>{
return {
food:route.query.food
}
}, //表示允許parent.vue組件中props:{}中接受food參數值,然後可以直接渲染在頁面{{food}}
component: () => import( './views/argu.vue')
}
6. 導航守衛:如根據是否登錄或登錄者的權限跳轉不同的頁面。
6.1 全局守衛:即在全局設置一個守衛。在router/index.js中配置全局守衛
const router=new Router({
routes
})
const IS_LOGIN=true //是否登錄的判斷
router.beforeEach((to,form,next)=>{ //router實例的beforeEach方法是註冊一個全局前置守衛,從from路由對象到to路由對象,
if(to.name !=="login"){ //如果即將跳轉的頁面不是登錄頁面
if(IS_LOGIN) {
next() //如果已即登錄,就直接跳轉
}else{
next({name:'login'}) //如果沒有登錄,就跳轉到登錄頁面
}
}else{ //如果即將跳轉的頁面是登錄頁面
if(IS_LOGIN) {
next({name:'home'}) //如果已即登錄,就直接跳轉首頁,{name:'home'}也可是'/home'
}else{
next() //如果沒有登錄,就直接跳轉
}
}
})
router.beforeResolve((to,form,next)=>{ //router實例的beforeResolve方法是註冊一個全局守衛,從from路由對象到to路由對象,即頁面跳轉前所有鉤子執行完最後執行該函數
})
router.afterEach((to,form)=>{ //router實例的afterEach方法是註冊一個全局鉤子,從from路由對象到to路由對象,即頁面跳轉之後執行,
//loading=false
})
6.2 組件獨享守衛:即該組件獨有的守衛。 如 在router/router.js中path:"/"中配置組件守衛 。
{
path: '/',
component: Home, //普通寫法
beforEnter:(to,from,next)=>{ //該組件獨有的守衛
if(from.name="login"){
alert("這是從登陸頁面跳轉過來的")
}else{
alert("這不是從登陸頁面跳轉過來的")
}
next() //一定要在最後執行next(),否則不會跳轉
},
6.3 在組件裏面的3種守衛,如在login.vue組件裏面與生命週期同級:
#a.beforeRouteEnter:即將跳轉到當前頁面,但是頁面還沒有渲染,所有裏面的this不指向實例vue該組件
beforeRouteEnter(to,from,next){
console.log(to.name)
next(vm=>{
console.log(vm) //而這裏的vm就是該組件的實例了。
})
}
#b.beforeRouteLeave:即將離開當前頁面時執行,如即將離開編輯頁面,彈出提醒框,提醒你是否保存編輯內容。
beforeRouteLeave(to,from,next){ //此時組件是已經渲染了,this可以執行vue實例
const leave=confirm("你確定要離開本頁面麼?")
if(leave){
next()
}else{
next(false)
} //false表示不發生頁面跳轉
}
#c.beforeRouteUpdate:即路由發生改變,組件(複用組件)被複用時,執行。如同一個頁面,在url上修改了參數之後,該頁面被複用了,就會執行
beforeRouteUpdate(to,from,next){ //此時組件是已經渲染了,this可以執行vue實例
console.log(to.name)
}
注意:整個導航守衛的流程:
a. 導航被觸發:即url路由地址發生改變。
b. 在失活的組件:即將離開的頁面組件,裏調用離開守衛函數(beforeRouteLeave)。
c. 調用全局的前置守衛:即函數beforeEach。
d0. 如果跳轉的是重/複用的組件裏調用:在複用/重用的組件裏調用函數beforeRouteUpdate。
d1. 如果跳轉的是新的組件調用:在新的組件裏調用beforeRouteEnter。
e. 調用路由獨享的守衛:即router/router.js裏面的函數beforEnter 。
f. 解析異步路由組件:
g. 在被激活的組件裏(即將進入的頁面):調用beforeRouteEnter。
h. 調用全局的解析守衛:即調用beforeResolve。
i. 導航被確認。
j. 調用全局的後置守衛:afterEach。
k. 觸發DOM的渲染。
l. 用創建好的實例調用beforeRouteEnter守衛傳給next()的回調函數。
7. 通過頁面組件的meta:字段設置每個頁面的window.document.title標題。
7.1 如在login.vue組件中:
export default{
meta:{ //與生命週期平級。
title:"我是登錄頁面標題"
}
}
7.2 然後在router/index.js的beforeEach中設置 to.meta.title && setTitle(to.meta.title),import {setTitle} from "@/lib/util.js"
router.beforeEach((to, from, next) => {
//設置docutment.title的標題內容
to.meta && setMetaTitle(to.meta.title)
var IS_LOGIN=true; //是否登錄的判斷,getCookie("isLogin")可以有cookie中是否有登錄信息決定
if(IS_LOGIN){
next()
}else{
if(to.name==="login"){
next()
}else{
next({name:'login'})
}
}
})
7.3 在lib/util.js中定義一個設置url上方的標題的函數。
export const setTitle =(title)=>{
window.document.title=title || "title不存在時的默認值"
}
8. 設置路由跳轉時的動畫效果:
8.1 靜態設置動畫效果:
/*一組路由視圖,設置動畫效果,必須寫key值。然後在css中設置.slide-enter/leave-to/active樣式*/
<transition-group name="slide">
<router-view key="default"/>
<router-view key="email"/>
<router-view key="tel"/>
</transition-group>
<style>
.slide-enter-active{ //動畫執行的時間
transition: all 0.3s;
}
.slide-enter,.slide-leave-to{ //進入和離開時的動畫
transform: translate3d(100%,0,0);
}
</style>
8.2 動態設置動畫效果:
//一組路由視圖,設置動畫效果,必須寫key值。然後在css中設置.router-enter/leave-to/active樣式。
<transition-group :name="transitionName">
<router-view key="default"/>
<router-view key="email"/>
<router-view key="tel"/>
</transition-group>
data(){
return{
transitionName:""
}
},
watch:{
"$route"(to){ //to表示當前頁面,表示如果在url傳入參數transitionName存在如值爲"router",則將其賦值給this.transitionName
to.query && to.query.transitionName && (this.transitionName =to.query.transitionName)
}
}
9. 父組件與子組件的傳值:
9.1 父傳值給子組件:props傳值
父組件:
<div class="parent">
<son-assembly :sendVal="value"></son-assembly>
</div>
子組件:
<div class="son">
{{value}}
</div>
props:{
son:{
type:[String,Number],
default:"123"
}
}
9.2 子傳值給父組件:自定義事件傳值
子組件:
<div class="son"></div>
this.$emit("aceHandle",val)
父組件:
<div class="parent" @aceHandle="myHandle"></div>
myHandle(val){
console.log("這是自定義事件傳值",val)
}
9.3 父子組件之間相互調用彼此的方法:
父組件調用子組件的方法:this.$refs.son.方法名。
子組件調用父組件的方法:方法一:this.$emit("aceHandle",val)。方法二:this.$parent.方法名。
注意:在vue中@input="inputHandle" 表示input輸入值改變時觸發的內置事件。inputHandle(e){console.log("輸入框的值是",e,target.val)}
10.bus傳值,狀態管理:bus即空的vue實例,如用於簡單場景下的兄弟組件之間傳值。
10.1 src下創建bus/index.js文件。
import Vue from "vue"
const Bus=new Vue()
export default Bus
10.2 在main.js引入bus,並添加到vue的原型對象裏,然後在任何地方都可以不需要在引入,直接使用this.$bus.
import Bus from './bus/index.js'
Vue.prototype.$bus=Bus
10.3 從兄弟組件A傳值給兄弟組件B:
A組件:
this.$bus.$emit("myHandle",val)
B組件:
mouted(){
this.$bus.$on("myHandle",(val)=>{ //監聽自定義事件myHandle
//在這裏處理接收到B組件傳遞過來的值
})
}
11. vuex傳值,狀態管理:src下創建store文件用於vuex狀態管理
11.1 index.js入口文件的管理: 然後在main.js中引入import store from './store/index.js',然後全局設置:
import Vue from 'vue'
import Vuex from 'vuex'
import state from "./state" //全局狀態參數管理
import getters from "./getters" //相當於計算屬性,對state參數的計算
import mutations from "./mutations"
import actions from "./actions"
import user from "./mudule/user.js" //單獨某個模塊如用戶模塊user的狀態管理
Vue.use(Vuex)
export default new Vuex.Store({
state , //es6語法相當於state:state
getters,
mutations ,
actions,
mudules:{
user
},
plugins:[saveInLocal] //加載插件
})
11.2 state.js全局狀態參數管理:
定義全局參數:
const state={
appName:"我是全局參數,在組件內都可傳遞值"
}
export default state
調用全局參數值:
方法1:
在某個組件內{{this.$store.state.appName}}就可獲取該值了,或通過可在該組件寫入到計算屬性中computed
<p>{{appName}}</p>
computed:{
appName(){
return this.$store.state.appName
}
}
方法2:
<p>{{appName}}</p>
import {mapState} from "vuex"
computed:{
...mapState(["appName"]) //這2種寫法一樣。...表示展開一個對象。
// ...mapState({
// appName:state=>state.appName
// })
}
11.3 user.js 獲取模塊中user.js裏面的狀態參數管理:
定義單獨某個模塊中全局參數:
const state={
userName:"我是user模塊的參數值"
}
const mutations={}
const actions={}
export default{
state,
mutations,
actions
}
調用單獨某個模塊全局參數值:
方法1:
在某個組件內{{this.$store.state.user.userName}}就可獲取該值了,或通過可在該組件寫入到計算屬性中computed
<p>{{userName}}</p>
computed:{
userName(){
return this.$store.state.user.userName
}
}
方法2:
<p>{{userName}}</p>
import {mapState} from "vuex"
computed:{
...mapState(["userName"]) //這3種寫法一樣。...表示展開一個對象。
// ...mapState({
// userName:state=>state.user.userName
// })
// ...mapState("user",{ //傳入模塊名
// userName:state=>state.userName
// })
}
注意:在模塊狀態管理中如果有命令空間,即
export default{
namespaced:true, //設置命名空間爲true,使得模塊更加密閉,不受到外界的干擾
state,
mutations,
actions
}
方法3:
<p>{{userName}}</p>
import {createNamespacedHelpers} from "vuex"
const {mapState}=createNamespacedHelpers("user") //參數user是命令空間的名稱(模塊名,user.js)
computed:{
...mapState(["userName"]) //這2種寫法一樣。...表示展開一個對象。
// ...mapState({ //不許要在傳入模塊名了
// userName:state=>state.userName
// })
}
11.4 getters.js相當於組件的computed計算屬性,是對state狀態的計算處理。 在模塊如user.js中使用getters和使用state方法一樣
定義getters(計算屬性):
const getters={
appNameVersion:(state)=>{ //依賴於state.js
return state.appName+"v2.0"
}
}
export default getters
調用getters(計算屬性的結果):
方法1:
在某個組件內{{this.$store.getters.appNameVersion}}就可獲取該值了,或通過可在該組件寫入到計算屬性中computed
<p>{{appNameVersion}}</p>
computed:{
appNameVersion(){
return this.$store.getters.appNameVersion
}
}
方法2:
<p>{{appNameVersion}}</p>
import {mapGetters} from "vuex"
computed:{
...mapGetters(["appNameVersion"]) //這3種寫法一樣。...表示展開一個對象。
// ...mapGetters({
// userName:state=>state.appNameVersion
// })
}
11.5 mutations.js修改state狀態參數的值;
在mutations定義修改state的事件:
import vue from "vue"
const mutations={
set_app_name(state,params){ //state表示store/state.js,params是要修改state狀態中參數的新值,可能是對象,或字符串
state.appName=params //參數時字符串
//state.appName=params.appName //參數時對象
},
set_app_version(state){ //如果stata.js中沒改屬性參數,這個表示給state.js中添加version並賦值v2.0
vue.set(state,"version","v2.0")
}
}
export default mutations
在組件裏調用上面定義的事件:
方法1:
this.$store.commit("set_app_name","我是state.js裏新修改的appName的值") //參數是字符串
this.$store.commit("set_app_name",{appName:"我是state.js裏新修改的appName的值"}) //參數是對象
this.$store.commit({type:"set_app_name",appName:"我是state.js裏新修改的appName的值"}) //參數是對象,且事件也包含在對象裏
方法2:
在組件的方法裏
import {mapMutations} from "vuex"
methods(){
...mapMutations([
"set_app_name",
"set_app_version"
]),
Handle(){
this.set_app_version("newAppName");
this.set_app_name()
}
}
注意:mutations和getters和actions在模塊裏面是在模塊裏且沒有命令空間限制,會默認將模塊中的mutations和getters和actions註冊在全局store文件下的mutations.js和getters.js和actions.js,如下寫,且不需要傳入模塊名,如"user"
...mapMutations([
"set_app_name",
"set_app_version"
]),
但是如果有命令空間限制namespaced:true, //設置命名空間爲true,使得模塊更加密閉,不受到外界的干擾
...mapMutations("user",[ //需要傳入模塊
"set_app_name",
"set_app_version"
]),
11.6 action.js異步修改state.js的狀態值,如通過獲取後臺數據,將state.js的值修改成後臺獲取的數據:
定義異步修改狀態值的方法:
import {getAppName} from "@/api/app.js"
const action={ //異步操作狀態的改變,如通過接受接口的api返回的數據,從而改變state.js的狀態值
// updateAppName({commit}){ //es6寫法:{commit},相當於func(obj){const commit=obj.commit}
// getAppName().then(res=>{
// console.log(res)
// const {info:{appName}}=res; //es6的
// commit("set_app_name",appName);
// commit("set_app_name",res.info.appName); //通過commit關聯mutations.js中的是set_app_name()方法,從而異步修改state.js狀態值
// }).catch(err){
// console.log(err)
// }
//}
async updateAppName({commit}){ //es8的語法,與上面執行結果一樣,只是將異步變成同步。
try{
const {info:{appName}} =await getAppName()
commit("set_app_name",appName);
}catch(err){
console.log(err)
}
}
}
export default action
調用異步修改的狀態值的方法:
方法1:
mport {mapAction} from "vuex"
methods(){
...mapAction([
"updateAppName"
]),
Handle(){
this.updateAppName()
}
}
方法2:
this.$store.dispatch("updateAppName",val)
11.7 在模塊中定義 action方法
user.js中:
const state={}
const mutations={}
const actions={
updateUserName({commit,state,rootState,dispatch}){ //commit用於提交到mutation,state值當前文件下state,rootState值store文件夾下state.js
rootState.appName
}
}
export default{
state,
mutations,
actions
}
11.8 api/app.js模擬後臺接口返回數據:
export const getAppName=()=>{
return new Promise((resolve,reject)=>{
const err=null;
setTimeout(()=>{ //模擬接口操作請求
if(!err) resolve({code:200,info:{appName:"newAppName"}})
else reject(err)
})
})
}
12. vuex進階-store插件----持久本地存儲:
新建文件:store/plugin/saveInLocal.js封裝:定義一個持久化存在在本地的state狀態管理的插件,即使每次刷新,也不會影響修改後的值 。
export default state=>{
console.log('store初始化時執行該函數');
if(localStorage.state){store.replaceState(JSON.stringify(localStorage.state))} //如此本地已經存儲了,每次刷新頁面(即初始化store時),
//就用mutation提交後的本地存儲替換提交前的
store.subscribe((mutation,state)=>{
console.log("每次有提交mutation時就執行"); //每次提交mutation時,時都將state值存儲在本地
localStorage.state=JSON.stringify(state); //state 是一個對象,轉換爲json字符串,存儲在本地
})
}
在store/index.js中引入:
import saveInLocal from './plugin/saveInLocal.js'
export default new Vuex.Store({
plugins:[saveInLocal] //加載插件
})
13. store下的嚴格模式:在嚴格模式下,不能直接修改state裏面的值(this.$store.state.user.userName="newName"),需要在mutation提交中修改,否則會報錯(雖然也會修改)
在store/index.js下:
export default new Vuex.Store({
strict:true, //是否開啓嚴格模式,process.env.NODE_ENV ==="devalopment" ,在開發環境下是嚴格模式,否則不是
state , //es6語法相當於state:state
mutations ,
getters,
actions,
mudules:{
user
},
plugins:[saveInLocal] //加載插件
})
14. v-model:如果綁定的屬性不是組件data(){return{} }裏面的屬性,也是state裏面的屬性,就存在問題,因爲state裏面的屬性時需要在mutation中修改。
v-model:的本質就是:在組件上綁定一個屬性傳值,再綁定一個@input事件監聽,當輸入的值有變化時,就直接替換掉原來的值,
解決雙休綁定state值的方法:
方法1:
<input :value="stateVal" @input="changeStateVal"/>
...mapMutations({"SET_STATE_VALUE"})
changeStateVal(val){
this.SET_STATE_VALUE(val) //在mutations.js中設置:SET_STATE_VALUE(state,val{state.stateVal=val} ,在state.js中設置stateVal:"abc"
}
方法2:
<input v-model="stateVal"/>
...mapMutations({"SET_STATE_VALUE"})
computed:{
stateVal:{ //計算屬性裏stateVal是對象不是方法,有set()和get()方法
get(){
return this.$store.state.stateVal;
},
set(val){
this.SET_STATE_VALUE(val)
}
}
}
15. ajax請求:
15.1解決跨域問題:
方法1:在vue.config.js裏面設置跨域代理請求:
module.exports = {
lintOnSave:false, //是否校驗eslint
productionSourceMap:false, //打包時是否生成.map文件
outputDir: 'www', //打包輸出的文件名
publicPath: './', //打包輸出的位置
devServer: { //跨域代理
open: true,
host: '0.0.0.0',
port: 8082,
https: false,
hotOnly: false,
proxy: { //可設置多個跨域代理
'/api': {//以/api開始的路由的baseUrl由target:'http://xxx.xxx.x.xxx:8083'代理
target: 'http://xxx.xxx.x.xxx:8083',
changeOrigin: true,
ws: false,
secure: false,
pathRewrite:{
'^/api':''
}
},
'/ace': {//以/ace開始的路由的baseUrl由target:'http://xxx.xxx.x.xxx:123'代理
target: 'http://xxx.xxx.x.xxx:123',
changeOrigin: true,
ws: false,
secure: false,
pathRewrite:{
'^/ace':''
}
}
}
}
}
在發送axios請求裏執行:
axios.post("/login",{
uname:"Ace",
upwd:"1234"
}).then(data=>{
//返回數據的處理
})
方法二:在後臺設置跨越。
在後臺(如node的app.js)設置跨域:
app.all("*",(req,res,next)=>{ //*代表所有請求。
req.header("Access-Contral-Allow-Origin","*");
req.header("Access-Contral-Allow-Headers","X-Requested-Width,Content-Type");
req.header("Access-Contral-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
next();
})
在發送axios請求裏執行:
axios.post("http://localhost:3000/login",{
uname:"Ace",
upwd:"1234"
}).then(data=>{
})
15.2 封裝axios:
15.2.1 請求/響應攔截:創建文件lib/axios.js:
/*
* 1. 引入axios,創建一個類,封裝新的axios。
* 2. "@/config/index.js" export const baseURL=process.env.NODE_ENV==='procution'?'http://127.207.1.460':'' ;
* */
import axios from "axios"
import iview from "iview"
class HttpRequest{
constructor(baseUrl){ //創建constructor方法,設置默認值
this.baseUrl=baseUrl;
this.queue={}; //隊列空對象,用於存放所有未請求的url,當隊列爲空時,表示所有url請求完,這樣可以只需要一次添加loading加載效果,不需要重複添加loading效果
}
getInsideConfig(){ //創建一個方法,配置全局通用配置如url,methods,headers等,返回一個內部的配置
const config={
baseUrl:this.baseUrl,
headers:{
'platform':'pc',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}
return config
}
distroy (url) {
delete this.queue[url]
if (!Object.keys(this.queue).length) {
iview.Spin.hide()
}
}
interceptors(instance,url){ //攔截器,參數axios的實例 ,url是用於存放queue空隊列中
instance.interceptors.request.use(config=>{ //axios請求攔截器,config是請求成功時的處理函數,error是請求出現錯誤時的處理函數
//添加請求前的控制,如添加loading....加載效果,如iview的Spin標籤
if(!Object.keys(this.queue).length){ //如果this.queue隊列所有的Key值組成的數組爲空的情況下,即
iview.Spin.show() //顯示iview中的加載動畫效果
}
this.queue[url]=true; //將請求的url放入隊列中
return config
},error=>{
return Promise.reject(error) //請求失敗時,直接拋出這個錯誤
})
instance.interceptors.response.use(res=>{ //axios響應攔截器,對返回的結果的處理篩選
delete this.queue[url] //響應成功後就刪除該隊列對應的url
this.distroy(url)
return res
// const {data,status}=res //對響應結果進行篩選,只需要data和status值
// return {data,status}
},error=>{
iview.Spin.hide()
this.distroy(url)
delete this.queue[url] //響應失敗後就刪除該隊列對應的url
return Promise.reject(error) //響應失敗時,直接拋出這個錯誤
})
}
request(options){ //創建一個請求request方法,options是單獨傳入請求的配置 如參數
const instance=axios.create(); //創建一個axios實例
options=Object.assign(this.getInsideConfig(),options); //將內部的配置與傳入的配置合併成新的options對象
this.interceptors(instance,options.url); //axios實例爲參數的攔截器
return instance(options); //返回axios的實例,配置參數是合併後的參數
}
}
export default HttpRequest
15.2.2 在config/index.js配置基礎信息:用於存放項目的配置url。
export const baseUrl=process.env.NODE.ENV==="production"
?"http://www.ace.com" //如果是運行環境,就寫運行環境的接口
:"" //如果是在開發環境下,且已經設置了本地跨域代理 devServer:{proxy:'http://localhost:4000'},
//這裏設置空字符串,如果沒有設置本地跨域代理這裏就寫'http://localhost:4000'
15.2.3 在api/index.js實例化axios
import HttpRequest from "@/lib/axios"
const axios=new HttpRequest() //創建一個HttpRequest實例
export default axios
15.2.4 在api/user.js中定義login.vue組件的登錄接口:
import axios from "./index.js"
import iView from 'iview'
export var getUserInfo=({name})=>{
return axios.request({
url:"/login",
methods:"post",
timeout: 30000,
data:{
name:name
}
})
}
export var sendAxiosHttp=(baseUrl,sendMethod,timeOut,params,func)=>{
axios.request({
url:baseUrl,
methods:"post",
timeout: 30000,
params:params
}).then(data=>{
if(data.status==200){
var dataVal=data.data;
if(dataVal.returnCode=="0"){ //成功獲取數據
func(data)
}else if(dataVal.returnCode=="-9995"){ //後臺判斷登錄超時,重新登錄
var host=window.location.protocol+"//"+window.location.host;
window.location.href = host + "/login"; //xxx/index.html這是發佈的時候默認的登錄頁面
return;
}else{
iView.Message.info(dataVal.returnMessage)
}
}else{
iView.Message.info("登錄接口,網絡故障!")
}}).catch( (error)=> {
alert(error);
});
}
15.2.5 在login.vue組件裏引入api/user.js中定義好的登錄接口方法,然後調用:
import {getUserInfo} from "@/api/user.js"
getInfo(){
getUserInfo({uname:"Ace"}).then(res=>{
console.log(res)
})
}
16. 組件的ID命名,插槽的實現,DOM獲取,以及父組件調用子組件的方法:
子:
<div>
<slot name="slot1"></slot>
<span :id="myId" ref="child"> 我是子組件</span>
<slot name="slot2"></slot>
</div>
父:
<div>
<v-child ref="childSpan"></v-child>
</div>
16.1 如何給組件命名id不會衝突其他組件的id名:this._uid在項目中,每個組件都有獨一無二的_uid,給裏面組件命名時,帶上這個獨一無二的_uid就會避免與其他組件命名衝突。
computed:{
myId(){
return `child_${this._uid}`
}
}
16.2 怎麼獲取子組件dom裏面的內容(原生js獲取和ref獲取):
ref獲取:this.$refs.child.innerText
原生js獲取:document.getElementById(myId).innerText
16.3 父組件怎麼調用子組件的方法:
this.$refs.childSpan.getChildInfo()
17. vue中操作dom元素:通過數據操作dom的width/height/top/bottom....
17.1 通過數據操作dom的width/height/top/bottom....
<div ref="div" :style={left:divLeft;width:`${divWidth}px` }>
<span @mousedown="downHandle"></span>
</div>
computed:{
divLeft(){
return ``
},
divWidth(){
return `clac(30% - 4px)` //css3的屬性clac()計算出百分比和px的結果, 符號前後必須空格
}
}
17.2 鼠標按下移動事件:
downHandle(e){
document.addEventListener("mousemove/mouseup",mousemoveHandle/mouseupHanlde) ; //給doucment綁定鼠標移動、鬆開事件
e.pageX //是獲取觸發downHandle的是鼠標距離頁面左邊的距離
this.$refs.div.getBoundingClientRect() ; //返回ref='div'元素domwidth,height,top,bottom,right....等信息的對象。
}
18. 在組件引入外部css的3種方法:
在script裏面:import "../index.css"
在style裏面:@import "../index.css"
在style裏面:@import url("../index.css")