Vue全家桶 + webpack 构建单页应用初体验

17次阅读

共计 5265 个字符,预计需要花费 14 分钟才能阅读完成。

文章指南
主题
  承接这上一篇 Vue + Webpack 构建模块化开发框架详解,我们知道了如何使用 webpack 对 vue 进行打包,从而开始我们的前端模块化开发之路,这一篇在上一篇的基础上讲解 Vue 全家桶(vue+vuex+vue-router+axios) + webpack 构建一个单页应用 Demo
前提
  阅读本篇内容之前,除了需要掌握上一篇内容中的前提部分的知识,还需要了解以下内容????
Vue 全家桶系列,掌握 vuex,vue-router 以及 axios,不了解请移步官方教程 axios-npm,vuex,vue-router

vue 全家桶简单介绍 vuex : vuex 是一种集中式状态管理模式,什么意思呢?我们在模块化开发过程中,我们以组件来作为模块单位,模块之间存在于不同的命名空间,作用域互不干预,这样保证了我们模块之间变量函数名称等不会冲突,但是有时候我们我们需要组件之间共享一些数据或者状态,我们通常的做法是传参,但是传参的做法至少有两个弊端,一是麻烦(尤其是当需要传递的参数很多时),二是不好管理且冗余(给多个组件传参就需要多份参数列表,而且容易出错)。vuex 提供的集中式管理就解决了这个问题,通过把要共享的数据或状态集中起来管理,别的组件需要时就去访问变更,大大提高了可维护性和开发效率 vue-router : vue-router 是一个前端路由管理器,这个和后端常听说的路由有些不同(个人觉得),这里的路由管理器更像是一个组件注册器,vue-router 为分散的组件注册一个路由或者叫地址也未尝不可,以方便我们控制组件的层级嵌套关系以及隐藏还是显示,这样我们可以很方便高效的构建单页应用
axios : axios 和 jquery.ajax/vue-resource 一样,都是 HTTP 异步请求的工具,axios 和 vue-resource 的 API 很像,但是个人觉得,axios 的 API 更丰富一些

正文

 本文的小 Demo 大概功能就是一个登陆的功能,我们这里 app.vue 封装了一个登陆组件,success.vue 封装了一个提示面板,通过 vuex 来集中管理登陆状态,用 vue-router 来路由提示面板,用 axios 来异步提交到一个跨域的服务器(这里后台服务是 nginx+php 提供,所以在 dev-server 中要设置一个反向代理,也就是 nginx 来代理我们的 dev-server 的请求从而解决 axios 跨域问题)
先来看看我们的项目结构 [自定义的]

这里的目录设置,并不是一成不变的金科玉律,也不一定是最好的,读者可以根据自己的想法设定文件结构,了解如何配置单入口单出口的 webpack.config.js,可以看我的上一篇文章(开头提到了),或者移步 webpack 官方网站。本篇内容,不再讲解过多 webpack 配置,和上一篇是基本相同的,下面主要讲解 Vue 全家桶相关内容
index.js
import Vue from ‘vue’;
import Router from ‘./routers/index-router’;
import Store from ‘./stores/index-store’;
import App from ‘./components/App.vue’;

