Vue笔记(指令/组件/生命周期/路由)

文章目录

Vue创建elementUI项目

vue create my-app
cd my-app
vue add element-plus

Vue 两大特性

  1. 数据驱动视图

    1. 数据的变化会驱动视图自动更新
    2. 好处: 程序员只管把数据维护好, 那么页面结构就会被 vue 自动渲染出来
  2. 双向数据绑定

    1. 在网页中, form 表单负责采集数据, Ajax 负责提交数据
    2. js 数据的变化,会自动被渲染到页面上
    3. 页面上表单财季的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中

    注意: 数据驱动视图和数据双向数据绑定的底层原理是 MVVM(Model 数据源, View 视图, ViewModle 就是 vue 的实例)

Vue 指令

1. 内容渲染指令:用来辅助开发者渲染 DOM 元素的文本内容

  • v-text(几乎不用)
    • 缺点:会覆盖元素内部原有的内容
  • {{}} 插值表达式(Mustache)(使用较多)
  • v-html: 可以把带有标签的字符串, 渲染成真正的 HTML 内容

2. 属性绑定指令(注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!)

  • v-bind 可以为元素的属性动态绑定值, 简写成冒号( : )
    • 在使用 v-bind 属性绑定期间, 如果绑定内容需要进行动态拼接, 则字符串的外面应该包裹单引号

3. 事件绑定

  1. v-on:简写是@
  2. 语法格式为:
<button @click="add"></button>

methods: {
	add() {
			this.count += 1
	}
}
  1. e v e n t 的 应 用 场 景 : 如 果 默 认 的 事 件 对 象 e 被 覆 盖 了 , 则 可 以 手 动 传 递 一 个 event 的应用场景: 如果默认的事件对象 e 被覆盖了, 则可以手动传递一个 event的应用场景:如果默认的事件对象e被覆盖了,则可以手动传递一个event
<button @click="add(3, $evnet)"></button>

methods: {
	add(n, e) {
			this.count += 1
}
}
  1. 事件修饰符

    • .prevent 阻止默认事件
    <a @click.prevent="xxx">url</a>
    
    • .stop 阻止冒泡事件
    <button @click.stop="xxx">button</button>
    
    • .capture 以捕捉模式触发当前的时间处理函数
    • once 绑定的时间只触发一次
    • self 只有在 event.target 是当前元素自身时触发事件处理函数

4. v-model 指令

  1. input 输入框
    1. type=“radio”
    2. type=“checkbox”
    3. type=“xxxx”
  2. textarea
  3. select
  • 为了方便对用户输入的内容进行处理, vue 为 v-model 指令提供了 3 个修饰符
修饰符 作用 示例
.number 自动将用户的输入值转为数值类型

5. 条件渲染指令

  • 条件渲染指令用来辅助开发者按需控制 DOM 的显示和隐藏
  1. v-if: 每次动态创建或移除元素, 实现元素的显示和隐藏(实际开发常用)
    • 如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好
    • v-if 的配套指令: v-else-if / v-else
  2. v-show: 动态为元素添加或移出 display:none 样式,实现元素的显示和隐藏
    • 如果要频繁的切换元素的显示状态, 用 v-show 性能会更好

6. 循环渲染指令

  1. 要循环哪个元素就给它加 v-for
  2. 官方建议: 只要用到了 v-for 指令,那么一定要绑定一个:key 属性, 而且, 尽量把 id 作为 key 的值
  3. 官方对 key 的值类型,要求为: 字符串或数字类型
  4. key 的值不能重复, 否则终端会报错
<div id="app">
    <table class="table table-bordered table-hover">
        <thead>
        <th>索引</th>
        <th>Id</th>
        <th>姓名</th>
        </thead>
        <tbody>
        <tr v-for="(item, index) in list" :key="item.id">
            <td>{{ index }}</td>
            <td>{{ item.id }}</td>
            <td>{{ item.name }}</td>
        </tr>
        </tbody>
    </table>
</div>

监听器(watch)

  1. 方法格式的监听器
    • 缺点 1: 无法再刚进入页面的时候, 自动触发
    • 缺点 2: 如果监听的是一个对象, 如果对象中的属性发生了变化, 不会触发监听器
  2. 对象格式的监听器
    • 好处 1: 可以通过 immediate 选项, 让监听器自动触发
    • 好处 2: 可以通过 deep 选项, 让监听器深度监听对象中每个属性的变化

