分析
- vue-router是一个插件
- 实现VueRouter类
- 处理路由选项
- 监听url变化,
hashchange
事件
- 响应这个变化
- 实现install
- 挂载$router
- 注册router-view,router-link
创建VueRouter类
// VueRouter.js
import Link from "./RouterLink";
import View from "./RouterView";
let Vue;
class VueRouter {
constructor(options) {
this.$options = options;
const initial = location.hash.slice(1) || "/";
/*
使current变成一个响应式对象(触发router-view更新)
这里不能使用Vue.set(this, 'current', initial);因为Vue.set()第一个参数必须是响应式对象
*/
Vue.util.defineReactive(this, "current", initial);
window.addEventListener("hashchange", this.onHashChange.bind(this));
}
onHashChange() {
this.current = location.hash.slice(1);
}
}
// Vue.use时传入Vue
VueRouter.install = function (_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
// 只有根组件才有router
if (this.$options.router) {
Vue.prototype.$router = this.$options.router; // 全局挂在$router
}
},
});
Vue.component("router-link", Link);
Vue.component("router-view", View);
};
export default VueRouter;
// RouterLink.js
export default {
props: {
to: {
type: String,
required: true,
},
},
render(h) {
return h(
"a",
{
attrs: {
href: "#" + this.to,
},
},
[this.$slots.default],
);
},
};
// RouterView.js
export default {
render(h) {
let component = null;
const { $options, current } = this.$router;
const route = $options.routes.find((route) => route.path === current);
route && (component = route.component);
return h(component);
},
};
使用
import Vue from "vue/dist/vue.esm";
import VueRouter from "./vue/vueRouter/VueRouter";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{
name: "hello",
path: "/hello",
component: () => import("./vue/vueRouter/Hello"),
},
],
});
new Vue({
el: "#app",
router,
});
创建vuex
let Vue;
class Store {
constructor(options) {
this._mutations = options.mutations;
this._actions = options.actions;
// Vue.util.defineReactive(this, 'state', options.state) // 避免state直接暴露
this._vm = new Vue({
data() {
return {
$$state: options.state,
};
},
});
this.commit = this.commit.bind(this); // 防止dispatch时this丢失
this.dispatch = this.dispatch.bind(this); //
}
get state() {
return this._vm._data.$$state;
}
set state(v) {
console.error("you have to commit mutation to change state");
}
commit(type, payload) {
const entry = this._mutations[type];
entry && entry(this.state);
}
dispatch(type, payload) {
const entry = this._actions[type];
entry && entry(this);
}
}
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store;
}
},
});
}
export default { Store, install };
使用
import Vue from "vue/dist/vue.esm";
import VueRouter from "./vue/vueRouter/VueRouter";
import Vuex from "./vue/vuex/vuex";
Vue.use(VueRouter);
Vue.use(Vuex);
const router = new VueRouter({
routes: [
{
name: "hello",
path: "/hello",
component: () => import("./vue/vueRouter/Hello"),
},
],
});
const store = new Vuex.Store({
state: {
counter: 0,
},
mutations: {
add(state, payload) {
state.counter++;
},
},
actions: {
add({ commit }, payload) {
setTimeout(() => {
commit("add");
}, 1000);
},
},
});
new Vue({
el: "#app",
router,
store,
});