v-bind:class=“user” : vue属性绑定 ,v-bind: 指令可以缩写为 : 符号
v-model=“userName” : vue数据绑定
v-if=“boolean” : 指令的表达式返回 true 时才会显示
一般用于弹框的显示与关闭
v-for : 指令需要以 site in sites 形式的特殊语法, sites 是源数据数组并且 site 是数组元素迭代的别名。
vue变量
用var命令声明的变量,是在全局范围内有效的,且变量可以先使用再进行声明,针对其中的问题,vue中不推荐使用var 而是提供了let和const
let: es6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
const:一个只读的常量。一旦声明,常量的值就不能改变
computed : 计算属性在处理一些复杂逻辑时,比如一个字段需要依赖另一个字段并进行处理得到的(例如年龄需要依赖出生日期来计算),methods中使用方法也能达到相同的效果,但是computed的性能更好,它是基于依赖项的变化才会进行变化
watch : Vue3 监听属性 我们可以通过 watch 来响应数据的变化,如果监听的对象内部属性变化则使用deep:true
1、数据变化时执行异步或开销较大的操作时
2、1父多子组件每一个子组件都用父组件的数据,同时子修改父数据,子修改后能确保所有子都能获取到新的数据 则所有子组件可以使用watch来监听数据。
watch和computed区别
vue事件相关指令 : 我们可以使用 v-on 指令来监听 DOM 事件,v-on 指令可以缩写为 @ 符号。
# 单击事件
<a v-on:click="doThis"></a>
<a @click="doThis"></a>
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- click 事件只能点击一次 -->
<a v-on:click.once="doThis"></a>
# onchange事件
<a v-on:change="doThis"></a>
<a @change="doThis"></a>
路由中有三个基本的概念 route, routes, router。
route:它是一条路由,指代一个具体的路由页面
routes: 是一组路由,把上面的每一条路由组合起来,形成一个数组。
router: 是一个机制,相当于一个管理者,它来管理路由。
路由方式 | 功能 | 原理 |
---|---|---|
router-link | 支持用户在具有路由功能的应用中 (点击) 导航 | 就是渲染出一个元素(默认为a),触发该元素上的事件会产生路由跳转 |
编程方式 | 编码的形式跳转 | 上述两种方式最终都是通过push()进行路由跳转的 |
最终都需要使用router-view去进行选择匹配后的渲染呈现.
组件支持用户在具有路由功能的应用中(点击)导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的。作用类似于a标签
属性 | 作用 |
---|---|
to (类型: string | Location) | 表示目标路由的链接。当被点击后,内部会立刻把 to 的值传到 router.push()或者router.replace() |
tag | vue路由被渲染成什么类型的html标签 默认渲染为 标签 |
replace | 设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push() ,于是导航后不会留下 history 记录 |
event | 触发路由跳转的事件 默认为click |
append | 设置 append 属性后,则在当前(相对)路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b |
exact | 严格匹配 路径完成匹配成功才进行跳转。 |
参考例子
除了使用
创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
方法 | 作用 |
---|---|
router.push(location) | 跳转到location对应目标路由。这个方法同时会向 history 栈添加一个新的记录 |
router.replace(location) | 跟 router.push 类似但是它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。 |
router.go(n) | 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步 正数前进 负数后退 |
Vue-router 中hash模式和history模式的关系 在vue的路由配置中有mode选项 最直观的区别就是在url中 hash 带了一个很丑的 # 而history是没有#的
hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。比如这个 URL:www.abc.com/#/hello的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
主要用于在每次路由跳转过程中通过钩子函数去动态的改变导航行为,可以用来进行验证 用户登录与否权限控制。
全局, 单个路由独享, 组件级
每个守卫方法接收三个参数:
to: Route
: 即将要进入的目标 路由对象from: Route
: 当前导航正要离开的路由next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next
方法的调用参数。
next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from
路由对应的地址。next('/')
或者 next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next
传递任意位置对象,且允许设置诸如 replace: true
、name: 'home'
之类的选项以及任何用在 router-link
的 to
prop 或 router.push
中的选项。next(error)
: (2.4.0+) 如果传入 next
的参数是一个 Error
实例,则导航会被终止且该错误会被传递给 router.onError()
注册过的回调。 1. router.beforeEach
注册一个全局前置守卫,进入路由之前
router.beforeResolve
注册一个全局守卫。这和 router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
router.afterEach
全局后置钩子 进入路由之后
router.beforeEnter注册一个路由独享的前置守卫,与一个全局前置守卫类似
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
在真是开发场景中,嵌套路由多用于多级菜单栏的展示,他的用法如下:
//#嵌套路由
{
path: '/nesting',
name: 'nesting',
component: nesting,
children:[
{
// 当 /nesting/home2 匹配成功,
// home2 会被渲染在 nesting 的 <router-view> 中
path: 'home2',
component: home2
},
{
// 当 /nesting/studentManager 匹配成功,
// StudentManager 会被渲染在 nesting 的 <router-view> 中
path: 'studentManager',
component: StudentManager
},
]
},
http://localhost:8083/#/nesting
routes: [
//访问/home 则请求重定向到/home2对应的vue组件
{ path: '/home', redirect: '/home2' }
]
请求 /aliasVue 请求url保持不变访问是/hom2对应的组件
{ path: '/home2', alias:"/aliasVue" }
路由配置:
import Main from './views/Main'
routes: [
{
path: '/main',
name: 'main', # 为该路由起名,通过name即可指定该路由
component: Main
}
]
视图使用:
1):
主页
2):
this.$router.push({
name:'course-detail',
});
router-link:会被vue渲染成a标签,但是点击这样的a标签不能发生页面的转跳,只会出现组件的替换
a:也可以完成同样的效果,但是会发生页面的转跳
router.js文件
routes: [
// ...
{
path: '/course/:id/detail',
name: 'course-detail',
component: CourseDetail
},
]
注意:
path如果是通过to直接访问,路由必须完全对应
:id代表可以完成任意内容匹配,用变量id保存
跳转.vue
{{ course.name }}
接收.vue
created() {
let id = this.$route.params.id;
}
router.js
routes: [
// ...
{
path: '/course/detail',
name: 'course-detail',
component: CourseDetail
},
]
跳转.vue
{{ course.name }}
接收.vue
created() {
let id = this.$route.query.id;
}
methods: {
goPage() {
// 返回上两页
this.$router.go(-2);
// 前进一页
this.$router.go(1);
}
}
组件 (Component)
是用来构成你的 App
的业务模块,它的目标是 App.vue
插件 (Plugin)
是用来增强你的技术栈的功能模块,它的目标是 Vue
本身
简单来说,插件就是指对Vue
的功能的增强或补充。
插件通常用来为 Vue
添加全局功能。插件的功能范围没有严格的限制,可以添加资源、方法属性等。
插件的实现应该暴露一个 install
方法
将一系列的标签(html)、样式(css)、脚本(JS) 统一命名封装成一个组件一种机制,组件系统让我们可以用独立可复用的小组件来构建大型应用。项目中所有后缀为vue局均可以看做组件。
好处: 降低整个系统的耦合度,提高可维护性,调试方便
从功能维度区分:业务组件(与业务逻辑耦合比较深的),功能组件(与业务无关 通用组件 比如分页组件)
// template : 有且只能有一个跟标签
// script : 必须将组件对象导出 export default { }
// style : style标签明确scoped属性,代表该样式只在组件内部起作用(即:样式组件化)
1) 在views文件中创建视图组件
2) 在router.js文件中配置路由
3) 设置路由跳转,在指定路由下渲染该页面组件(替换根组件中的router-view标签)
补充:
// 1) 小组件代码书写完后将其导出
// 2) 页面组件需要哪个小组件就将该小组件导入并注册,同时将页面组件导出
// 3) 在路由组件中导入页面组件并配置注册
text2
import Vue from 'vue'
import Router from 'vue-router'
// 导入页面组件
import Home from './views/Home.vue'
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
// 配置页面组件路由,名称,并注册
path: '/',
name: 'home',
component: Home
},
},
]
})
import Vue from 'vue' // 为项目加载vue环境
import App from './App.vue' // 加载根组件用于替换挂载点
import router from './router' // 加载路由脚本文件,进入路由相关配置
import store from './store' // 加载数据仓库环境
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app'); // 挂载index.html文件中的 div 标签
补充:
加载插件环境:路由、仓库、ajax、cookie、element-ui...
加载自定义环境:全局样式(global.css)、全局配置(settings.js)
渲染根组件
改写:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false;
new Vue({
el: '#app',
router,
store,
render: function (readFn) {
return readFn(App)
}
});
补充: 静态资源要在main.js中配置
// 配置全局样式
import '@/assets/css/global.css'
vue.extend
//1.使用extend创建组件
//按照Java的开发思想,创建时采用驼峰命名法,使用的时候每个单词必须用-隔开
let extendButton = Vue.extend({
data: function () {return {count: 0} },
template: ''});
Vue.component("extend-button",extendButton);
//注册为全局组件
Vue.component('appILS',appILS);
//使用 vue页面直接使用即可
<h1>1、extend-button</h1>
<extend-button></extend-button>
vue.component
注意:全局组件必须写在vue实例上方,不然渲染不出来。
//使用component创建组件 并注册
Vue.component('appTemplete',{
template: "这是不使用extend创建的组件
"
})
//使用component创建组件 并注册
Vue.component('appTemplete',{
template: "#compentName"
})
//使用 vue页面直接使用即可
<h1>2、component组件注册 模板耦合</h1>
<normal-button></normal-button>
局部注册
//在需要项目中导入
import partButton from './button-counter'
export default {
name: 'myButton',
components: { partButton },
}
//使用
<h1>4、局部组件</h1>
<part-button></part-button>
父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息
父组件
<template>
<div class="parent">
<h1>{{ msg }}</h1>
<p>爸爸的资产:<b>{{money}}</b></p>
<p>我给儿子起的名字:<b>{{sonName}}</b></p>
<h1>使用$event参数</h1>
<children :name="sonName" @updateDemo="money-=$event" @update:name="sonName=$event"/>
<h1>使用方法接受参数</h1>
<!-- v-on缩写@ -->
<children :name="sonName" v-on:updateDemo="spendMoney" @update:name="sonName=$event" />
<children :name="sonName" @updateDemo="spendMoney" @update:name="sonName=$event" />
<h1>使用sync双向绑定</h1>
<children :name.sync="sonName" @updateDemo="spendMoney" />
<!-- <children :name="sonName" @update:name="$event" />-->
</div>
</template>
<script>
import children from "./children.vue";
export default {
name: 'parent',
components:{children},
data () {
return {
msg: '父组件',
money: 100,
sonName:"狗蛋"
}
},
methods:{
spendMoney:function (money) {
debugger
this.money-=money;
}
},
}
子组件
<template>
<div class="parent">
<h1>{{ msg }}</h1>
<p>我爸给我起名为:<b>{{name}}</b></p>
<button @click="getMoney(10)">我花我爸10块钱</button>
<button @click="changeName()">我要改名</button>
</div>
</template>
<script>
export default {
name: 'son',
props:{
name:{
type: String,
required: true
},
},
data () {
return {
msg: '子组件',
}
},
created() {
},
methods:{
getMoney:function (money) {
this.$emit("updateDemo",money);
},
changeName:function () {
this.$emit('update:name',"铁锤");
}
},
}
</script>
子类可以通过$parent 访问其中的属性和方法,例子如下:
changeNameByPro:function () {
//$parent访问属性
this.$parent.sonName = "铁锤";
},
changeNameByMethod:function () {
//$parent访问方法
this.$parent.changeSonName("铁柱");
},
对于层级比较复杂的组件,我们可以使用provide+inject父组件通过provide提供数据,其他任何层级的子组件可以使用inject注入数据
provide 选项应该是一个对象或返回一个对象的函数。
inject 选项应该是:一个字符串数组,或一个对象,对象的 key 是本地的绑定名。
父组件
provide(){
return {
lucykMoney:this.money,
addSnacks:this.addSnacks,
}
},
子组件
inject: ["lucykMoney","addSnacks"],
EventBus
又称为事件总线。在Vue中可以使用 EventBus
来作为沟通桥梁的概念,可以向 e m i t ( ) 该中心注册发送事件或 emit()该中心注册发送事件或 emit()该中心注册发送事件或on接收事件,类似于一个vue全局缓存,用 e m i t ( ) ( < f o n t c o l o r = " r e d " > 注册事件 < / f o n t > )去写数据用 emit()(注册事件)去写数据用 emit()(<fontcolor="red">注册事件</font>)去写数据用on()读数据(事件监听)。
**1. 创建eventBus **
/**
* 创建一个全局的bus
*/
import Vue from 'vue';
const eventBus = new Vue();
// 导出便于使用
export default eventBus;
**2. 添加全局eventBus **
## main.js
//全局导入 eventBus
import eventBus from './components/eventBus/eventBus'
Vue.prototype.$eventBus = eventBus;
3. 创建修改数据
addMoney:function () {
this.companyMoney+=this.money;
alert("当前公司资金:"+this.companyMoney+"W");
//自定义一个money事件 提交事件修改数据
this.$eventBus.$emit("money",this.companyMoney);
}
4. 获取数据
//实时监听 获取数据 方法一般放在 mounted和create中
this.$eventBus.$on(
"money",
val => {
this.companyMoney = val;}
);
sessionStorage(临时存储) :为每一个数据源维持一个存储区域,在浏览器打开期间存在,包括页面重新加载浏览器的会话时间(
localStorage(长期存储) :与 sessionStorage 一样,但是浏览器关闭后,数据依然会一直存在
一般的浏览器能存储的是5MB左右
# 设置
let info = "张三"
localStorage.setItem('hou', JSON.stringify(info));
localStorage.setItem('zheng', str);
#获取
let data1 = JSON.parse(localStorage.getItem('hou'));
let data2 = localStorage.getItem('zheng');
#删除
//删除某个
localStorage.removeItem('hou');
//删除所有
localStorage.clear();
- localStorage:永久存储数据
this.cTitle && (localStorage.cTitle = this.cTitle);- sessionStorage:临时存储数据(刷新页面数据不重置,关闭再重新开启标签页数据重置)
- cookie:临时或永久存储数据(由过期时间决定)
- vuex的仓库(store.js):临时存储数据(刷新页面数据重置)
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
{{ count }}
`,
// actions
methods: {
increment () {
this.count++
}
}
})
出现多个组件共享状态变更状态 就需要使用VUEX
import Vue from 'vue'
import Vuex from 'vuex'
import age from "./modules/age";
Vue.use(Vuex)
//vuex管理的状态值
const state = {
count: 0
};
//vuex不允许直接修改state中的值
//而是需要通过显示的执行 commit("mutationName",[args])修改状态
const mutations = {
increment (state) {
state.count++;
},
update (state,count) {
state.count = count;
}
};
// store 中的 state 中派生出一些状态(计算数学) 可以提供一个getter
//Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来
const getters = {
joinCountStr:state => state.count+"w",
}
//Action 类似于 mutation,不同在于:
// Action 提交的是 mutation,而不是直接变更状态。
// Action 可以包含任意异步操作。
//Action 通过 store.dispatch 方法触发 (直接dispatch mutations方法(mutation 必须同步执行这个限制么?Action 就不受约束))
const actions = {
incrementCount({ commit }) {
commit('increment');
},
updateCount({ commit },count) {
commit('update',count);
},
};
const store = new Vuex.Store({
state,
getters,
mutations,
actions,
});
export default store
new Vue({
el: '#app',
router,
store,
components: { App },
template: ' '
})
store.js
export default new Vuex.Store({
state: {
cTitle:'课程页',
hTitle:'主页'
},
mutations: {},
actions: {}
})
SetTitle.vue
修改课程页标题
修改主页标题
在任意组件中给仓库变量赋值
this.$store.state.title = 'newTitle'
this.$store.commit('setTitle', 'newTitle')
在任意组件中取仓库变量的值
console.log(this.$store.state.title)
beforeCreate 组件创建了,但数据和方法还未提供
created 组件创建了,数据和方法已提供,该钩子需要掌握,一般该组件请求后台的数据,都是在该钩子中完成
destroyed 组件销毁时触发
1) router-link会被解析为a标签,用to完成指定路径跳转,但是不能添加系统事件(因为是组件标签)
2) 在js方法中可以用 this.$router.push('路径') 完成逻辑跳转
3) 在js方法中可以用 this.$route.path 拿到当前请求的页面路由
{{ title }}
# 1) 加载mian.js启动项目
i) import Vue from 'vue' 为项目加载vue环境
ii) import App from './App.vue' 加载根组件用于渲染替换挂载点
iii) import router from './router' 加载路由脚本文件,进入路由相关配置
# 2) 加载router.js文件,为项目提供路由服务,并加载已配置的路由(链接与页面组件的映射关系)
注:不管当前渲染的是什么路由,页面渲染的一定是根组件,链接匹配到的页面组件只是替换根组件中的
# 3) 如果请求链接改变(路由改变),就会匹配新链接对应的页面组件, 新页面组件会替换渲染router-view标签,替换掉之前的页面标签(就是完成了页面跳转)
总结:
main.js
=> router.js
=> 链接
=> 页面组件
=> 替换根组件中的 router-view 标签完成页面渲染
=> 通过 router-link | this.$router.push() 切换路由(链接)
=> 完成渲染组件的替换
=> 页面的跳转
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false;
new Vue({
el: '#app',
router,
store,
render: function (readFn) {
return readFn(App)
}
});
// 补充: 一般项目开发该文件内也就这五行代码
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import RedPage from "./views/RedPage";
import BluePage from "./views/BluePage";
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/red',
name: 'red',
component: RedPage
},
{
path: '/blue',
name: 'blue',
component: BluePage
}
]
})
RedPage.vue与BluePage都是添加下方三个步骤代码
cnpm install axios
import axios from 'axios' // 导入插件
Vue.prototype.$axios = axios; // 直接配置插件原型 $axios
axios.create({
//以下是常用的配置属性
baseURL:'', //请求的域名,请求地址 例如:'http://localhost:8080'
timeout:'', //请求超时时间,超过此时间将返回401,(ms),时间过长会阻塞后端传输的数据
url:'', //请求的路径,服务接口
method:'', //请求的方法(get,post,put,patch,delete等)
headers:'', //设置请求头
params:{}, //请求参数拼接在url上
data:{} //请求阐述拼接在请求体中
}).then(请求成功的回调函数).catch(请求失败的回调函数)
# 添加拦截器
//请求发起拦截处理 添加请求头,权限信息等
service.interceptors.request.use()
//响应拦截处理 通用数据转换,异常处理
service.interceptors.response.use
api接口(AxiosRequestConfig).then(data => {
//调用成功业务逻辑处理
}).catch(err => {
//异常处理
})
单独一个配置用于aixos 全局配置,请求/响应拦截
xxxapi文件声明放置接口:请求方式和请求相对路径
vue页面引用调用
import axios from "axios";
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
Axios.post(
"/levenx/common/post",
{
name: "levenx",
},
{ cancelToken: source.token }
)
.then((res) => {
debugger;
})
.catch((err) => {
alert(err);
return err;
});
debugger;
source.cancel("取消请求");
var fun2 = async function(){
await fun1();
}
// get请求
this.$axios({
url: 'http://127.0.0.1:8000/test/ajax/',
method: 'get',
params: {
username: this.username
}
}).then(function (response) {
console.log(response)
}).catch(function (error) {
console.log(error)
});
// post请求
this.$axios({
url: 'http://127.0.0.1:8000/test/ajax/',
method: 'post',
data: {
username: this.username
}
}).then(function (response) {
console.log(response)
}).catch(function (error) {
console.log(error)
});补充: response 是后端响应回来的数据
cnpm install vue-cookies
// 第一种方式
import cookies from 'vue-cookies' // 导入插件
Vue.use(cookies); // 加载插件
new Vue({
// ...
cookies, // 配置使用插件原型 $cookies
}).$mount('#app');
// 第二种方式
import cookies from 'vue-cookies' // 导入插件
Vue.prototype.$cookies = cookies; // 直接配置插件原型 $cookies
// 增(改): key,value,exp(过期时间)
// 1 = '1s' | '1m' | '1h' | '1d'
this.$cookies.set('token', token, '1y');
// 查:key
this.token = this.$cookies.get('token');
// 删:key
this.$cookies.remove('token');
注:cookie一般都是用来存储token的
// 1) 什么是token:安全认证的字符串
// 2) 谁产生的:后台产生
// 3) 谁来存储:后台存储(session表、文件、内存缓存),前台存储(cookie)
// 4) 如何使用:服务器先生成反馈给前台(登陆认证过程),前台提交给后台完成认证(需要登录后的请求)
// 5) 前后台分离项目:后台生成token,返回给前台 => 前台自己存储,发送携带token请求 => 后台完成token校验 => 后台得到登陆用户
cnpm i element-ui -S
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
依照官网 https://element.eleme.cn/#/zh-CN/component/installation api