上面是项目结构!!!
kvue-router.js文件是简单实现vue-router
kvuex.js文件是简单实现vuex
里面有各种注释,学习时候弄的,比较乱,凑合看吧!!!
main.js
import Vue from 'vue'
import App from './App.vue'
// import VueRouter from 'vue-router'
// import Vuex from 'vuex'
import KvueRouter from './kvue-router/kvue-router.js'
import Kvuex from './kvuex/kvuex.js'
import HelloWorld from './views/HelloWorld.vue' //这里一个点,需要引入绝对路径,不能是相对路径
import Home from './views/Home.vue' //注意大小写
// Vue.use(VueRouter);
// Vue.use(Vuex);
//vuex:状态管理模式
//集中管理所有组件的状态
//可预测的状态变化
Vue.use(KvueRouter)
Vue.use(Kvuex)
//vue.use() 传入点必须是函数或者是对象
//如果是对象,对象里面必须有install方法
//vue.use()会把vue对象传入函数或者install方法中
//vue.use()的原理:
//判断传入的是不是对象或者是函数
//判断vue是否注册过这个插件
//判断是否有install方法,如果有直接调用install方法,
// 如果没有,直接把整个plugin当install方法执行
Vue.config.productionTip = false
const routes = [{
path: '/HelloWorld',
name: 'HelloWorld',
component: HelloWorld,
children: [{
path: '/info',
component: {
render(h) {
return h('div', "我是info")
}
}
}]
},
{
path: '/Home',
name: 'Home',
component: Home
}
]
const router = new KvueRouter({
routes
});
const store = new Kvuex.Store({ //单一状态树:一个store实例
state: { //state:数据源(各种状态)
count: 1
},
getters: { //getters:store的计算属性
number(state) { //接收一个参数state
return state.count * 2;
}
},
mutations: { //唯一改变state状态的方法,必须是同步函数
addcount(state, payLoad) { //接收参数state,payLoad支持对象写法,也可以是一个值
// console.log(state);
state.count = state.count + payLoad;
},
reducecount(state, payLoad) {
state.count = state.count - payLoad;
}
},
actions: { //类似mutation,但是可以执行异步函数
reducecount(context, payLoad) { //接收参数context,payLoad支持对象写法,也可以是一个值
//也可以直接接收 { commit,dispatch,state,getters }
// console.log(context, payLoad); //context是一个对象,里面有state,getters,commit,dispatch,rootState,rootGetters
setTimeout(() => {
context.commit('reducecount', payLoad)
}, 1000);
}
}
});
new Vue({
router, //将router实例挂载到vue实例中,可以在插件安装时,注册实例
store, //将store注入到new Vue({ store }) 中,各个组件中可以直接使用
//this.$store
render: h => h(App),
}).$mount('#app')
app.vue
<template>
<div id="app">
<router-link to="/HelloWorld">HelloWorld</router-link>
|
<router-link to="/Home">Home</router-link>
|
<router-link to="/HelloWorld/info">info</router-link>
<router-view />
<p>22222222</p>
</div>
</template>
<script>
/* eslint-disable */
export default {
data() {
return {
o: null,
count: 0
}
},
methods: {
},
created() {
}
/*
1、promise的用法
2、async :事件之前用,会让事件返回一个promise对象,后面可以直接用 .then()
await :和 async 一起用,在async事件里面用,可以获取promise对象里面的resolve或者reject函数里面的东西 的用法
3、watch : immediate:true 侦听开始之后立即调用
handler:函数,方法名
deep: true 深层遍历
*/
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
home.vue
<template>
<div>
我是Home
<h1>我是count:{{ count }}</h1>
<button @click="add">增加10</button>
<button @click="reduce">减少</button>
<h1>{{ $store.getters.number }}</h1>
<!-- <router-view></router-view> -->
</div>
</template>
<script>
/* eslint-disable */
export default {
data() {
return {
}
},
methods: {
add() {
this.$store.commit('addcount', 10);
//对象风格提交方式
//this.$store.commit({
// type:'addcount',
// amount:10
//});
},
reduce() {
this.$store.dispatch('reducecount', 1);
/*
this.$store.dispatch({
type:'reducecount',
amount:2
});
*/
}
},
computed: {
count() {
// console.log(this);
return this.$store.state.count
}
},
mounted() {
//普通函数的this指向:指向调用他的对象,谁离得近就指向谁
//构造函数下,指向被创建的对象
//DOM事件,指向被触发事件的元素
// 箭头函数的this指向:定义箭头函数的所在的上下文的this,即指向定义函数的对象
}
}
</script>
<style>
</style>
hellowWorld.vue
<template>
<div class="hello">
我是HelloWorld
<router-view />
</div>
</template>
<script>
/* eslint-disable */
export default {
data() {
return {
// count:0
}
},
methods: {
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
kvuex.js
let Vue;
//实现一个store类
class Store {
constructor(options) { //options是创建实例时传进来的选项
// console.log(options,111)
this.$options = options;
//把state 做响应式处理
// this.state = new Vue({//Vue初始化的时候会把data里的数据进行初始化,并且代理data里的数据到Vue实例上
// data:{
// state:this.$options.state
// }
// });
let computed = {};
this.getters = {}
let store = this;
for (let item in this.$options.getters) {
let fn = this.$options.getters[item];
computed[item] = function() {
// console.log(this)//这里的this是vue对象,因为下面把这个computed放进了vue中
return fn(store.$options.state);
}
//直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
Object.defineProperty(this.getters, item, {
//这里把getters里的item定义成都是可读的
get() {
// console.log(this)//这里到this指向的是this.getters
return store._vm[item] //由于_vm里的代理机制,所以可以直接访问到item ,computed里到数据被代理到vue对象上里
}
})
}
//隐藏state
this._vm = new Vue({ //存到_vm中
data: {
$$state: this.$options.state //$$state 不会被代理
},
computed //computed 是一个对象
// computed:{
// number(){
// }
// }
});
// console.log(this._vm);
// console.log(this._vm._data.$$state); //this.state 指的时Vue实例,里面有个_data里面的数据是响应式的 _ob_ 观察者模式
//在构造函数里就绑定上下文了
this.commit.call(this);
this.dispatch.call(this);
}
get state() { //当外面访问this.$store.state 的时候,返回值
return this._vm._data.$$state
}
set state(v) {
console.error('请使用replaceState()重置')
}
//commit事件
commit(type, payLoad) {
//获取mutations里面对应的type事件,把payLoad传进去,执行该函数
let fn = this.$options.mutations[type];
if (!fn) {
console.log('mutations不存在')
return
}
fn(this.state, payLoad); //call的作用是:把this改成当前的this,即vuex类
// fn.call(this,this.state,payLoad)
//这里试过不加call,没有报错
//call(this指向,payLoad1,payLoad2,...)
//apply(this的指向,[payLoad1,payLoad2,...])
//bind(this的指向,payLoad1,payLoad2,...)()
}
//dispatch事件
dispatch(type, payLoad) {
//获取mutations里面对应的type事件,把payLoad传进去,执行该函数
let fn = this.$options.actions[type];
if (!fn) {
console.log('mutations不存在')
return
}
fn(this, payLoad);
//视频里这里报错了,但是我这里没有报错啊,不知道为啥
// fn.call(this,this,payLoad);//call的作用是:把this改成当前的this,即vuex类
//这里试过不加call,没有报错
//call(this指向,payLoad1,payLoad2,...)
//apply(this的指向,[payLoad1,payLoad2,...])
//bind(this的指向,payLoad1,payLoad2,...)()
}
}
function install(Vue_) {
Vue = Vue_;
//挂载$store 到Vue实例上,为了可以使用this.$store
Vue.mixin({
beforeCreate() {
//这里到this指向的是组件实例,$options指的是选项
// console.log(this);
if (this.$options.store) {
Vue.prototype.$store = this.$options.store;
}
}
})
}
export default {
Store,
install
}
kvue-router.js
/* eslint-disable */
let Vue
//实现一个类
class KvueRouter {
constructor(options) {
// console.log(this);//这里对this指的是KvueRouter
// console.log(options);//这里是指传入Kvuerouter的选项
this.$options = options;
// let initPath = window.location.hash.slice(1);
// Vue.util.defineReactive(this, 'current', initPath);//响应式current
this.current = window.location.hash.slice(1) || '/';
Vue.util.defineReactive(this, 'matched', []);
this.match();
//监听url的变化,hashchange
let this_ = this;
window.addEventListener('hashchange', function() {
//获取当前的path
this_.current = window.location.hash.slice(1);
this_.matched = [];
this_.match();
}, false)
}
//match方法可以递归遍历路由表,获得匹配的路由关系
match(routes) {
routes = routes || this.$options.routes;
//for ... of 遍历数组
//for ... in 遍历对象
for (let route of routes) {
if (route.path === '/' && this.current === '/') {
this.matched.push(routes);
return;
}
if (route.path !== '/' && this.current.indexOf(route.path) != -1) {
this.matched.push(route);
if (route.children) {
this.match(route.children);
}
return;
}
}
console.log(this.matched);
}
}
//挂载一个install方法,在vue.use()的时候调用
KvueRouter.install = function(Vue_) {
Vue = Vue_;
// Vue.prototype.$router = router 这里到router应该是router实例,
// 而router实例挂载到了new vue()里
//将Vue里的router实例挂载到vue原型上
//用的时候this.$router
//但是vue.use()在new Vue()之前执行
//所以需要 全局混入minix() 把这里到挂载原型执行到时间移到 new vue() 后面
//才能拿到new vue()里到router实例
Vue.mixin({
beforeCreate() {
// Vue.prototype.$router = this.
// console.log(this);
//这里到this指的是组件实例,组件实例指的是:vue根实例,#app的div实例,两个router-link实例
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
//$options是各种选项,里面有很多东西
}
})
//实现两个组件:router-link router-view
Vue.component('router-link', {
props: {
to: {
type: String,
required: true
}
},
//浏览器中进行大量的DOM操作会让性能很差,所以虚拟DOM产生
//使用render函数描述虚拟DOM时,
// 提供一个函数createElement,用来构建虚拟DOM,
//所以render返回的是VNode(虚拟节点)
// 后面用diff算法进行比较,更新发生变化的DOM,diff算法只会比较同一层级的DOM元素,不会跨层级比较(比较的是DOM树)
//没有发生变化的DOM不会进行操作
//不是全部重绘,所以渲染性能提高,
//官网上给他起了个名字叫 createElement。
// 还有约定的简写叫 h,
// vm中有一个方法 _c, 也是这个函数的别名
render(h) {
//render函数接受两个参数,第一个是创建Vnode,第二个是接受上下文信息
//h接收参数:
//第一个是标签名字
//第二个是标签内容
//第三个是子节点
return h(
'a', {
attrs: { //attrs:HTML标签的特性
href: '#' + this.to
}
},
this.$slots.default); //这里取的是组件的插槽值: <router-link to="/a">XXX</router-link>
}
})
Vue.component('router-view', {
render(h) {
// let component = {render(createElement){return createElement('h1','balabala')}};
//render(createElement){return createElement('h1','balabala')}
//这里返回的是一个VNode,节点描述对象
//函数没有function,需要写在对象里
//h()的参数可以是String,Object,还能传sync函数,但是没有成功理解
//这里的component,是个描述节点的对象
// console.log(this.$router);
//this指的是当前组件
//$router之前被挂载到Vue的原型上了,所以组件继承了Vue,也有$router
//拿到的this.$router是Kvuerouter实例,再拿$options
let current = this.$router.current;
let component = null;
// let router = this.$router.$options.routes.find(item => item.path === current)
//find函数
//返回成功匹配的第一个元素
//没有就返回undefined
//不改变原始数组值
//find((currentValue,index,arr)=>{ })
this.$vnode.data.routerView = true; //标记每个router-view是routerView = true
let depth = 0;
let parent = this.$parent; //获取当前vnode的父级
// console.log(this)//这里指向Proxy,猜测是指的当前的vnode
while (parent) {
const vnodeData = parent.$vnode && parent.$vnode.data;
if (vnodeData && vnodeData.routerView) {
depth++;
}
// console.log(this);
parent = parent.$parent;
}
let route = this.$router.matched[depth];
if (route) {
component = route.component;
}
// console.log(router, component);
return h(component)
}
})
}
export default KvueRouter
//嵌套路由思想:
//首先确定当前的深度,作为router-view
//做一个响应式的matched数组,里面是当前路径和routes匹配的关系
//例如: home/info
//info是第一层,前面有一个home,是第零层
//在match方法里根据当前路径对路由表进行递归遍历(matched是响应式数组,虽然current不是响应式数据)
//有children的,进行递归match
// 然后把匹配到当前的路径的对象push到matched数组里
//匹配home/info的路径有数组里有两个值:一个是home一个是info,然后去拿相应的depth的数组的component
//进行渲染就好了