vue 状态管理及SSR

150 阅读3分钟

课程目标

  • vue的状态管理
  • SSR

知识点

  • vuex3

MVCmodelviewcontroller

  • M:data
  • V:html+css
  • c:js MVVMmodelviewviewmodel
  • M:data
  • V:html+css
  • VM:双向绑定

面试题

MVVM的理解

  1. 区别:抛弃对dom的严格感知
  • 无需对流程的过过程化操作
  1. 写法上、代码可维护

vue的组件传值

单例模式,只能有一个实例。

  1. 父子传值
  • props
  • on $emit
  • $parent
  1. 兄弟节点
  • 通过父节点
  • eventBus
  • provide inject
  • attrs listeners
  • vuex

vuex 是单例模式,vue-router也是单例,全局只能有一个。如果复杂的话,可以考虑vuex-module模块处理。

vuex

component -- actions -- mutations -- states -- component

  • 为何要兜一圈? action 可能会有 async 存在 ,vuex是同步过程
  • 异步如何转同步?
  • 比如promise怎么转同步,可以用.then .then
  • es
<script>
exprot default {
    name: 'app',
    method: {
        asyncFunc() {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(true);
                    console.info('async');
                })
            })
        }
    },
    async mounted() {
        this.asyncFun.then(res => {
            console.info('mouted');
        })
        //es语法
        await this.asyncFun();
        console.info('mounted');
    }
}
</sript>

如果有多个异步怎么办?会涉及到串联,串联有两种方式:全部执行竞争执行

<script>
exprot default {
    name: 'app',
    method: {
        asyncFunc() {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(true);
                    console.info('async');
                })
            })
        },
        asyncFunc2() {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(true);
                    console.info('async 2');
                })
            })
        }
    },
    async mounted() {
        this.asyncFun().then(res => {
            console.info('mouted');
        })
        //es语法
        await this.asyncFun();
        console.info('mounted');
        //多个异步怎么执行?全部执行
        Promise.all([this.asyncFunc(), this.asyncFunc2()]).then(() => {
            console.info("all done");
        });
        //竞争执行
        Promise.race([this.asyncFunc(), this.asyncFunc2()]).then(() => {
            console.info("all done");
        });
        //多个异步依赖执行,比如表单提交依赖
        //可以使用一个比较笨的方法
        await this.asyncFun();
        await this.asyncFun2();
        
        //还可以使用`co库`  -- 简单实现 - 迭代器模式
        const pipeLine = [this.asyncFun, this.asyncFun2]
        
        //先有一个迭代器 永远执行下一个
        setIterator(pipeLine)
        
        //next() - function* + yield - 生成器
        function* setGenerator(pipeLine) {
            for(const fn of pipeLine) {
                yield fn()
            }
        }
        //迭代器模式。配置迭代器 - main
        function setIterator(pipeLine) {
            const generator = setGenerator(pipeLine)
            
            GFC(generator)
        }
        //流水线  区分同步异步,依次执行
        function GFC(gen) {
            const item = gen.next();
            
            if(item.done) {
                return item.value
            }
            //value是内容,done是状态
            const {value, done } = item;
            
            //判断是同步还是异步的
            if(value instanceof Promise) {
                //递归调用下一个
                value.then(e => GFC(gen));
            }else{
                //直接执行
                GFC(gen);
            }
        }
    }
}
</sript>

vuex创建及使用

// store/index.js
improt Vue from 'vue'
improt Vuex from 'vuex'

// use上
Vue.use(Vuex)

//创建store实例
const store = new Vuex.Store({
    actions: {
        setNodeInfo({ commit }, info) {
            //可以做异步处理
            commit('SET_NODE_INFO', {
                info
            }) 
        }
    },
    mutations: {
        SET_NODE_INFO(sate, { info }) {
            state.nodeInof = info
        }
    },
    state: {
        nodeInfo: {
            name: '-',
            age: 0
        }
    }
})

exprot default store;

//dispatch的用法
this.$store.dispatch('setNodeInfo', this.nodeInfo);
//获取全局store
this.$store.state.nodeInfo;

还有其它四种方式为store提供便捷的辅助函数。

  1. mapState
