状态管理
前端以前是没有状态管理的,直到Fackbook提出一个叫Flux的概念,才有了状态管理。
以前前端是通过MVC模式管理代码,但后来我们使用例如Vue、React、Angular这类通过声明式开发的框架,发现状态很难管理,容易出现状态被任意修改。当应用越来越大,这种不确定性会导致系统不稳定,而且定位bug也变得困难。
Vue2的状态管理使用的是vuex ,在正常开发中,我们都知道应该做到低耦合,数据不应该在多组件间相互影响,因为这样会难以维护。但是如果项目中确实需要一批数据在多组件中进行操作,且会互相影响。我们就需要使用vuex 。
让我们来实现一个简单的vuex的mutation
简单的mutation
首先我们要知道的是,我们new一个Vue会自动产生响应式,接下来就可以进行vuex的编写了
function createStore ({ state, mutations }) {
return new Vue({
data: {
state
},
methods: {
commit (mutation) {
if (!mutations.hasOwnProperty(mutation)) {
throw new Error('Unknown mutation')
}
mutations[mutation](state)
}
}
})
}
实际上,上面的内容就是核心原理,接下来我们创建一个vuex实例
const store = createStore({
state: { count: 0 },
mutations: {
inc (state) {
state.count++
}
}
})
接下来使用即可
获取值
const Counter = {
render (h) {
return h('div', store.state.count)
}
}
修改值
methods: {
inc () {
store.commit('inc')
}
}
路由
在vue中我们经常会使用到路由,路由有hash和history两种模式,二者的实现思路基本相同,为了简便,我们以hash模式为例
监听路由变化的事件,修改data中的url,再通过动态组件变化,这样就实现了一个最基本的路由。
<div id="app">
<component :is="url"></component>
<a @click="routeTo('#foo')" href="#foo">foo</a>
<a @click="routeTo('#bar')" href="#bar">bar</a>
</div>
<script>
window.addEventListener('hashchange', () => {
app.url = window.location.hash.slice(1)
})
const app = new Vue({
el: '#app',
data: {
url: 'foo'
},
components: {
foo: { template: `<div>foo</div>`},
bar: { template: `<div>bar</div>`},
},
methods: {
routeTo (route) {
window.location.hash = route
}
}
})
</script>
当然,上面的路由都是写死的,如何做一个可配置的路由呢
可配置路由
接收一个路由表routeTable ,监听路由变化并修改vue实例中的url,使用计算属性得出当前的组件名,再动态显示
<div id="app">
<component :is="matchedComponent"></component>
<a href="#foo">foo</a>
<a href="#bar">bar</a>
</div>
<script>
const Foo = { template: `<div>foo</div>` }
const Bar = { template: `<div>bar</div>` }
const NotFound = { template: `<div>not found!</div>` }
const routeTable = {
foo: Foo,
bar: Bar
}
window.addEventListener('hashchange', () => {
app.url = window.location.hash.slice(1)
})
const app = new Vue({
el: '#app',
data: {
url: 'foo'
},
computed: {
matchedComponent () {
return routeTable[this.url] || NotFound
}
}
})
</script>
动态路由
我们使用第三方库path-to-regexp 用于路由规则匹配
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="./path-to-regexp.js"></script>
<div id="app"></div>
<script>
const Foo = {
props: ['id'],
template: `<div>foo with id: {{ id }}</div>`
}
const Bar = { template: `<div>bar</div>` }
const NotFound = { template: `<div>not found!</div>` }
const routeTable = {
'/foo/:id': Foo,
'/bar': Bar
}
const compiledRoutes = []
Object.keys(routeTable).forEach(key => {
const dynamicSegments = []
const regex = pathToRegexp(key, dynamicSegments)
const component = routeTable[key]
compiledRoutes.push({
component,
regex,
dynamicSegments
})
})
window.addEventListener('hashchange', () => {
app.url = window.location.hash.slice(1)
})
const app = new Vue({
el: '#app',
data: {
url: window.location.hash.slice(1)
},
render (h) {
const path = '/' + this.url
let componentToRender
let props = {}
compiledRoutes.some(route => {
const match = route.regex.exec(path)
componentToRender = NotFound
if (match) {
componentToRender = route.component
route.dynamicSegments.forEach((segment, index) => {
props[segment.name] = match[index + 1]
})
return true
}
})
return h('div', [
h(componentToRender, { props }),
h('a', { attrs: { href: '#foo/123' }}, 'foo 123'),
' | ',
h('a', { attrs: { href: '#foo/234' }}, 'foo 234'),
' | ',
h('a', { attrs: { href: '#bar' }}, 'bar'),
' | ',
h('a', { attrs: { href: '#garbage' }}, 'garbage')
])
}
})
</script>
|