大家好,我是前端嵐楓,今天主要跟大家分享我整理的筆記2021前端面試題系列:vue傳值方式、vuex、router、vue服務器端渲染、多頁面、權限、開發環境跨域方案等等,此方面內容在我們的工作中常用到, 也是面試官經常提問的問題,希望下面文章對大家有所幫助。
vue 傳值方式
vue傳值‘
- 父 子 傳值 使用props接受
- 子 父 傳值 父親寫事件函數 子 $emit觸發 傳值
- 兄弟傳值 $bus 中轉站
- 如果組件之間 關係很遠 是很多組件都要用的值 vuex
- provide, inject注入方式
vuex 就是一個全局狀態數據管理 簡單來說 他的數據類似全局變量 哪個組件都可以使用
在項目中使用vuex
- 下載 vuex 包 並導入 use一下
import Vuex from 'vuex'
Vue.use(Vuex)
- 需要new 一下 寫上全局數據
// store
new Vuex.Store({
state: {
count:1 //這個count 就是全局的數據
},
mutations: {
},
actions: {
}
})
- 需要掛載到new vue上
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
<template>
<div class="home">
<!-- home啊
<hr>
{{$store.state.m1.m1Name}}
<button @click="add">點擊</button> -->
<!-- <hr> -->
<!-- <my-swiper :list="list"></my-swiper> -->
<button @click="getAll">發送請求</button>
home組件啊
<hr>
<h1>count的值:{{$store.state.count}}</h1>
<button @click="addCount">讓全局count+1</button>
<hr>
<h2>m1下的全局數據 {{$store.state.m1.m1Name}} </h2>
<button @click="add">點擊修改某個模塊的數據</button>
<!-- 3 哪個組件要用 就直接 使用註冊的組件標籤名 -->
<hr>
<!-- 用組件 傳了list數據進去 -->
<my-swiper :list="list"></my-swiper>
</div>
</template>
<script>
// 要在組件使用全局數據
// 1 在html範圍 直接 $store.state.名字
// 2 在js範圍 this.$store.state.名字
// @ is an alias to /src
// import HelloWorld from '@/components/HelloWorld.vue'
import { mapMutations , mapActions ,mapState } from 'vuex'
export default {
name: 'Home',
components: {
// HelloWorld
},
data(){
return {
list:[
{id:1,src:'http://122.51.238.153/images/1.jpg'},
{id:2,src:'http://122.51.238.153/images/2.jpg'},
{id:3,src:'http://122.51.238.153/images/3.jpg'},
{id:4,src:'http://122.51.238.153/images/4.jpg'}
]
}
},
created(){
console.log('created')
console.log('store',this.$store)
},
mounted(){
console.log("home 的 mounted")
},
methods:{
// 這句話的意思是 直接 解構出 全局 m1模塊下的 loginMutation
// 把loginMutation 放到this上 並且幫你寫好了 commit
// 相當於幫你簡化了代碼
...mapMutations('m1', ['loginMutation']),
//不是modules的直接寫 ...mapMutations( ['loginMutation'])
add(){
// console.log('add',this)
// console.log('add',this.$route.meta)
// this.$store.commit("m1/loginMutation")
// 或者下面的 先mapMutations 相當於幫你寫了commit
this.loginMutation()
// this.$store.commit("m1/loginMutation")
// 和剛剛的思路 就是加上一個模塊前綴 m1/
// this.$store.dispatch("m1/loginAction")
},
async getAll(){
// http://localhost:8080/
// 請求 http://122.51.238.153/getok.php
// let res=await this.$http.get("http://122.51.238.153/getok.php")
// console.log('res',res)
let res=await this.$http.get("/api/getok.php")
console.log('res',res)
},
addCount(){
// 讓全局數據count+1
// 1 正常情況
// dispatch 觸發action
// -》commit觸發mutation
// -》在mutation修改全局數據
//2 其他情況 可以直接跳過action 但是必須mutation修改
// console.log('$store',this.$store)
// this.$store.dispatch( 'countAction' )
this.$store.commit("countMutation")
}
}
}
</script>
這個步驟是寫死的 你可以記一下 下載使用腳手架直接就可以選vuex
他的使用邏輯是什麼?
在 store裏面的 state 寫的數據 是全局數據 所有組件都可以使用
使用邏輯
操作全局vuex的 state數據
正常情況 必須 dispatch (action)--->action去commit觸發 mutation--》mutation裏面才能修改state全局數據
action--->mutation--->修改state
其他情況 你也可以跳過 action 去 直接 commit mutation--》修改state全局數據
vuex怎麼合理規範管理數據,及mutations和actions區別
解析: 此題考查 vuex中數據的管理和數據結構的設計,還有mutations 和actions的區別
**解答**
: 首先要明確一個特別重要的原則, 就是 不是所有的數據都要放在vuex中, 因爲vuex有一句名言:假如你並不知道爲什麼要使用vuex,那就不要使用它 !那麼什麼樣式的數據需要放在vuex中呢 ? 首先這個數據肯定要被多個組件頻繁用到, 如果只是被一個組件 用到, 那完全沒有任何必要爲了使用vuex和使用vuex
舉例: 一個網站用戶的暱稱,賬號,資料,像這種系統級別的信息 隨時可能在業務中展示,使用, 如果在組件中存儲, 那麼要獲取N次, 所以**系統級別的數據**
是需要放置在vuex中的, 那麼系統級別數據 也不能所以的放置,爲了讓數據看着更有層級結構感,可以按照像下面這樣設計,
{
// 系統消息
system: {
user: {},
setting: {}
}
}
上面這種結構,一看 便知道我們應該哪裏獲取系統數據即 設置數據
如果有些業務數據,也需要共享,最好按照模塊的具體業務含義分類 , 比如下面
{
// 系統消息
system: {
user: {},
setting: {}
},
product: {
productList: [], // 商品信息列表
productOrders: [] // 商品訂單啊列表
}
}
如上圖代碼所示,我們很清晰的能夠分清楚 每個模塊的數據,這樣不會導致數據管理的混亂
mutations和 actions 的區別
不同於redux只有一個action, vuex單獨拎出了一個mutations, 它認爲 更新數據必須是同步的, 也就是隻要調用了 提交數據方法, 在mutation裏面纔可以修改數據
那麼如果我們想做 異步請求,怎麼做? 這裏 vuex提供了專門做異步請求的模塊,action, 當然action中也可以做同步操作, 只不過 分工更加明確, 所有的數據操作 不論是同步還是異步 都可以在action中完成,
mutation只負責接收狀態, 同步完成
**數據快照**
所以可以認爲
state => 負責存儲狀態
mutations => 負責同步更新狀態
actions => 負責獲取 處理數據(如果有異步操作必須在action處理 再到mutation), 提交到mutation進行狀態更新
vuex模塊化module管理,使用的時候有注意事項
pro111
分析: 此題考查 當vuex維護的數據越來越複雜的時候, 模塊化的解決方案
**解析**
:使用單一的狀態樹,應用的所有狀態都會**集中在一個比較大的對象**
上面,隨着項目需求的不斷增加,狀態樹也會變得越來越臃腫,增加了狀態樹維護的複雜度,而且代碼變得沉長;因此我們需要**modules(模塊化)**
來爲我們的狀態樹**分隔**
成不同的模塊,每個模塊擁有自己的state,getters,mutations,actions;而且允許每個module裏面嵌套子module;如下:
store
├── index.js # 我們組裝模塊並導出 store 的地方
├── actions.js # 根級別的 action
├── mutations.js # 根級別的 mutation
├── state.js # 根級別的 state
└── modules
├── module1.js # 模塊1的state樹
└── module2.js # 模塊2的state樹
上面的設計中, 每個vuex子模塊都可以定義 state/mutations/actions
需要注意的是 我們原來使用
**vuex輔助函數**
mapMutations/mapActions 引入的是 全局的的mutations 和actions , 並且我們vuex子模塊 也就是module1,module2 ... 這些模塊的aciton /mutation 也註冊了全局,也就是如果 module1 中定義了 loginMutation, module2中也定義了 loginMutation, 此時, mutation就衝突了
如果重名,就報錯了.....
如果不想衝突, 各個模塊管理自己的action 和 mutation ,需要 給我們的子模塊一個 屬性
**namespaced: true**
那麼 組件中怎麼使用子模塊的action 和 mutations
// 你可以將模塊的空間名稱字符串作爲第一個參數傳遞給上述函數,這樣所有綁定都會自動將該模塊作爲上下文
methods:{
...mapMutations('m1', ['loginMutation']),
add(){
console.log('add',this)
// this.$store.commit("m1/loginMutation")
// 或者下面的 先mapMutations 相當於幫你寫了commit
// this.loginMutation()
}
}
// 這句話的意思是 直接 解構出 全局 m1模塊下的 loginMutation
// 把loginMutation 放到this上 並且幫你寫好了 commit
// 相當於幫你簡化了代碼
...mapMutations('m1', ['loginMutation']),
//不是modules的直接寫 ...mapMutations( ['loginMutaton])
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 1 下載vuex 導入 use一下
// 2 new Vuex.Store
// 3 掛載到new vue上
export default new Vuex.Store({
state: {
// 在這裏寫的就是所有組件都能有 全局數據了
// 名字:值
// 如果我1000個全局數據 有可能重名
count:100
},
mutations: {
countMutation(state){
// state 就是那個全局state
console.log('mutation觸發了',state)
state.count++
}
},
actions: {
// action對應的函數
countAction(obj){
console.log('action觸發了',obj)
// obj對象 裏面有commit
obj.commit("countMutation")
}
},
// modules 在store全局數據 是可以來分模塊管理的
// 他可以用來區分每個不同模塊的數據
// 15:10 上課
modules: {
// 模塊:{ 一套state action mutation }
m1: {
namespaced: true,//開啓命名空間大白話 他是m1下的 不會影響其他人
// 模塊內容(module assets)
state: { // 模塊內的狀態已經是嵌套的了,使用 `namespaced` 屬性不會對其產生影響
m1Name:"我是m1模塊的m1Name"
},
actions: {
loginAction () {
console.log('m1的action')
} // -> dispatch('m1/loginAction')
},
mutations: {
loginMutation () {
console.log('loginMutation-執行啦')
} // -> commit('m1/loginMutation')
}
},
home:{
namespaced: true,
state:{
count:1
}
},
about:{
namespaced: true,
state:{
count:100
},
actions:{
}
},
}
})
此題具體考查 Vuex雖然是一個公共狀態, 但是公共狀態還可以切分成若干個子狀態模塊, 也就是moduels,
解決當我們的狀態樹過於龐大和複雜時的一種解決方案. 但是筆者認爲, 一旦用了vuex, 幾乎 就認定該項目是較爲複雜的
封裝Vue組件的步驟
組件是什麼?組件是一段功能代碼 ---大白話 就是一段html +js +css 你可以重複使用
封裝輪播圖 -
1 新建vue組件
2 Vue.component註冊組件
3 在其他組件使用 標籤名
參數: 可以傳入數據 使用props接受 比如 數組 定時器時間等
分析: 本題考查 對於Vue組件化開發的熟練程度
解析: 首先明確 組件是本質是什麼?
組件就是一個單位的HTML結構 + 數據邏輯 + 樣式的 操作單元
Vue的組件 繼承自Vue對象, Vue對象中的所有的屬性和方法,組件可自動繼承.
- 組件的要素 template => 作爲頁面的模板結構
- script => 作爲數據及邏輯的部分
- style => 作爲該組件部分的樣式部分
要封裝一個組件,首先要明確該組件要做的具體業務和需求, 什麼樣的體驗特徵, 完成什麼樣的交互, 處理什麼樣的數據
明確上述要求之後, 着手模板的結構設計及搭建,也就是 常說的html結構部分, 先完成 靜態的html結構
結構完成, 着手數據結構的設計及開發, 數據結構一般存儲於組件的data屬性 或者 vuex 狀態共享的數據結構
數據設計完成/ 結構完成 接下來 完成數據和模塊的結合 , 利用vuejs中指令和 插值表達式的特性 將靜態結構
**動態化**
展現的部分完成, 接下來完成
**交互部分**
,即利用 組件的生命週期的鉤子函數 和 事件驅動 來完成 邏輯及數據的處理與操作
最後組件完成,進行測試及使用
常用的組件屬性 => data/ methods/filters/ components/watch/created/mounted/beforeDestroy/computed/props
常用組件指令: v-if/v-on/v-bind/v-model/v-text/v-once
Vue中的data是以函數的形式還是對象的形式表示
分析: 此題考查 data的存在形式
**解析**
: 我們在初步學習Vue實例化的時候寫的代碼時這個樣子
上面代碼中的data 是一個對象, 但是我們在開發組件的時候要求data必須是一個帶返回值的函數
new Vue({
el: '#app',
data: {
name: 'hello world'
}
})
export default {
data () {
return {
name: '張三'
}
}
}
爲什麼組件要求必須是帶返回值的函數? 因爲 我們的組件在實例化的時候, 會直接將data數據作用在視圖上,
對組件實例化, 會導致我們組件的data數據進行共享, 好比 現在有兩輛新車, 你一踩油門, 不光你的車往前車,另輛車也和你一樣往前衝! 這顯然不符合我們的程序設計要求, 我們希望組件內部的數據是相互獨立的,且互不響應,所以 採用
**return {}**
每個組件實例都返回新對象實例的形式,保證每個組件實例的唯一性
使用Proxy代理跨域
pro111
什麼是跨域?
域名 協議 ip地址 端口 任何一個不一樣 就跨域
解決跨域?
1 jsonp ---使用script的src發送 只能get 請求
2 cors 後臺設置允許跨域 需要後臺設置 允許跨域
所有後臺語言 都可以設置
3 服務器代理
重點 現在 前端 vue 框架 是可以自己設置 服務器代理的 proxy
配置:vue在 vue.config.js 可以配置重寫webpack
分析: 本題考查如何解決跨域問題
解析: 解決跨域問題的方式有幾種,
- 一種是服務端設置 , 但這種方式依賴服務端的設置,在前後分離的場景下 ,不太方便
- 還有一種jsonp形式, 可以利用script標籤 的特性解決同源策略帶來的跨域問題,但這是這種方案對於請求的類型有限制,只能get
- 還有一種就可以在開發環境(本地調試)期間,進行代理, 說白了 就是通過 在本地通過nodejs 啓動一個微型服務,
- 然後我們先請求我們的微型服務, 微型服務是服務端, 服務端代我們去請求我們想要的跨域地址, 因爲服務端是不受同源策略的限制的, 具體到開發中,打包工具webpack集成了代理的功能,可以採用配置webpack的方式進行解決, 但是這種僅限於 本地開發期間, 等項目上線時,還是需要另擇代理 nginx
Access-Control-Allow-Origin:*
Access-Control-Allow-Methods:"POST, GET, OPTIONS, DELETE"
以下爲webpack配置代理的配置
// vue.config.js
module.exports = {
// 修改的配置
devServer: {
proxy: {
'/api': {
target: 'http://122.51.238.153',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
}
target:接口域名;
changeOrigin: 如果設置爲true
,那麼本地會虛擬一個服務端接收你的請求並代你發送該請求;
pathRewrite:如果接口中是沒有api的,那就直接置空(如上)如果接口中有api,就需要寫成{‘^/api’:‘’}
上線瞭如果還有跨域 可以讓後臺設置 允許跨域
服務器代理流程圖
Vue中的watch如何深度監聽某個對象
分析: 此題考查Vue的選項watch的應用方式
解析: watch最基本的用法是
上面代碼中: 有個原則監聽誰,寫誰的名字,然後是對應的執行函數, 第一個參數爲最新的改變值,第二個值爲上一次改變的值, 注意: 除了監聽 data,也可以監聽計算屬性 或者一個 函數的計算結果
那怎麼深度監聽對象 ,兩種方式
- 字符串嵌套方式
- 啓用深度監聽方式
export default {
data () {
return {
name: '張三'
}
},
watch: {
name (newValue, oldValue) {
}
}
}
export default {
data () {
return {
a: {
b: {
c :'張三'
}
}
}
},
watch: {
"a.b.c": function (newValue, oldValue) {
}
}
}
export default {
data () {
return {
a: {
b: {
c :'張三'
}
}
}
},
watch: {
a: {
deep: true // deep 爲true 意味着開啓了深度監聽 a對象裏面任何數據變化都會觸發handler函數,
handler(){
// handler是一個固定寫法
}
}
}
}
Vue keep-alive使用
分析: 此題考查Vue中組件緩存的使用
解析: keep-alive是 Vue提供的一個全局組件, Vue的組件是有銷燬機制的,比如條件渲染, 路由跳轉時 組件都會經歷銷燬, 再次回到頁面時,又會回到 重生, 這一過程保證了生命週期鉤子函數各個過程都會在這一生命週期中執行.
但是,我們辛辛苦苦獲取的數據 滑動的頁面 會因爲組件的銷燬 重生 而 歸零,這影響了交互的體驗, 所以 keep-alvie出現了, 可以幫助我們緩存想要緩存的組件實例, 只用用keep-alive 包裹你想要緩存的組件實例, 這個時候, 組件創建之後,就不會再進行 銷燬, 組件數據和狀態得以保存
但是,沒有了銷燬,也就失去了重生的環節, 我們失去了 原有的鉤子函數, 所以keep-alive包裹的組件 都獲取了另外兩個事件 --如果緩存組件需要重新獲取數據
喚醒 activated重新喚醒休眠組件實例時 執行: 緩存的組件還提供了activated生命週期 可以在這裏面重新發送請求
休眠 deactivated組件實例進入休眠狀態時執行
但是我們不能緩存所有的組件實例, 如果是針對 組件容器 router-view 這個組件進行的緩存, 一般的策略是在路由的元信息 meta對象中設置是否緩存的標記, 然後根據標記決定是否進行緩存
<div id="app">
<keep-alive>
<!-- 裏面是當需要緩存時 -->
<router-view v-if="$route.meta.isAlive" />
</keep-alive>
<!-- 外面是不需要緩存時 -->
<router-view v-if="!$route.meta.isAlive" />
</div>
還有需要注意的問題是: 被緩存的組件中如果還有子組件, 那麼子組件也會一併擁有 激活和喚醒事件,並且這些事件會在同時執行
app.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<!-- <keep-alive> -->
<!-- 裏面是當需要緩存時 -->
<!-- <router-view v-if="$route.meta.isAlive" /> -->
<!-- </keep-alive> -->
<!-- 外面是不需要緩存時 -->
<!-- <router-view v-if="!$route.meta.isAlive" /> -->
<!-- 這樣寫的 組件顯示 就不會緩存
在路由裏面配置的meta 可以直接使用
$route獲取
現在我配置 了 home 不需要緩存
home 每次都重新創建--生命週期會走一遍
about需要緩存 --你再回到about組件 他不會重新創建一遍
-->
<router-view v-if="!$route.meta.isAlive"></router-view>
<!-- 如果組件需要緩存 用keep-alive 包裹起來 -->
<keep-alive>
<router-view v-if="$route.meta.isAlive"></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
created(){
// console.log('$route',this.$route)
}
}
</script>
<style lang="less">
*{
margin: 0;
padding: 0;
}
li{
list-style: none;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
</style>
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home,
// meta 標識符 也是路由配置裏面可以寫的
// 他可以用來 判斷一些操作
meta:{
// 名字:值
isAlive:false // 我想 isAlive false代表不緩存
// 需要緩存的就在這寫成 isAlive:true
}
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
meta:{
isAlive:true // about組件 需要緩存
}
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
需要緩存的組件? 最常見的事 列表(因爲很長 到詳情頁返回 又回到列表)
keep-alive圖說明
vue的雙向數據綁定原理是什麼
分析 :此題考查 Vue的MVVM原理
解答: Vue的雙向綁定原理其實就是MVVM的實現原理, Vuejs官網已經說明, 實際就是通過 Object.defineProperty方法 完成了對於Vue實例中數據的 劫持, 通過對於 data中數據 set的監聽,
然後通過觀察者模式, 通知 對應的綁定節點 進行節點數據更新, 完成數據驅動視圖的更新
同理, 通過對於節點的表單值改變事件的監聽, 執行對於數據的修改
簡單概述 : 通過Object.defineProperty 完成對於數據的劫持, 通過觀察者模式, 完成對於節點的數據更新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button id="btn">點擊修改獲取值</button>
<h1 id="con">
name的值是:{{name}}
</h1>
<script>
// Object.defineProperty() 原生js就自帶的方法
// vue 也是js 他就是一個封裝了 js的 庫而已
// Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
// vue的原理
// Object.defineProperty(對象,屬性名,{ get set 等配置 })
// data.name='李四' 設置值 就會觸發 set
// vue 原理
// vue 通過 原生js的 Object.defineProperty 監聽到了 我們寫的data數據
// 這個data裏面的數據 有修改 就會觸發 Object.defineProperty 裏面的 set
// 在set裏面 我們可以 獲取到最新的修改的值 去頁面上 正則匹配到對應地方 替換修改
// 你看過vue 源碼 如果沒看過就說 沒怎麼看過 只是瞭解了一下 稍微寫了一下
function Vue(){
this.data={
name:'zs'
}
// 那 vue的data 那麼多數據怎麼辦?
// vue裏面 就循環執行下面這段話 不就全部data 監聽到了嗎?
// for()
Object.defineProperty(this.data,'name',{
get:function(){
// 當獲取監聽對象的某個 值 就可以 執行get函數
console.log('get 獲取了值')
},
set:function(newval){ // 設置的新值
console.log('set 設置了值',newval)
// 當然vue 沒有這麼簡單去找 他寫了很多正則表達式去替換
// 但是思路是這個
// 我只需要 監聽到 name值改了 就去頁面修改 對應的地方就行 變成新值
let con=document.getElementById("con")
// con.innerHTML 獲取內容 name的值是:{{name}} .replace("查找的字符串","替換成這個")
let str=con.innerHTML.replace("{{name}}",newval)
// 重新修內容 innerHTML 是獲取內容 設置內容的
con.innerHTML=str
}
})
}
let vm=new Vue()
// vm.data
// console.log(data.name) ;// 獲取
// vue 的核心 如果數據改變了 那麼頁面就跟着改變成最新數據了
// 爲什麼vue可以知道你的數據更新了?
// 因爲vue 幫我監聽了 set 然後你只要設置值 就觸發set 只需要在set裏面找到頁面對應的數據修改就行
document.getElementById("btn").onclick=function(){
// data.name='小雷'
// console.log(data.name) ;// 這就是獲取值
vm.data.name='李四'
}
</script>
</body>
</html>
頁面刷新了之後vuex中的數據消失怎麼解決
分析:此題考查 如果將vuex數據進行本地持久化
解析: vuex數據位於內存, 頁面的刷新重置會導致數據的歸零,也就是所謂的消失, 本地持久化可以解決這個問題.本地持久化用到的技術也就是 本次存儲 sesstionStorage 或者 localStorage ,
如果需要保持的更長久 ,瀏覽器關掉 再打開依然存有數據,需要使用後者
實施方案: state的持久化 也就是分別需要在 state數據初始化 /更新 的時候 進行讀取和設置本地存儲操作
代碼如下
export default new Vuex.store({
state: {
user: localStorge.getItem('user') // 初始化時讀取 本地存儲
},
mutations: {
updateUser (state, payload) {
state.user = payload.user
localStoregae.setItem('user',payload.user) // 數據更新時 設置本地存儲
}
}
})
vue做服務端渲染
分析: 爲什麼要做服務端渲染, 首先要明白 服務端渲染解決什麼問題
解析: vuejs 官網說的很明白, 要做服務端渲染首先必須是有對應的需求,即對 實時到達時間(頁面訪問時間)的絕對需求. 如果只是簡單的一個管理系統, 區區幾百毫秒的優化 顯得十分小題大做.
服務端渲染這裏 有一個成熟優秀的框架 nuxt.js , 正如next.js對於react,nuxt是vue服務端渲染的優秀解決方案
nuxt的出現可以讓渲染內容完全服務端化,解決seo不夠友好, 首屏渲染速度不夠迅速的問題,
但是這裏需要注意: 並不是所有頁面都需要服務端渲染, 因爲服務端渲染比重多大 對於服務器的訪問處理能力 要求也會急劇增大
步驟這個nuxt腳手架不需要安裝node,默認自帶
1 腳手架 npx create-nuxt-app <項目名>
2 yarn dev 啓動開發
上線
yarn build
yarn start
爲什麼使用nuxt.js?
普通vue項目 打開地址查看源代碼 是空 他主要是用切換的時候纔會有內容
nuxt.js項目 查看源代碼 他是已經渲染好了很多html了
- 這個和seo 搜索引擎 比如百度 他會去 找到所有網站 挨個看你的網站內容 有沒有 好不好--爬蟲
如果普通vue 項目 是 空的 那麼 就沒有內容 seo不行 網站就很垃圾
如果nuxt.js項目 有內容 就比較好 利於seo
- 普通vue項目 內容打包到js了 那個js會很大 首頁就顯示很慢
如果nuxt.js項目 只是一些 js 其他的他服務器端就渲染好了 稍微快
vue單頁面應用渲染是從服務器獲取所需js,在客戶端將其解析生成html掛載於
id爲app的DOM元素上,這樣會存在兩大問題。
- 由於資源請求量大,造成網站首屏加載緩慢,不利於用戶體驗。
- 由於頁面內容通過js插入,對於內容性網站來說,搜索引擎無法抓取網站內容,不利於SEO。
Nuxt.js 是一個基於Vue.js的通用應用框架,預設了利用Vue.js開發服務端渲染的應用所需要的各種配置。可以將html在服務端渲染,合成完整的html文件再輸出到瀏覽器。
除此之外,nuxt與vue還有一些其他方面的區別。
-
路由
nuxt按照 pages 文件夾的目錄結構自動生成路由
vue需在 src/router/index.js 手動配置路由 -
入口頁面
nuxt頁面入口爲 layouts/default.vue
vue頁面入口爲 src/App.vue -
webpack配置
nuxt內置webpack,允許根據服務端需求,在 nuxt.config.js 中的build屬性自定義構建webpack的配置,覆蓋默認配置
vue關於webpack的配置存放在build文件夾下 - asyncData 裏面發送ajax 這個東西跟生命週期這些都是平級的
要理解asyncData方法執行時,其實是在服務端完成的,這個數據是在服務端渲染好了的
nuxt.js的ajax,你先別往你那個異步上去思考,其實這裏面所有的ajax最後都會形成頁面。你別想着,我一點按鈕,調用一個方法,然後再ajax去加載數據。因爲我們最後全部都會生成靜態,所以任何的獲取數據的操作,最後都會變成頁面的跳轉。
所以,官方給了一套寫法,你必須按照這個去寫,
並且這裏的ajax會再頁面渲染之前就執行。這個東西跟生命週期這些都是平級的。
1 npm install @nuxtjs/axios --save
2 .plugins目錄新建axios.js
import * as axios from 'axios'
let options ={}
//需要全路徑才能工作
if(process.server){
options.baseURL=http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}/api
}
export default axios.create(options)
3.Nuxt.config.js增加axios配置
modules:[
'@nuxtjs/axios'
],
4 使用 asyncData 裏面發送ajax 這個東西跟生命週期這些都是平級的 在頁面渲染之前
export default {
async asyncData({app}){
let res =await app.$axios({
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'get',
url: `http://test.yms.cn/testjson.asp`,
data: ''
})
// app.$axios
console.log('res',res.data)
return{
testData:res.data.title
}
},
created(){
console.log('nuxt reg組件')
}
}
下圖爲關於nuxt的簡單概述
vue-router傳參
分析:考查vue-router的傳值方式
解析 vue-router 傳值 可以通過 地址傳值
最簡單的就是url傳值, url傳值又兩種, params 和 query參數傳值
- params傳值 是指的動態路由傳值
- query傳值,指通過?後面的拼接參數傳值
{ path: '/user/:id' } // 定義一個路由參數
<router-link to="/user/123"></router-link> // 傳值
this.$route.params.id // 取值 params參數
<router-link to="/user?id=123"></router-link> // 傳值
this.$route.query.id // 取值 query參數
前端鑑權一般思路
以前講過 我們可以在axios的,請求攔截器裏面配置token
- 有些axios請求需要token,我們是可以陪着請求攔截器
- 有些頁面需要登錄才能看,我們也可以路由導航守衛, router.beforeEach,判斷token
- 導航菜單欄,一般後臺側邊欄,有不同的人登錄,根據角色權限看到的欄目不一樣
分析: 考查前後分離的鑑權思路
**解析**
: 首先要明白 爲什麼要在前端鑑權? 因爲傳統項目都是在後端鑑權, 然後通過進行攔截 跳轉 對應操作
因爲 我們做的並不是傳統的項目,而是前後分離項目,也就是前端項目和後端服務進行了**剝離**
, 後端沒有辦法用session來存儲你任意一個前端項目域名下的身份信息, 所以jwt 鑑權模式應運而生.
也就是後端不再提供會話的身份存儲,而是通過一個鑑權接口將用戶的身份,登錄時間,請求端口,協議頭..等等信息 組裝成一個加密的串 返給前端請求, 前端拿到了這個串,就可以認爲自己登錄成功
那麼這個**加密串**
就成了 前端用戶是否登錄的成功標誌, 這就是我們的token , 那麼在接下來的接口請求中,我們幾乎都要攜帶這個加密串,因爲它是**唯一**
能**證明我們身份**
的信息.
爲了方便,我們會一般在請求工具 axios(舉例)的攔截器中**統一注入token**
, 減少代碼的重複
token 同時具有時效性,我們也需要在此時對token過期進行處理,一旦出現過期的請求碼, 就需要進行 換取新token 或者重新登錄的解決方案
除此之外,我們還需要依據**有無加密串**
在前端對於某些頁面的訪問進行限制, 這個會用到我們的Vue-Router中的導航守衛.
vue 單頁項目涉及到多角色用戶權限問題,不同的角色用戶擁有不同的功能權限, 不同的功能權限對應的不同的頁面
一開始 有一些 默認的路由
登錄後 比如你是總經理 後臺會返回給前端 總經理能看見的 路由頁面地址 數組
前端在router.beforeEach 路由導航守衛裏面 拿到返回的地址 使用 router.addRouter 動態加上 這個項目路由就好了
routes= 後臺返回的 符合條件的 路由數據 類似我們自己寫的那個path 等等
this.$router.addRoutes(routes)
例子
router.beforeEach((to, from, next) => {
//判斷user信息是否已經獲取
if (token) {
//根據用戶的角色類型來生成對應的新路由
const newRouter = [{path:"/xxx" ...} ..]
//將新路由添加到路由中
router.addRoutes(newRouter)
//爲了正確渲染導航,將對應的新的路由添加到vuex中
渲染對應的側邊欄
}
})
獲取路由數組的流程圖
前端如何做?
- 寫一個所有的路由數組
- 根據後臺返回路由列表權限,拿着兩個數組對比,處理數據得到需要的路由數組
vue數據流 和 react數據流
vue,react數據流不是雙向的數據綁定,是單向的。
在vue 、React中數據流向是單向的,由父節點流向子節點,如果父節點的props發生了改變,那麼React會遞歸遍歷整個組件
父組件通過綁定 props 的方式,將數據傳遞給子組件,但是子組件自己並沒有權利修改這些數據,如果要修改,只能把修改這一個行爲通過 event 的方式報告給父組件,由父組件本身決定改如何處理數據。
vue 另一個概念 v-model雙向數據 無論數據改變,或是用戶操作,都能帶來互相的變動,自動更新。
如何在組件中監聽Vuex的數據變化
分析: 此題考查Vuex的應用及 Vue內部的監聽數據變化的機制
解答: 首先確定 Vuex是爲了解決什麼問題而出現的 ? Vuex是爲了解決組件間狀態共享而出現的一個框架.
其中有幾個要素 是組成Vuex的關鍵, state(狀態) mutations actions ,
- state 表示 需要共享的狀態數據
- mutations 表示 更改 state的方法集合 只能是同步更新 不能寫ajax等異步請求
- actions 如果需要做異步請求 可以在actions中發起 然後提交給 mutations mutation再做同步更新
也就是 state 負責管理狀態 , mutation負責同步更新狀態 action負責 異步獲取數據 同提交給mutation
所以 組件監聽Vuex數據變化 就是 監聽 Vuex中state的變化,
1. 我們可以在組件中通過組件的 watch方法來做, 因爲組件可以將state數據映射到 組件的計算屬性上,
然後 監聽 映射的計算屬性即可 代碼如下
// vuex中的state數據
state: {
count: 0
},
// A組件中映射 state數據到計算屬性
computed: {
...mapState(['count'])
}
// A組件監聽 count計算屬性的變化
watch: {
count () {
// 用本身的數據進行一下計數
this.changeCount++
}
}
2. vuex中store對象本身提供了watch
函數 ,可以利用該函數進行監聽
- watch(fn: Function, callback: Function, options?: Object): Function
響應式地偵聽 fn
的返回值,當值改變時調用回調函數。fn
接收 store 的 state 作爲第一個參數,其 getter 作爲第二個參數。最後接收一個可選的對象參數表示 Vue 的 [vm.$watch](https://cn.vuejs.org/v2/api/#vm-watch)
方法的參數。
代碼
created () {
this.$store.watch((state, getters) => {
return state.count
}, () => {
this.changeCount++
})
}
Vue單頁面和多頁面的使用
<>
分析: 首先分析,單頁面應用和 多頁面應用的根本區別
**解答**
: 單頁面即所有的模塊統統置於一個html文件之上,切換模塊,不會重新對html文件和資源進行再次請求,服務器不會對我們**換頁面**
的動作 產生任何反應, 所以我們感覺不到任何的刷新動作,速度和體驗很暢快多頁面應用 即多個html頁面 共同的使用, 可以認爲一個頁面即一個模塊,但是不排除 多個單頁應用混合到一起的組合情況 , 多頁面切換一定會造成 頁面資源的重新加載, 這也就意味着 如果 多頁面之間切換,一定會造成很數據的
**重置**
一個項目分成很多 小vue項目 你去其實也可以直接創建兩個項目
1 **新建多個頁面 每個頁面是一個單獨的小vue類型 **
2 配置 多入口頁面在vue.config.js裏寫上這些 重點是入口選擇對應頁面的main.js
// vue.config.js
module.exports = {
// 我配置完成 和 文件夾 匹配好
// 相當於 寫了兩套項目 vue代碼
// pages 配置 多頁面入口 index 和ui
// 配置和 你的文件夾匹配 兩套 文件
pages: {
index: {
// page 的入口
entry: "src/views/index/main.js",
// 模板來源
template: "public/index.html",
// 在 dist/index.html 的輸出
filename: "index.html",
// 當使用 title 選項時,
// template 中的 title 標籤需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: "Index Page"
},
ui: {
// page 的入口
entry: "src/views/ui/main.js",
// 模板來源
template: "public/ui.html",
// 在 dist/ui.html 的輸出
filename: "ui.html",
// 當使用 title 選項時,
// template 中的 title 標籤需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: "ui Page"
}
// 。。。。
}
};
3 public 寫上不同的渲染的 html
4 main.js 不同的入口 對應上自己的 根組件和 頁面元素
5 通過a標籤跳轉
<div id="app">
ui頁面啊啊啊
<a href="home.html">去home頁面</a>
</div>
關注:程序員石磊,獲取更多面試資料