var app = new Vue({// 创建一个 Vue 实例
router : Router, // 加入路由配置
store : Store, // 加入状态管理
components : {App} // 加入 App 组件
}).$mount(‘#app’); // 挂载节点

需要注意的是,路由和状态都需要加入一个 vue 实例中去才有意义。这里有一点很有意思,就是在路由 router 中注册的组件 (success.vue) 依然可以访问到状态 store 实例,而官方教程上并没有特别强调这一点,在下面我们可以看到这个有趣的事情
stores/index-store.js
import Vuex from ‘vuex’;
import Vue from ‘vue’;
import axios from ‘axios’;

Vue.use(Vuex); // 在 vue 中加入 Vuex 插件
const Store = new Vuex.Store({// 实例化一个 Store
state : {// 这里是我们需要集中管理的登陆状态
account : “ads”,
password : “123456”,
islogin : null
},
mutations : {// 这是唯一可以变更 state 的途径,需要通过一个提交
updateAccount(state,payload){// 具有载荷的 mutation
state.account = payload.account
},
updatePassword(state,payload){
state.password = payload.password
},
islogin(state,payload){
if(payload == 1){
state.islogin = true
}else if(payload == 0){
state.islogin = false
}
}
},
actions : {// 这是可以支持异步执行提交的 actions
login (context,payload){
// 这里是去访问我们的反向代理服务器上的一个 php 文件
axios.get(‘/index.php’,{
params : {
account : payload.account,
password : payload.password
}
}).then(function(response){
if(response.data.code == ‘success’){
// 变更 store 状态
context.commit(‘islogin’,1) // 这里做了在异步逻辑中的状态提交
}else{
context.commit(‘islogin’,0)
}
}).catch(function(error){
console.log(error)
alert(‘fail’)
})
}
}
})

export default Store
我们这里的 Store 有三个属性,state 是我们需要集中管理的状态或者数据,mutations 是维护变更我们状态的一个方式,actions 是为了在异步逻辑中提交状态的一个中转站,需要注意的是:
在实例化 Store 之前,必须先 Vue.use(Vuex),先在 vue 中加入 Vuex 插件
这里需要说一下的是,如何开启 dev-server 的 proxy,让 nginx 来代理 axios 的请求,也就是跨域访问了(因为 dev-server 跑的是 8080 端口,nginx 跑的是 80 端口,不设置代理的话浏览器将拒绝跨域访问),感觉很深奥,其实很简单,我们需要修改一下 webpack.config.js 中 devServer 的配置:
webpack.config.js
devServer : {
contentBase : ‘./dist’,
watchContentBase : true,
compress : true,
port : 8080,
hot : true,
inline : true, // 开启页面自动刷新
open : true,
proxy : {
‘/index.php’ : {
target : ‘http://localhost:80/phpinfo.php’,
secure : false
}
}
}
然后,我们的 axios 在配置 url 时使用 /index.php,就可以相当于访问 http://localhost:80/phpinfo.php
routers/index-router.js
import VueRouter from ‘vue-router’;
import Vue from ‘vue’;
import success from ‘../components/success.vue’;

// 组件注册路由,这里 success 组件注册路由是 ’/app’
var routes = [
{
path : ‘/app’,component : success
}
]
Vue.use(VueRouter); // 在 vue 中加入 VueRouter 插件
var Router = new VueRouter({// 实例化一个 router
routes // 这里的写法相当于 routes:routes
})

export default Router;
这里的路由只注册了一个组件,vue-router 的能力远不止如此,还可以嵌套定义组件的层级关系,更多到官方文档了解,路由器和状态管理器一样都是 vue 官方核心插件,使用前都需要先 Vue.use(VueRouter),我们其实可以看到,这里把路由和状态分别从 vue 实例中抽离出来,单独管理,通过暴露实例 Store Router,加入到 vue 实例中去,这样可以方便 debug 和后期维护
components/App.vue 和 components/success.vue
App.vue
<template>
<div>
<input type=”text” v-model=”account”>
<input type=”password” v-model=”password”>
<button type=”button” @click=”login”>show</button>
</div>
</template>
<script>
export default {
data : function(){
return {
account : “”,
password : “”
}
},
created : function(){
this.account = this.$store.state.account
this.password = this.$store.state.password
},
watch : {// 侦听属性
account : function(){ // 当 accout 改变就立即提交状态
this.$store.commit(‘updateAccount’,{
account : this.account
})
},
// 当 password 改变就立即提交状态
password : function(){
this.$store.commit(‘updatePassword’,{
password : this.password
})
}
},
methods : {
login : function(){ // 通过分发到 action,来异步请求登录服务,并变更状态
this.$store.dispatch(‘login’,{
account : this.account,
password : this.password
})
this.$router.push(‘/app’);// 同时显示登陆状态
}
}
}
</script>
<style scoped>
input {
height: 40px;
width: 300px;
border: 1px solid blue;
box-shadow: none;
}
</style>
success.vue
<template>
<div>
<p>{{islogin}}</p>
</div>
</template>
<script>
export default {
computed : {// 计算属性
// 动态响应状态的变更
islogin : function(){
var islogin = this.$store.state.islogin
if(islogin == null){
return ‘No option’
}else if(islogin == true){
return ‘Login Success’
}else if(islogin == false){
return ‘Login Fail’
}
}
}
}
</script>
<style scoped>
p {
height: 40px;
text-align: center;
}
</style>
templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″>
<title>index</title>
</head>
<body>
<div id=”app”>
<!– App 组件渲染出口 –>
<App></App>
<!– 注册路由的组件渲染出口 –>
<router-view></router-view>
</div>
</body>
</html>
总结
好了,到此为止,代码基本完成,我们来理一理,vue+vuex+vue-router 以及组件之间是如何配合工作的?
用户在 App.vue 组件中输入,v-model 双向绑定了 App 组件中的 data,App 组件中的侦听属性发现 data 改动,立即向 vuex 的 Store 实例提交了状态变更,当用户点击 show,App 组件中 methods.login 事件被触发,它向 Store 实例发起一个分发,并导航到 /app,Store 的 action 收到分发调用 axios 去异步请求位于 localhost:80 下的 php 后台服务,得到的登陆状态立即提交,与此同时,Router 去渲染 /app 路由对应的组件到模板中去,于是我们就可以看到一个动态响应式的状态提示了

正文完
 0