1.Vue Snippets
2.Vetur
数据驱动视图:
双向数据绑定:
在网页中,form 表单负责采集数据,Ajax 负责提交数据。
数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例
VivwModel作为MVVM的核心
1.导入vue.js的script脚本
<script src="./lib/vue-2.6.12.js"></script>
2.在页面声明一个将要被vue所控制的DOM区域
<div id="app">{{username}}div>
3.创建VM实例对象
const vm = new Vue({
el:'#app',
data:{
username:'zhangsan'
}
})
指令是vue为开发者提供的模板语法
1.内容渲染
2.属性绑定
3.事件绑定
4.双向绑定
5.条件渲染
6.列表渲染
v-text
指令的缺点:会覆盖元素内部原有的内容!{{ }}
插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!
<div :title="'box'+index">这是一个divdiv>
v-html
指令的作用:可以把带有标签的字符串,渲染成真正的 HTML 内容!注意:插值表达式只能用在元素的内容节点中 ,不能用在元素的属性节点中!
在 vue 中,可以使用 v-bind:
指令,为元素的属性动态绑定值;
简写是英文的 :
在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:
<input type="text" :placeholder="tips">
data:{
tips:'请输入用户名'
}
v-on:
简写是 @
语法格式为:
<p>count的值是:{{count}}p>
<button @click="add">点击加1button>
methods: {
add() {
// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
this.count += 1
}
}
绑定传参
<button @click="add(2)">点击加2button>
add(index){
this.count += index;
}
$event
是vue内置变量,表示原生的DOM事件对象<button @click="add(1,$event)">如果count是偶数,则按钮背景变成红色,否则不变色button>
add(n,e){
this.count += n;
if(this.count % 2 == 0){
e.target.style.backgroundColor = 'red'
}else{
e.target.style.backgroundColor = ''
}
}
事件修饰符 | 说明 |
---|---|
.prevent | 阻止默认行为(a链接跳转、表单提交) |
.stop | 阻止事件冒泡 |
.capture | 以捕获模式触发当前的事件处理函数 |
.once | 绑定的事件只触发一次 |
.self | 只有在event.target是当前元素自身触发事件处理函数 |
.prevent
阻止默认行为
阻止a链接跳转行为
<a href="http://www.baidu.com" @click.prevent="show">跳转到百度首页a>
.stop
阻止事件冒泡
<div style="height:150px;backgroundColor:orange;padding-left:100px;line-height:150px" @click="divHandler">
<button @click.stop="btnHandler">按钮button>
div>
@keyup
<input type="text" @keyup.esc="clearInput($event)" @keyup.enter="commitAjax">
clearInput(e){
e.target.value=""
},
commitAjax(){
console.log('触发了提交');
}
帮助用户在不操作DOM的前提下,快速获取表单的数据
<p>用户的名字是:{{username}}p>
<input type="test" v-model="username">
<p style="white-space: pre-line;">{{ message }}p>
<br>
<textarea v-model="message" placeholder="add multiple lines">textarea>
修饰符 | 作用 |
---|---|
.number | 自动将用户的输入值转为数值类型 |
.trim | 自动过滤用户输入的首尾空白字符 |
.lazy | 在“change”时而非“input”时更新 |
<input type="text" v-model.number="n1"> + <input type="text" v-model.number="n2"> = <span>{{n1+n2}}span>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-445ECG0H-1648046654845)(D:\images\92518294628c3a565cb92ed70a861c1a0b5970378989976dc7630cd3d646fd2e.png)]
<input type="text" v-model.trim="username">
<button @click="show">获取用户名button>
v-show
的原理是:动态为元素添加或移除 display: none
样式,来实现元素的显示和隐藏
v-if
的原理是:每次动态创建或移除元素,实现元素的显示和隐藏
在实际开发中,绝大多数情况,不用考虑性能问题,直接使用 v-if 就好了!!!
v-if 指令在使用的时候,有两种方式:
直接给定一个布尔值 true 或 false
<p v-if="true">被 v-if 控制的元素p>
给 v-if 提供一个判断条件,根据判断的结果是 true 或 false,来控制元素的显示和隐藏
<p v-if="type === 'A'">良好p>
<template v-if="loginType === 'username'">
<label>Usernamelabel>
<input placeholder="Enter your username" key="username-input">
template>
<template v-else>
<label>Emaillabel>
<input placeholder="Enter your email address" key="email-input">
我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
li>
ul>
建议给每个v-for提供一个唯一的key属性,既提升性能,又防止列表状态絮乱
1.key值只能是字符串或数字
2.具有唯一性
3.使用index做key没有任何意义
所有的侦听器,都应该被定义到 watch 节点下
侦听器本质上是一个函数,要监视哪个数据的变化,就把数据名作为方法名即可
新值在前,旧值在后
watch: {
username(newVal,oldVal){
console.log("变换前"+oldVal);
console.log("变换后"+newVal);
}
}
判断用户名是否被占用
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使
用 immediate 选项。示例代码如下:
watch: {
// 定义对象格式的侦听器
username: {
// 侦听器的处理函数
handler(newVal, oldVal) {
console.log(newVal, oldVal)
},
// immediate 选项的默认值是 false
// immediate 的作用是:控制侦听器是否自动触发一次!
immediate: true
}
}
handler是固定写法
如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项
两种写法
直接获取要监听的属性名
watch: {
'info.username'(newVal) {
console.log(newVal)
}
通过deep选项监听
info:{
handler(newVal){
console.log(newVal);
},
deep:true
}
特点:
好处:
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage () {
return this.message.split('').reverse().join('')
}
}
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数
axios 是一个专注于网络请求的库!
发起 GET 请求:
axios({
// 请求方式
method: 'GET',
// 请求的地址
url: 'http://www.liulongbin.top:3006/api/getbooks',
// URL 中的查询参数
params: {
id: 1
}
// result 是返回的结果
}).then(function (result) {
console.log(result)
})
发起 POST 请求:
document.querySelector('#btnPost').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
// await 只能用在被 async “修饰”的方法中
const { data: res } = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(res)
})
如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await
await 只能用在被 async “修饰”的方法中
document.querySelector('#btnPost').addEventListener('click', async function () {
const { data } = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(data)
})
解构赋值的时候,使用 : 进行重命名
document.querySelector('#btnGet').addEventListener('click', async function () {
const { data: res } = await axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks'
})
console.log(res.data)
})
const { data: res } = await axios.post('http://www.liulongbin.top:3006/api/post', { name: 'zs', gender: '女' })
基本使用
<template>
<button @click="postInfo">发起 POST 请求button>
template>
<script>
// 1. 导入 axios
import axios from 'axios'
export default {
//2. 在 methods 定义 axios请求方法
methods: {
async postInfo() {
const { data: res } = await axios.post('http://www.liaoyia.top:3306/api/post', { name: 'zs', age: 20 })
console.log(res)
}
}
}
script>
缺点: 在每次使用时候都要导入 axios 文件,写请求地址 (对后期维护不友好)
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.config.productionTip = false
// 全局配置 axios 的请求根路径 (官方提供配置项)
axios.defaults.baseURL = 'http://www.liaoyia.top:3006'
// 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用
Vue.prototype.$http = axios
// 今后,在每个 .vue 组件中要发起请求,直接调用 this.$http.xxx
// 但是,把 axios 挂载到 Vue 原型上,有一个缺点:不利于 API 接口的复用!!!
new Vue({
render: h => h(App)
}).$mount('#app')
<template>
<button @click="btnGetBooks">获取图书列表的数据</button>
</template>
<script>
export default {
methods: {
// 点击按钮,获取图书列表的数据
async btnGetBooks() {
const { data: res } = await this.$http.get('/api/getbooks')
console.log(res)
}
}
}
</script>
缺点:无法实现API接口的复用 : 在多个组件想使用同一个请求方法(API)的时候,只能在每个组件重新定义一次。
1.如果项目中有多个请求地址,我们可以根据多个地址使用工厂模式封装 js 模块,创建多个 axios 实例对象,并设置请求根路径 (baseURL) :
步骤如下: 在项目的 src
目录下创建utils
文件夹并新建一个 request.js
文件:
import axiox from 'axios'
const request =axiox.create({
//baseURL会在发送请求的时候拼接在url参数的前面
baseURL:'http://jsonplaceholder.typicode.com/',
timeout:5000
})
// 向外导出
export default request
使用这种方法时: 一般我们只会在一个 js 模块创建一个axios 实例对象,并向外导出。
如果有多个服务器地址,那就创建多个 js模块,并在里面创建axios实例对象。
2.为了实现复用性,我们还可以把所有请求,都封装在API模块里,在API模块中,按需导出一个方法,这个方法调用 request.get 或 request.post 来请求一个接口,最后return 一个Promise 对象。
比如想调用接口获取用户相关信息:
在根目录新建 utils 文件夹并在里面新建 userAPI.js 文件
//导入 utils 文件夹下的 request.js
import request from '@/utils/request.js'
export const getArticleListAPI = function(_page, _limit) {
return request.get('/articles', {
params: {
_page,
_limit
}
})
}
2.为了实现复用性,我们还可以把所有请求,都封装在API模块里,在API模块中,按需导出一个方法,这个方法调用 request.get 或 request.post 来请求一个接口,最后return 一个Promise 对象。
比如想调用接口获取用户相关信息:
在根目录新建 utils 文件夹并在里面新建 userAPI.js 文件
//导入 utils 文件夹下的 request.js
import request from '@/utils/request.js'
export const getArticleListAPI = function(_page, _limit) {
return request.get('/articles', {
params: {
_page,
_limit
}
})
}
https://cli.vuejs.org/zh/guide/
npm install -g @vue/cli
vue 项目中 src 目录的构成:
assets 文件夹:存放项目中用到的静态资源文件,例如:css 样式表、图片资源
components 文件夹:程序员封装的、可复用的组件,都要放到 components 目录下
main.js 是项目的入口文件。整个项目的运行,要先执行 main.js
App.vue 是项目的根组件。
import Vue from 'vue' // 导入Vue这个包 得到Vue构造函数
import App from './App.vue' // 导入App组件,将App.vue模板结构渲染到html页面上
import Test from './Test.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(Test), // reader函数指定的组件,渲染到HTML页面上
}).$mount('#app') // 替代el 写法
<template>
<div class="text-box">
<h3>这是用户自定义的test.vueh3>
div>
template>
<script>
// 默认导出
export default{
// data数据源 data必须是一个函数
data(){
return {
username:'zs'
}
}
}
script>
<style lang="less">
.text-box{
background-color: pink;
}
style>
####4.1使用组件的步骤
1.使用import语法导入需要的组件
2.使用components节点注册组件
3.以标签形式使用注册好的组件
在vue项目的main.js入口文件中,通过Vue.component()方法
import Count from '@/components/Count' // 导入全局需要注册的组件
Vue.component('MyCount',Count)
允许使用者通过自定义属性,为当前组件指定初始值
Count.vue
<p>count的值是:{{init}}p>
<script>
export default {
props:['init']
}
Left.vue
<MyCount init="9">MyCount>
// 不加: 传的值是字符串
<MyCount :init="6">MyCount>
想修改props值,可以将数值转存到data中
data(){
return{
count:this.init
}
}
将props作为一个对象存储
// props:['init'],
props:{
// 自定义属性A:{配置选项}
init:{
default:0
}
},
props:{
// 自定义属性A:{配置选项}
init:{
default:0,
type:Number
}
},
如果设定了以后,就必须传某个属性值,否则会报错
required:true
默认情况,写在.vue组件中的样式会全局生效
添加scoped
<style lang="less" scoped>
当使用到第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到/deep/
/deep/ h5{
color:red
}
生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。
生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。
注意:生命周期强调的是时间段,生命周期函数强调的是时间点。
子 -> 父 自定义事件
1.创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
2.在数据发送方,调用 bus.$emit(‘事件名称’, 要发送的数据) 方法触发自定义事件
3.在数据接收方,调用 bus.$on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件
ref辅助开发者不依赖jquery获取DOM元素或组件的引用
在每个组件的实例上,都包含一个$refs
对象里面存储着对应的DMO元素
里面存储着对应的DOM元素或组件的引用。
默认情况下,组件的$refs
指向一个空对象
<h3 ref="myh3">MyRef组件<h3/>
this.$refs.myh3.style.color='red'
情景引入:在父组件上有一个button能重置子组件的值
<Left ref="comLeft">Left>
this.$refs.comLeft.resetCount()
需求:
1.点击按钮时,按钮消失,输入框呈现
2.输入框失去焦点时,输入框消失,按钮呈现
data(){
return{
inputVisible:false // 控制输入框按钮的按需切换 默认值false表示展示按钮隐藏输入框
}
},
<input type="text" v-if="inputVisible" @blur="showButton" ref="iptRef"/>
<button v-else @click="showInput">展示输入框button>
showButton(){
this.inputVisible = false;
},
// 展示输入框
showInput(){
this.inputVisible = true;
this.$refs.iptRef.focus();
}
原因在于在生命周期中,数据渲染到页面上需要一定时间,不能直接在数据变化马上获取到dom元素,此时页面上dom元素还未渲染完成,会导致undefined 元素未找到错误
组件的 $nextTick(cb)
方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的
DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。
showInput(){
// this.$refs.iptRef.focus();
this.inputVisible = true;
this.$nextTick(()=>{
this.$refs.iptRef.focus();
})
}
传统foreach循环找到元素后无法被终止,浪费性能,使用some代替
some 可以使用return true 来终止循环
接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
<script>
// 导入axios请求库
import axios from "axios";
// 导入Header组件
import Header from "@/components/Header/Header.vue";
export default {
created() {
// 调用请求数据的方法
this.initCartList();
},
methods: {
// 封装请求列表数据的方法
async initCartList() {
// 调用axios的get方法,请求列表数据
const { data: res } = await axios.get("https://www.escook.cn/api/cart");
console.log(res);
},
},
components: {
Header,
},
};
</script>
data(){
return{
// 用来存储购物车列表数据
list:[]
}
},
methods: {
// 封装请求列表数据的方法
async initCartList() {
// 调用axios的get方法,请求列表数据
const { data: res } = await axios.get("https://www.escook.cn/api/cart");
if(res.status == 200){
this.list = res.list
}
},
},
<Goods v-for="item in list" :key="item.id">Goods>
使用父子属性传值
不要传一个对象,对于组件复用性不好
<Goods v-for="item in list" :key="item.id" :Flist="item" :title="item.goods_name" :pic="item.goods_img" :price="item.goods_price" :status="item.goods_state" >Goods>
props: {
title: {
title: "",
type: String,
},
pic: {
title: "",
type: String,
},
price: {
title: "",
type: Number,
},
status: {
title: "",
type: Boolean,
},
},
子组件勾选状态变化之后,需要通过子–>父 的形式 通知父组件根据id修改对应商品的勾选状态
<input
type="checkbox"
class="custom-control-input"
id="cb1"
:checked="status"
@change="stateChange"
/>
子组件定义stateChange 向父组件传值
methods:{
stateChange(e){
const newState = e.target.checked // 复选框状态值
this.$emit("state-change",{
id:this.id,
value:newState
})
}
}
父组件监听state-change事件
@state-change="getNewState"
使用some方法比对组件id
如果id相同则更新状态值并结束循环
// 接收子组件传递过来的数据
getNewState(val){
console.log(val)
this.list.some(item=>{
if(item.id == val.id){
item.goods_state = val.value
return true;
}
})
}
// 计算属性
computed:{
// 动态计算出全选状态是true 还是false
fullState(){
return this.list.every(item=>item.goods_state)
}
},
父组件传状态值给子组件
子组件要完成对状态值监听实时渲染
<Footer :isFull="fullState">Footer>
props:{
// 全选的状态
isFull:{
default:true,
type:Boolean
}
}
<input type="checkbox" class="custom-control-input" id="cbFull" :checked="isFull" />
使用@change 监听复选框变化状态,绑定fullChange方法
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
id="cbFull"
:checked="isFull"
@change="fullChange"
/>
methods
// 监听到了全选的状态变化
fullChange(e){
this.$emit('full-change',e.target.checked)
}
@full-change=“getFullState”
<Footer :isFull="fullState" @full-change="getFullState">Footer>
根据子组件传值,对数组进行赋值
// 接收子组件全选状态
getFullState(val){
this.list.forEach(item => (item.goods_state = val))
}
在计算属性中定义amt
// 已勾选商品的总价格
amt(){
// 1.先filter过滤
// 2. 再reduce累加
return this.list
.filter(item => item.goods_state)
.reduce((total,item)=>total += item.goods_price * item.goods_count,0)
<Footer
:isFull="fullState"
@full-change="getFullState"
:amount="amt"
>Footer>
props:{
// 总价格
amount:{
type:Number,
default:0
}
},
<span class="total-price">¥{{ amount }}</span>
父组件将商品数量传给Goods
:count="item.goods_count"
Goods接收参数
// 商品的购买数量
count:{
type:Number,
default:1
}
<Counter :num="count"></Counter>
Count.vue
props:{
num:{
type:Number,
default:1
}
}
<span class="number-box">{{ num }}</span>
接收商品id值,使用EventBus方案,将数据传递给App.vue的时候,需要通知App组件,更新哪个商品的数量
props:{
id:{
type:Number,
required:true
},
num:{
type:Number,
default:1
}
}
···js
import Vue from ‘vue’
export default new Vue()
···
import bus from '@/components/eventBus.js'
add(){
const obj = {
id:this.id,
value:this.num + 1
}
bus.$emit('share',obj)
}
created() {
// 调用请求数据的方法
this.initCartList();
bus.$on('share',val=>{
this.list.some(item=>{
if(item.id == val.id){
item.goods_count = val.value;
return true;
}
})
})
},
// 已勾选商品的总数量
total(){
return this.list
.filter(item => item.goods_state)
.reduce((t,item)=>t+=item.goods_count,0)
}
复制add ,需要注意数量不能为负数,需要做个前置判断
sub(){
if(this.num - 1 === 0)
return;
const obj = {
id:this.id,
value:this.num - 1
}
bus.$emit('share',obj)
}
组件,专门用来实现动态组件的渲染 相当于占位符// 1. 当前需要渲染的组件名称
data(){
return { comName:'Left' }
}
<!-- 2.通过is属性,动态指定要渲染的组件 -->
<component :is="comName"></component>
<!-- 3.点击按钮,动态切换组件名称 -->
<button @click="comName = 'Left' ">展示Left组件</button>
<button @click="comName = 'Right' ">展示Right组件</button>
默认情况下,切换组件无法保持组件的状态,此时可以使用vue内置的
组件保持动态组件的状态
可以把内部的组件进行缓存,而不是销毁组件
<template>
<div class="left-container">
<h3>Left 组件 --- {{count}}h3>
<button @click="count += 1">+1button>
div>
template>
<script>
export default {
data(){
return{
count:0
}
}
}
script>
父组件在渲染left组件时 使用keep-live
<keep-alive>
<component :is="comName">component>
keep-alive>
我们有时会需要组件在被缓存时去执行某件事,这时需要用到生命周期
- 当组件被缓存时,会自动触发组件的 deactivated 生命周期函数。
- 当组件被激活时,会自动触发组件的 activated 生命周期函数
include属性用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号分隔
使用场景:默认情况下keep-alive标签下的组件都会被缓存,而有时我们并不需要部分组件被缓存,故可以使用include指定部分组件被缓存
exclude不能与include同时使用
exclude 指定某个组件不被缓存
如果在“声明组件”的时候,没有为组件指定name名称,则组件的名称默认就是“注册时候的名称”
export default {
name:"MyRight", // 指定name
}
如果组件指定name名称,keep-live标签中的include exclude也要匹配响应的name
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的
部分定义为插槽。可以把插槽认为是组件封装期间,为用户预留的内容的占位符。
App.vue
<Left>
<p>这是在Left组件中的内容区域声明的p标签p>
Left>
Left.vue
<slot>slot>
在为具名插槽提供内容时,在元素上使用
v-slot
指令,可使用#
简写
<template v-slot:default>
<p>template创建的p标签p>
template>
简写
<template #default>
<p>template创建的p标签p>
template>
template 只是一个虚拟的标签,只起到包裹性质的作用
如果没有为插槽指定具体的内容,如空的template标签,希望能够给出提示
可以在slot 标签中填充内容。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效
<slot name="default">这是default插槽的默认内容slot>
在封装组件的过程中,可以为预留的
绑定props数据
这种带有props数据的
插槽叫做作用域插槽
例如: msg=“hello vue”
<slot name="content" msg="hello vue">slot>
在使用组件时候可以使用v-slot:的形式接收作用域插槽对外传的值
<template #content="scope">
<p>{{scope}}p>
div>
<p>{{scope.msg}}p>
接收名不固定,但建议使用scope
<slot name="content" :user="user">slot>
1.Goods添加插槽
<slot name="default">slot>
2.在Goods组件中渲染Counter组件
<Counter @default :num="item.goods_count">Counter>
3.在Counter组件中 绑定点击时间 向Goods传值
add(){
this.$emit('num-change',this.num + 1)
},
4.Goods组件上接收数据,并接收本组件商品对象作为参数传入
默认函数会接收到$event
,但如果有自定义接收参数,则会覆盖默认$event
,需要手动显示声明参数
<Counter
:num="item.goods_count"
@num-change="getNewNum(item,$event)"> Counter>
5.将商品数量更新到当前对象中
// 获取Counter组件发过来的最新的数量值
getNewNum(item,e){
item.goods_count = e;
}
在每个 vue 组件中,可以在 directives 节点下声明私有自定义指令。
<h1 v-color>App 根组件h1>
当指令第一次被绑定到元素上的时候,会立即触发bind函数
形参中的el表示当前指令所绑定到的DOM对象
// 自定义指令
directives:{
color:{
bind(el){
el.style.color="red"
}
}
}
=
为当前指令动态绑定参数<h1 v-color="color">App 根组件h1>
// 自定义指令
directives:{
color:{
bind(el,bingding){
el.style.color=bingding.value
}
}
}
3.如果使用了binding接收指令的参数,依然可以传入字符串
<h1 v-color="color">App 根组件h1>
<p v-color="'red'">cesp>
当DOM更新的时候,会触发update函数
// 自定义指令
directives: {
color: {
bind(el, bingding) {
console.log("触发了bingding");
el.style.color = bingding.value;
},
update(el, bingding) {
console.log("触发了update");
el.style.color = bingding.value;
},
},
},
如果bind 和 update完全一样则可以简写
// 自定义指令
directives: {
color(el,binding){
el.style.color = binding.value;
}
},
在main.js中通过Vue.directive()
声明
Vue.directive('color',function(el,binding){
el.style.color = binding.value
});
路由就是对应关系,不同的Hash 展示不同的页面。
#后面是hash地址
SPA
单页面应用程序 所有组件的展示与切换都在这唯一的一个页面内完成。
此时,不同组件之间的切换需要依赖 前端路由(router)来实现。
1.安装vue-router包
npm i vue-router -S
2.创建路由模块
在 src 源代码目录下,新建 router/index.js
路由模块,并初始化
//1.导入Vue和VueRouter的包import Vue from'vue'
import VueRouter from‘vue-router'
//2.调用Vue.use()函数,把VueRouter 安装为Vue的插件
Vue.use(VueRouter)
//3.创建路由的实例对象
const router=new VueRouter()
//4.向外共享路由的实例对象
export default router
3.导入并挂载路由模块
在 src/main.js
入口文件中,导入并挂载路由模块:
import Vue from'vue'
import App from'./App.vue'
//1.导入路由模块
import router from@/router'
new Vue ({
render:h=>h(App),
//2.挂载路由模块
router:router
}).$mount('#app')
4.声明路由链接和占位符
在 src/App.vue
组件中,使用 vue-router 提供的
和
声明路由链接和占位符:
<tcmplate>
<div class="app-container">
<h1>App组件h1>
<!--1.定义路由链接-->
<router-link to="/home">首页router-link>
<router-link to="/movie">电影router-link>
<router-link to="/about">关于router-link>
<!--2.定义路由的占位符-->
<router-view>router-view>
div>
template>
5.声明路由的匹配规则
在 src/router/index.js 路由模块中,通过 routes 数组声明路由的匹配规则:
//导入需要使用路由切换展示的组件
import Home from'@/components/Home.vue'
import Movie from'@/components/Movie.vue'
import About from'@/components/About.vue'
//2.创建路由的实例对象
const router=new VueRouter({
//在routes数组中,声明路由的匹配规则
routes:[
//path 表示要匹配的hash地址;component表示要展示的路由组件
{path:'/home',component: Home},
{path:'/movie',component: Movie},
{path:'/about',component: About}
]
})
当访问地址 A 的时候,强制用户跳转
到地址C, 从而展示特定的组件页面。
通过路由规则的 redirect
属性,指定一个新的路由地址来实现路由的重定向。
const router=new VueRouter({
//在routes数组中,声明路由的匹配规则
routes:[
// 重定向的路由规则
{ path: '/', redirect: '/home' },
//path 表示要匹配的hash地址;component表示要展示的路由组件
{path:'/home',component: Home},
{path:'/movie',component: Movie},
{path:'/about',component: About}
]
})
通过路由实现组件的嵌套展示,叫做嵌套路由。
<template>
<div class="about-container">
<h3>About 组件h3>
<router-link to="/about">tab1router-link>
<router-link to="/about/tab2">tab2router-link>
<hr />
<router-view>router-view>
div>
template>
默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
在 src/router/index.js
路由模块中,导入需要的组件,并使用 children
属性声明子路由规则:
import Tab1 from '@/components/tabs/Tab1.vue'
import Tab2 from '@/components/tabs/Tab2.vue'
// 创建路由的实例对象
const router = new VueRouter({
routes: [
{
path: '/about',
component: About,
// redirect: '/about/tab1',
children: [
// 子路由规则
// 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
}
]
})
子路由 path: 不推荐加
/
动态路由指的是:把 Hash 地址中可变的部分
定义为参数项
,从而提高路由规则的复用性
。
<router-link to="/movie/1"> 电影1 router-link>
<router-link to="/movie/2"> 电影2 router-link>
<router-link to="/movie/3"> 电影3 router-link>
定义3个规则,未免太繁琐,路由规则复用性差
{path:'/movie/1',component:Movie}
{path:'/movie/2',component:Movie}
{path:'/movie/3',component:Movie}
使用:
来定义路由的参数项
//路由中的动态参数以 : 进行声明,冒号后面的是动态参数的名称
{ path:'/movie/:id',component:Movie}
当前路由规则已定义,需要去movie组件去接收id
值
$route.params
访问动态匹配的参数值在 动态路由
渲染出来的组件中,可以使用 this.$route.params
对象访问到动态匹配的参数值:
<h3> Movie 组件-- {{this.$route.params.id}} h3>
为了简化路由参数的获取形式,``vue-router允许在路由规则中开启
props传参, 在定义路由规则时,声明
props : true` 选项 ,
Movie组件中,使用``props`接收参数
<template>
<!-- 直接使用props中接收的路由参数 -->
<h3> MyMovie组件--{{id}}h3>
template>
<script>
export default{
//使用props接收路由规则中匹配到的参数项
props:['id']
script>
在路由规则中 声明 props:true
表示启用props
传参
// 1. 声明 props : true 选项
{ path:'/movie/:id',component: Movie,props:true }
在hash地址中,
/
后面的参数项,叫做路径参数
<router-link to="/movie/1">洛基router-link>
在路由参数对象中,需要使用this.$route.params来访问路径参数
在 hash 地址中,? 后面的参数项,叫做查询参数
<router-link to="/movie/2?name=zs&age=20">雷神router-link>
在路由参数对象中,需要使用this.$route.params来访问查询参数
在 this.$route 中,path 只是路径部分;fullPath 是完整的地址
/movie/2?name=zs&age=20
是 fullPath
的值/movie/2
是 path
的值vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:
API | 说明 |
---|---|
this.$router. push (‘hash 地址’) |
跳转到指定 hash 地址,并增加一条历史记录 |
this.$router.replace (‘hash 地址’) |
跳转到指定的 hash 地址,并替换掉当前的历史记录 |
this.$router. go (数值 n) |
实现导航历史前进、后退 |
$router.back () |
后退到上一个页面 |
$router.forward () |
前进到下一个页面 |
调用 this.$router.push
() 或者 $router.replace
()方法,可以跳转到指定的 hash 地址,展示对应的组件页面
<template>
<div class="home-container">
<h3> Home组件 h3>
<button @click="gotoMovie">跳转到Movie页面button>
div>
template>
<script>
export default{
methods:{
gotolMovie(){
this.$router.push("/movie/1")
}
}
script>
$router.go(-1) 表示后退一层
如果后退的层数超过上限,则原地不动
<template>
<h3> MyMovie组件---{{id}}h3>
<button @click="goBack" >后退button>
template>
<script>
export default{
props:['id'],
methods:{
goBack(){
this.$router.go(-1) //后退到之前的组件页面
}
},
script>
$router.go的简化用法
<button @click="$router.back()">back 后退button>
<button @click="$router.forward()">forward 前进button>
导航守卫可以控制路由的访问权限
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,我们可以对每个路由进行访问权限的控制:
//创建路由实例对象
const router = new VueRouter({..…})
//调用路由实例对象的beforeEach方法,即可声明“全局前置守卫"
//每次发生路由导航跳转的时候,都会自动触发这个“回调函数”
router.beforeEach((to,from,next) =>{
/* 必须调 next 函数 */
})
守卫方法的 3 个形参:
to
是将要访问的路由的信息 (对象)
from
是将要离开的路由的信息对象
next
是一个函数,一定要调用 next () 才表示放行,允许这次路由导航 。
注意:
在守卫方法中如果不声明 next 形参,则默认允许用户访问每一个路由!
在守卫方法中如果声明了 next 形参,则必须调用 next() 函数,否则不允许用户访问任何一个路由!
next()
false
)router.beforeEach(function(to, from, next) {
// 分析:
// 1. 要拿到用户将要访问的 hash 地址
// 2. 判断 hash 地址是否等于 /main。
// 2.1 如果等于 /main,证明需要登录之后,才能访问成功
// 2.2 如果不等于 /main,则不需要登录,直接放行 next()
// 3. 如果访问的地址是 /main。则需要读取 localStorage 中的 token 值
// 3.1 如果有 token,则放行
// 3.2 如果没有 token,则强制跳转到 /login 登录页
if (to.path === '/main') {
// 要访问后台主页,需要判断是否有 token
const token = localStorage.getItem('token')
if (token) {
next()
} else {
// 没有登录,强制跳转到登录页
next('/login')
}
} else {
next()
}
})
token从这查看