VUE2与VUE3
引入VUE
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
{{}}语法
-
不会自动侵入html
-
需要对html设置id,引入vue以及配合
{{}}
才会被vue解析,并按照JS执行<div id="box"> {{myname}} </div> <script> //将节点的数据全挂到全局变量ve中 var ve=new Vue({ el:"#box", data:{ myname:"zsl" } }) </script>
Vue拦截原理
-
Object.definedProperty方法可以在一个对象上直接定义一个新的属性、或修改一个对象已经存在的属性,最终返回这个对象
缺点:
- 无法监听es6的Set与Map变化
- 无法监听cllass类型的数据
- 无法监听新加或删除的属性
- 无法监听新加或删除的数组元素
但在VUE3中都能很好地解决了,但存在兼容问题,不兼容会自动降级为Object.definedProperty的数据监听系统
-
原理:每次更改的时候,通过我们的set拦截通知watcher进行触发更新
模板语法
事件绑定:@事件类型
- 改变属性:
:
//切换内容
<div is="box">
{{myname}}-{{myage}}
<button @click="handlerChange">change</button>
</div>
<script>
var ve=new Vue({
el:"#box",
data:{
myname:"zsl",
myage:19
},
methods:{
handlerChange(){
ve.myname="haha",
myage=60
}
}
})
<?script>
//切换属性
<div is="box">
{{myname}}-{{myage}}
<div:class="whichcolor">切换背景颜色</div>
<button @click="handlerChange">change</button>
</div>
<script>
var ve=new Vue({
el:"#box",
data:{
myname:"zsl",
myage:19,
whichcolor:'red'
},
methods:{
handlerChange(){
ve.myname="haha",
myage=60,whichcolor:'yellow'
}
}
})
<?script>
当内容或属性可以被vue解析时,里面的内容可以写成表达式
如改变颜色
<div is="box">
{{myname}}-{{myage}}
<div:class="whichcolor?'red':'yellow'">切换背景颜色</div>
<button @click="handlerChange">change</button>
</div>
<script>
var ve=new Vue({
el:"#box",
data:{
myname"zsl",
myage=19,
whichcolor=true
},
methods:{
handlerChange(){
ve.myname="haha",
myage=60,
whichcolor=!this.whichcolor
}
}
})
<?script>
指令
-
v-show
:动态隐藏与显示 -
v-if
动态创建与删除 -
v-for
动态遍历
<div v-show="isShow"> </div>
<div v-if="isCreated"> </div>
<div v-for="item in list">{{item}} </div>
-
v-bind:src
简写成:src
:就是改变属性的方法 -
v-once
:添加后,组件模块不再更新 -
v-on:click
简写成@click
-
v-model
:双向绑定表单 -
v-html
: 把一块html片段解析后直接渲染在页面中//没加粗,直接渲染mytext {{}} //解析后渲染加粗后的字体 <div v-html="mytext"></div> <script> new Vue({ el:'#box', data:{ mytext:'<b>加粗的</b>' } }) </script>
动态表格案例
<div id="box">
//model双向绑定一个输入框的value
<input type="text"/ v-model="mytext">{{mytext}}
<button @click="handlerAdd()">sdd</button>
<ul v-show="datalist.length">
<li v-for="(data,index) in datalist">
{{data}}
<button @click="handlerDel(index)">del</button>
</li>
</ul>
<div v-show="!datalist.length">无待办事项</div>
</div>
<script>
var ve=new Vue({
el:"#box",
data:{
datalist:["111"."222","333"],
mytext:"aaaa"
},
methods:{handlerAdd(){
this.datalist.push(this.mytext)
this.mytext=""
},
handlerDel(index){
this.datalist.splice(index,1)
}
})
</script>
案例:点击选项变色
<style>
*{margin:0;
padding:0;}
li{
flex:1;
height:50px;
line-height:50px;
text-align:center;
}
.active{
background:green;
}
</style>
<div id="box">
<ul>
<li v-for="da ta in datalist" :class="current ===index?'active':''" @click="handlerClick(index)">
{{data}}
</li>
</ul></div>
<script>
new Vue({
el:"#box",
data:{
datalist:["首页","列表","我的"],
current:0
},
methods:{
handlerClick(index){
this.current=index
}
}
})
</script>
Vue2中动态类名&属性
(以类名为例子)
<div id="box">
<div :class="classobj"></div>
</box>
<script>
var ve=new Vue({
el:"#box",
data:{
classobj:{
aa:true,
bb:true,
cc:false
}
}
})
</script>
有不能后期临时添加类名的缺陷,Vue3可以
解决方法:
-
Vue.set(对象,属性,true)
删除就false -
数组方法可以后期添加删除,push,slpce
<div id="box"> <div :class="classarr"></div> </box> <script> var ve=new Vue({ el:"#box", data:{ classarr:["aa","bb"] } }) </script>
Vue3中动态类名&属性
<div id="box">
{{myname}}
<input type="text" v-model="mytext">
<button @click="handlerAdd">add</button>
</box>
<script>
//函数式的写法
var obj={
data(){
return{
myname:"zsl"
},
classobj:{
aa:true,
bb:false
}
},
methods:{
handlerAdd(){
this.classobj.add=true
}
}
}
Vue.createApp(obj).mount("#box")
</script>
条件渲染
-
只有v-if可用
<div id="box"> <h2>所有订单</h2> <ul> <li v-for='item in datalist'> {{item.title}}--- <span v-if="item.state===0">未付款 </span> <span v-if="item.state===1">代发货 </span> <span v-if="item.state===2">已发货 </span> <span v-else>已签收 </span> </li> </ul> </div> <script> var ve=new Vue({ el:"#box", data:{ //isCreated:true, datalist:[ { title:"111", state:"0" }, { title:"111", state:"1" }, { title:"111", state:"2" }, { title:"111", state:"3" } ] } }) </script>
- template标签是Vue提供的包装元素,会真实创建在页面中
列表渲染
v-for
-
v-for:遍历数组内容(常用)
in 也可以用 of 代替
<body> <div id="div1"> <li v-for='(p,i) in persons' :key=i> {{p.name}}--{{p.age}} <!-- 张--18 李--19 刘--17 --> </li> </div> </body> <script type="text/javascript"> new Vue({ el:"#div1", data:{ persons:[ {id:'001',name:"张",age:18}, {id:'002',name:"李",+age:19}, {id:'002',name:"刘",age:17}, ] } }) </script>
-
v-for:遍历对象内容(不常用)
index相当于key
<body> <div id="div1"> <li v-for='(item,key) in persons' > {{item}}--{{key}} <!-- 张--name 18--age --> </li> </div> </body> <script type="text/javascript"> Vue.config.produnctionTps=false new Vue({ el:"#div1", data:{ persons:{ name:"张", age:18 } } })
-
遍历指定次数(不常用)
<body> <div id="div1"> <li v-for='(item,key) in 5' :key=i> {{item}}--{{key}} <!-- 1--0 2--1 3--2 4--3 5--4 --> </li> </div> </body>
-
key值
key值特殊的属性主要使用在Vue的虚拟DOM算法,在新旧node对比,数据修改的时候,他会根据key值去判断某一个值是否修改,如果修改则重新渲染该项,否则复用之前的元素。在v-for中key值一般为id,也就是唯一的值,但是一般不要使用index作为key(没有删除或添加Li的时候可以用)。
-
虚拟dom
Vue的虚拟DOM算法,在新旧node对比时辨识VNodes,如果不使用key,Vue会使用一种最大限度减少动态元素,并且尽可能的尝试就地复用,相同类型元素的算法,而使用key值时,它会就key的变化重新排列元素顺序,并且会移出key不存在的元素
数组更新检测
对原数组产生影响的方法会被拦截到
-
push(),pop(),shift(),unshift(),splice(),sort(),reverse()
以上方法原数组会发生变动
-
filter(),concat(),slice(),map()
以上方法原数组不会发生变动,只能用新数组替换旧数组方案
解决方案:
vm.items[indexOfitem]=newValue //(1) Vue.set(example.itemsindexOfitem,newValue) //(2) splice
模糊查询案例
方案1:
<div id="box">
<input type="text" @input="handlerInput" v-model="mytext" />
<ul>
<li v-for="data in newlist" :key="data">
{{data}}
</li>
</ul>
</div>
<script>
//用户输入后获得数据,过滤,输出
var vm = new Vue({
el: "#box",
data: {
datalist: ["aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg"],
newlist: ["aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg"],
mytext: ""
},
methods: {
//input事件可以实现对input输入框值的实时监控,只要input输入框值发生改变就会触发
handlerInput() {
//创建于原数组一样的新数据,永远用旧的过滤给新的
this.newlist = this.datalist.filter(item => item.includes(this.mytext))
//console.log(newlist)
}
}
})
</script>
方案2:函数表达式
<div id="box">
<input type="text" v-model="mytext" />
<ul>
<!-- 在属性中放表达式执行函数 -->
<!-- 利用函数返回值 -->
<li v-for="data in test()" :key="data">
{{data}}
</li>
</ul>
</div>
<script>
//用户输入后获得数据,过滤,输出
var vm = new Vue({
el: "#box",
data: {
datalist: ["aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg"],
mytext: ""
},
methods: {
test() {
return this.datalist.filter(item => item.includes(this.mytext))
}
}
})
</script>
事件处理器
是否加小括号的区别
- @click=“handleClick”
- 不加小括号的事件处理方式,vue会自动传递事件对象(事件源),可以在事件处理函数中获取事件对 象,但是无法传递自定义的参数
- @click=“handleClick()”
- 可以传递自定义的参数
- @click=“handleClick($event,参数,参数…)”
- 既能获取事件对象也能传参,$event是固定的,自定义的参数传递从第二个参数开始
事件修饰符
-
.stop:阻止事件冒泡(加给子元素事件)
在vue中子父级都绑定了同一事件,如原生一样会有事件冒泡行为
原生js使用e.stopPropagation()阻止事件冒泡
-
.self:阻止事件冒泡(加给父元素事件)
-
.prevent:阻止默认事件的发生
-
.once:设置事件只能触发一次
-
案例:模态框(悬浮输入框,点击狂周围的位置,输入框会小时,点击输入框内不会)
<style> #overlay { background: rgba(0, 0, 0, 0.6); width: 100%; margin: auto; position: fixed; top: 0; right: 0; left: 0; bottom: 0; } #center { background: #ffff; border-radius: 5px; padding-top: 15px; padding-left: 30px; padding-bottom: 15px; width: 200px; height: 160px; position: fixed; margin: auto; left: 0; right: 0; top: 0; bottom: 0; } </style> <div id="box"> <button @click="isShow=true">show</button> <div id="overlay" v-show="isShow" @click="isShow=false"> <div id="center" @click.stop> <div>用户名<input type="text"></div> <div>密码<input type="password"></div> <div><button>登录</button></div> </div> </div> </div> <script> //用户输入后获得数据,过滤,输出 var vm = new Vue({ el: "#box", data: { isShow: "false" } }) </script>
按键修饰符
与键盘中按键事件绑定在一起
- .enter
- .tab
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
- .keycode值
表单控件绑定
<body>
<div id="box">
<!-- 记住用户名 -->
<div>
<div>用户名:<input v-model="mytext" /></div>
<input type="checkbox" v-model="isRemember" />记住用户名
<button @click="handlerLogin">登录</button>
</div>
<!-- 多选框记忆 -->
<div>
<h2>注册页面-兴趣爱好 </h2>
<input type="checkbox" v-model="checklist" value="vue">vue
<input type="checkbox" v-model="checklist" value="react">react
<input type="checkbox" v-model="checklist" value="weixin">小程序
</div>
<!-- 单选 -->
<div>
<h2>
性别选择
</h2>
<input type="radio" v-model="select" value="a">男
<input type="radio" v-model="select" value="b">女
</div>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
mytext: localStorage.getItem("username"),
isRemember: true,
checklist: [],
select: "a"
},
methods: {
handlerLogin() {
if (this.isRemember) {
localStorage.setItem("username", this.mytext)
}
}
}
})
</script>
购物车案例
表单修饰符
-
number:将表单的值转为数字类型
与text输入框配合使用,输入框内有数字与字母时,会自动取出字母
-
trim:将表单值左右两侧的空格去除
-
lazy:将表单的input事件改为change事件,当失去焦点或按下enter键时,绑定的数据会发生改变
<div><input type="number" placeholder="请输入内容" v-model.number="age"></div>
<div><input type="text" placeholder="请输入文本" v-model.trim="info"></div>
<div><input type="text" placeholder="请输入文本" v-model.lazy="msg"></div>
计算属性
为了计算某段计算逻辑得到结果,是写在computed对象中的属性
修改英文串首字母大小写为例:
三种方法
- 模板语法:虽然模板语法使用非常便利,但是它是被设计成用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护
- method方法:因为计算属性是基于缓存实现的,只在计算属性所依赖的数据发生改变时它们才会重新求值,否则访问 计算属性会立即返回之前的计算结果,而不必再次执行函数。 相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
- 计算属性:任何复杂逻辑其计算结果需要被缓存的都应当使用计算属性
<div id="box">
<!-- 模板语法: -->
{{my.name.substring(0,1).toUpperCase()+myname.substring(1)}}
<!-- method方法: -->
{{myMethodsName()}}
<!-- 计算属性: -->
{{myComputedName}}
</div>
<script>
var ve=new({
el:"#box",
data:{
myname:"zsl"
},
methods:{
myMethodsName(){
return this.my.name.substring(0,1).toUpperCase()+myname.substring(1)
}
},
computed:{
myComputedName(){
return this.my.name.substring(0,1).toUpperCase()+myname.substring(1)
}
}
})
</script>
监听watch
监听的属性发生变化时,会自动调用回调函数,执行相关操作
-
语法:
watch:{ mytext(){} }
区分data,methods,computed与watch
- data:状态;被拦截的数据
- 方法:事件绑定,逻辑计算,可以不用return,没有缓存
- 计算属性:重视结果,必须有return,有缓存,同步
- watch:重视过程,监听值的变化,不用返回值,异步同步都可以
fetch
Fetch 是一个现代的概念, 等同于XMLHttpRequest。它提供了许多与XMLHttpRequest相同的功能。
但兼容性不太好,可以通过引入https://github.com/camsong/fetch-ie8
解决
-
get
语法
methods:{ handlerFetch(){ fetch("...") .then(res=>res.json()) .then(res=>{ console.log(res) }) } }
-
post
key=value&key=value格式
fetch("...", {
method: "post",
headers: {//请求头,请求数据格式
"Content-Type": "application/x-www-form-urlencoded"
},
body: "name=key$age=2"//请求体
}).then(res => res.json())
.then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
axios
Axios是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装
-
get
axios.get("...") .then(res => { console.log(res.data); //这里data是axios要求的,不是指状态的data })
-
post
axios会自动根据你传入的是字符串还是对象判断添加相应的请求头。
axios.post("...", "name=wer&age=1") .then(res => { console.log(res.data); })
过滤器
Vue3不支持过滤器
<div id="box">
<ul>
<li v-for="item in datalist" :key="item.id">
<img :src="item.img|imgFilter1">
{{item.nm}}
</li>
</ul>
</div>
<script>
Vue.filter("imgFilter1",(url)=>{
return url.replace('','')
})
var ve = new({
......
组件
定义
将html,css,js封装在一个容器内,优势是能复用编码,简化项目代码,提高运行效率
- 组件名字不能与系统标签重名
- 组件名js用驼峰,html用-
- data状态必须是函数的写法
- dom部分无高亮显示
- css只能写行内样式
- 每个组件是孤岛,无法访问外界的状态或方法
- template只能有一个跟组件
- new Vue 也是一个组件
- 组件注册要在 new Vue完成
语法
<div id="box">
根组件标签
</div>
<script>
Vue.component("标签名",{
template:"dom结构",
data(){return {}}
methods: {},
computed: {},
watch:{},
})
</script>
组件注册
-
全局组件
在script中
Vue.component
的是全局组件,可以插入到其他组件的dom结构,也可以单独自己成为根节点 -
局部组件
在component中
components:{"组件名":{},...}
的是全局组件,可以插入到其他组件的dom结构,也可以单独自己成为根节点
传值
利用属性
-
父传子-用属性
- 在根组件中
<navbar myname="父组件传的信息"></navvar>
,要是动态的就需要设置动态属性:
- 在component中
props:["接收的父组件属性"]
- 接下来在component的dom结构中的子组件就可以调用信息
例子:
<div id="box"> <navbar myname="电影" :myright="true" :myparent="parent"></navbar> <navbar myname="电影" :myright="false" :myparent="parent"></navbar> </div> <script> Vue.component("navbar",{ props:["myname","myright","myparent"], template:"<div> <span>{{myname}}--{{myparent}}</span> <button v-show="myright"></button> </div>" </script>
- 在根组件中
-
属性验证
不符合数据类型会报错
props;{ myname:String, myright:Boolean }
-
默认属性
例子理解
props:{ myname:{ type:String, defalut:"" }, myname:{ type:Boolean, defalut:"true" } }
-
子传父-用事件
- 子组件中定义数据
- 子组件中定义一个方法,用于触发传值事件
- 子组件中通过$emit(“自定义方法名”,需要传的值)传值
- 在父组件中定义自定义事件接收子组件传递的事件
- 在父组件data中定义一个值存储接受的值
抽屉例子:将isshow取反
<div id="box"> //在navbar中监听一个事件handleEvent,此事件是在父组件box中提前定义好的 <navbar @myevent="handleEvent"></navbar> <sidebar v-show="isShow"></isdebar> </div> <script> Vue.component("navbar",{ template:'<div style="background-color:red;"> //一点击按钮,触发监听事件 <button @click="handlerClick()">点击</button></div>', methods:{ handlerClick(){ this.$emit("myevent") } } }) Vue.component("sidebar",{ template:'<div style="background-color:yellow;"> <ul> <li>11</li> <li>22</li> <li>33</li> </div>' }) new Vue({ el:"#box", data:{ isShow:true }, methods:{ handleEvent(data) { this.isShow=!this.isShow } } }) </script>
-
兄弟相传
子传父后再父传另一个子
例子理解
<div id="box"> <button @click="handleAjax">ajax</button> <!-- 2.监听事件 --> <film-item v-for="item in datalist" :key="item.filmId" :mydata="item" @event="handleEvent"></film-item> <film-detail :film-data="filmData"></film-detail> </div> <script> Vue.component("filmItem", { //1.获取父组件的datalist里的item props: ["mydata"], template: '<div class="item"><img :src="mydata.paster"/>{{mydata.name}}<div><button @click="hanleClick">详情</button></div></div>', methods: { //3.自定义处理器,传数据 handleClick() { this.$emit("event", this.mydata.synopsis) } } }) Vue.component("filmDetail", { //5.接收父组件的数据 props: ["filmDate"], template: '<div class="filminfo">{{filmData}}</div>' }) new Vue({ el: "#box", data: { datalist: [], filmData: "" }, methods: { handleAjax() { fetch("...") .then(res => res.json()) .then(res => { console.log(res.data.films) this.datalist = res.data.films }) }, //4.接收数据 handleEvent(data) { //临时变量不能渲染到页面,将data临时变量赋值给父组件 this.filmData = data } } }) </script>
-
中央数据总线bus
各个组件内部要传输的数据或者要执行的命令信息,靠bus来通信。
靠订阅发布模式实现,创建Bus总线
var bus=new Vue
,有一方是订阅者bus.$on(""*.()=>{})
,一方是发布者bus.$emit("")
代码理解
<div id="box"> <button @click="handleAjax">ajax</button> <film-item v-for="item in datalist" :key="item.filmId" :mydata="item"></film-item> <film-detail></film-detail> </div> <script> //1.定义一个全局的总线 var bus = new Vue() Vue.component("filmItem", { props: ["mydata"], template: '<div class="item"><img :src="mydata.paster"/>{{mydata.name}}<div><button @click="hanleClick">详情</button></div></div>', methods: { handleClick() { //发布者传数据 bus.$emit("zsl", this.mydata.synopsis) } } }) Vue.component("filmDetail", { data() { return { info: "" } }, //生命周期,组件挂好就触发 mounted() { //订阅者接收数据 bus.$on("zsl", (data) => { this.info = data }) }, template: '<div class="filminfo">{{info}}}</div>' }) new Vue({ el: "#box", data: { datalist: [] }, methods: { handleAjax() { fetch("...") .then(res => res.json()) .then(res => { console.log(res.data.films) this.datalist = res.data.films }) } } }) </script>
②单独创建bus.js文件
-
组建通信ref
绑定节点可以得到节点对象,绑定组件得到组件对象
在子组件绑定
ref=""
,在父组件中$ref.
可以获取子组件<div id="box"> <input type="text" ref="mytext"/> <div ref="mydiv">111</div> <button @click="handleAdd"></button> <child ref="mychild"></child> </div> <script> vue.component("child",{ data(){ return{ myname:"1111" } }, template:'<div>{{name}}}}</div>' }) new Vue({ el:"#box", methods:{ handleAdd(){ console.log(this.$refs.mytext,this.$refs.mydiv,this.$refs.mychild) console.log(this.$refs.mychild.myname) this.$refs.mychild.myname=2222 } } }) </script>
组件注意
组件属性:父组件传给子组件的属性,只有父组件可以重新传,但不允许子组件随意修改
组件状态:组件内部状态,可以随便修改
Vue3的组件语法
//dom结构无改变
<div id="box">
<navbar myname="**">
<div>111</div>
</navbar>
</div>
<script>
//用对象封装好vue内的
var obj = {
data() {
return {
myname: "aa"
}
},
methods: {},
computed: {}
}
//语法1:
Vue.creatApp(obj).component("navbar", {
props: ["myname"],
template: '...'
}).mount("#box")
//语法2:好看些
var app = Vue.creatApp(obj)
app.component("navbar", {
props: ["myname"],
template: '...'
})
app.component("...", {})
//相当于el:"#box"
app.mount("#box")
</script>
动态组件
选项卡:方案1
每一个选项组件设置一个组建文件中,通过修改状态控制组件显示与隐藏
<div id="box">
<home v-show="which==='home'"></home>
<list v-show="which==='list'"></list>
<shopcar v-show="which==='shopcar'"></shopcar>
<footer>
<ul>
<li @click="which='home'">首页</li>
<li @click="which='list'">列表</li>
<li @click="which='shopcar'">购物车</li>
</ul>
</footer>
</div>
<script>
vue.component("home",{
template:'<div>home</div>'
})
vue.component("list",{
template:'<div>list</div>'
})
vue.component("shopcar",{
template:'<div>shopcar</div>'
})
var vm=new Vue({
el:"#box",
data:{
which:"home"
}
})
</script>
方案2:
Vue内置了一个动态组件<component></componnet>
,可以设置有一个is
属性,控制创建的是哪个组件
-
动态组件有一个特点:每次修改is属性时,之前创建的组件会删除再创建新的组件,所以每次转变选项卡,用户在前面选项卡留下的数据不会被保存下来
解决方案:
可以在动态组件套一个
<keep-alive></keep-alive>
<div id="box">
<component :is="which"></component>
<footer>
<ul>
<li @click="which='home'">首页</li>
<li @click="which='list'">列表</li>
<li @click="which='shopcar'">购物车</li>
</ul>
</footer>
</div>
<script>
vue.component("home",{
template:'<div>home</div>'
})
vue.component("list",{
template:'<div>list</div>'
})
vue.component("shopcar",{
template:'<div>shopcar</div>'
})
var vm=new Vue({
el:"#box",
data:{
which:"home"
}
})
插槽slot
扩展组件能力,提高组件的复用性
可以混合父组件内容子组件自己的模板
分类
-
单个插槽
-
语法:
HTML中:
<组件标签>想插入html结构<组件标签>
组件的template的dom结构中:
<slot></solt>
结果想插入的html结构会插入到slot标签中
-
-
具名插槽
-
语法:
-
HTML中:
<组件标签>想插入html结构加上slot属性<组件标签>
组件的template的dom结构中:
<slot name-""></solt>
结果想插入的html结构会插入到对应name的slot标签中
-
Vue3新语法
HTML改:
<组件标签>
<template>
想插入html结构,想要要创建具名插槽就加上v-slot=“...”指令或#...特殊符号
</template>
<组件标签>
slot改进抽屉
<div id="box">
<navbar></navbar>
<!-- box组件可以直接访问插槽的isShow -->
<button @click="isShow=!isShow"></button>
<slidebar v-show="isShow"></slidebar>
</div>
<script>
vue.component("navbar", {
template: '<div><slot></slot></div>'
})
Vue.component("slidebar",{
template:'<ul style="background-color:yellow;width:200px;height:500px;"><li>首页</li><li>钱包</li><li>设置</li></ul>'
})
new Vue({
el:"#box",
data:{
isShow:false
}
})
</script>
过渡效果
动画过渡
检测到transition标签内节点的改变而更换class值
appear属性:一开始页面加载完默认无动画,此属性可以加载有动画
mode=“out-in”:实现transition内两标签同行进出
-
语法一:
<style> //设置css动画 .zsl-enter-active{ animation: aa 1.5s; } .zsl-leave-active{ animation:aa 1.5s reverse } @keyframes aa{} </style> <div id="box"> //class自动切换控制动画进出 <button @click="isShow=!isShow">change</button> <transition enter-active-class="zsl-enter-active" leave-active-class="zsl-leave-active"> <div v-show="isShow">11111111111</div> </transition> </div> <script> new Vue({ el:"#box", data:{ isShow:false } }) </script>
-
语法二:
CSS:
.***-enter-active
&.***-leave-active
HTML:
<transition name="***">...</transition>
过渡中的diff算法
引:transition标签内的div互斥,两者只能出现一个,如果想加两个div,那么必须为两个div加上key值
vue更新原理,通过拦截到数据发生改变后,Vue中有watcher观察者通知所有与此节点相关的dom与组件重新渲染,渲染时,会渲染新的虚拟dom节点,其本质是对象,与老的虚拟dom节点对比。对比过程中Vue内置了diff算法,此算法会保证最优的算法对比,以最小的代价更新dom
组件模板更新时
-
同层级对比:适用于一层有1个标签
-
同key值对比
key一样会对比,key不一样会直接删除再创建新的
检测到两个div一样,会实施替换行为(删除节点);但两标签有key值或不同标签,检测到两者大为不同,实施动画过渡行为
-
同组件对比
列表过渡
<transition-group>
默认实例化为一个span标签呈现,通过tag
属性更换实例化为其他元素
语法:为列表外层添加<transition-group name="***"></transition-group>
包裹的列表必须提供唯一的key属性值(可以是状态的item)
可复用过度
将动画封在组件中实现可复用
封装多套动画时,可以通过html设置自定义属性传值给组件实现不同动画的切换。语法如下
<!-- 动画封装-->
<style>
.right-enter-active{}
.right-leave-active{}
@keyframes aa{}
.left-enter-active{}
.left-leave-active{}
@keyframes bb{}
...
</style>
<body>
<组件标签 mode="动画"></组件标签>
<script>
Vue.component("组件名",{
props:["mode"],
template:`<transition name="mode"></transition>`
})
</script>
生命周期
创建阶段
创建阶段的以下函数只会执行一次。
-
beforeCreate
在组件实例初始化完成之后立即调用。
此时无法访问到实例的状态,methods,computed等。
-
created
在组件实例处理完与状态相关的选项后调用。
此时可以访问到实例的状态,methods,computed等。
可以在这个函数中进行初始化状态和挂载一些属性到当前实例等操作;
因为拦截不到状态以外的,这里挂载的属性不会被拦截。就是页面上如果显示了这个属性。属性值改变了,页面上不会更新。
-
beforeMount
组件被挂载之前调用。调用这个函数的时候,已经完成了其响应状态的设置,但还没有创建DOM节点。还没有上树。能拿到解析之前的DOM节点。
-
mounted
组件被挂载之后(上树)调用。
能达到真实的dom节点
在mounted函数里可以进行以下操作:
1.依赖DOM创建之后才做初始化工作的插件(swiper)
2.订阅:bus.$on
3.加载页面时的Ajax请求
更新阶段
有任何状态发生改变就会调用
-
beforeUpdate
在组件的状态改变,或DOM结构发生改变时调用
状态立即更新,dom异步更新
-
updated
状态改变之后,更新完DOM树之后调用。在updated里访问节点才是最安全的
销毁阶段
用于组件删除前的清除定时器,事件解绑
-
beforeDestroy (Vue3:beforeUnmount)
组件销毁之前调用。调用这个函数是组件实例任然保留着之前的所有功能
-
destroyed (Vue3:unmounted)
组件销毁完之后调用。所有的子组件已经被销毁,所有的相关响应式作用都已经停止
swiper
具有滑动特效插件,能实现触屏焦点图、触屏Tab切换、触屏轮播图切换等常用效。
基于dom,在Vue中不管用
官网:https://www.swiper.com.cn/
使用:可以在基础演示中查看特效源代码复制粘贴
dom使用
静态使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="../allin/swiper-min-css.css">
<script src="../allin/swiper-min-js.js"></script>
<style>
.zsl {
height: 500px;
}
</style>
</head>
<body>
<header>导航</header>
<div class="swiper-container zsl">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
<!-- 如果需要导航按钮 -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<!-- 如果需要滚动条 -->
<div class="swiper-scrollbar"></div>
</div>
<footer>底部内容</footer>
<script>
new Swiper(".zsl", {
//垂直方向
// direction: "vertical",
// 如果需要分页器
//循环返回第一张
loop: true,
pagination: {
el: '.swiper-pagination',
},
// 如果需要前进后退按钮
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
// 如果需要滚动条
scrollbar: {
el: '.swiper-scrollbar',
},
//自动播放
autoplay: {
delay: 2500,
disableOnInteraction: false,
},
})
</script>
</body>
</html>
动态使用:
swiper有初始化过早的问题,swiper更新完成后,AJAX才想后端请求数据,此时swiper中无动态节点,没办法实现特效。
解决方案:把swiper放在ajax请求后执行
Vue动态使用
将new swiper放在updated()里面,等dom加载更新完成后再调用
但有缺点:
- 无复用性
- 当页面其他状态更新,update重新运行,new swiper会执行多次,出现bug
swiper组件使用
-
同时解决动态问题与初始化问题
<div id="box"> 初始化前key值为0,初始化后值为3;新老dom对比时后删除老的,创建新的。创建新的时,数据已经完善,就不会出现wisper初始化过早的问题 <swiper key="datalist.length"> <swiper-item v-for="data in datalist" :key="data"> <img :src="data"> </swiper-item> </swiper> </div> <script> Vue.component("swiperItem", { template: `<div class="swiper-slide"><slot></slot></div>` }) Vue.component("swiper", { template: `<div class="swiper-container zsl"><div class="swiper-wrapper"><slot></slot></div><div class="swiper-pagination"></div></div>`, mounted() { new Swiper(".zsl", { pagination: { el: '.swiper-pagination' }, loop: true, autoplay: { delay: 2500, disableOnInteraction: false, } }) }, }) new Vue({ el: "#box", data: { datalist: [] }, mounted() { //模拟AJAX请求数据,并映射成div渲染页面 setTimeout(() => { this.datalist = ["...", "...", "..."] }, 2000) } })
-
在dom传属性控制特效
HTML: //传属性 <swiper :loop="***"></swiper> JS: Vue.component("swiper", { //接并判断,设置默认值 props: { loop: { type: Boolean, default: true } }, template: ``, mounted() { new Swiper("", { //赋值控制特效 loop: true }) }
指令
为了操作底层dom
通过指令可以知道dom什么时候创建完成,从而进行初始化工作
指令的生命周期
- bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个绑定时执行一次的初始化动作。
- inserted:第一次插入父节点时调用(父节点存在即可调用,不必存在于document中)。
- update:模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
- componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
- unbind:只调用一次,指令与元素解绑时调用。
Vue3指令的生命周期
- created:在绑定元素的 attribute 或事件监听器被应用之前调用;
- beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用;
- mounted:在绑定元素的父组件被挂载后调用;
- beforeUpdate:在更新包含组件的 VNode 之前调用;
- updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用;
- beforeUnmount:在卸载绑定元素的父组件之前调用;
- unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次;
语法
HTML中<div v-指令名><div>
JS中Vue.directive('指令名',{})
可以传值
HTML中<div v-指令名="'**'"><div>
JS中
Vue.directive('指令名',{
inserted(el,binding){
el.style.background=binding.value
}
})
指令应用
利用指令可以知道dom什么时候创建完成
轮播图swiper
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="../allin/swiper-min-css.css">
<script src="../allin/swiper-min-js.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box">
<header>导航-{{myname}}</header>
<div class="swiper-container zsl">
<div class="swiper-wrapper">
<!-- 传多个值 -->
<div class="swiper-slide" v-for="(data,index) in datalist" :key="data" v-swiper="{index:index,length:datalist.length}"><img :src="data"></div>
</div>
<div class="swiper-pagination"></div>
</div>
<footer>底部</footer>
</div>
<script>
Vue.directive("swiper", {
//每一个节点插入都执行一次
inserted(el, binding) {
let {
index,
length
} = binding.value
if (index == length - 1) {
new Swiper(".zsl", {
pagination: {
el: '.swiper-pagination',
},
loop: true,
autoplay: {
delay: 2500,
disableOnInteraction: false,
}
})
}
}
})
var vm = new Vue({
el: "#box",
data: {
datalist: [],
myname: "zsl"
},
mounted() {
setTimeout(() => {
this.datalist = ["https://gw.alicdn.com/bao/uploaded/O1CN010GiV8T1sGVnjOuwVK_!!6000000005739-0-yinhe.jpg_210x210q75.jpg_.webp", "https://gw.alicdn.com/bao/uploaded/O1CN01KViP3y1LIuXLEwfeU_!!6000000001277-0-yinhe.jpg_210x210q75.jpg_.webp", "https://gw.alicdn.com/bao/uploaded/i1/6000000002039/O1CN01tuS3H41QvuO8gMTMz_!!6000000002039-0-picassoopen.jpg_210x210q75.jpg_.webp"]
}, 2000)
},
/* updated(){
new Swiper(".zsl",{
pagination:{
el:'.swiper-pagination',
},
loop:true,
autoplay:{
delay:2500,
disableOnInteraction:false,
}
})
} */
})
</script>
</body>
</html>
nextTick
Vue在更新 DOM时是异步执行的。当数据发生变化,Vue将开启一个异步更新。
nexttick是Vue 内部的异步队列的调用方法,它比updated执行晚并只执行依次,但无任何复用性
语法
this.$nextTick(() => {
//swiper写在这
})
单文件组件
在很多 Vue 项目中,我们使用 Vue.component
来定义全局组件,紧接着用 new Vue({ el: '#container '})
在每个页面内指定一个容器元素。
这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图。但当在更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显:
- 全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复
- 字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的
\
- 不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
- 没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug (formerly Jade) 和 Babel
文件扩展名为 .vue
的 单文件组件为以上所有问题提供了解决方法
文件内部结构与语法
-
template 组件的模板区域
template内部只能有唯一根节点
-
script 业务逻辑区域
<script> //ES6导出规范,bable会将es6转化为es5 export default{ data(){ return{} } } </script>
-
style 样式区域
可以用scss写法(让css支持简单的逻辑计算) <style lang="scss"> $width:300px; ul{ li{ backgground:yellow; width:$width; } } </style>
Vue-cli脚手架
组件注册
单文件组件中插入子单文件组件时,要在父文件中注册子组件
-
全局注册
import ** from "子组件路径" import Vue from "vue" Vue.component("子组件", **)
-
局部注册
import ** from "子组件路径" export default{ components:{ ** } }
通讯
通讯,插槽与生命周期可正常使用
但父组件的css样式会影响子组件的样式
解决方案:在父组件的css加上scoped
属性,为样式加上唯一的属性选择器,<style lang="scss" scoped></style>
axios获取数据(72集)
默认情况下,我们的项目中并没有对axios包的支持,所以我们需要下载安装。
-
在项目根目录中使用 npm安装包
npm install axios
-
main.js文件中,导入axios并把axios对象 挂载到vue属性中作为一个子对象,这样我们才能在组件中使用
import axios from 'axios' // 把对象挂载vue中 Vue.prototype.$axios = axios; // 把对象挂载vue中 new Vue({ el: '#app', components: { App }, template: '<App/>' })
-
在组建中利用axios获取数据
<script> export default{ methods:{ // 使用axios请求数据 get_w() { this.$axios.get("http://wthrcdn.etouch.cn/weather_mini?city=青岛").then((response) => { console.log(response.data); }) } }, </script>
反向代理
在前后端分离开发的场景,前端有个服务器(提供静态页面),后端有个服务器(提供接口);此时,前端需要连接后端的接口,就会出现跨域问题
反向代理原理:
让proxy通过vue产生一个代理服务器,然后通过这个代理服务器去请求数据,最后把请求的数据返回
注意:一般都需要重新运行
-
首先在项目根目录下创建 vue.config.js
module.exports = { lintOnSave:false, devServer:{ //设置反向代理 proxy:{ '/ajax':{ target:'...',//要跳转的位置 changeOrigin:true } //反向代理执行过程: axios访问地址中的 /zsl 转换为 target + /zsl: 创建虚拟服务器 去掉/api '/zsl': { target: '...', changeOrigin: true, pathRewrite: { '^/zsl': "" }, } } }
别名
@
是webpack配置的别名,代表指定到src这个文件夹的路径
使用例子:
import navbar from '@/mycomponent/Navbar'
单页面应用spa
单页应用:只有一个主页面的应用,一开始只需要加载一次js、css等相关资源。单页应用跳转,就是使用路由跳转切换相关组件,仅刷新局部资源。
多页应用:多个独立的页面的应用,每个页面必须重复加载js、css等相关资源,多页应用跳转,需要整页资源刷新
单页应用(SPA) | 多页应用(MPA) | |
---|---|---|
结构 | 一个主页面 + 许多模块的组件 | 许多完整的页面 |
资源文件(css,js) | 组件公用的资源只需要加载一次 | 每个页面都要自己加载公用的资源 |
刷新方式 | 页面局部刷新 | 整页刷新 |
url模式 | a.com/#/page1 | a.com/page1.html |
a.com/#/page2 | a.com/page1.html | |
用户体验 | 页面切换快,体验佳;当初次加载文件过多时,需要做相关的调优。 | 页面切换慢,网速慢的时候,体验尤其不好 |
搜索引擎优化(SEO) | 对体验度和流畅度有较高要求的应用,不利于 SEO(可借助 SSR 优化 SEO),适用于经常切换页面的场景和数据传递较多,多表单的场景 | 适用于对 SEO 要求较高的应用 |
过渡动画 | Vue 提供了 transition 的封装组件,容易实现 | 很难实现 |
内容更新 | 相关组件的切换,即局部更新 | 整体 HTML 的切换,费钱(重复 HTTP 请求) |
路由模式 | 可以使用 hash ,也可以使用 history | 普通链接跳转 |
数据传递 | 因为单页面,使用全局变量就好(Vuex) | cookie 、localStorage 等缓存方案,URL 参数,调用接口保存等 |
相关成本 | 前期开发成本较高,后期维护较为容易 | 前期开发成本低,后期维护就比较麻烦,因为可能一个功能需要改很多地方 |
html文件请求 | 第一次进入页面的时候会请求一个html文件,刷新清除一下。切换到其他组件,此时路径也相应变化,但是并没有新的html文件请求,页面内容也变化了 | 每一次页面跳转的时候,后台服务器都会给返回一个新的html文档 |
首屏时间 | 首屏时间慢,首屏时需要请求一次html,同时还要发送一次js请求,两次请求回来了,首屏才会展示出来 | 首屏时间快,访问页面的时候,服务器返回一个 html,页面就会展示出来,这个过程只经历了一个HTTP请求 |
路由
路由route是一组映射关系,多个路由需要路由器router进行管理。
设定访问路径,并将路径和组件映射起来,用于局部刷新页面
配置
复习77集
重定向
就是通过各种方法将各种网络请求重新定个方向转到其他位置
解读重定向:当路径是‘/’时跳到/center
{
path: '/',
redirect: '/center'
}
声明式导航
难点:刷新页面后,页面如何得知当前页面的选中的选项
解决:
window.onhashchange可以监听路径改变, location.hash
可以获取路径
App.vue中:
<template>
<div>
<!-- 声明式导航:a链接跳转 -->
<ul>
<!-- vue-router 声明式导航 -->
<li>
<!-- 点击后会自己加上router-link-active的class,可以利用sctive-class属性修改其自动加上的class名 --> <router-link to="/#/movies">电影</router-link>
</li>
<li>
<router-link to="/#/cinemas">影院</router-link>
</li>
<li>
<router-link to="/#/center">我的</router-link>
</li>
</ul>
<!-- 路由容器 -->
<router-view></router-view>
</div>
</template>
<style scoped>
/* 点击后会自己加上router-link-active的class,借此设置样式 */
.router-link-active{
color: red;
}
</style>
嵌套路由
app.vue
<template>
<div id="box">
<ul>
<li> <router-link to="/films">电影</router-link>
</li>
<li>
<router-link to="/cinemas">影院</router-link>
</li>
<li>
<router-link to="/center">我的</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
</script>
<style scoped>
.router-link-active{
color: red;
}
</style>
index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Films from '@/views/Films';
import Nowplaying from '@/views/films/Nowplaying';
import Comingsoon from '@/views/films/Comingsoon';
import Cinemas from '@/views/Cinemas';
import Center from '@/views/Center';
Vue.use(VueRouter); //注册路由插件
//配置表
const routes = [{
path: '/films',
component: Films,
children: [{
path: '/films/nowplaying',
component: Nowplaying,
},
{
path: '/films/comingsoon',
component: Comingsoon,
}
]
},
{
path: '/cinemas',
component: Cinemas,
},
{
path: '/center',
component: Center,
},
/* 重定向:当路径是/时跳到 */
{
path: '/',
redirect: '/films'
}
];
const router = new VueRouter({
routes,
});
export default router;
films.vue
<template>
<div>
<div style="height:200px;background:yellow;">轮播图</div>
<div>二级声明式导航</div>
<router-view></router-view>
</div>
</template>
<script>
</script>
center.vue、cinemas.vue
<template>
<div>
center
</div>
</template>
编程式导航
不使用router-link组件标签 而是使用路哟对象上的push方法实现组件之间的跳转
例子:导航栏下的选项页面中的列表的详情页
Nowplaying .vue
<template>
<div>
nowplaying
<ul>
<!-- 声明式导航写法 -->
<!-- <li v-for="data in datalist" :key="data">
<router-link to="/detail">
{{data}}</router-link>
</li> -->
<!-- 编程式写法 -->
<li v-for="data in datalist" :key="data" @click="handleChangePage">{{data}}</li>
</ul>
</div>
</template>
<script>
export default {
data(){
return{
datalist:["111","222","333"]
}
},
methods:{
handleChangePage(){
this.$router.push('/detail')
}
}
}
</script>
index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Films from '@/views/Films';
import Nowplaying from '@/views/films/Nowplaying';
import Comingsoon from '@/views/films/Comingsoon';
import Cinemas from '@/views/Cinemas';
import Center from '@/views/Center';
import Detail from '@/views/Detail';
Vue.use(VueRouter); //注册路由插件
//配置表
const routes = [{
path: '/films',
component: Films,
children: [{
path: '/films/nowplaying',
component: Nowplaying,
},
{
path: '/films/comingsoon',
component: Comingsoon,
},
{
path: '/films',
redirect: '/films/Nowplaying'
}
]
},
{
path: '/detail',
component: Detail,
},
{
path: '/cinemas',
component: Cinemas,
},
{
path: '/center',
component: Center,
},
/* 重定向:当路径是/时跳到 */
{
path: '/',
redirect: '/films'
}
];
const router = new VueRouter({
routes,
});
export default router;
动态路由
过程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bv9WHNpi-1676059071426)(C:\Users\ABC\AppData\Roaming\Typora\typora-user-images\image-20230209183151787.png)]
后端给两个接口,一个列表接口,一个详情信息接口,
-
朝后端列表接口请求回来列表数据然后布局;
-
点完列表绑定事件,进行动态路由跳转到详情页面;(id是直接被放在了地址上)
-
在详情页面获取这个id;
这个id指的是每个商品唯一的id,然后通过这个id,浏览器才知道我们点击的是哪个商品,才往这个商品详情页面跳转,加载详情页面信息也是通过这个id向后端要的商品信息数据
-
利用获取到的id发请求给后端,后端有相应的接口返回真实的详情数据,
-
布局页面
用:
代表动态路径
nowplaying.vue
<template>
<div>
nowplaying
<ul>
<!-- 声明式导航写法 -->
<!-- <li v-for="data in datalist" :key="data">
<router-link to="/detail">
{{data}}</router-link>
</li> -->
<!-- 编程式写法 -->
<li v-for="data in datalist" :key="data" @click="handleChangePage(data)">{{data}}</li>
</ul>
</div>
</template>
<script>
export default {
data(){
return{
datalist:["111","222","333"]
}
},
methods:{
handleChangePage(id){
this.$router.push(`/detail/${id}`)
}
}
}
</script>
index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Films from '@/views/Films';
import Nowplaying from '@/views/films/Nowplaying';
import Comingsoon from '@/views/films/Comingsoon';
import Cinemas from '@/views/Cinemas';
import Center from '@/views/Center';
import Detail from '@/views/Detail';
Vue.use(VueRouter); //注册路由插件
//配置表
const routes = [{
path: '/films',
component: Films,
children: [{
path: '/films/nowplaying',
component: Nowplaying,
},
{
path: '/films/comingsoon',
component: Comingsoon,
},
{
path: '/films',
redirect: '/films/Nowplaying'
}
]
},
{
//动态二级路由
path: '/detail/:id',
component: Detail,
},
{
path: '/cinemas',
component: Cinemas,
},
{
path: '/center',
component: Center,
},
/* 重定向:当路径是/时跳到 */
{
path: '/',
redirect: '/films'
}
];
const router = new VueRouter({
routes,
});
export default router;
detail.vue
<template>
<div>
detail
</div>
</template>
<script>
export default {
created(){
//拿到当前匹配的路由id
//params传值页面数据消失,传递不显示参数,params相对于query来说较安全一点
console.log('created',this.$route.params.id)
//axios利用id发请求到详情接口,获取数据,布局页面
}
}
</script>
路由命名
通过命名路由来进行从列表页面往详情页面跳转
{
name: '**',
path: '...',
component:...
}
handleClick (id) {
// 1-通过路径跳转
// this.$router.push(`...`)
// 2-通过命名路由跳转
this.$router.push({
name: '**',
params: {
id: myid
}
})
}
路由模式
网页链接中是否包含#
,带#
一定是前端路由,但不带的不一定
网浏览器也不能分清前后端接口,他可能会向前端路由发后端请求
庆幸的是,开发服务器已经做好了处理,凡是它不接受的接口,会重新把index页面渲染到浏览器端,让前端路由接管
路由拦截
现象:点击一个功能时,会跳转到登录页面让我们先登录,登录之后再进行功能操作;但是如果我们登录了,它就不会跳转。
解释:在路由跳转之前,进行路由拦截检查是否登录
全局路由拦截
index.js
//to,from是从哪来,到哪去;next是函数,对某些不拦截的路由执行的函数
router.beforeEach((to, from, next) => {
if (需要拦截验证的路由) {
if (验证成功) {
next()
} else {
// 登录页面跳转
path:'/login'
}
} else {
next()
}
})
export default router;
-
fullpath:
-
fullPath是路由全地址,包括连接携带的参数,如:192.168.0.1/index?page=1,fullPath为/index?page=1
-
path是路径,不带参数,如:192.168.0.1/index?page=1,path为/index
-
-
token
当我们登录时填写账号和密码点击登录的时候,会向服务器发送一个认证,如果认证成功服务器会返回一个token值,这个token值是就相当于身份证一样,如果再次向服务器请求时,由于已经带上了身份证,这样就可以免去再次输入账号和密码的麻烦
-
query
页面跳转的时候,可以在地址栏看到请求参数,即可以看到从哪里跳转
router.beforeEach((to, from) => { console.log(to); if (to.meta.isZslRequired) { // 在本地存储中查看是否有token if (false) {} else { return { // 登录页面跳转 path: '/login', query: { redirect: to.fullPath }, //next('/login'); }; } } else {} }); export default router;
// 跳回上一步 this.$router.push(this.$route.query.redirect);
-
拦截方法:router.beforeEach(回调函数),每个路由跳之前都会执行这个函数
-
回调函数有三个参数:
- to:跳转后路由
- from:跳转前路由
- next:函数
-
不需要拦截的路由通行;
并不需要对所有路由进行拦截,不需要拦截的就next()
局部路由拦截
在需要拦截的路由内部写代码
{
path: '/order',
component: Order,
meta: {
isKerwinRequired: true
},
beforeEnter: (to, from, next) => {}
}
路由懒加载
不在路由文件index.js开始就用import导组件,而是当点到某个路径时,才会加载
语法
取消import导入法,换成回调函数导入法:
{
path: '/center',
component: () => import('../views/CenterView.vue'),
meta: {
isKerwinRequired: true
}
},
组件的rem
在index.html的头部中
<script> document.documentElement.style.fontSize = document.documentElement.clientWidth / 设备宽 * 100 + 'px' </script>
在app.vue中用通用选择器统一设置字体fontsize:16px