计算属性(computed)

  • 特点
    • 定义的时候, 要被定义为方法
    • 在使用计算属性的时候, 当普通的属性使用即可
  • 好处
    • 实现了代码的复用
    • 只要计算属性中依赖的数据源变化了, 则计算属性会自动重新求值!

axios

axios 是一个专注于网络请求的库

1. 发起 get 请求

axios({
        // 请求方式
        method: 'GET',
        // 请求地址
        url: 'http://www.liulongbin.top:3006/api/getbooks',
        // url 中的查询参数(一般 get 请求)
        params: {
            id: 1
        },
        // 请求体参数(一般 post 请求)
        data: {}
    }).then(function (result) {
        console.log(result);
    });
// Get 请求简化
document.querySelector('#btnGet').addEventListener('click', async () => {
    // 解构赋值的时候, 使用 : 进行重命名
    // 1. 调用 axios 之后, 使用 async/await 进行简化
    // 2. 使用解构赋值, 从 axios 封装的大对象中, 把 data 属性解构出来
    // 3. 把解构出来的 data 属性, 使用冒号进行重命名, 一般都重命名为 { data: res}
    const {data: res} = await axios({
        method: 'GET',
        url: 'http://www.liulongbin.top:3006/api/getbooks'

    });
    console.log(res.data);
});
// Get 请求模板
document.querySelector('#btnGet').addEventListener('click', async () => {
    const {data: res} = await axios.get(
        // 请求 url
        '',
        // 请求参数
        {
            params: {}
        }
    );
    console.log(res.data);
});

2. 发起 post 请求

document.querySelector('#btnPost').addEventListener('click',
    async () => {
        // 如果调用某个方法的返回值是 Promise 实例, 则可以前面添加 await
        // await 只能用在被 async 修饰的方法中
        // 1. 调用 axios 方法得到的返回值是 Promise 对象
        const {data} = await axios({
            // 请求方式
            method: 'POST',
            // 请求地址
            url: 'http://www.liulongbin.top:3006/api/post',
            // url 中的查询参数(一般 get 请求)
            params: {},
            // 请求体参数(一般 post 请求)
            data: {
                name: 'zs',
                age: 20
            }
        });
        console.log(data);
    });
// Post 请求模板
document.querySelector('#btnPost').addEventListener('click', async () => {
    // axios.post('url',{/*Post 请求体数据*/})
    const {data: res} = await axios.post(
        // 请求 url
        '',
        // 请求体
        {});
    console.log(res);
});

3. 在 Vue 中使用 axios

methods: {
  async btnGetBooks() {
    const { data: res } = await axios.get('/api/getbooks')
    console.log(res)
  }
}

vue-cli 的使用

  1. 在终端下运行如下的命令, 创建指定名称的项目
vue create 项目的名称
  1. vue 项目中 src 目录的构成
assets 文件夹: 存放项目中用到的静态资源文件, 例如: css 样式表, 图片资源
components 文件夹: 程序员封装的, 可复用的组件, 都要方法哦 components 目录下
main.js 是项目的入口文件.整个项目的运行, 都要先执行 main.js
App.vue 是项目的根组件

使用组件的三个步骤

  1. 使用 import 语法导入需要的组件
import Left from '@/components/Left.vue'
  1. 使用 components节点注册组件
export default {
	components: {
			Left
	}
}
  1. 以标签形式使用刚才注册的组件
<div class="box">
		<Left></Left>
</div>

注册全局组件

在 vue 项目的 main.js 入口文件中, 通过 Vue.component()方法, 可以注册全局组件

import Count from '@/components/Count.vue'
Vue.component('MyCount', Count)
// 参数 1:字符串格式, 表示组件的"注册名称"
// 参数 2:需要被全局注册的那个组件

组件的 props

props 是组件的自定义属性, 在封装通用组件的时候, 合理地使用 props 可以极大地提高组件的复用性!

  • props 是只读的: 若想要修改 props 的值, 可以把 props 的值转存到 data 中
export default {
  props: ['自定义属性A','自定义属性B','其他自定义属性...']
}
// props 设置 default 值, 设置 type 值类型, 设置 required 必填项
export default {
  props: {
    init: {
      default: 0,
      type: Number,
      required: true
    }
  },
}

