在正式進(jìn)入之前先拋出幾個(gè)問題:
1 vue-router能不能放在react中使用?
2 use vueRouter的時(shí)候發(fā)生了什么?
3 為什么要把router作為一個(gè)選項(xiàng)放在new vue 中?
4 為什么router-link router-view 不需要注冊(cè),就可以直接使用?
答案就在文章中~~
vue-router
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,讓構(gòu)建單頁面應(yīng)用變得易如反掌。主要解決的問題就單頁面應(yīng)用的導(dǎo)航問題。
router的任務(wù)分析
- 解析routers選項(xiàng)
- 監(jiān)控url變化
html5 history api /login
hash xx.html#login
//vue-router.js
//聲明插件:vue插件需求實(shí)現(xiàn)一個(gè)install靜態(tài)方法
let Vue; //保存vue構(gòu)造函數(shù)引用 目的是不用吧vue打包進(jìn)去
class KVueRouter{
}
//參數(shù)是vue構(gòu)造函數(shù)
KVueRouter.install = function(_vue){
Vue = _Vue;
//實(shí)現(xiàn)一個(gè)混入
Vue.mixin({
beforeCreate(){
//獲取KVueRouter實(shí)例并掛載到Vue.prototype
if(this.$options.router){
// 在跟組件beforeCreate時(shí)執(zhí)行一次且只會(huì)執(zhí)行一次
Vue.prototype.$router = this.$options.router;
}
}
})
}
這時(shí)候有一個(gè)疑問,為什么要寫把router掛載的配置寫在混入里,而不是直接寫在install方法呢?
這是因?yàn)楹蛯?shí)例的關(guān)系很大,我們先執(zhí)行了Vue.use的方法,其實(shí)是執(zhí)行了插件的install的方法,但是這時(shí)候,實(shí)例還不存在呢,那怎么辦呢,我們只好退而求其次,把代碼延后執(zhí)行,延后到當(dāng)beforeCreate的時(shí)候,才執(zhí)行。
接下來我們需要注冊(cè)兩個(gè)全局組件 router-view 和 router-link
...
Vue.component('router-link',{})
Vue.component('router-view',{})
接下來來完成核心任務(wù)
class KVueRouter{
//解析routes
//監(jiān)聽事件
//聲明組件
constructor(options){
this.$options = options;
this.routeMap = {}; // {'/index': {component: Index,...}}
//當(dāng)前url需要響應(yīng)式的
this.app = new Vue({
data : { current : '/'}
})
}
//初始化
init(){
//監(jiān)聽事件
this.bindEvents();
//解析routes
this.createRouteMap();
//聲明組件
this.initComponent();
}
bindEvents(){
window.addEventListener('hashchange', this.onHashchange.bind(this))
}
onHashchange(){
this.app.current = window.location.hash.slice(1) || '/'
}
createRouteMap(){
//遍歷用戶配置路由數(shù)組
this.$options.routes.forEach(route => {
this.routeMap[route.path] = route;
})
}
initComponent(){
//轉(zhuǎn)換目標(biāo): <a href = '/'>xx</a>
// <router-link to = '/'>
Vue.component('router-link', {
props: {
to: String
},
render(h){
// h(tag, data,children)
return h( 'a' , {
attrs:{href : '#' + this.to}
}, [ this.$slots.default ]) // 這里還可以放其他的具名插槽 甚至作用域插槽
// 也可以使用jsx
}
})
}
}
最后執(zhí)行一下install方法,把KVueRouter導(dǎo)出一下
export default KVueRouter
這時(shí)候可以引入寫好的文件,來調(diào)試一下了
//router.js
import Vue from 'vue'
// import Router from 'vue-router'
import Router from './kvue-router'
import Home from './views/Home.vue'
// 1.應(yīng)用插件:做了什么?
Vue.use(Router) // use執(zhí)行了插件install()
// 2.創(chuàng)建Router實(shí)例
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
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')
}
]
})
在main.js中掛載router
//main.js
import router from './router'
new Vue({
router, // 配置router實(shí)例
render: h => h(App),
}).$mount("#app");
//app.vue
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view></router-view>
這時(shí)候我們可以實(shí)現(xiàn)路由跳轉(zhuǎn)了,但是并不能真正的加載對(duì)應(yīng)的組件,就差渲染內(nèi)容組件了
router-view 這個(gè)組件負(fù)責(zé)實(shí)現(xiàn)渲染組件工作,我們拿出我們要渲染的component
// 獲取path對(duì)應(yīng)的Component將它渲染出來
Vue.component("router-view", {
render: (h) => {
const Component = this.routeMap[this.app.current].component;
return h(Component)
}
})
另外vue也像我們暴露了一個(gè)可以定義一個(gè)響應(yīng)式數(shù)據(jù)的方法
const initial = window.location.hash.slice(1) || ‘/’
Vue.util.defineReactive(this,’current’,initial)
接下來,我們來總結(jié)一下
關(guān)于實(shí)現(xiàn)路由,我們要做的第一件事是實(shí)現(xiàn)一個(gè)插件install(),這個(gè)install方法,會(huì)把vue的構(gòu)造函數(shù)傳進(jìn)來,拿到vue的構(gòu)造函數(shù)之后,我們就可以做很多事情,接下來我們另一個(gè)要實(shí)現(xiàn)的kvuerouter里,監(jiān)聽事件,事件發(fā)生變化以后,我們要做的事情把this.app的current的改成新的hash,但是為什么修改完新的hash之后,會(huì)在router-view把對(duì)應(yīng)的組件渲染出來呢,原因是只要render函數(shù)里面用到了某個(gè)響應(yīng)式的數(shù)據(jù),這個(gè)數(shù)據(jù)發(fā)生變化了,我們的組件就會(huì)重新執(zhí)行render,這就是典型的依賴收集,意思就是說render函數(shù)里只要用的data里的東西,就會(huì)產(chǎn)生依賴,編輯器在執(zhí)行render函數(shù)的時(shí)候,會(huì)先執(zhí)行依賴收集的過程,先把依賴全部找到,vue里的current只要改變,和它相關(guān)的組件都會(huì)發(fā)生改變,也就會(huì)導(dǎo)致router-view的component的重新執(zhí)行,界面就渲染了。
該示例沒有解決嵌套路由的問題,我們可以參考一下官方的文檔。
vuex數(shù)據(jù)管理
vuex是一個(gè)專門為vue.js應(yīng)用開發(fā)的狀態(tài)管理模式,集中式存儲(chǔ)管理應(yīng)用所有組件的狀態(tài)。它是一個(gè)單項(xiàng)數(shù)據(jù)流的設(shè)計(jì)思想,它為了讓數(shù)據(jù)可控,數(shù)據(jù)可追蹤,設(shè)計(jì)出這樣一個(gè)單項(xiàng)數(shù)據(jù)流, 我們?cè)趯?shí)踐的時(shí)候,也要避免同時(shí)被父子組件操作的情況,維持這樣一個(gè)單向的關(guān)系,怎么去維系呢,我們把一些通用的全局的數(shù)據(jù),把他抽象到一個(gè)store里去保管,只能用不能改,如果想改數(shù)據(jù),只能commit一個(gè)mutaions,或者dispath一個(gè)actions,讓actions去commit一個(gè)mutaions,而且在vuex 里面必須實(shí)現(xiàn)一個(gè)數(shù)據(jù)的響應(yīng)式,實(shí)現(xiàn)的方式也是利用了vuex的構(gòu)造初始化的時(shí)候做了響應(yīng)式。
核心概念
state狀態(tài),數(shù)據(jù)
- mutations更改狀態(tài)的函數(shù)
- actions異步操作
- store包含以上概念的容器
狀態(tài)和狀態(tài)的變更
state保存數(shù)據(jù)狀態(tài),mutations用于修改狀態(tài),store.js
export default new Vuex.Store({
state: {count : 0},
mutations:{
increment(state){
state.count += 1;
}
}
})
vuex的任務(wù)分析
- 實(shí)現(xiàn)插件: $store掛載
- 實(shí)現(xiàn)store: 解析vuex配置,持有state,實(shí)現(xiàn)dispatch,commit,getters
- 借助vue實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式
//kvuex.js
let Vue;
class Store {
// 持有state,并使其響應(yīng)化
// 實(shí)現(xiàn)commit和dispatch兩個(gè)方法
constructor(options) {
// 數(shù)據(jù)響應(yīng)式
// this.state是Vue實(shí)例,訪問this.state.count
this.state = new Vue({ data: options.state });
this.mutations = options.mutations;
this.actions = options.actions;
this.getters = options.getters;
// bind this
this.commit = this.commit.bind(this);
this.dispatch = this.dispatch.bind(this);
this.getters = this.getters.bind(this);
}
// 實(shí)現(xiàn)commit:可以修改state中的數(shù)據(jù)
commit(type, arg) {
this.mutations[type](this.state, arg);
}
dispatch(type, arg) {
return this.actions[type](this, arg);
}
getters(getters){
//遍歷getters選項(xiàng),為this.getters定義property
//屬性名就是選項(xiàng)中的key ,只需定義get函數(shù)保證只讀性
Object.keys(getters).forEach(key =>{
Object.defineProperty(this.getters, key, {
get : () =>{
return getters[key](this.state)
}
})
})
}
// 聲明插件install
// _Vue是形參:Vue構(gòu)造函數(shù),use會(huì)把它傳進(jìn)來
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
// this指的是組件實(shí)例
if (this.$options.store) {
Vue.prototype.$store = this.$options.store;
}
},
});
}
// 導(dǎo)出Vuex
export default { Store, install };
vuex和vuerouter的實(shí)現(xiàn)思想大致相同,以上在不考慮代碼健壯性的前提下,來實(shí)現(xiàn)核心思想的。