1.vue-cli的介绍
cli: Command Line 命令行工具,vue-cli就是vue的命令行工具,也称之为脚手架
,使用vue-cli提供的各种命令可以拉取、创建、运行我们需要使用到的框架,比如webpack、Element UI、Element Admin等等。那么要想使用vue-cli命令,需要先安装node.js
。
1.1node.js的介绍及安装
1.1.1node.js的介绍
node.js提供了前端程序的运行环境,可以把node.js理解成是运行前端程序的服务器。
1.1.2node.js的安装
从官网下载安装即可: 下载 | Node.js 中文网
http://nodejs.cn/download/
不要勾选这个,否则会下载很多东西
测试node.js是否安装成功: 在DOS窗口中输入“node -v” 查看版本,如果看到版本,就表示安装成功。(环境变量已经自动配置好)
node -v 是 查看node的版本
npm -v 是 查看npm版本
npm是安装工具的工具包,类似于Linux中yum,Linux中安装软件 yum install ,node中安装软件使用npm install
设置npm源 , 配置镜像后,下载资源就会很快
npm config set registry https://registry.npmmirror.com
1.2使用node.js安装vue.cli
(管理员权限)使用如下命令安装vue-cli
npm install @vue/cli -g # 如果出错,可以卸载,重新执行上一步安装 npm uninstall @vue/cli -g
npm: 使用node.js的命令
install: 安装
@vue/cli: 要安装的vue-cli
-g: 全局安装
npm install @vue/cli -g # 如果出错,可以卸载,重新执行上一步安装 npm uninstall @vue/cli -g
当出现以下界面,表示正在安装: |
---|
|
安装vue-cli检测
1.3idea开发vue
1.4Bug总结
解决方案: 以管理员身份启动idea,打开项目运行
2.Vue-router 路由
2.1介绍
路由的本质就是一种对应关系,比如说我们在url地址中输入我们要访问的url地址之后,浏览器要去请求这个url地址对应的资源。
那么url地址和真实的资源之间就有一种对应的关系,就是路由。
路由分为前端路由和后端路由
1)后端路由是由服务器端进行实现,并完成资源的分发(我们之前的项目)
2)前端路由是依靠hash值(锚链接)的变化进行实现 (vue官网就是使用这种)
前端路由的基本概念: 根据
不同的事件来
显示不同的页面内容,即事件与事件处理函数之间的对应关系 前端路由主要做的事情就是监听事件并分发执行事件处理函数
2.2 vue的路由
Vue默认属于单页面的程序,主要通过URL中的hash来实现不同页面之间的切换。这种切换方式称作前端路由。
Vue Router的核心作用
跳转/切换页面
声明式(写标签)
<router-link to="要跳转的组件名称">
编程式(写代码)
this.$router.push("要跳转的组件名称")
跳转携带数据
编程式导航实现传递参数
传递 this.$router.push({path:'路径',params:{参数名:值,...}})
接收 this.$router.params.参数名
路由模式
hash模式:监听浏览器地址hash值变化,执行相应的js切换网页;
history模式:利用history API实现url地址改变,网页内容改变;
它们最明显的区别就是hash会在浏览器地址后面增加#号
2.3 安装路由模块
npm install vue-router # 如果报错,可能是npm版本过高,存在兼容性问题,那么使用如下命令 npm install --legacy-peer-deps vue-router@3.5.2 npm install --legacy-peer-deps vue-router@3.1.3 ps: 今天练习过一次以后,后续在创建项目时可以直接选择路由部分内容,创建出的项目直接就配置好了
3.声明式路由
需求:在主页与个人中心之间切换显示
3.1 创建MyInfo.vue
路由页面建议创建在src/views/下
<template> <div> <h1>个人中心</h1> </div> </template> <script> export default { name: "MyInfo" } </script> <style scoped> </style>
3.2创建Admin.vue
<template> <div> <h1>主页</h1> <p>主页</p> <p>主页</p> <p>主页</p> <p>主页</p> <p>主页</p> <p>主页</p> <p>主页</p> <p>主页</p> </div> </template> <script> export default { name:'Admin' } </script> <style scoped> </style>
3.3 创建静态路由表
在/src下创建router文件夹,
在/src/router/创建index.js文件
填充以下内容
import Vue from 'vue' import Router from 'vue-router' import HelloWorld from '../components/HelloWorld' import MyInfo from "../views/MyInfo"; Vue.use(Router) // export是导出路由对象,只有在这里导出了,main.js文件头上面才能 import 导入 export default new Router({ routes: [ { path: '/', // 匹配<router-link to="">中的路径 name: 'HelloWorld', // 这个name属性可写可不写 component: HelloWorld // 要跳转的组件对象 }, { path: '/MyInfo', name: 'MyInfo', component: MyInfo } ], // mode: 'history' mode: 'hash' })
3.4 main.js引入路由模块并使用
在main.js中引入路由模块并使用
import Vue from 'vue' import App from './App' import router from './router' //引入上一步导出的路由模块,并设置名字为router Vue.config.productionTip = false new Vue({ render: h => h(App), // router:router // 使用路由,可以简写为router router }).$mount('#app')
3.5 App.vue使用路由
<template> <div id="app"> <ul> <li> <!-- <router-link>用于导航,其实就是a标签 to表示要跳转的资源的路径 tag 将router-link渲染成想要的原生标签,默认是a标签,可以改成button --> <router-link to="/" tag="button">首页</router-link> </li> <li> <router-link to="/MyInfo">个人信息</router-link> </li> </ul> <!-- router-view 路由填充位,要跳转的组件在此展示,位置任意 --> <router-view/> </div> </template> <script> export default { name: 'App', } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
启动测试
练习完后可以重写创建vue项目,选择Router依赖,这样创建出的项目自带路由配置
3.6 编程式路由
刚才演示的是声明式路由,也可以使用编程式路由
演示: 主页与公共页面切换
创建公告页Note.vue
<template> <div> <h1>公告页面</h1> <ol> <li>老杨不瞌睡了</li> <li>萌萌也+1</li> <li>好好做操</li> </ol> </div> </template> <script> export default { name: "Note" } </script> <style scoped> </style>
src/router/index.js 设置路由规则
export default new Router({ routes: [ { path: '/', name: 'HelloWorld', component: HelloWorld }, { path: '/MyInfo', name: 'MyInfo', component: MyInfo }, { path: '/Note', name: 'Note', component: Note } ], // mode: 'history' mode: 'hash' })
App.vue设置标签,绑定事件,事件触发函数,函数内编写代码实现路由
this.$router.push("/路径") --> <router-link to="/路径">
this.$router.push("/Note") // 注意!!!!别写错,有个叫做this.$route
bug: 第一次点击跳转没事,再点一次报错
解决:
-
方式1: 设置异常捕获
// 在router中的index.js中加上以下代码,注意加在use之前写 const routerPush = Router.prototype.push; Router.prototype.push = function (location) { return routerPush.call(this, location).catch((err) => {}); };
方式2:
在编程式是路由,即this.$router.push() 修改成这样
const currentRoute = this.$router.currentRoute; if (currentRoute.path !== '/Note') { this.$router.push({ path:"/Note" }) }
4.参数的传递
4.1 声明式路由传参
演示App.vue中将数据传递给MyInfo.vue
在路由表中设参
export default new Router({ routes: [ ... { path:'/MyInfo/:id', //设参 component:MyInfo } ] })在App.vue中传参
<template> <div id="app"> <ul> <li> <router-link to="/" tag="button">首页</router-link> </li> <li> <!-- 跳转至MyInfo,并携带id 1001 --> <router-link to="/MyInfo/1001">个人信息</router-link> </li> </ul> <!-- router-view 用于显示匹配的组件,类似显示路由到的组件的区域,位置任意 --> <router-view/> </div> </template> <script> ... </script> <style> ... </style>
在MyInfo.vue中接参
<template> <div> <h1>个人信息</h1> <span>id-->{{id}}</span> </div> </template> <script> export default { name: "EmployeeList", data:function(){ return { id:this.$route.params.id // 注意是$route不是$router } } } </script> <style scoped> </style>
4.2 编程式路由传参
演示App.vue中将数据传递给Note.vue
App.vue的编程跳转js中设置要传递的数据
methods:{ toNote(){ //this.$router.push("/Note") this.$router.push({path:"/Note",query:{username:'老王'}}) } }
Note.vue中接收参数
<template> <div> <h1>网站公告</h1> <span>id --> {{id}}</span> </div> </template> <script> export default { name: "Note", data:function(){ return { id:this.$route.query.username // 注意是query,因为是按照路径方式发送,注意查看浏览器url } } } </script>
ps: 后续也可以通过VueX来完成传递数据
4.3 嵌套路由
多层多级组件嵌套时的路由
例如:
需求: 在个人中心组件中,再设置三个链接,可以点击切换[头像|家庭信息|学校信息]
创建三个组件页面Touxiang.vue,Family.vue,School.vue
在MyInfo组件中设置路由链接发出请求路径,并且设置路由填充位
<template> <div> <h1>个人中心</h1> <h3>id = {{id}}</h3> <!-- /my/tx--> <!-- /my/family--> <!-- /my/school--> <router-link to="/my/tx">头像</router-link>| <router-link to="/my/family">家庭信息</router-link>| <router-link to="/my/school">学校信息</router-link>| <router-view></router-view> </div> </template>
在router/index.js中设置myinfo组件的子路由
path:'/my', name:'MyInfo', component:MyInfo, children:[ // 子路径要与父类拼接成完整路径,此处不需要加/ { path:'tx',component:Touxiang}, { path:'family',component:Family}, { path:'school',component:School}, ] },
如果有路径中有数据的话,子组件一样可以取值
<!-- MyInfo.Vue--> <template> <div> <h1>个人中心</h1> <h3>id = {{id}}</h3> <!-- /my/tx--> <!-- /my/family--> <!-- /my/school--> <router-link to="/my/1001/tx">头像</router-link>| <router-link to="/my/1002/family">家庭信息</router-link>| <router-link to="/my/1003/school">学校信息</router-link>| <router-view></router-view> </div> </template>{ path:'/my/:id',// :id是用来匹配 路由路径中的变量的 name:'MyInfo', component:MyInfo, children:[ // 子路径要与父类拼接成完整路径 { path:'tx',component:Touxiang}, { path:'family',component:Family}, { path:'school',component:School}, ] },
子组件Touxiang.vue,Family.vue,School.vue中可以使用this.$route.params.id
取出路径中的数据1001,1002,1003
4.4其他
路由重定向,导航守卫等等信息,查看官网自查
路由元信息 | Vue Router (vuejs.org)
5.Vuex
5.1 什么是Vuex
Vuex 是一个专为 Vue.js 应用程序开发的
状态管理模式
。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。说人话: 实现多个组件的
数据共享
,即一个数据需要在多个组件中使用,那么就需要把这个数据存储在VueX中
5.4.1Vuex五大核心要素
state
:存放状态,比如需要全局共享的数据getters:可以对state中的数据进行额外处理,类似计算属性的作用
mutations
:通过提交mutations来改变state的状态actions:异步的mutations,可以通过dispatch调用mutaitons,从而改变state
modules:模块化状态管理,每个模块拥有自己的 state、mutation、action、getter
Vue组件通过dispatch调用Vuex中actions的方法,进行异步操作。
actions中的方法中,通过commit,调用mutations中方法,以保证state中数据的同步。
如果不需要异步操作,可以直接在组件通过commit调用mutations中的方法对state中共享的数据进行操作。
5.2 安装
在项目根目录执行如下命令来安装 Vuex
若失败,可使用cnpm
# 本例使用的vue2,所以安装vuex装的是vuex3!!! vue3对应的Vuex4 npm install vuex@3
5.3 配置 vuex
在 src 目录下创建一个名为 store 的目录并新建一个名为 index.js 文件用来配置 Vuex,代码如下:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { }, mutations: { } })
修改 main.js 增加刚才配置的 store/index.js,关键代码如下:
import Vue from 'vue' import App from './App.vue' // 引入路由模块 import router from './router' // 引入Vuex模块 import store from './store' Vue.config.productionTip = false // vue2 创建方式 new Vue({ render: h => h(App), router, store // 使用Vuex }).$mount('#app')
17.2 17.3小节中的[安装|配置]也可以不做,因为后续再创建项目时直接选以下依赖,创建好项目直接就配置好了
一个是路由Router,一个是状态管理Vuex
5.4 演示
5.4.2存&取
-
在Vuex中设置数据,在任何组件取出
// store/index.js文件的state中定义数据 export default new Vuex.Store({ state: { username:"无名", count:400, tga:"tga" }, mutations: { } })
在任何组件中使用 以下命令取值
this.$store.state.username // username是state中定义的key
5.4.3计算属性使用state
<template> <div> 计算属性取值:{{num}} </div> </template> <script> export default { name: "TestVueX", computed:{ // 【注意: 计算属性的写法】 num() { return this.$store.state.count } } } </script>
重要用法: state中的数据要放在组件的computed中而不是data中!
为什么?
这是因为data 中的内容只会在 created 钩子触发前初始化一次,具体来说就是data中设置count: this.$store.state.count则count的值是created钩子执行前this.$store.state.count的值,赋值之后属性的值就是纯粹的字面量,之后this.$store.state.count 如何变化均影响不到count的取值。而 computed 则是通过依赖追踪实现的,计算属性在它的相关依赖发生改变时会重新求值。
简而言之就是说,Vuex存储的数据如果有变化,computed中的数据就会变化,但是data中的不会变化
5.4.4mapState
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState
辅助函数
帮助我们生成计算属性
<template> <div> 计算属性取值:{{count}}| 计算属性取值:{{tga}}| 计算属性取值:{{username}}| </div> </template> <script> // 【注意】 是 {} 引入 // 使用 import mapState from 'vuex' 的方式会将整个 vuex 模块导入,并将其中的 mapState 函数赋值给 mapState 变量。 // 而使用 import { mapState } from 'vuex' 的方式则是只导入 vuex 模块中的 mapState 函数 import {mapState} from 'vuex' export default { name: "TestVueX", // computed:mapState({ // count: function (state) { // return state.count // } // }), // 如果使用和状态名一样的计算属性名,还可以这样写 // 映射 this.count 为 store.state.count // computed: mapState(['count','tga','username']), // 如果有其他的计算属性,并且需要将vuex的state与其混合使用 // 可以使用对象展开运算符'...' computed: { // 使用对象展开运算符将此对象混入到外部对象中 // 映射为当前组件的计算属性 ...mapState(['count','tga','username']), }, } </script> <style scoped> </style>
5.4.5修改数据
-
在任何组件中修改Vuex中的数据
// store/index.js文件的mutations中定义方法修改数据 import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { username:"无名" }, mutations: { // 参数1 是Vuex-state对象 // 参数2 是其他组件使用时传过来的值,这个参数叫做payload updateName(state,v){ state.username = v // 修改Vuex中的值 } } })
在其他组件中通过事件触发函数,在其中使用Vuex的方法修改数据
<template> <div id="app"> <!-- 演示Vuex取值 --> <h1>Vuex --> {{ username }}</h1> <input v-model="username"> <button @click="editName">修改vuex中的值</button> </div> </template> <script> import {mapState} from 'vuex' export default { name: 'App', methods: { editName(){ // 修改Vuex数据 // updateName是 src/store/index.js中mutations中定义的函数名 this.$store.commit('updateName', this.username) } }, data: function () { return { // 从Vuex中取出数据 , 这里数据是不会根据变化而变化,要使用计算属性取值 username1: this.$store.state.username } }, // 这里可以追踪到最新的变化 computed:mapState(['username']) } </script>
5.5解决浏览器刷新后 Vuex 数据消失问题
-
问题描述
Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。但是有一个问题就是:vuex 的存储的数据只是在页面的中,相当于我们定义的全局变量,刷新之后,里边的数据就会恢复到初始化状态。但是这个情况有时候并不是我们所希望的。
-
解决方案
采用持久化,VueX整合插件实现持久化:vuex-persistedstate
-
安装插件
npm install vuex-persistedstate
-
配置
在/src/store/index.js中实现配置
import Vue from 'vue' import Vuex from 'vuex' // 1引入持久化插件 import vuexPersistedstate from "vuex-persistedstate"; Vue.use(Vuex) export default new Vuex.Store({ state: { username:"无名" }, mutations: { updateName(state,v){ state.username = v } }, plugins:[vuexPersistedstate()] // 2加入插件 })
-
测试
浏览器刷新测试效果
6.Axios【重点】
6.1 什么是 Axios
Axios 是一个开源的可以用在浏览器端和 NodeJS 的异步通信框架,它的主要作用就是实现 AJAX 异步通信,其功能特点如下:
-
从浏览器中创建 XMLHttpRequests
-
从 node.js 创建
http 请求
-
支持 Promise API
-
拦截请求和响应
-
转换请求数据和响应数据
-
取消请求
-
自动转换 JSON 数据
-
客户端支持防御 XSRF(跨站请求伪造)
由于 Vue.js 是一个 视图层框架 并且作者(尤雨溪)严格准守 SoC (关注度分离原则),所以 Vue.js 并不包含 AJAX 的通信功能,为了解决通信问题,作者单独开发了一个名为 vue-resource 的插件,不过在进入 2.0 版本以后停止了对该插件的维护并推荐了 Axios 框架
6.2 Axios的使用
6.2.1 安装vue axios
npm install --save axios vue-axios
6.2.2 在需要使用的页面中引入
比如UserInfo.vue页面需要发请求,那就在页面的script中引入即可
import axios from 'axios'
6.2.3 发送ajax请求
<template> <div> <button @click="getInfo">查询数据</button> <hr> {{deptList}} <hr> <table> <th> 部门编号 </th> <th> 部门名称 </th> <th> 部门地址 </th> <tr v-for="dept in deptList"> <td>{{dept.deptno}}</td> <td>{{dept.dname}}</td> <td>{{dept.loc}}</td> </tr> </table> </div> </template> <script> // 引入axios import axios from 'axios' export default { name: "TestAxios", data(){ return { deptList:null } }, methods:{ getInfo(){ // axios.get('/api/dept').then(function(ret){ // console.log(ret) // this.deptList = ret.data // 【有bug,因为this问题】 // }) axios.get('/api/dept').then((ret)=>{ console.log(ret) // 【注意:这个返回值不一般】 // 详情请见19.4章节【19.4 Axios的响应】 // 可以查看官网https://www.axios-http.cn/docs/res_schema this.deptList = ret.data.data }) } } } </script> <style scoped> </style>
6.2.4 服务端解决跨域问题
SpringBoot的controller层类上添加@CrossOrigin
注解即可
6.2.5 BUG
在axios的回调中,无法使用this.数据
获得vue data中的数据
造成axios取不到值得原因主要是this回调问题。当执行完函数function时,this指向放生变化。导致this指向函数体本身。这时this.data里的数据取不到。
简单直接的解决就是把function写成箭头函数
形式,箭头函数不是构造函数,不会指定this,而是抓取上下文this作为自己的this
6.3 其他api
可以查看官网请求配置 | Axios 中文文档 | Axios 中文网 (axios-http.cn)
也可以看示例
6.4 Axios的响应
官网: 响应结构 | Axios中文文档 | Axios中文网
响应结果的主要属性:
data: 服务器响应回的数据
headers: 响应头信息
status: 响应状态码
statusText: 响应状态信息
需要特别注意,我们后台返回的数据在data中,即data中是后台返回的R,我们ret.data获得到的是R,如果还有继续获得其中的数据,还需要再ret.data.data
6.5 axios的全局配置
我们讲一个特别实用的,我们在项目中调用数十次乃至百次接口是很常见的,后期一旦根域名发生改变,所有接口都需要修改,非常繁琐且容易出错。
axios提供了设置根域名的方法。 在
main.js
全局入口文件中,设置:import axios from "axios"; axios.defaults.baseURL = 'http://localhost:8888'在其他vue页面中使用axios发请求的时候
axios.get('/api/dept').then((ret)=>{ console.log(ret) this.deptList = ret.data.data })
6.6 响应拦截
响应拦截可以拦截到axios发请求后的那个回调response对象,然后对其进行处理
实战,将response数据简化后返回
// 添加Axios响应拦截器 axios.interceptors.response.use(function (response) { //console.log('响应拦截',response) return response.data; }, function (error) { console.log(error) });这样,在使用axios的页面,从回调中获得数据时,只需要ret.data,而不需要再ret.data.data