scoped 和 deep

  • scoped 范围样式
  • deep: 样式前加 /deep/ 可以在父组件中修改子组件的样式(常用来修改第三方组件的默认样式)

组件的生命周期

生命周期(Life Cycle)是指一个组件从 创建->运行->销毁 的整个阶段, 强调的是一个时间段

  1. 组件创建阶段
    • new Vue() -> beforeCreate -> created -> breforeMount -> mounted
  2. 组件运行阶段
    • beforeUpdate -> updated
  3. 组件销毁阶段
    • beforeDestroy -> destroyed
阶段 说明 特点 是否常用
beforeCreate 创建阶段的第1个生命周期函数 不常用
created 发起Ajax请求 (调用methods中的方法, 请求服务器的数据, 并且把请求到的数据, 转存到data*template渲染使用) 初始化 props/data/methods 非常常用
beforeMount 极少用
mounted 如果要操作 DOM,最早要在 mounted 阶段 初始化 DOM 常用
beforeUpdate 一般
updated 当数据发生变化后, 为了能够操作最新的 DOM 结构,必须把代码写到 updated 生命周期函数中 常用
beforeDestroy 一般
destroyed 一般
Vue笔记(指令/组件/生命周期/路由)

组件之间的数据共享

  1. 父组件向子组件共享数据

    • 需要在子组件使用自定义属性(props), 在父组件中用 v-bind 传值
  2. 子组件向父组件共享数据

    • 子组件向父组件共享数据使用自定义事件
// 子组件 
methods: {
    add() {
      this.count += 1;
      this.$emit('numChange', this.count);
    }
  }
// 父组件
<h1>{{ countFromSon }}</h1>
<Right @numChange="getNewCount"></Right>

data() {
    return {
      // 定义数据项来接收子组件传递过来的值
      countFromSon: 0
    };
  },
  methods: {
    // 获取子组件传递过来的数据
    getNewCount(val) {
      console.log('numChange 事件被触发了!', val);
      this.countFromSon = val;
    }
  }
  1. 兄弟组件之间的数据共享
    • 在 vue 2.x 中, 兄弟组件之间数据共享的方案是 EventBus
// eventBus.js
import Vue from 'vue'

export default new Vue()
// 子组件
<template>
  <div class="Count">
    <button type="button" @click="sub">-</button>
    <span class="number-box">{{ num }}</span>
    <button type="button" @click="add">+</button>
  </div>
</template>

<script>
import bus from '@/components/eventBus.js';

export default {
  name: "Counter",
  props: {
    id: {
      type: Number,
      required: true
    },
    num: {
      type: Number,
      default: 1
    }
  },
  methods: {
    add() {
      bus.$emit('share',
          {
            id: this.id,
            value: this.num + 1
          });
    },

    sub() {
      bus.$emit('share',
          {
            id: this.id,
            value: this.num - 1
          });
    }
  }
};
</script>
// 兄弟组件
import bus from '@/components/eventBus.js';

  created() {
    bus.$on('share', val => {
      this.list.some(item => {
        if (item.id === val.id) {
          item.goods_count = val.value;
          return true
        }
      });
    });
  }

ref 引用

  • 用来辅助开发者在不依赖jQuery 的情况下, 获取 DOM 元素或组件的引用
  • 每个 vue 的组件实例上, 都包含一个** r e f s 对 象 ∗ ∗ , 里 面 存 储 着 对 应 的 D O M 元 素 或 组 件 的 引 用 . 默 认 情 况 下 , ∗ ∗ 组 件 的 refs 对象**, 里面存储着对应的 DOM 元素或组件的引用. 默认情况下, **组件的 refs对象∗∗,里面存储着对应的DOM元素或组件的引用.默认情况下,∗∗组件的refs 指向一个空对象**
  • 可以利用 ref 实现组件之间的数据共享
<h1 ref="myh1">App 根组件</h1>
console.log(this.$refs.myh1);

this.$nextTick(cb) 方法

  • 组件的$nextTick(cb) 方法, 会把 cb 回调推迟到下一个 DOM 更新周期之后执行. 通俗的理解是: 等组件 DOM 更新完成后, 再执行 cb 回调函数. 从而保证 cb 回调函数可以操作到最新的 DOM 元素
