1.mvvm 框架是什么?
定義:M:Model(服務器上的業務邏輯操作)? V:View(頁面)VM:ViewModel(Model與View之間核心樞紐,比如Vue.js)
大體上:vm層(視圖模型層)通過接口從后臺m層(model層)請求數據,vm層繼而和v(view層)實現數據的雙向綁定。
三者關系:Model與ViewModel之間的雙向關系
Model通過Ajax通信,發送數據給ViewModel。
ViewModel也可以通過Ajax通信,發送請求給Model。
ViewModel與View之間的雙向關系
ViewModel中的數據改變,可以同時改變View上的顯示內容。
View上的內容改變(比如輸入框中的內容),也可以同時改變ViewModel中對應的數據。
前端框架MVVM出現的最大意義是什么:
mvvm層實現了前后端更好的分離(前端需要的數據只需要請求后端的接口即可)
MVVM 的出現促進了 GUI 前端開發與后端業務邏輯的分離,極大地提高了前端開發效率。
MVVM用接口實現了前后端數據的通信,這樣可以使前后端之間的業務邏輯沒有什么關系。
MVVM在感覺上要比mvc模式前后端要分的更開
前端框架MVVM中的vm層是干嘛的
ViewModel 是由前端開發人員組織生成和維護的視圖數據層。在這一層,前端開發者對從后端獲取的 Model 數據進行轉換處理,做二次封裝,以生成符合 View 層使用預期的視圖數據模型。需要注意的是 ViewModel 所封裝出來的數據模型包括視圖的狀態和行為兩部分,而 Model 層的數據模型是只包含狀態的,比如頁面的這一塊展示什么,那一塊展示什么這些都屬于視圖狀態(展示),而頁面加載進來時發生什么,點擊這一塊發生什么,這一塊滾動時發生什么這些都屬于視圖行為(交互),視圖狀態和行為都封裝在了 ViewModel 里。這樣的封裝使得 ViewModel 可以完整地去描述 View 層。由于實現了雙向綁定,ViewModel 的內容會實時展現在 View 層,這是激動人心的,因為前端開發者再也不必低效又麻煩地通過操縱 DOM 去更新視圖,MVVM 框架已經把最臟最累的一塊做好了,我們開發者只需要處理和維護 ViewModel,更新數據視圖就會自動得到相應更新,真正實現數據驅動開發。看到了吧,View 層展現的不是 Model 層的數據,而是 ViewModel 的數據,由 ViewModel 負責與 Model 層交互,這就完全解耦了 View 層和 Model 層,這個解耦是至關重要的,它是前后端分離方案實施的重要一環。
View一般就是我們平常說的HTML文本的Js模板,里面可以嵌入一些js模板的代碼,比如Mustache,比如jstl類似的模板偽代碼
ViewModule層里面就是我們對于這個視圖區域的一切js可視業務邏輯,舉個例子,比如圖片走馬燈特效,比如表單按鈕點擊提交,這些自定義事件的注冊和處理邏輯都寫在ViewModule里面了
Module就更簡單了,就是對于純數據的處理,比如增刪改查,與后臺CGI做交互
2.vue-router 是什么?它有哪些組件
路由就是SPA(單頁應用)的路徑管理器,適合用于構建單頁面應用。vue的單頁面應用是基于路由和組件的,路由用于設定訪問路徑,并將路徑和組件映射起來。傳統的頁面應用,是用一些超鏈接來實現頁面切換和跳轉的。在vue-router單頁面應用中,則是路徑之間的切換,也就是組件的切換。路由模塊的本質 就是建立起url和頁面之間的映射關系。
SPA(single page application):單一頁面應用程序,只有一個完整的頁面;它在加載頁面時,不會加載整個頁面,而是只更新某個指定的容器中內容。單頁面應用(SPA)的核心之一是: 更新視圖而不重新請求頁面;vue-router在實現單頁面前端路由時,提供了兩種方式:Hash模式和History模式;根據mode參數來決定采用哪一種方式。
1、Hash模式:
vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,于是當 URL 改變時,頁面不會重新加載。?hash(#)是URL 的錨點,代表的是網頁中的一個位置,單單改變#后的部分,瀏覽器只會滾動到相應位置,不會重新加載網頁,也就是說 #是用來指導瀏覽器動作的,對服務器端完全無用,HTTP請求中也不會不包括#;同時每一次改變#后的部分,都會在瀏覽器的訪問歷史中增加一個記錄,使用”后退”按鈕,就可以回到上一個位置;所以說Hash模式通過錨點值的改變,根據不同的值,渲染指定DOM位置的不同數據
2、History模式:
由于hash模式會在url中自帶#,如果不想要很丑的 hash,我們可以用路由的 history 模式,只需要在配置路由規則時,加入"mode: 'history'",這種模式充分利用 history.pushState API 來完成 URL 跳轉而無須重新加載頁面。
//main.js文件中const router = new VueRouter({ mode: 'history', routes: [...]})
要在服務端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。
exportconstroutes = [
{path:"/", name:"homeLink", component:Home}
{path:"/register", name:"registerLink", component: Register},
{path:"/login", name:"loginLink", component: Login},
{path:"*", redirect:"/"}]
此處就設置如果URL輸入錯誤或者是URL 匹配不到任何靜態資源,就自動跳到到Home頁面
常用的形式:
<router-link :to='' class='active-class'> //路由聲明式跳轉 ,active-class是標簽被點擊時的樣式
<router-view>? ? ? ? ? ? ? ? ? //渲染路由的容器
<keep-alive>? ? ? ? ? ? ? ? ? ? //緩存組件
3.active-class 是哪個組件的屬性?
active-class屬于vue-router的樣式方法 當routerlink標簽被點擊時將會應用這個樣式 使用有兩種方法routerLink標簽內使用
<router-link to='/'active-class="active">首頁</router-link>
首頁的active有時會有bug,會一直被應用
為了解決上面的問題,還需加入一個屬性exact,類似也有兩種方式:
在router-link中寫入exact
<router-link to='/'active-class="active"exact>首頁</router-link>
4.怎么定義 vue-router 的動態路由? 怎么獲取傳過來的值
可以通過query ,param兩種方式
區別:?query通過url傳參,刷新頁面還在? ? params刷新頁面不在了
? ? params的類型:
配置路由格式:/router/:id
傳遞的方式:在path后面跟上對應的值
?傳遞后形成的路徑:/router/123
<!-- 動態路由-params -->
//在APP.vue中
? ? <router-link :to="'/user/'+userId" replace>用戶</router-link>? ?
//在index.js
? ? ?{
? ? path: '/user/:userid',
? ? component: User,
? ? },
跳轉方法:
// 方法1:
<router-link :to="{ name: 'users', params: { uname: wade }}">按鈕</router-link>
// 方法2:
this.$router.push({name:'users',params:{uname:wade}})
// 方法3:
this.$router.push('/user/' + wade)
可以通過$route.params.userid 獲取你說傳遞的值
? ?query的類類型
? 配置路由格式:/router,也就是普通配置
? 傳遞的方式:對象中使用query的key作為傳遞方式
? 傳遞后形成的路徑:/route?id=123
<!--動態路由-query -->
//01-直接在router-link 標簽上以對象的形式
<router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">檔案</router-link>
/*
? ? 02-或者寫成按鈕以點擊事件形式
? ? <button @click='profileClick'>我的</button>? ?
*/
//點擊事件
profileClick(){
? this.$router.push({
? ? ? ? path: "/profile",
? ? ? ? query: {
? ? ? ? ? name: "kobi",
? ? ? ? ? age: "28",
? ? ? ? ? height: 198
? ? ? ? }
? ? ? });
}
跳轉方法:
// 方法1:
<router-link :to="{ name: 'users', query: { uname: james }}">按鈕</router-link>
// 方法2:
this.$router.push({ name: 'users', query:{ uname:james }})
// 方法3:
<router-link :to="{ path: '/user', query: { uname:james }}">按鈕</router-link>
// 方法4:
this.$router.push({ path: '/user', query:{ uname:james }})
// 方法5:
this.$router.push('/user?uname=' + jsmes)
可以通過$route.query?獲取你說傳遞的值
5.vue-router 有哪幾種導航鉤子?
1、全局守衛:router.beforeEach
使用router.beforeEach注冊一個全局前置守衛:
constrouter=newVueRouter({...})? router.beforeEach((to,from, next) =>{// ...})
當一個導航觸發時,全局前置守衛按照創建順序調用。守衛是異步解析執行,此時導航在所有守衛resolve 完之前一直處于等待中。
每個守衛方法接收三個參數:
to: Route: 即將要進入的目標 路由對象
from: Route: 當前導航正要離開的路由
next: Function: 一定要調用該方法來resolve這個鉤子。執行效果依賴 next 方法的調用參數。
next(): 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是confirmed(確認的)。
next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應的地址。
next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。你可以向next 傳遞任意位置對象,且允許設置諸如replace: true、name: 'home' 之類的選項以及任何用在router-link的to prop或router.push中的選項。
next(error): (2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給router.onError()注冊過的回調。
確保要調用next方法,否則鉤子就不會被 resolved。
2、全局解析守衛:router.beforeResolve
你可以用?router.beforeResolve?注冊一個全局守衛。這和?router.beforeEach?類似,區別是:在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之后,解析守衛就被調用。
3、全局后置鉤子:router.afterEach
你也可以注冊全局后置鉤子,然而和守衛不同的是,這些鉤子不會接受 next 函數也不會改變導航本身:
router.afterEach((to,from) =>{// ...})
4、路由獨享的守衛:beforeEnter
constrouter=newVueRouter({routes:[{path:'/foo',component: Foo,beforeEnter:(to,from, next) =>{// ...}}]})
這些守衛與全局前置守衛的方法參數是一樣的。
5、組件內的守衛:beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave
const Foo={ template: `...`,?
beforeRouteEnter(to, from,next){
//在渲染該組件的對應路由被 confirm 前調用
//不!能!獲取組件實例 `this`//因為當守衛執行前,組件實例還沒被創建
},
//不過,你可以通過傳一個回調給next來訪問組件實例。
//在導航被確認的時候執行回調,并且把組件實例作為回調方法的參數。
?beforeRouteEnter(to, from,next){next(vm=>{//通過 `vm` 訪問組件實例})},?
beforeRouteUpdate(to, from,next){
//在當前路由改變,但是該組件被復用時調用//舉例來說,對于一個帶有動態參數的路徑/foo/:id,在/foo/1和/foo/2之間跳轉的時候,
//由于會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鉤子就會在這個情況下被調用。
//可以訪問組件實例 `this`},?
?beforeRouteLeave(to, from,next){
//導航離開該組件的對應路由時調用
//可以訪問組件實例 `this`}}
注意:beforeRouteEnter是支持給next 傳遞回調的唯一守衛。對于beforeRouteUpdate和beforeRouteLeave來說,this已經可用了,所以不支持傳遞回調,因為沒有必要了:
beforeRouteUpdate(to,from, next){// just use `this`this.name= to.params.name
? next()}
離開守衛beforeRouteLeave:通常用來禁止用戶在還未保存修改前突然離開。該導航可以通過next(false)來取消:
beforeRouteLeave(to, from,next){
constanswer= window.confirm('Do you really want to leave? you have unsaved changes!')
if(answer){next()}else{next(false)}}
6.route和router 的區別
1.router是VueRouter的一個對象,通過Vue.use(VueRouter)和VueRouter構造函數得到一個router的實例對象,這個對象中是一個全局的對象,他包含了所有的路由包含了許多關鍵的對象和屬性。
舉例:history對象
$router.push({path:'home'});本質是向history棧中添加一個路由,在我們看來是?切換路由,但本質是在添加一個history記錄
方法:
$router.replace({path:'home'});//替換路由,沒有歷史記錄
2.route是一個跳轉的路由對象,每一個路由都會有一個route對象,是一個局部的對象,可以獲取對應的name,path,params,query等
$route.path
字符串,等于當前路由對象的路徑,會被解析為絕對路徑,如"/home/news"。
$route.params
對象,包含路由中的動態片段和全匹配片段的鍵值對
$route.query
對象,包含路由中查詢參數的鍵值對。例如,對于/home/news/detail/01?favorite=yes,會得到$route.query.favorite == 'yes'。
$route.router
路由規則所屬的路由器(以及其所屬的組件)。
$route.matched
數組,包含當前匹配的路徑中所包含的所有片段所對應的配置參數對象。
$route.name
當前路徑的名字,如果沒有使用具名路徑,則名字為空。
$route.path, $route.params, $route.name, $route.query這幾個屬性很容易理解,主要用于接收路由傳遞的參數
7.vue-router響應路由參數的變化
當使用路由參數時,例如從?/user/aside導航到?/user/foo,原來的組件實例會被復用。因為兩個路由都渲染同個組件,比起銷毀再創建,復用則更加高效。不過,這也意味著組件的生命周期鉤子不會再被調用。
注意:
(1)從同一個組件跳轉到同一個組件。
(2)生命周期鉤子created和mounted都不會調用。
可以使用router的組件內鉤子函數
beforeRouteUpdate(to,from,next){
//在這個鉤子函數中:to表示將要跳轉的路由對象,from表示從哪個路由跳轉過來,next多數就是需要調用
//created和mounted不調用,無法拿到需要的動態值,就通過to.path,to.params等
//可以在這個函數中打印to,具體看to對象有什么可以使用的屬性
}
添加watch監聽
watch: {
// 方法1 //監聽路由是否變化
? '$route' (to, from) {
? if(to.query.id !== from.query.id){
? ? ? ? ? ? this.id = to.query.id;
? ? ? ? ? ? this.init();//重新加載數據
? ? ? ? }
? }
}
//方法 2? 設置路徑變化時的處理函數
watch: {
'$route': {
? ? handler: 'init',
? ? immediate: true
? }
}
為了實現這樣的效果可以給router-view添加一個不同的key,這樣即使是公用組件,只要url變化了,就一定會重新創建這個組件。
<router-view :key="$route.fullpath"></router-view>
8.vue-router實現路由懶加載( 動態加載路由 )
1. vue異步組件技術
vue-router配置路由,使用vue的異步組件技術,可以實現按需加載。
但是,這種情況下一個組件生成一個js文件。
舉例如下:
{path:'/promisedemo',name:'PromiseDemo',component:resolve=>require(['../components/PromiseDemo'],resolve)}
ts文件下需要設置
2. es提案的import()
推薦使用這種方式(需要webpack > 2.4)
webpack官方文檔:webpack中使用import()
vue官方文檔:路由懶加載(使用import())
vue-router配置路由,代碼如下:
// 下面2行代碼,沒有指定webpackChunkName,每個組件打包成一個js文件。
const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
// 下面2行代碼,指定了相同的webpackChunkName,會合并打包成一個js文件。
// const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
// const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
export default new Router({
routes: [
????????{ path: '/importfuncdemo1', name: 'ImportFuncDemo1', component: ImportFuncDemo1 },
????????{ path: '/importfuncdemo2', name: 'ImportFuncDemo2', component: ImportFuncDemo2 } ]
})
3、webpack提供的require.ensure()
vue-router配置路由,使用webpack的require.ensure技術,也可以實現按需加載。
這種情況下,多個路由指定相同的chunkName,會合并打包成一個js文件。
舉例如下:
{path:'/promisedemo',name:'PromiseDemo',component:resolve=>require.ensure([],()=>resolve(require('../components/PromiseDemo')),'demo')},{path:'/hello',name:'Hello',// component: Hellocomponent:resolve=>require.ensure([],()=>resolve(require('../components/Hello')),'demo')