Vue是一套用于构建用户界面的渐进式JavaScript框架
特点: 轻量级:体积小 渐进式:不需要学完全部功能,用到什么学什么 响应式:数据更新之后,视图不需要刷新,会自动更新 学习成本低:基于HTML和js,官方文档都是中文 官方教程文档:https://cn.vuejs.org/v2/guide/
安装引入 Vue有两年环境版本 开发版本: 包含完整的警告和调试模式 本地下载:https://cn.vuejs.org/js/vue.js
生产版本: 删除了警告,33.30KB min+gzip 本地下载:https://cn.vuejs.org/js/vue.min.js
CDN引入:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>示例:
<div id="app">{{message}}</div> <script type="text/javascript"> var vm =new Vue({ el:"#app", data:{ message:"hello Vue!" } }) </script>页面显示结果:
hello Vue!el叫做挂载点,对应的值是html元素的id。一个Vue应用会将其挂载到一个DOM元素上,即这个id对应的元素。挂载点支持css选择器,推荐使用id选择器。挂载点的DOM只支持双标签(和标签除外),不支持单标签。 data是渲染的时候动态的数据,当这些数据改变时,视图会进行重渲染(不需要手动刷新),也就是响应式。 在Vue中使用{{属性名}} (插值)来获取data中的值,如上面代码中的{{message}}对应的就是data中的message
指令:v-if 控制一个元素是否显示,使用指令。在Vue中,元素中V-前缀的特殊属性叫做指令。指令的职责是,当表达式的值改变时,将其产生的连带影响。
<div id="app"> <p v-if="seen">直接显示的内容</p> <p v-else >默认隐藏</p> </div> var vm =new Vue({ el:"#app", data:{ seen:true } })浏览器显示:
直接显示的内容指令:v-show 也是控制一个元素是否显示,不过改变的是样式(适合频繁切换的元素),而v-if改变的是DOM树。
<div id="app"> <p v-show="seen">直接显示的内容</p> </div> <script type="text/javascript"> var vm =new Vue({ el:"#app", data:{ seen:false } }) </script>浏览器显示:
当seen为false改变了css样式
<p style="display: none;">直接显示的内容</p>指令:v-for 显示的数据是一个数组
<div id="app"> <ul> <li v-for="todo in todos">{{todo.text}}</li> </ul> </div> <script type="text/javascript"> var vm =new Vue({ el:"#app", data:{ todos:[{text:"爱学习,爱java"},{text: "java是世界上最好的语言"},{text: "day day up"}] } }) </script>页面效果:
爱学习,爱java java是世界上最好的语言 day day up在控制台里,输入vm.todos.push({ text: '新的内容' }),列表最后会添加新内容。 复杂一点的数据
<div id="app"> <ul> <li v-for="goods in goodsList">商品名:{{goods.name}}<br>商品价格:{{goods.price}}</li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { goodsList: [{"id": 1, "name": "苹果", "price": 10}, {"id": 2, "name": "香蕉", "price": 12}, {"id": 3, "name": "梨", "price": 8}] } }) </script>浏览器显示:
商品名:苹果 商品价格:10 商品名:香蕉 商品价格:12 商品名:梨 商品价格:8获取索引可以使用v-for="(goods,index) in goodsList" ,其中index是当前元素的索引,{{index}}可以获取到当前元素的索引
如果响应式的内容是HTML,就不能使用双大括号,需要使用v-html指令
<div id="app"> <ul> <li>差值表达式支持输出的内容:{{rawHtml}}</li> <li>指令v-html输出的内容:<span v-html="rawHtml"></span></li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { rawHtml:"<span><a href='#' style='color: red'>文字内容</a></span>" } }) </script>浏览器显示:
差值表达式支持输出的内容:<span><a href=’#’ style=‘color: red’>文字内容</a></span>` 指令v-html输出的内容:文字内容
对于所有的数据绑定,Vue 都提供了完全的JavaScript 表达式支持。每个绑定都只能包含单个表达式
<div id="app"> <ul> <li>{{!seen?"false":"true"}}</li> <li>{{num+1}}</li> <li>{{text+",你来自哪里"}}</li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { seen:true, num:1, text:"你是谁" } }) </script>浏览器显示:
true 2 你是谁,你来自哪里指令:v-bind 缩写是::属性名 用于响应式地更新HTML属性
<div id="app"> <ul> <li v-bind:class="id"></li> <li :class="id"></li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { id:"top" } }) </script>审查元素看元素节点属性:
<ul> <li class="top"></li> <li class="top"></li> </ul>可以传给v-bind:class一个对象,以动态地切换class, 也可以传递数组,或者进行一些计算。 使用v-bind:style绑定css样式。
<style type="text/css"> .active{ color: red; } .large-cls{ font-weight: bolder; } </style> <div id="app"> <ul> <li v-bind:class="{active:isActive}">文本内容1</li> <!--静态和指令共存--> <li :class="{active:isActive}" class="large-cls">文本内容2</li> <!--绑定数组--> <li :class="[activeClass,largeClass]">文本内容3</li> <!--绑定对象--> <li :class="classObj1">文本内容4</li> <!--绑定计算的对象--> <li :class="classObj2">文本内容5</li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { isActive:false, activeClass:"active", largeClass:"large-cls", classObj1:{active:true} }, computed:{ classObj2:function () { return{ active:this.isActive, 'large-cls':!this.isActive//名字中有特殊符号,如“-”,用单引号 } } } }) </script>生成的静态class属性
<ul> <li class="">文本内容1</li> <li class="large-cls">文本内容2</li> <li class="active large-cls">文本内容3</li> <li class="active">文本内容4</li> <li class="large-cls">文本内容5</li> </ul>指令:v-on:事件名 简写:@事件名 使用v-on绑定事件
<div id="app"> <ul> <li >num={{num}} <button v-on:click="num+=1" >add 1</button></li> <li ><button v-on:click="method1(str)">点击领红包</button></li> <li ><button @click="method2">hello</button></li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { num:0, str:"恭喜你获得500元" }, methods:{ method1:function (str) { alert(str) }, method2:function () { console.log("helllo vue") } } }) </script>显示效果:
指令:v-model 在表单元素上创建双向数据绑定。v-model会忽略表单元素默认值,而将Vue实例的数据作为数据来源。
<div id="app"> <ul> <li >用户名:<input type="text" name="userName" v-model="nameVal"></li> <li >密码:<input type="password" name="password" v-model="passwordVal"></li> <li >性别:<input type="radio" name="sex" value="1" v-model="sexVal">男<input type="radio" name="sex" value="2" v-model="sexVal">女</li> <li >爱好:<input type="checkbox" name="hobby" value="1" v-model="hobbyVal">看书<input type="checkbox" name="hobby" value="2" v-model="hobbyVal">电影<input type="checkbox" name="hobby" value="3" v-model="hobbyVal">唱歌</li> <li ><select name="city" v-model="sityVal"> <option value="1">深圳</option> <option value="2">上海</option> </select></li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { nameVal:"张三", passwordVal:"a66666", sexVal:1, hobbyVal:[1,2], sityVal:1 } }) </script>浏览器显示:
浏览器显示:
HELLO VUE Hello Vuearg1arg2对于复杂逻辑,需要使用计算属性。computed将不经常变化的计算结果进行缓存,以节约系统开销。
<div id="app"> <ul> <li >{{getTime1}}</li> <li >{{getTime2()}}</li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", computed:{ getTime1:function () { return new Date(); } }, methods:{ getTime2:function () { return new Date(); } } }) </script>methods和computed区别是: methods需要带括号, computed不需要 computed计算的值会缓存起来,多次调用,其值不变:
组件可复用的Vue实例。可以把组件看成小型的,独立的U模块。 一个组件必须有一个根节点 全局组件,在当前Vue实例中都可用,局部注册的组件只在父组件中可用。 父子组件data无法直接共享
<div id="app"> <button-counter></button-counter> <button-counter></button-counter> <comp2></comp2> </div> <script type="text/javascript"> //声明组件 Vue.component('button-counter', { template:'<div>num={{num}} <button v-on:click="num+=1">add 1</button> <button v-on:click="method1(str)">点击领红包</button> <button @click="method2()">hello</button><innerChild></innerChild></div>', methods: { method1: function (str) { alert(str) }, method2: function () { alert("hello vue") } }, data:function () { return{ num:0, str: "恭喜你获得500元" } }, components:{ innerChild:{ template:'<span>局部组件</span>' } } }) Vue.component('comp2',{ template:'<button-counter></button-counter>' }) var vm = new Vue({ el: "#app" }) </script>引入文件
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> <div id="app"> <button @click="search">查询</button> <ul><li v-for="data in dataList">{{data.id}}-{{data.name}}-{{data.price}}</li></ul> </div> <script type="text/javascript"> var vm= new Vue({ el:"#app", data:{ dataList:[] }, methods:{ search:function () { axios.get("./data.json").then(res=>{ console.log(res.data) this.dataList=res.data; }); } } }) </script>data.json
[{"id": 1, "name": "苹果", "price": 10}, {"id": 2, "name": "香蕉", "price": 12}, {"id": 3, "name": "梨", "price": 8}]打开浏览器点击查询显示数据
自定义属性props:组件的props中声明的属性 原生属性attrs:没有声明的属性,默认自动挂载到组件根元素上。 特殊属性class、style:挂载到组件根元素上,支持字符串、对象。数组等多种语法
属性可以通过数组的形式列出:
props:['title','commentIds','author']但在正式开发的时候,为了系统的维护性,我们还是推荐使用如下形式,注意组件中的属性名,在html标签中并不是使用驼峰。如果使用的是数字类型或者其它动态值,需要使用v-bind:
<div id="app"> <!--数字和布尔绑定需要动态赋值,不然会报错,:prop-c="abc"会报错,字母动态赋值会被认为是方法或变量--> <button-counter :prop-a="1" :prop-b="1" prop-c="abc" :prop-d="20" prop-f="success"></button-counter> </div> <script type="text/javascript"> //声明组件 Vue.component('button-counter', { template: '<div>propA:{{propA}}<br/>propB:{{propB}}<br/>propC:{{propC}}<br/>propD:{{propD}}<br/>propE:{{propE}}<br/>propF:{{propF}}</div>', //声明属性 props: { //基础的类型检查('null'和、'undefined'会通过任何类型验证) propA: Number, //多个可能的类型 propB: [String, Number], //必填的字符串 propC: { type: String, required: true }, //带有默认值的数字类型 propD: { type: Number, default: 100 }, //带有默认值的对象类型 propE: { type: Object, //对象或数组默认值必须从一个工厂函数获取 default: function () { return { message:"hello Vue!" } } }, propF:{ validator:function (value) { //这个值必须匹配下列字符串的一个 return ['success','warning','danger'].indexOf(value)!==-1 } } } }) var vm = new Vue({ el: "#app" }) </script>浏览器显示:
propA:1 propB:1 propC:abc propD:20 propE:{ "message": "hello Vue!" } propF:success普通事件:@click、@change等事件, 通过this.$emit( 'myEvent' )触发(子传父)。 修饰符事件:@input.trim、@click.stop等, 自定义组件需要自行开发。
<div id="app"> <my-component></my-component> </div> <script type="text/javascript"> Vue.component('my-component',{ template: '<div @click="HandlerDivClick"><button @click="HandlerButClick">向上传递</button><button @click.stop="HandlerButClick">阻止冒泡</button></div>', methods:{ HandlerDivClick:function () { console.log("aaa") }, HandlerButClick:function () { console.log("bbb") } } }) var vm=new Vue({ el:"#app" }) </script>浏览器显示: 父传子靠属性,子传父靠事件
<div id="app"> <!--不带括号默认传event,默认写法--> <child @myevent="father"></child> <!--带括号$event固定写法--> <child @myevent="father($event)"></child> </div> <script type="text/javascript"> Vue.component('child', { template: ` <div> <button @click="callParent()">子传父</button> </div>`, data:function () { return { childName:"aaa" } }, methods: { callParent: function () { console.log("child") this.$emit('myevent',this.childName)//子组件触发父组件的方法 } } }) var vm = new Vue({ el: "#app", methods: { father:function (e) { console.log("I’m father:"+e) } } }) </script>控制台输出:
child I’m father:aaa在Vue中如果在自定义组件的标签内部写东西,默认是不显示的。使用slot实现插槽功能,可以显示在组件中写的内容。
<div id="app"> <div-counter>默认不显示</div-counter> </div> <script type="text/javascript"> //声明组件 Vue.component('div-counter', { template: ` <div> <ul> <slot></slot> <li>这是内容1</li> <li>这是内容1</li> </ul> </div>`, }) var vm = new Vue({ el: "#app" }) </script>浏览器显示:
默认不显示 这是内容1 这是内容1具名插槽 把内容插入到指定的插槽中,当插槽名都有名字,而插入的内容没有名字指向,则内容不会显示
<div id="app"> <div-counter><div slot="a">插槽a的内容</div><div slot="b">插槽b的内容</div></div-counter> </div> <script type="text/javascript"> //声明组件 Vue.component('div-counter', { template: ` <div> <ul> <slot name="a"></slot> <li>这是内容1</li> <li>这是内容1</li> <slot name="b"></slot> </ul> </div>`, }) var vm = new Vue({ el: "#app" }) </script>浏览器显示:
插槽a的内容 这是内容1 这是内容1 插槽b的内容兄弟组件之间相互调用,可以使用中央事件组件
<div id="app"> <c1></c1> <c2></c2> </div> <script type="text/javascript"> var bus=new Vue(); Vue.component('c1', { template: `<div><input ref="msg"> <button @click="HandleClick">这是组件1</button> </div>`, methods:{ HandleClick:function () { console.log(this.$refs.msg.value) bus.$emit('myListener',this.$refs.msg.value)//发布携带的事件myListener } } }) Vue.component('c2', { template: `<div> <button>这是组件2</button> </div>`, mounted:function () { bus.$on('myListener',function (data) {//订阅事件 console.log('组件2接收的内容:'+data) }) } }) var vm = new Vue({ el: "#app" }) </script>控制台显示内容
动态组件使用<component>标签占位,使用is属性指定具体替换为哪个组件。每次切换都会重新渲染组件。使用<keep-alive>
<div id="app"> <!--组件只被初始化一次--> <keep-alive> <component :is="nav"></component> </keep-alive> <!--点击改变nav值切换组件--> <button @click="nav='home'">home</button> <button @click="nav='list'">list</button> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", components:{ list:{ template:`<div>list<input></div>`, mounted:function () { console.log('mounted')//查看组件初始化次数 } }, home:{ template: ` <div>home</div> ` } }, data:{ nav:"list" } }) </script>浏览器显示:
每个Vue实例在被创建时都要经过一系列的初始化过程,叫做生命周期钩子
<div id="app"> <button @click="message='content changed'" >{{ message }}</button> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { message : "Hello Vue!" }, beforeCreate: function () { console.group('beforeCreate 创建前状态===============》'); console.log("%c%s", "color:red" , "el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //undefined console.log("%c%s", "color:red","message: " + this.message)//undefined }, created: function () { console.group('created 创建完毕状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, beforeMount: function () { console.group('beforeMount 挂载前状态===============》'); console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, mounted: function () { console.group('mounted 挂载结束状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, beforeUpdate: function () { console.group('beforeUpdate 更新前状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, updated: function () { console.group('updated 更新完成状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, beforeDestroy: function () { console.group('beforeDestroy 销毁前状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, destroyed: function () { console.group('destroyed 销毁完成状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message) } }) </script>生命周期代码来源:https://segmentfault.com/a/1190000008010666 挂载结束前的状态 更新状态
官网下载:https://nodejs.org/en/download/ 安装版直接双击安装,解压版解压文件 解压后创建两个文件夹
配置环境变量,在系统变量下新建一个NODE_HOME变量,把新建的NODE_HOME添加到PATH中 cmd配置刚才新建的两个文件夹 执行命令
npm config set cache "E:\study\node-v12.18.2-win-x64\npm_cache" npm config set prefix "E:\study\node-v12.18.2-win-x64\npm_global"再添加环境变量
%NODE_PATH%\npm_global安装cnmp,加快下载速度
npm install -g cnpm --registry=https://registry.npm.taobao.org安装vue脚手架:
cnpm install -g @vue/cli创建一个项目路径,在该项目路径中执行vue create 名字,按上下箭头切换,选择手动配置Manually这个再回车进入下一步 空格选中需要安装的模块 后面几步选择,右侧有对应值 之后开始下载…
工程下载完成 按照提示信息,进入到项目内,执行命令
npm run serve提示访问地址 根据提示访问地址,出现如下页面表示项目创建成功
idea中打开启动方式 idea中的npm工具启动 在工具中启动,默认端口8080,修改端口在Arguments使用
-- --port 80在App.vue写入如下内容: 单页面分为三部分:模板标签、js和css
<template> <div id="app"> <div> <input ref="msg"> <button @click="add()">添加</button> <ul> <li v-for="data in dataList" :key="data">{{data}}</li> </ul> </div> </div> </template> <script> export default { // ()相当于:funtion() data () { return { dataList: [] } }, methods: { add () { console.log(this.$refs.msg.value) this.dataList.push(this.$refs.msg.value) } } } </script> <style> ul,li{ list-style: none; background-color: #ccc; } </style>使用nmp run lint 可以将代码格式化,符合ES6风格。 使用npm run build 可以将项目打包,打包成功后会有一个dist文件夹, 将这个文件夹中的内容给后端开发人员即可(emmm就是你自己咯)
跨域:端口、协议或域名不一致 模块化开发需要在当前的工程下安装axios模块npm install axios ,在页面中引入axios模块,跨域的时候,axios的请求路径不写域名,交给代理实现
<template> <div> <ul> <button @click="search">查询</button> <li v-for="data in dataList">{{data.id}}-{{data.name}}</li> </ul> </div> </template> <script> import Vue from 'vue' import axios from 'axios' export default { data () { return { dataList: [] } }, methods: { search: function () { axios.get('/tupes') .then(res => { this.dataList = res.data }) } } } </script>axios跨域请求时会报错,可以请求到数据,但无法使用 这时需要配置反向代理,在项目根目录src下创建vue.config.js文件:
module.exports = { devServer: { proxy: { '/types': { target: 'http://localhost:8099', changeOrigin: true } } } }对于单页面应用,使用路由切换组件,先创建两个组件
<template> <div> <h1>用户</h1> </div> </template> <script> export default {} </script>在router文件夹下的index.js中配置路由信息。这个路径是可以自定义的,和main.js中的路径相对应
import Vue from 'vue' import VueRouter from 'vue-router' import User from '../views/User' import Order from '../views/Order' Vue.use(VueRouter) const routes = [ { path: '/user', // 对应页面中的#/user component: User // 组件User }, { path: '/order', component: Order } ] const router = new VueRouter({ routes }) export default routermain.js中引入路由文件:此处创建项目会默认配置 在App.vue主页面中,需要展示组件的地方加上<router-view></ router-view>
<ul> <li><a href="#/user">用户页面</a></li> <li><a href="#/order">订单页面</a></li> </ul> <!--类似插槽,渲染切换页面的组件--> <router-view></router-view>访问路径: http://localhost:8080/#/order, 其中的/#/order就是路由的请求路径了
使用路由标签,可以管理路由连接和高亮样式 to:路径 tag:渲染成什么标签 activeClass:高亮的样式
<div> <ul> <router-link to="/user" tag="button" active-class="myActive">用户管理</router-link> <router-link to="/order" tag="button" active-class="myActive">订单管理</router-link> </ul> <!--类似插槽,渲染切换页面的组件--> <router-view></router-view> </div> <style> .myActive{ color: #444; background-color: coral; } button:hover{ cursor:pointer } </style>浏览器显示:
路由中指定一个默认的地址,当访问路径没有匹配到组件的时候,会重定向到指定页面:
{ path: '*', redirect: '/order' }如果在子组件中更新部分内容们可以使用二级路由,如Order.vue中 :
<template> <div> <h1>订单页面</h1> <ul> <router-link to="paid" tag="button" active-class="myActive">已支付</router-link> <router-link to="notpay" tag="button" active-class="myActive">未支付</router-link> </ul> <router-view></router-view> </div> </template> <script> export default {} </script>路由配置(在一级路由的节点上加children节点):
{ path: '/order', component: Order, children: [{ path: 'paid', component: Paid }, { path: '/order/notpay', component: NotPay }, { path: '', // 默认重定向 redirect: '/order/notpay' }] }浏览器效果:
如果路由中有动态的参数,可以进行如下配置: 发起请求的组件(Nopay.vue):
<template> <div> <h2>未支付订单</h2> <ul> <li v-for="num in dataList" :key="num" @click="changePage(num)">文章{{num}}</li> </ul> </div> </template> <script> export default { data () { return { dataList: ['1', '2', '3'] } }, methods: { changePage: function (id) { console.log(id) this.$router.push(`/order/detail/${id}.html`) // 传url动态参数 } } } </script>路由配置:
{ path: '/order/detail/:myid.html', // myid是动态参数 component: Detail },详情页面组件
<template> <div> <h3>订单{{num}}的详情内容</h3> </div> </template> <script> export default { mounted () { console.log(this.$route.params.myid) // 输出动态id }, data () { return { num: this.$route.params.myid } } } </script>浏览器显示:
配置路由的时候设置一个name属性
{ name: 'od', path: '/order/detail/:myid.html', // myid是动态参数 component: Detail }跳转的时候
this.$router.push({ name: 'od', params: { myid: id } })在定义路由的地方加上mode: ' history'可以去掉路径中的#:
const router = new VueRouter({ mode: 'history', routes })这种模式会真的向后端发送一个请求, 后端需要配置:如果URL 匹配不到任何资源,则返回同一个index.html
全局解析守卫,定义在路由js里 使用路由守卫,相当于一个过滤器:
// 模拟用户没有登录 const auth = { isLogin () { return false } } // to去哪个页面,form从哪个页面来,下一个页面 router.beforeEach((to, from, next) => { console.log(to.path) if (/\/order\/.*/.test(to.path)) { // 匹配正则路径,所有以order开头的页面 console.log('拦截') if (auth.isLogin()) { next() } else { next('/login') } } else { // 不需要登录的页面直接放行 next() } })组件内的守卫,定义在组件内
<script> // 模拟用户没有登录 const auth = { isLogin () { return false } } export default { beforeRouteEnter (to, from, next) { // 在渲染该组件的对应路由被confirm前调用 // 不能获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 if (auth.isLogin()) { next() } else { next('/login') } } } </script>Vue开发实例:Vue+springboot简单实现点餐系统开发