this.$nextTick(() => {
  this.$refs.iptRef.focus();
})

动态组件渲染

  • vue 提供了一个内置的组件, 专门用来实现动态组件的渲染
  • 动态组件每次切换的时候都会重新销毁和创建-- keep-alive解决
<template>
    <div class="app-container">
        <h1>App 根组件</h1>
        <hr/>

        <button @click="comName='Left'">展示 Left</button>
        <button @click="comName='Right'">展示 Right</button>
        <div class="box">
            <!-- 渲染 Left 组件和 Right 组件 -->
            <!-- 1. component 标签是vue内置的, 作用: 组件的占位符-->
            <!-- 2. is 属性的值, 表示要渲染的组件的名字-->
            <!-- 3 is 属性的值, 应该是组件要再components节点下的注册名称-->
            <!-- keep-alive 可以把内部的组件进行缓存, 而不是销毁组件-->
            <keep-alive include="Left, Right">
                <component :is="comName"></component>
            </keep-alive>
        </div>
    </div>
</template>

data() {
        return {
            // comName: 表示要展示的组件的名字
            comName: 'Left'
        };
    }
  • keep-alive 对应的生命周期函数

    • 当组件被缓存时, 会自动触发组件的 deactivated 生命周期函数
    • 当组件被激活时, 会自动触发组件的 activated 生命周期函数
  • keep-alive 的include 属性: 指定名称匹配的组件会被缓存, 多个组件之间使用英文的逗号分隔

  • keep-alive 的exclude 属性: 排除指定名称组件的缓存(include 和 exclude 二选一)