improt { mapState } from 'vuex';
//使用方法:三种方法都可以
//第一种
...mapState(['nodeInfo'])
//第二种
...mapSate({
    nodeInfo: state => state.nodeInfo
})
//第三种
this.$store.state.nodeInfo;

  • vuex3主要基于mixin
//mixin.js 源码
//面试可能会提问到,vuex什么时候进行的初始化,是在beforeCreate时初始化的。
Vue.mixin({ beforeCreate: vuexInit })
//store injection  源码
//store注入到每个实例中
function vuexInit()

//store.js 源码

let Vue;
//实例对象的描述
exprot class Store {
    constructor (option = {}) {
        //cdn直接引用的方式,可以自动instally
        if(!Vue && typeof window !== 'undefined' && window.Vue) {
            install(window.Vue)
        }
    }
}

//1.挂载
//use时候的开始
export function install( _Vue ) {

}
//面试题:vuex自定义了告警,为啥不用console.assert? - throw Error 是为了把进程打断。
//面试题:Object.create(null)和{}的区别:区别在于原型链
//Object.create(null).__prototype__为undefined,就可以规避掉其它原型链上的东西。
//{}.__prototype__是Object.prototype
//...
//因为是单例模式,确认this是当前store的实例
const store = this;
const { displatch, commit } = this;
this.dispatch = function boundDispatch (type, payload) {
    return dispatch.call(store, type, payload);
}
this.commit =  function boundCommit(type, payload, options) {
    return commit.call(store, type, payload, options);
};
//init all modules
installModule(this, state, [], this._modules.root)
//reset vm,vue是怎么做响应式的
resetStoreVM(this, state)
function resetStoreVM(store, state, hot) {
    const oldVm_ = store._vm;
    //...
    const computed = {}
    forEachValue(wrappedGetters, (fn, key) => {
        computed[key] = partial(fn, store)
        //遍历地将所有getters桥接上store,并配置成computed属性
        Object.defineProperty(store.getters, key, {
            get: () => store._vm[key],
            enumerable: true
        })
    })
    //...
    //利用vue的能力,做响应式
    store._vm = new Vue({
        data: {
            $$state: state
        },
        computed
    })
    //...
    //销毁
    if( oldVm ){
        if( hot ){
            store._withCommit(() => {
                oldVm_.data.$$state = null;
            })
        }
        Vue.nextTick(() => oldVm.$destory())
    }
}

  • vuex4主要基于provide

SSR

SSR一般只用于首页。

  • 优点:首屏
  • 缺点:不利于SEO搜索,server压力大,如果服务器给力就没啥问题
  1. 建立服务

npm i vue-server-renderer express -D:是一个服务端渲染器,用于渲染vue的。

  1. 搭建node服务
//server/index.js
//0.加载依赖
const express = require('express');
const Vue = require('vue');

const app = express()
const renderer = require('vue-server-renderer').createRenderer();

//渲染器渲染page得到html内容
//1.page
const page = new Vue({
    template: '<div>hello,ssr</div>'
})

//2.传递接口
app.get('/', async(req, res) => {
    try {
        const html = await renderer.renderToString(name);
        res.send(html);
    } catch(error) {
        res.status(500).send('server inner error');
    }
})

//3. 启动监听服务
app.listen(3000, () => {
    console.info('server start');
})
  1. 执行nodejs文件
  2. 路由文件写法
//router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '../component/HelloWorld.vue'

Vue.use(Router)
//传统路由写法
//exprot default new Router({
    //routers: [{
        //path: '/',
        //name: 'hello world',
        //component: HelloWorld
    //}]
//})

//ssr路由写法
//面试题:为什么会这样写?为什么不导出一个router?
//因为用户的每个请求都要创建一个实例
exprot default function createRouter() {
    return new Router({
        routers: [{
            path: '/',
            name: 'hello world',
            component: HelloWorld
        }]
    })
}
//app.js
import Vue from 'vue'
import App from './App.vue'
import createRouter from '../router'

exprot default function createApp() {
    const router = createRouter();
    const app = new Vue({
        router,
        render: h => h(App)
    })
    return { app, router }
}