vue-cli3.0技術棧筆記
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:
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:自定義設定檔案的配置,如跨域請求地址,路徑簡寫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中設定.router-enter/leave-to/active樣式*/
<transition-group name="router">
<router-view key="default"/>
<router-view key="email"/>
<router-view key="tel"/>
</transition-group>
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',然後全域性設定new Vue({store}).$mount('#app')
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
}
})
11.2 state.js全域性狀態引數管理:
定義全域性引數:
const state={
appName:"我是全域性引數,在元件內都可傳遞值"
}
export default state
呼叫全域性引數值:
方法1:
在某個元件內{{this.$store.state.appName}}就可獲取該值了,或通過可在該元件寫入到計算屬性中computed
computed:{
appName(){
return this.$store.state.appName
}
}
<p>{{appName}}</p>
方法2:
import {mapState} from "vuex"
computed:{
...mapState(["appName"]) //這2種寫法一樣。...表示展開一個物件。
// ...mapState({
// appName:state=>state.appName
// })
}
<p>{{appName}}</p>
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
computed:{
userName(){
return this.$store.state.user.userName
}
}
<p>{{userName}}</p>
方法2:
import {mapState} from "vuex"
computed:{
...mapState(["userName"]) //這3種寫法一樣。...表示展開一個物件。
// ...mapState({
// userName:state=>state.user.userName
// })
// ...mapState("user",{ //傳入模組名
// userName:state=>state.userName
// })
}
<p>{{userName}}</p>
注意:在模組狀態管理中如果有命令空間,即
export default{
namespaced:true, //設定名稱空間為true,使得模組更加密閉,不受到外界的干擾
state,
mutations,
actions
}
方法3:
import {createNamespacedHelpers} from "vuex"
const {mapState}=createNamespacedHelpers("user") //引數user是命令空間的名稱(模組名,user.js)
computed:{
...mapState(["userName"]) //這2種寫法一樣。...表示展開一個物件。
// ...mapState({ //不許要在傳入模組名了
// userName:state=>state.userName
// })
}
<p>{{userName}}</p>
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
computed:{
appNameVersion(){
return this.$store.getters.appNameVersion
}
}
<p>{{appNameVersion}}</p>
方法2:
import {mapGetters} from "vuex"
computed:{
...mapGetters(["appNameVersion"]) //這3種寫法一樣。...表示展開一個物件。
// ...mapGetters({
// userName:state=>state.appNameVersion
// })
}
<p>{{appNameVersion}}</p>
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:
import {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({
state , //es6語法相當於state:state
mutations ,
getters,
actions,
mudules:{
user
},
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裡面設定跨域代理請求:
devServer:{ //設定跨域,即將本檔案內任何沒有匹配到的靜態檔案,都指向跨域地址
//http://xxx.xxx.x.xxx:30337
proxy:'http://xxx.xxx.x.xxx:30337'
}
在傳送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'元素dom的width,height,top,bottom,right....等資訊的物件。
}
18. 在元件引入外部css的2種方法:
在script裡面:import "../index.css"
在style裡面:@import "../index.css"