插槽

  • 插槽(slot) 是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的希望由用户指定的部分定义为插槽。
    • 如果要把内容填充到指定名称的插槽中,需要使用 v-slot: 这个指令
    • v-slot: 后面要跟上插槽的名称
    • v-slot: 指令不能直接用在元素身上, 必须用在template标签页上(或者直接用在组件上!
    • template 这个标签, 它是一个虚拟的标签, 只起到包裹性质的作用, 但是不会被渲染为任何实质性的 html 元素
  • v-slot 简写是 #
// 子组件
<!-- vue 官方规定, 每一个slot插槽, 都要有一个 name 名称, 省略默认为 default-->
<slot name="default">这是 default 插槽的默认内容</slot>

// 父组件
<Left>
    <template #default>
		<p>这是在Left组件的内容区域, 声明的p标签</p>
    </template>
</Left>

作用域插槽

  • 在封装组件时,为预留的 slot 提供属性对应的值,这种用法,叫做"作用域插槽"
// 子组件
<slot name="content" msg="hello vue" :user="userinfo"></slot>

// 父组件:用法 1
<template #content="obj">
  <div>
    <p>{{ obj.msg }}</p>
    <p>{{ obj.user}}</p>
  </div>
</template>

// 父组件:用法 2
<template #content="{msg, user}">
  <div>
    <p>{{ msg }}</p>
    <p>{{ user }}</p>
  </div>
</template>

自定义指令

1. 私有自定义指令

<h1 v-color="color">App 根组件</h1>
<h3 v-color="'red'">测试</h3>

data() {
        return {
            color: 'blue'
        };
    },
// 私有自定义指令的节点
directives: {
        // 定义名为 color 的自定义指令,指向一个配置对象
        color: {
            // 当指令第一次被绑定到元素上的时候,会立即触发 bind 函数
            // 形参中的 el 表示当前指令所绑定的那个 DOM 对象
            bind(el, binding) {
                console.log('触发了 v-color 的 bind 函数');
                el.style.color = binding.value;
            },
            // 每次 DOM 更新时被调用
            update(el, binding) {
                console.log('触发了 v-color 的 update 函数');
                el.style.color = binding.value;
            }
        }
}
// 如果 bind 和 update 事件一样,可以简写
directives: {
    color(el, binding) {
        el.style.color = binding.value;
    }
}

2. 全局自定义指令

// 全局自定义指令
Vue.directive('color', function (el, binding) {
    el.style.color = binding.value;
});

挂载 axios 到 Vue 原型上

// main.js
import axios from 'axios'

// 全局配置 axios 的请求根路径
axios.defaults.baseURL = 'http://localhost:8080'
// 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用
Vue.prototype.$http = axios

// 今后,在每个 .vue 组件中要发起请求,直接调用 this.$http.xxx
// 但是,把 axios 挂载到 Vue 原型上,有一个缺点:不利于 API 接口的复用!!!

路由

  • 路由(router)就是对应关系
  • 前端路由Hash 地址与组件之间的对应关系
  • 前端路由的工作方式
    1. 用户点击了页面上的路由链接
    2. 导致了 URL 地址栏中的 Hash 值发生了变化
    3. 前端路由监听到了 Hash 地址的变化
    4. 前端路由把当前 Hash 地址对应的组件渲染到浏览器中
  • Vue-router 安装和配置的步骤
    1. 安装 vue-router 包
    2. 创建路由模块
    3. 导入并挂载路由模块
    4. 声明路由链接占位符
// src/router/index.js 就是当前项目的路由模块
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from '@/components/Home';
import Movie from '@/components/Movie';
import About from '@/components/About';
import Tab1 from '@/components/tabs/Tab1';
import Tab2 from '@/components/tabs/Tab2';

Vue.use(VueRouter);

// 创建路由的实例对象
const router = new VueRouter({
    // routes 是一个数组,作用:定义 hash 地址与组件之间的对应关系
    routes: [
        // 重定向的路由规则
        {path: '/', redirect: '/home'},

        // 路由规则
        {path: '/home', component: Home},
        {path: '/movie/:id', component: Movie, props: true},
        {// about 页面的路由规则(父级路由规则)
            path: '/about',
            component: About,
            redirect: '/about/tab1',
            children: [
                // 子路由规则
                // 默认子路由:如果 children 数组中,某个路由规则的 path 值为空,字符串,则这条路由规则,叫做默认子路由
                {path: 'tab1', component: Tab1},
                {path: 'tab2', component: Tab2}
            ]
        }
    ]
});

export default router;
<!-- 当安装和配置了 vue-router 后,就可以用 router-link 来替代普通的 a 链接了-->
<router-link to="/home">首页</router-link>

<!--只要在项目中安装和配置了 vue-router,就可以使用 router-view 这个组件了-->
<!--作用:占位符-->
<router-view></router-view>
  • 路由重定向:用户在访问地址 A 的时候,强制用户跳转到地址 C,从而展示特定的组件页面。通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向。

  • 动态路由:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。在 vue-router 中使用英文的冒号(:)来定义路由的参数项。

  • props: true :为路由规则开启 props 传参,从而方便的拿到动态参数的值

<!-- 当安装和配置了 vue-router 后,就可以用 router-link 来替代普通的 a 链接了-->
<router-link to="/home">首页</router-link>
<!-- 注意:在 hash 地址中,/后面的参数项,叫做"路径参数"-->
<!-- 在路由参数对象中,需要使用 this.$route.params 来访问路径参数-->
<!-- 注意2:在 hash 地址中,?后面的参数项,叫做"查询参数"-->
<!-- 在路由参数对象中,需要使用 this.$route.query 来访问查询参数-->
<!-- 注意3:在 this.$route中,path 只是路径部分,fullPath 是完整的地址-->
<!--例如:-->
<!--fullPath:/movie/2?name=zs&age=20-->
<!--/movie/2 是 path 的值-->
<router-link to="/movie/1">电影1</router-link>
<router-link to="/movie/2?name=zs&age=20">电影2</router-link>
<router-link to="/movie/3">电影3</router-link>
<router-link to="/about">关于</router-link>

Vue-router 中的编程式导航 API

  1. this.$router.push(‘hash 地址’)
    • 跳转到指定 hash 地址,并增加一条历史记录
  2. this.$router.replace(‘hash 地址’)
    • 跳转到指定 hash 地址,并替换掉当前的历史记录
  3. this.$router.go(数值 n) (前进后退)
  4. $router.go 的简化用法
    • $router.back()
    • $router.forward()

路由守卫

  1. 全局前置守卫
    • 每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由访问权限的控制。
router.beforeEach((to, from, next) => {
    // to 是将要访问的路由信息对象
    // from 是将要离开的路由的信息对象
    // next 是一个函数,调用 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') {
        const token = localStorage.getItem('token');
        if (token) {
            next();
        } else {
            next('/login');
        }
    } else {
        next();
    }
});
上一篇:Java 之 常用函数式接口


下一篇:取消axios请求