在正式進入之前先拋出幾個問題:
1 vue-router能不能放在react中使用铅鲤?
2 use vueRouter的時候發(fā)生了什么拐辽?
3 為什么要把router作為一個選項放在new vue 中?
4 為什么router-link router-view 不需要注冊桨吊,就可以直接使用盗胀?
答案就在文章中~~
vue-router
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成车酣,讓構建單頁面應用變得易如反掌。主要解決的問題就單頁面應用的導航問題。
router的任務分析
- 解析routers選項
- 監(jiān)控url變化
html5 history api /login
hash xx.html#login
//vue-router.js
//聲明插件:vue插件需求實現(xiàn)一個install靜態(tài)方法
let Vue; //保存vue構造函數(shù)引用 目的是不用吧vue打包進去
class KVueRouter{
}
//參數(shù)是vue構造函數(shù)
KVueRouter.install = function(_vue){
Vue = _Vue;
//實現(xiàn)一個混入
Vue.mixin({
beforeCreate(){
//獲取KVueRouter實例并掛載到Vue.prototype
if(this.$options.router){
// 在跟組件beforeCreate時執(zhí)行一次且只會執(zhí)行一次
Vue.prototype.$router = this.$options.router;
}
}
})
}
這時候有一個疑問湖员,為什么要寫把router掛載的配置寫在混入里贫悄,而不是直接寫在install方法呢?
這是因為和實例的關系很大娘摔,我們先執(zhí)行了Vue.use的方法窄坦,其實是執(zhí)行了插件的install的方法,但是這時候凳寺,實例還不存在呢鸭津,那怎么辦呢,我們只好退而求其次读第,把代碼延后執(zhí)行曙博,延后到當beforeCreate的時候,才執(zhí)行怜瞒。
接下來我們需要注冊兩個全局組件 router-view 和 router-link
...
Vue.component('router-link',{})
Vue.component('router-view',{})
接下來來完成核心任務
class KVueRouter{
//解析routes
//監(jiān)聽事件
//聲明組件
constructor(options){
this.$options = options;
this.routeMap = {}; // {'/index': {component: Index,...}}
//當前url需要響應式的
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(){
//轉換目標: <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導出一下
export default KVueRouter
這時候可以引入寫好的文件,來調(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.應用插件:做了什么吴汪?
Vue.use(Router) // use執(zhí)行了插件install()
// 2.創(chuàng)建Router實例
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實例
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>
這時候我們可以實現(xiàn)路由跳轉了惠窄,但是并不能真正的加載對應的組件,就差渲染內(nèi)容組件了
router-view 這個組件負責實現(xiàn)渲染組件工作漾橙,我們拿出我們要渲染的component
// 獲取path對應的Component將它渲染出來
Vue.component("router-view", {
render: (h) => {
const Component = this.routeMap[this.app.current].component;
return h(Component)
}
})
另外vue也像我們暴露了一個可以定義一個響應式數(shù)據(jù)的方法
const initial = window.location.hash.slice(1) || ‘/’
Vue.util.defineReactive(this,’current’,initial)
接下來杆融,我們來總結一下
關于實現(xiàn)路由,我們要做的第一件事是實現(xiàn)一個插件install(),這個install方法霜运,會把vue的構造函數(shù)傳進來脾歇,拿到vue的構造函數(shù)之后,我們就可以做很多事情淘捡,接下來我們另一個要實現(xiàn)的kvuerouter里藕各,監(jiān)聽事件,事件發(fā)生變化以后焦除,我們要做的事情把this.app的current的改成新的hash激况,但是為什么修改完新的hash之后,會在router-view把對應的組件渲染出來呢膘魄,原因是只要render函數(shù)里面用到了某個響應式的數(shù)據(jù)乌逐,這個數(shù)據(jù)發(fā)生變化了,我們的組件就會重新執(zhí)行render创葡,這就是典型的依賴收集浙踢,意思就是說render函數(shù)里只要用的data里的東西,就會產(chǎn)生依賴灿渴,編輯器在執(zhí)行render函數(shù)的時候成黄,會先執(zhí)行依賴收集的過程呐芥,先把依賴全部找到,vue里的current只要改變奋岁,和它相關的組件都會發(fā)生改變,也就會導致router-view的component的重新執(zhí)行荸百,界面就渲染了闻伶。
該示例沒有解決嵌套路由的問題,我們可以參考一下官方的文檔够话。
vuex數(shù)據(jù)管理
vuex是一個專門為vue.js應用開發(fā)的狀態(tài)管理模式蓝翰,集中式存儲管理應用所有組件的狀態(tài)。它是一個單項數(shù)據(jù)流的設計思想女嘲,它為了讓數(shù)據(jù)可控畜份,數(shù)據(jù)可追蹤,設計出這樣一個單項數(shù)據(jù)流, 我們在實踐的時候欣尼,也要避免同時被父子組件操作的情況爆雹,維持這樣一個單向的關系,怎么去維系呢愕鼓,我們把一些通用的全局的數(shù)據(jù)钙态,把他抽象到一個store里去保管,只能用不能改菇晃,如果想改數(shù)據(jù)册倒,只能commit一個mutaions,或者dispath一個actions,讓actions去commit一個mutaions磺送,而且在vuex 里面必須實現(xiàn)一個數(shù)據(jù)的響應式驻子,實現(xiàn)的方式也是利用了vuex的構造初始化的時候做了響應式。
核心概念
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的任務分析
- 實現(xiàn)插件: $store掛載
- 實現(xiàn)store: 解析vuex配置甲捏,持有state演熟,實現(xiàn)dispatch,commit司顿,getters
- 借助vue實現(xiàn)數(shù)據(jù)響應式
//kvuex.js
let Vue;
class Store {
// 持有state芒粹,并使其響應化
// 實現(xiàn)commit和dispatch兩個方法
constructor(options) {
// 數(shù)據(jù)響應式
// this.state是Vue實例,訪問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);
}
// 實現(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選項大溜,為this.getters定義property
//屬性名就是選項中的key 化漆,只需定義get函數(shù)保證只讀性
Object.keys(getters).forEach(key =>{
Object.defineProperty(this.getters, key, {
get : () =>{
return getters[key](this.state)
}
})
})
}
// 聲明插件install
// _Vue是形參:Vue構造函數(shù),use會把它傳進來
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
// this指的是組件實例
if (this.$options.store) {
Vue.prototype.$store = this.$options.store;
}
},
});
}
// 導出Vuex
export default { Store, install };
vuex和vuerouter的實現(xiàn)思想大致相同钦奋,以上在不考慮代碼健壯性的前提下座云,來實現(xiàn)核心思想的疙赠。