github传送门
webpack之一webpack基础配置
webpack之二webpack部署优化
webpack之四集成ssr
-
集成vue-router
- 新建/client/config/router.js和/client/config/routes.js
- 安装vue-router
cnpm i vue-router -S
- 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
}
]
- 修改webpack.config.client.js里的
HtmlWebpackPlugin({template: path.join(__dirname, './template.html')})
然后新建/build/template.html - 在/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
})
}
- 在/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)
})
- 在App组件内 添加<router-view>标签
-
vue-router的选项配置
url的hash模式和history模式: hash路由用来做路由定位, 不做状态记录, 不被浏览器解析, 不利于seo; history
- 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, 页面会跳转
- 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对象包含query, params, meta, name等等还可以通过props传
--props
: 默认为false, 返回一个对象, 对象的key就是传到组件中的props
- 1.定义为ture的话, 可以把路由参数作为props传入组件(组件需要定义对应的props), 可以更好的解耦组件,
- 还可以直接传值{id: '123'},
- 也可以是一个带route参数的函数,
route => ({id: route.query.b// route.meta.title 等等})
- 也可以是一个带route参数的函数,
- 路由动画<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>
- 具名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
}
}
]
-
路由导航守卫, 钩子
- 全局守卫
-- 1router.beforeEach(to, from, next)
-> 进入路由前, 进行数据校验
-- 2router.beforeReaolve(to, from, next)
-> next(path: '/', replace: true), 他在afterEach之前触发
-- 2router.afterEach(to, from)
-> 进入路由后 - routes下的守卫
--beforeEnter(to, from, next)
-> 进入路由之前, 在beforeEach和beforeResolve()之间 - 组件内的守卫
--beforeRouteEnter(to, from, next)
-> 进入本组件时触发
--beforeRouteUpdate(to, from, next)
-> 在使用路由参数时, 当同路由参数互相切换时触发
--beforeRouteLeave(to, from, next)
-> 离开本组件后,触发, 使用场景, 表单中提醒是否确认要离开
-- 组件内守卫的next参数, 接受一个vue实例, next(vm => { console.log(vm) })
-
异步组件
- 修改/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')
}
]
- 引入babel插件babel-plugin-syntax-dynamic-import, 在.babelrc里添加此插件,修改routes.js
component: () => import(/* webpackChunkName: "login-view" */ '../views/login/login.vue')
-
vuex集成
- 新建/client/store/store.js
import Vuex from 'vuex'
export default () => {
return new Vuex.Store({
state: {
count: 0
}
})
}
- 在/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的选项
- 基本选项:
--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>
-
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 - '
}
}
}
})
}
- vuex动态加载子模块: 调用
store.registerModule('c', { state, mutations, actions, getters})
- 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
}
})
})
}
- 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参数的函数