美文网首页
wepack从0开始配置vue环境之三: 集成vuex+vue-

wepack从0开始配置vue环境之三: 集成vuex+vue-

作者: 胖太_91bf | 来源:发表于2018-04-09 08:23 被阅读0次

    github传送门
    webpack之一webpack基础配置
    webpack之二webpack部署优化
    webpack之四集成ssr

    • 集成vue-router

    1. 新建/client/config/router.js和/client/config/routes.js
    2. 安装vue-routercnpm i vue-router -S
    3. routes.js用来存放路由映射
    import App from '../views/app/App.vue'
    import Login from '../views/login/login.vue'
    
    export default [
      {
        path: '/app',
        component: App
      },
      {
        path: '/login',
        component: Login
      }
    ]
    
    
    1. 修改webpack.config.client.js里的HtmlWebpackPlugin({template: path.join(__dirname, './template.html')})然后新建/build/template.html
    2. 在/client/config/router.js中引入vue-router, 导出匿名实例到index.js
    import VueRouter from 'vue-router'
    import routes from './routes.js'
    
    export default () => {
      return new VueRouter({
        // mode: 'history',
        routes
      })
    }
    
    
    1. 在/client/index.js中引入vue-router, 通过Vue.use()方法集成插件, 引入router实例, 添加到vue实例中
    import Vue from 'vue'
    import App from './app.vue'
    import VueRouter from 'vue-router'
    import '@assets/css/reset.styl'
    import createRouter from './config/router'
    
    Vue.use(VueRouter)
    const router = createRouter()
    
    new Vue({
      el: '#root',
      router,
      render: h => h(App)
    })
    
    1. 在App组件内 添加<router-view>标签
    • vue-router的选项配置

    url的hash模式和history模式: hash路由用来做路由定位, 不做状态记录, 不被浏览器解析, 不利于seo; history

    1. router的选项:
      -- mode: ['hash', 'history', ''], 在使用history模式时, 需要配置webpack-dev-server, 添加historyApiFallBack, 把路由重新定位到index.html, output中的publicPath是需要和historyApiFallBack.index中的路径是相对应的
      historyApiFallback: {
        index: '/public/index.html'
      }
    

    -- routes: 配置路由和组件之间的映射
    -- base: '/base/' -> 为path添加一个基路径, 没有base也能跳转
    -- linkActiveClass: ' ' -> 路由的不完全匹配, 匹配后, 会在对应的<routet-link>添加一个active类
    -- linkExactActiveClass: ' '路由的完全匹配必须一模一样才行,会在对应的<routet-link>添加一个active类
    -- scrollBehavior: 用来记录之前浏览网页滚动的位置, to, 和from是route对象

    scrollBehavior (to, from, savedPosition) {
      if(savedPosition) {
        return savedPosition
      } else {
        return  {x:0, y:0}
      }
    }
    

    -- parseQuery(query){}: 可以自定义query转成json
    -- stringifyQuery(){obj}: 可以自动以json转成字符串
    -- fallback: 默认true, 在不支持history的浏览器上自动转成hash, 设置成false, 页面会跳转

    1. routes的选项:
      -- name: 命名, 可以通过name跳转
      -- path: 路由的路径
      -- component: 需要渲染的组件
      -- components: 具名router-view中使用, 接受一个对象, 没有name属性的router-view为default
      -- redirect: 路由重定向
      -- meta:obj -> spa用的都是一个html, 无法修改元信息, 通过meta可以储存页面的元信息, 存储在route对象上
      -- children: 设置自路由, 需要加router-view
      -- path: '/app/:id': 路由传参, 通过this.route.params.id获取,route对象包含query, params, meta, name等等还可以通过props传
      -- props: 默认为false, 返回一个对象, 对象的key就是传到组件中的props
    • 1.定义为ture的话, 可以把路由参数作为props传入组件(组件需要定义对应的props), 可以更好的解耦组件,
      1. 还可以直接传值{id: '123'},
      1. 也可以是一个带route参数的函数, route => ({id: route.query.b// route.meta.title 等等})
    1. 路由动画<transition">:一个小套路:
    <transition name="v" mode="out-in">
      <router-view></router-view>
    </transition>
    <style>
      .v-enter-active, .v-leave-active {
        transition: all 0.5s ease-in-out
      }
      .v-enter, .v-leave-to {
        opacity: 0; // fade
        transform: translate3d(-100%, 0, 0) // 右进场动画
      }
    </style>
    
    1. 具名router-view
    <router-view></router-view>
    <router-view name="top"></router-view>
    <router-view name="bottom"></router-view>
    routes: [
      {
        path: '/app',
        components: {
          default: App, // 不具名的router-view
          top: AppTop,
          bottom: AppBottom
        }
      }
    ]
    
    • 路由导航守卫, 钩子

    1. 全局守卫
      -- 1router.beforeEach(to, from, next) -> 进入路由前, 进行数据校验
      -- 2router.beforeReaolve(to, from, next) -> next(path: '/', replace: true), 他在afterEach之前触发
      -- 2router.afterEach(to, from) -> 进入路由后
    2. routes下的守卫
      -- beforeEnter(to, from, next) -> 进入路由之前, 在beforeEach和beforeResolve()之间
    3. 组件内的守卫
      -- beforeRouteEnter(to, from, next) -> 进入本组件时触发
      -- beforeRouteUpdate(to, from, next) -> 在使用路由参数时, 当同路由参数互相切换时触发
      -- beforeRouteLeave(to, from, next) -> 离开本组件后,触发, 使用场景, 表单中提醒是否确认要离开
      -- 组件内守卫的next参数, 接受一个vue实例, next(vm => { console.log(vm) })
    • 异步组件

    1. 修改/client/config/routes.js
    // import App from '../views/app/App.vue'
    // import Login from '../views/login/login.vue'
    
    export default [
      {
        path: '/',
        redirect: '/app'
      },
      {
        path: '/app/:id',
        // component: App
        component: import('../views/app/App.vue')
      },
      {
        path: '/login',
        // component: Login
        component: import('../views/login/login.vue')
      }
    ]
    
    1. 引入babel插件babel-plugin-syntax-dynamic-import, 在.babelrc里添加此插件,修改routes.js
     component: () => import(/* webpackChunkName: "login-view" */ '../views/login/login.vue')
    
    • vuex集成

    1. 新建/client/store/store.js
    import Vuex from 'vuex'
    
    export default () => {
      return new Vuex.Store({
        state: {
          count: 0
        }
      })
    }
    
    1. 在/client/index.js下配置vuex
    import Vue from 'vue'
    import App from './app.vue'
    import VueRouter from 'vue-router'
    import Vuex from 'vuex'
    import '@assets/css/reset.styl'
    import createRouter from './config/router'
    import createStore from './store/store'
    
    Vue.use(VueRouter)
    Vue.use(Vuex)
    
    const router = createRouter()
    const store = createStore()
    
    // router.beforeEach((to, from, next) => {
    //   console.log(to)
    //   if (to.fullPath === '/app') next({path: '/login', replace: true})
    //   else next()
    // })
    
    new Vue({
      el: '#root',
      router,
      store,
      render: h => h(App)
    })
    
    • vuex的选项

    1. 基本选项:
      -- strict: 定义为true时, 直接修改state(不通过mutations)会发出警告
      -- state: 存储数据
      -- mutations: 同步改变state
      -- actions: 异步改变state
      -- getters: 相当于computed, 属性值是state为参数的函数
    // 配置:
    import Vuex from 'vuex'
    
    export default () => {
      return new Vuex.Store({
        state: {
          count: 0,
          first: 'zhang',
          last: 'kay'
        },
        mutations: {
          updateCount (state, count) {
            state.count = count
          }
        },
        actions: {
          updateCountAsync (store, count) {
            setTimeout(() => {
              store.commit('updateCount', count)
            }, 1000)
          }
        },
        getters: {
          fullName: (state) => `${state.first}-${state.last}`
        }
      })
    }
    //map用法
    <template>
      <div>
        <p>count: {{myCount1}}</p>
        <p>name: {{myName}}</p>
        <button @click="myUC(1)">mutation</button><br>
        <button @click="updateCountAsync(10)">action</button>
      </div>
    </template>
    <script>
    import {
      mapState,
      mapActions,
      mapMutations,
      mapGetters
    } from 'vuex'
    export default {
      computed: {
        // ...mapState(['count']),
        ...mapState({
          myCount: 'count',
          myCount1: state => state.count
        }),
        // ...mapGetters(['fullName'])
        ...mapGetters({
          myName: 'fullName'
        })
      },
      methods: {
        // ...mapMutations(['updateCount']),
        ...mapMutations({
          myUC: 'updateCount'
        }),
        ...mapActions(['updateCountAsync'])
      }
    }
    </script>
    
    1. modules的应用
      -- 未使用namespaced参数之前, mutations, actions, getters是注册到全局命名空间的, 但是mutations里的state是module内的,用法与全局的一直
    <template>
      <div>
        <p>aText: {{aText}}</p>
        <p>_text: {{_text}}</p>
      </div>
    </template>
    <script>
    import {
      mapState,
      mapMutations,
      mapActions,
      mapGetters
    } from 'vuex'
    export default {
      computed: {
        ...mapState({
          aText: state => state.a.text
        }),
        ...mapGetters({
          _text: '_text'
        })
      },
      methods: {
        ...mapMutations(['updateA']),
        ...mapActions(['updateAAsync'])
      },
      mounted () {
        console.log(this.$store.state.a.aText)
        this.updateA('xxx')
        this.updateAAsync({
          text: 'action - a',
          time: 2000
        })
      }
    }
    </script>
    

    -- 使用namespaced的module,使用actions, mutations, getters,的注册需要以路径的形式, 如下, bModule为启动namespaced的模块

    <template>
      <div>
        <p>aText: {{aText}}</p>
        <p>a的_text: {{a_text}}</p>
        <p>bText: {{bText}}</p>
        <p>b的_text: {{b_text}}</p>
      </div>
    </template>
    <script>
    import {
      mapState,
      mapMutations,
      mapActions,
      mapGetters
    } from 'vuex'
    export default {
      computed: {
        ...mapState({
          aText: state => state.a.text,
          bText: state => state.b.text
        }),
        ...mapGetters({
          a_text: '_text',
          b_text: 'b/_text'
        })
      },
      methods: {
        ...mapMutations(['updateText', 'b/updateText']),
        ...mapActions(['updateTextAsync', 'b/updateTextAsync'])
      },
      mounted () {
        console.log(this.$store.state.a.aText)
        this.updateText('mutations - a')
        this.updateTextAsync({
          text: 'actions - a',
          time: 2000
        })
        this['b/updateText']('mutations - b')
        this['b/updateTextAsync']({
          text: 'actions - b',
          time: 2000
        })
      }
    }
    </script>
    

    -- 模块的actions可以调用全局mutations, 通过commit('mutations', data, {options})的第三个参数配置为{root: true}

     methods: {
        ...mapMutations(['updateText', 'b/updateText']),
        ...mapActions(['updateTextAsync', 'b/updateTextAsync', 'b/updateCountAsync'])
      },
    // ...
    this['b/updateCountAsync']()
    
    store.js的配置如下
    import Vuex from 'vuex'
    
    const isDev = process.env.NODE_ENV === 'development'
    
    export default () => {
      return new Vuex.Store({
        strict: isDev,
        state: {
          count: 0,
          first: 'zhang',
          last: 'kay'
        },
        mutations: {
          updateCount (state, count) {
            state.count = count
          }
        },
        actions: {
          updateCountAsync (store, count) {
            setTimeout(() => {
              store.commit('updateCount', count)
            }, 1000)
          }
        },
        getters: {
          fullName: (state) => `${state.first}-${state.last}`
        },
        modules: {
          a: {
            state: {
              text: 'aaaa'
            },
            mutations: {
              updateText (state, text) {
                state.text = text
              }
            },
            actions: {
              updateTextAsync ({commit}, {text, time}) {
                setTimeout(() => {
                  commit('updateA', text)
                }, time)
              }
            },
            getters: {
              _text: state => state.text + '-getters'
            }
          },
          b: {
            namespaced: true,
            state: {
              text: 'bbb'
            },
            mutations: {
              updateText (state, text) {
                state.text = text
              }
            },
            actions: {
              updateTextAsync ({commit, state, rootState}, {text, time}) {
                setTimeout(() => {
                  commit('updateText', text + rootState.first)
                }, time)
              },
              updateCountAsync ({commit, state, rootState}) {
                commit('updateCount', state.text, {root: true})
              }
            },
            getters: {
              // 可以使用全局的getters和全局state
              _text: (state, getters, rootState) => state.text + 'b-getters -  '
            }
          }
        }
      })
    }
    
    1. vuex动态加载子模块: 调用store.registerModule('c', { state, mutations, actions, getters})
    2. vuex的热重载:1. 将Store实例赋值给一个变量store 2. 使用module.hot.accept([], () => { 3. 在回调里调用store.hotUpdate({更新模块})}), 学modules的模块时需要注意, 加上modules
    import Vuex from 'vuex'
    import defaultState from './state/state'
    import mutations from './mutations/mutations'
    import actions from './actions/actions'
    import getters from './getters/getters'
    import a from './a'
    import b from './b'
    
    const isDev = process.env.NODE_ENV === 'development'
    
    export default () => {
      const store = new Vuex.Store({
        strict: isDev,
        state: defaultState,
        mutations,
        actions,
        getters,
        modules: {
          a,
          b
        }
      })
     
      if (module.hot) {
        module.hot.accept([
          './state/state',
          './mutations/mutations',
          './actions/actions',
          './getters/getters',
          './a',
          './b'
        ], () => {
          const newState = require('./state/state').default
          const newMutations = require('./mutations/mutations').default
          const newActions = require('./actions/actions').default
          const newGetters = require('./getters/getters').default
          const newA = require('./a').default
          const newB = require('./b').default
    
          store.hotUpdate({
            state: newState,
            mutations: newMutations,
            actions: newActions,
            getters: newGetters,
            modules: {
              a: newA,
              b: newB
            }
          })
        })
      }
      return store
    }
    

    vuex热重载总结: 通过webpack自带的module.hot.accept([], () => {})方法和vue的store.hotUpdate({}); module.hot.accept()第一个参数是一个数组, 存储这需要热重载的模块的路径, 第二个参数是一个回调韩式, 在这里从新引入[]里的模块, 在通过store.hotUpdate()方法重新赋值

     if (module.hot) {
        module.hot.accept([
          './state/state',
          './mutations/mutations',
          './actions/actions',
          './getters/getters',
          './a',
          './b'
        ], () => {
          const newState = require('./state/state').default
          const newMutations = require('./mutations/mutations').default
          const newActions = require('./actions/actions').default
          const newGetters = require('./getters/getters').default
          const newA = require('./a').default
          const newB = require('./b').default
    
          store.hotUpdate({
            state: newState,
            mutations: newMutations,
            actions: newActions,
            getters: newGetters,
            modules: {
              a: newA,
              b: newB
            }
          })
        })
      }
    
    1. vue的一些其他的API
      -- store.unregisterModule('a'): 卸载模块
      -- store.watch((state) => state.count + 1, (newCount) => {执行完前一个函数后调用 })
      -- store.subscribe(mutation, state) => { mutation.type, mutaion.payload(参数)} : 用来监听mutation的调用情况, 可以打日志
      -- store.subscribeAction(action, state) => {action.type, action.payload(参数)}
      -- plugins: vuex实例里的一个选项, 类似(state啥的), 值为一个数组, 数组内的每一项为一个带store参数的函数

    相关文章

      网友评论

          本文标题:wepack从0开始配置vue环境之三: 集成vuex+vue-

          本文链接:https://www.haomeiwen.com/subject/kxuphftx.html