vue init webpack hello-vue
安装依赖css
# 进入工程目录 cd hello-vue # 安装 vue-router npm install vue-router --save-dev # 安装 element-ui npm i element-ui -S # 安装 SASS 加载器 npm install sass-loader node-sass --save-dev # 安装依赖 npm install
npm install moduleName
:安装模块到项目目录下npm install -g moduleName
:-g 的意思是将模块安装到全局,具体安装到磁盘哪一个位置,要看 npm config prefix 的位置npm install -save moduleName
:--save 的意思是将模块安装到项目目录下,并在 package 文件的 dependencies 节点写入依赖,-S
为该命令的缩写npm install -save-dev moduleName
:--save-dev 的意思是将模块安装到项目目录下,并在 package 文件的 devDependencies 节点写入依赖,-D
为该命令的缩写npm run dev
iview 是一个强大的基于 Vue 的 UI 库,有不少实用的基础组件比 elementui 的组件更丰富,主要服务于 PC 界面的中后台产品。使用单文件的 Vue 组件化开发模式 基于 npm + webpack + babel 开发,支持 ES2015 高质量、功能丰富 友好的 API ,自由灵活地使用空间。前端
备注:属于前端主流框架,选型时可考虑使用,主要特色是移动端支持较多vue
Element 是饿了么前端开源维护的 Vue UI 组件库,组件齐全,基本涵盖后台所需的全部组件,文档讲解详细,例子也很丰富。主要用于开发 PC 端的页面,是一个质量比较高的 Vue UI 组件库。java
备注:属于前端主流框架,选型时可考虑使用,主要特色是桌面端支持较多node
在 views
目录下建立一个名为 Main.vue
的视图组件;该组件在当前章节无任何做用,主要用于登陆后展现登陆成功的跳转效果;webpack
<template> <div> 首页 </div> </template> <script> export default { name: "Main" } </script> <style scoped> </style>
在 views
目录下建立一个名为 Login.vue
的视图组件,其中 el-*
的元素为 ElementUI 组件;ios
<template> <div> <el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box"> <h3 class="login-title">欢迎登陆</h3> <el-form-item label="帐号" prop="username"> <el-input type="text" placeholder="请输入帐号" v-model="form.username"/> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" placeholder="请输入密码" v-model="form.password"/> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="onSubmit('loginForm')">登陆</el-button> </el-form-item> </el-form> <el-dialog title="舒适提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose"> <span>请输入帐号和密码</span> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="dialogVisible = false">确 定</el-button> </span> </el-dialog> </div> </template> <script> export default { name: "Login", data() { return { form: { username: '', password: '' }, // 表单验证,须要在 el-form-item 元素中增长 prop 属性 rules: { username: [ {required: true, message: '帐号不可为空', trigger: 'blur'} ], password: [ {required: true, message: '密码不可为空', trigger: 'blur'} ] }, // 对话框显示和隐藏 dialogVisible: false } }, methods: { onSubmit(formName) { // 为表单绑定验证功能 this.$refs[formName].validate((valid) => { if (valid) { // 使用 vue-router 路由到指定页面,该方式称之为编程式导航 this.$router.push("/main"); } else { this.dialogVisible = true; return false; } }); } } } </script> <style lang="scss" scoped> .login-box { border: 1px solid #DCDFE6; width: 350px; margin: 180px auto; padding: 35px 35px 15px 35px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; box-shadow: 0 0 25px #909399; } .login-title { text-align: center; margin: 0 auto 40px auto; color: #303133; } </style>
在 router
目录下建立一个名为 index.js
的 vue-router 路由配置文件git
import Vue from 'vue' import Router from 'vue-router' import Login from "../views/Login" import Main from '../views/Main' Vue.use(Router); export default new Router({ routes: [ { // 登陆页 path: '/login', name: 'Login', component: Login }, { // 首页 path: '/main', name: 'Main', component: Main } ] });
修改 main.js
入口代码github
import Vue from 'vue' import VueRouter from 'vue-router' import router from './router' // 导入 ElementUI import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import App from './App' // 安装路由 Vue.use(VueRouter); // 安装 ElementUI Vue.use(ElementUI); new Vue({ el: '#app', // 启用路由 router, // 启用 ElementUI render: h => h(App) });
修改 App.vue
组件代码web
<template> <div id="app"> <router-view/> </div> </template> <script> export default { name: 'App', } </script>
嵌套路由又称子路由,在实际应用中,一般由多层嵌套的组件组合而成。一样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件。
在 views/user
目录下建立一个名为 Profile.vue
的视图组件;该组件在当前章节无任何做用,主要用于展现嵌套效果;
<template> <div> 我的信息 </div> </template> <script> export default { name: "UserProfile" } </script> <style scoped> </style>
在 views/user
目录下建立一个名为 List.vue
的视图组件;该组件在当前章节无任何做用,主要用于展现嵌套效果;
<template> <div> 用户列表 </div> </template> <script> export default { name: "UserList" } </script> <style scoped> </style>
修改 router
目录下的 index.js
路由配置文件,代码以下:
import Vue from 'vue' import Router from 'vue-router' import Login from "../views/Login" import Main from '../views/Main' // 用于嵌套的路由组件 import UserProfile from '../views/user/Profile' import UserList from '../views/user/List' Vue.use(Router); export default new Router({ routes: [ { // 登陆页 path: '/login', name: 'Login', component: Login }, { // 首页 path: '/main', name: 'Main', component: Main, // 配置嵌套路由 children: [ {path: '/user/profile', component: UserProfile}, {path: '/user/list', component: UserList}, ] } ] });
说明:主要在路由配置中增长了 children
数组配置,用于在该组件下设置嵌套路由
接着上一节的代码,咱们修改 Main.vue
视图组件,此处使用了 ElementUI 布局容器组件,代码以下:
<template> <div> <el-container> <el-aside width="200px"> <el-menu :default-openeds="['1']"> <el-submenu index="1"> <template slot="title"><i class="el-icon-caret-right"></i>用户管理</template> <el-menu-item-group> <el-menu-item index="1-1"> <router-link to="/user/profile">我的信息</router-link> </el-menu-item> <el-menu-item index="1-2"> <router-link to="/user/list">用户列表</router-link> </el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="2"> <template slot="title"><i class="el-icon-caret-right"></i>内容管理</template> <el-menu-item-group> <el-menu-item index="2-1">分类管理</el-menu-item> <el-menu-item index="2-2">内容列表</el-menu-item> </el-menu-item-group> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="text-align: right; font-size: 12px"> <el-dropdown> <i class="el-icon-setting" style="margin-right: 15px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>我的信息</el-dropdown-item> <el-dropdown-item>退出登陆</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <span>Lusifer</span> </el-header> <el-main> <router-view /> </el-main> </el-container> </el-container> </div> </template> <script> export default { name: "Main" } </script> <style scoped lang="scss"> .el-header { background-color: #B3C0D1; color: #333; line-height: 60px; } .el-aside { color: #333; } </style>
说明:
<el-main>
元素中配置了 <router-view />
用于展现嵌套路由<router-link to="/user/profile">我的信息</router-link>
展现嵌套路由内容咱们常常须要把某种模式匹配到的全部路由,全都映射到同个组件。例如,咱们有一个 User 组件,对于全部 ID 各不相同的用户,都要使用这个组件来渲染。此时咱们就须要传递参数了;
{path: '/user/profile/:id', name:'UserProfile', component: UserProfile}
说明:主要是在 path
属性中增长了 :id
这样的占位符
<router-link :to="{name: 'UserProfile', params: {id: 1}}">我的信息</router-link>
说明:此时咱们将 to
改成了 :to
,是为了将这一属性当成对象使用,注意 router-link 中的 name 属性名称 必定要和 路由中的 name 属性名称 匹配,由于这样 Vue 才能找到对应的路由路径;
this.$router.push({ name: 'UserProfile', params: {id: 1}});
在目标组件中使用
{{ $route.params.id }}
props
的方式{path: '/user/profile/:id', name:'UserProfile', component: UserProfile, props: true}
说明:主要增长了 props: true
属性
同上
为目标组件增长 props
属性,代码以下:
export default { props: ['id'], name: "UserProfile" }
模板中使用
{{ id }}
{ path: '/main', name: 'Main', component: Main }, { path: '/goHome', redirect: '/main' }
说明:这里定义了两个路径,一个是 /main
,一个是 /goHome
,其中 /goHome
重定向到了 /main
路径,由此能够看出重定向不须要定义组件;
设置对应路径便可
<router-link to="/goHome">回到首页</router-link>
{ // 首页 path: '/main/:username', name: 'Main', component: Main }, { path: '/goHome/:username', redirect: '/main/:username' }
<router-link to="/goHome/Lusifer">回到首页</router-link>
路由模式有两种
#
符号,如 http://localhost/#/login
#
符号,如 http://localhost/login
修改路由配置,代码以下:
export default new Router({ mode: 'history', routes: [ ] });
建立一个名为 NotFound.vue
的视图组件,代码以下:
<template> <div> 页面不存在,请重试! </div> </template> <script> export default { name: "NotFount" } </script> <style scoped> </style>
修改路由配置,代码以下:
{ path: '*', component: NotFound }
beforeRouteEnter
:在进入路由前执行beforeRouteLeave
:在离开路由前执行案例代码以下:
export default { props: ['id'], name: "UserProfile", beforeRouteEnter: (to, from, next) => { console.log("准备进入我的信息页"); next(); }, beforeRouteLeave: (to, from, next) => { console.log("准备离开我的信息页"); next(); } }
参数说明:
to
:路由将要跳转的路径信息from
:路径跳转前的路径信息next
:路由的控制参数
next()
跳入下一个页面next('/path')
改变路由的跳转方向,使其跳到另外一个路由next(false)
返回原来的页面next((vm)=>{})
仅在 beforeRouteEnter 中可用,vm 是组件实例安装 Axios
npm install axios -s
引用 Axios(修改main.js)
import axios from 'axios' Vue.prototype.axios = axios;
在 beforeRouteEnter
中进行异步请求,案例代码以下:
export default { props: ['id'], name: "UserProfile", beforeRouteEnter: (to, from, next) => { console.log("准备进入我的信息页"); // 注意,必定要在 next 中请求,由于该方法调用时 Vue 实例尚未建立,此时没法获取到 this 对象,在这里使用官方提供的回调函数拿到当前实例 next(vm => { vm.getData(); }); }, beforeRouteLeave: (to, from, next) => { console.log("准备离开我的信息页"); next(); }, methods: { getData: function () { this.axios({ method: 'get', url: 'http://localhost:8080/data.json' }).then(function (repos) { console.log(repos); }).catch(function (error) { console.log(error); }); } } }
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
继续以前 vue-router 章节作的案例项目,咱们经过完善登陆功能将用户信息保存至 Vuex 中来体会它的做用;
在项目根目录执行以下命令来安装 Vuex
咱们利用路由钩子 beforeEach
来判断用户是否登陆,期间会用到 sessionStorage
存储功能
Login.vue
在表单验证成功方法内增长以下代码:
// 设置用户登陆成功 sessionStorage.setItem('isLogin', 'true');
main.js
利用路由钩子 beforeEach
方法判断用户是否成功登陆,关键代码以下:
// 在跳转前执行 router.beforeEach((to, form, next) => { // 获取用户登陆状态 let isLogin = sessionStorage.getItem('isLogin'); // 注销 if (to.path == '/logout') { // 清空 sessionStorage.clear(); // 跳转到登陆 next({path: '/login'}); } // 若是请求的是登陆页 else if (to.path == '/login') { if (isLogin != null) { // 跳转到首页 next({path: '/main'}); } } // 若是为非登陆状态 else if (isLogin == null) { // 跳转到登陆页 next({path: '/login'}); } // 下一个路由 next(); });
vuex
在 src
目录下建立一个名为 store
的目录并新建一个名为 index.js
文件用来配置 Vuex,代码以下:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex); // 全局 state 对象,用于保存全部组件的公共数据 const state = { // 定义一个 user 对象 // 在组件中是经过 this.$store.state.user 来获取 user: { username: '' } }; // 实时监听 state 值的最新状态,注意这里的 getters 能够理解为计算属性 const getters = { // 在组件中是经过 this.$store.getters.getUser 来获取,页面上使用{{$store.getters.getUser.username}}来获取 getUser(state) { return state.user; } }; // 定义改变 state 初始值的方法,这里是惟一能够改变 state 的地方,缺点是只能同步执行 const mutations = { // 在组件中是经过 this.$store.commit('updateUser', user); 方法来调用 mutations updateUser(state, user) { state.user = user; } }; // 定义触发 mutations 里函数的方法,能够异步执行 mutations 里的函数 const actions = { // 在组件中是经过 this.$store.dispatch('asyncUpdateUser', user); 来调用 actions asyncUpdateUser(context, user) { context.commit('updateUser', user); } }; export default new Vuex.Store({ state, getters, mutations, actions });
修改 main.js
增长刚才配置的 store/index.js
,关键代码以下:
import Vue from 'vue' import Vuex from 'vuex' import store from './store' Vue.use(Vuex); new Vue({ el: '#app', store });
Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地获得高效更新。可是有一个问题就是:vuex 的存储的数据只是在页面的中,至关于咱们定义的全局变量,刷新以后,里边的数据就会恢复到初始化状态。可是这个状况有时候并非咱们所但愿的。
监听页面是否刷新,若是页面刷新了,将 state 对象存入到 sessionStorage 中。页面打开以后,判断 sessionStorage 中是否存在 state 对象,若是存在,则说明页面是被刷新过的,将 sessionStorage 中存的数据取出来给 vuex 中的 state 赋值。若是不存在,说明是第一次打开,则取 vuex 中定义的 state 初始值。
在 App.vue
中增长监听刷新事件
export default { name: 'App', mounted() { window.addEventListener('unload', this.saveState); }, methods: { saveState() { sessionStorage.setItem('state', JSON.stringify(this.$store.state)); } } }
修改 store/index.js
中的 state
const state = sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')) : { user: { username: '' } };
因为使用单一状态树,应用的全部状态会集中到一个比较大的对象。当应用变得很是复杂时,store 对象就有可能变得至关臃肿。为了解决以上问题,Vuex 容许咱们将 store 分割成模块(module)。每一个模块拥有本身的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行一样方式的分割
user
模块在 store
目录下建立一个名为 modules
的目录并建立一个名为 user.js
的文件,代码以下:
const user = { // 由于模块化了,因此解决刷新问题的代码须要改造一下 state: sessionStorage.getItem('userState') ? JSON.parse(sessionStorage.getItem('userState')) : { user: { username: '' } }, getters: { getUser(state) { return state.user; } }, mutations: { updateUser(state, user) { state.user = user; } }, actions: { asyncUpdateUser(context, user) { context.commit('updateUser', user); } } }; export default user;
store/index.js
import Vue from 'vue' import Vuex from 'vuex' import user from './modules/user' Vue.use(Vuex); export default new Vuex.Store({ modules: { // this.$store.state.user user } });
备注:因为组件中使用的是 getters
和 actions
处理,因此调用代码不变
App.vue
export default { name: 'App', mounted() { window.addEventListener('unload', this.saveState); }, methods: { saveState() { // 模块化后,调用 state 的代码修改成 this.$store.state.user(须要定位到user这个对象) sessionStorage.setItem('userState', JSON.stringify(this.$store.state.user)); } } }