vue2黑马笔记

列表

一、基本语法

1、插值表达式 {{ 表达式 }} 不支持语法if

<h3>{{title}}<h3>

<p>{{nickName.toUpperCase()}}</p>

<p>{{age >= 18 ? '成年':'未成年'}}</p>

<p>{{obj.name}}</p>

<p>{{fn()}}</p>

2、v-html

 
  <div id="app">
    <h2>个人信息</h2>
    // 既然指令是vue提供的特殊的html属性,所以咱们写的时候就当成属性来用即可
    <p v-text="uname">姓名:</p> 
    <p v-html="intro">简介:</p>
  </div> 

<script>
        const app = new Vue({
            el:'#app',
            data:{
                uname:'张三',
                intro:'<h2>这是一个<strong>非常优秀</strong>的boy<h2>'
            }
        })
</script>

3、v-show 和 v-if

v-show

  1. 作用: 控制元素显示隐藏
  2. 语法: v-show = “表达式” 表达式值为 true 显示, false 隐藏
  3. 原理: 切换 display:none 控制显示隐藏
  4. 场景:频繁切换显示隐藏的场景

v-if

  1. 作用: 控制元素显示隐藏(条件渲染)
  2. 语法: v-if= “表达式” 表达式值 true显示, false 隐藏
  3. 原理: 基于条件判断,是否创建 或 移除元素节点
  4. 场景: 要么显示,要么隐藏,不频繁切换的场景

v-else 和 v-else-if

  1. 作用:辅助v-if进行判断渲染
  2. 语法:v-else v-else-if=“表达式”
  3. 需要紧接着v-if使用

4、v-on 事件绑定

事件绑定例如点击事件等操作

  • <button v-on:事件名=“内联语句”>按钮
  • <button v-on:事件名=“处理函数”>按钮
  • <button v-on:事件名=“处理函数(实参)”>按钮

简写:@:事件名=“处理函数”

内联语句

<div id="app">
    <button @click="count--">-</button>
    <span>{{ count }}</span>
    <button v-on:click="count++">+</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        count: 100
      }
    })
  </script>

5、v-bind 属性绑定指令


  1. **作用:**动态设置html的标签属性 比如:src、url、title

  2. 语法:**v-bind:**属性名=“表达式”

  3. **v-bind:**可以简写成 => :

    比如,有一个图片,它的 src 属性值,是一个图片地址。这个地址在数据 data 中存储。

    则可以这样设置属性值:

  <div id="app">
    <img v-bind:src="imgUrl" v-bind:title="msg" alt="">
    <img :src="imgUrl" :title="msg" alt="">
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        imgUrl: './imgs/10-02.png',
        msg: 'hello 波仔'
      }
    })
  </script>

6、 v-for 循环渲染

Vue 提供了 v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构。

v-for 指令需要使用 (item, index) in arr 形式的特殊语法,其中:

  • item 是数组中的每一项
  • index 是每一项的索引,不需要可以省略
  • arr 是被遍历的数组

此语法也可以遍历对象和数字

//遍历对象
<div v-for="(value, key, index) in object">{{value}}</div>
value:对象中的值
key:对象中的键
index:遍历索引从0开始

//遍历数字
<p v-for="item in 10">{{item}}</p>
item从1 开始

v-for中的key:如果这个第一次循环有样式的话,那么样式这一行删除了,其实样式还在所以key解决样式还在的问题

<ul>
  <li v-for="(item, index) in booksList" :key="item.id">
   <span>{{ item.name }}</span>
   <span>{{ item.author }}</span>
   <button @click="del(item.id)">删除</button>
  </li>
</ul>

7、v-model 双向绑定指令

所谓双向绑定就是:

  1. 数据改变后,呈现的页面结果会更新
  2. 页面结果更新后,数据也会随之而变

作用:表单元素(input、radio、select)使用,双向绑定数据,可以快速 获取设置 表单元素内容

**语法:**v-model=“变量”


8、@keyup.enter 按键修饰符


  • @keyup.enter —>当点击enter键的时候才触发

代码演示:

  <div id="app">
    <h3>@keyup.enter  →  监听键盘回车事件</h3>
    <input @keyup.enter="test" v-model="username" type="text">
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: ''
      },
      methods: {
        test() {
          this.username = "zs"
        }
      }
    })
  </script>

也可以

  <div id="app">
    <h3>@keyup  →  监听键盘回车事件</h3>
    <input @keyup="test" v-model="username" type="text">
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: ''
      },
      methods: {
        test(e) {
          // console.log(e.key,e.keyCode)
          console.log(e.target.value)
        }
      }
    })
  </script>

9、v-model修饰符


  • v-model.trim —>去除首位空格
  • v-model.number —>转数字

    姓名:<input v-model.trim="username" type="text"><br>
    年纪:<input v-model.number="age" type="text"><br>

10、v-on事件修饰符

  • @事件名.stop —> 阻止冒泡

  • @事件名.prevent —>阻止默认行为

  • @事件名.stop.prevent —>可以连用 即阻止事件冒泡也阻止默认行为

<a @click.prevent href="http://www.baidu.com">阻止默认行为</a>

  <h3>@事件名.stop     →  阻止冒泡   点击儿子就不会出发父亲的点击事件</h3>
    <div @click="fatherFn" class="father">
      <div @click.stop="sonFn" class="son">儿子</div>
    </div>

11、v-bind对样式控制的增强-操作class

为了方便开发者进行样式控制, Vue 扩展了 v-bind 的语法,可以针对 class 类名style 行内样式 进行控制 。

语法

<div> :class = "对象/数组">这是一个div</div>
  • 对象语法
<div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值 }"></div>
  • 数组语法
<div class="box" :class="[ 类名1, 类名2, 类名3 ]"></div>

12、computed计算属性

介绍

基于现有的数据,计算出来的新属性依赖的数据变化,自动重新计算。与方法不同的是他只会执行一次,缓存起来

语法

  1. 声明在 computed 配置项中,一个计算属性对应一个函数
  2. 使用起来和普通属性一样使用 {{ 计算属性名}}
  3. js中使用计算属性: this.计算属性

注意

  1. computed配置项和data配置项是同级
  2. computed中的计算属性虽然是函数的写法,但他依然是个属性
  3. computed中的计算属性不能和data中的属性同名
  4. 使用computed中的计算属性和使用data中的属性是一样的用法
  5. computed中计算属性内部的this依然指向的是Vue实例
<style>
    table {
      border: 1px solid #000;
      text-align: center;
      width: 300px;
    }
    th,td {
      border: 1px solid #000;
    }
    h3 {
      position: relative;
    }
    span {
      position: absolute;
      left: 145px;
      top: -4px;
      width: 16px;
      height: 16px;
      color: white;
      font-size: 12px;
      text-align: center;
      border-radius: 50%;
      background-color: #e63f32;
    }
  </style>

<div id="app">
    <h3>小黑的礼物清单🛒<span>?</span></h3>
    <table>
      <tr>
        <th>名字</th>
        <th>数量</th>
      </tr>
      <tr v-for="(item, index) in list" :key="item.id">
        <td>{{ item.name }}</td>
        <td>{{ item.num }}个</td>
      </tr>
    </table>

    <p>礼物总数:{{ totalCount }} 个</p>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        // 现有的数据
        list: [
          { id: 1, name: '篮球', num: 3 },
          { id: 2, name: '玩具', num: 2 },
          { id: 3, name: '铅笔', num: 5 },
        ]
      },
      computed: {
        totalCount () {
          // (sum, item) => sum + item.num, 0     0是sum的初始值
          let total = this.list.reduce((sum, item) => sum + item.num, 0)
          return total
        }
      }
    })
  </script>

完整的计算写法

      computed: {
          属性名称: {
              get(){
                  return 结果;
              },
              set(修改的值){
                  //修改值操作
              }
          		
          }

dev显示使用
属性名称为上述计算函数中的属性名称:使用的是get方法

{{属性名称}}

js计算使用
属性名称为上述计算函数中的属性名称:使用的是set方法,此时get方法获取的是123

this.属性名称 = 123

案例

<template>
  <div>
    <input type="text" v-model="first_name">
    <input type="text" v-model="last_name">
    <h4>你的姓名是: {{fullName}}</h4>
  </div>
</template>

<script>
export default {
  name: 'Demo2',
  data() {
    return {
      first_name: '张',
      last_name: '三',
    }
  },
  computed: {
    fullName: {
      get() {
        return this.first_name + '-' + this.last_name
      },
      set(value) {
        const arr = value.split('-')
        //要对this.first_name 进行赋值,上面的get才可以输出赋值后的属性值
        this.first_name = arr[0]
        this.last_name = arr[1]
      },
    },
  },
}
</script>

13、watch侦听器(监视器)

作用:

监视数据变化,执行一些业务逻辑或异步操作

语法:

  1. watch同样声明在跟data同级的配置项中
  2. 简单写法: 简单类型数据直接监视
  3. 完整写法:添加额外配置项
data: { 
  words: '苹果',
  obj: {
    words: '苹果'
  }
},

watch: {
  // 该方法会在数据变化时,触发执行
  数据属性名 (newValue, oldValue) {
    一些业务逻辑 或 异步操作。 
  },
  '对象.属性名' (newValue, oldValue) {
    一些业务逻辑 或 异步操作。 
  }
}

完整写法

  1. deep:true 对复杂类型进行深度监听
  2. immdiate:true 页面初始化 立刻执行一次
data: {
  obj: {
    words: '苹果',
    lang: 'italy'
  },
},

watch: {// watch 完整写法
  对象: {
    deep: true, // 深度监视
    immdiate:true,//立即执行handler函数
    handler (newValue) {
     console.log(newValue)
   }
  }
}

代码实例

 <script> 
      const app = new Vue({
        el: '#app',
        data: {
          obj: {
            words: '小黑',
            lang: 'italy'
          },
          result: '', // 翻译结果
        },
        watch: {
          obj: {
            deep: true, // 深度监视
            immediate: true, // 立刻执行,一进入页面handler就立刻执行一次
            handler (newValue) {
              //清楚id为timer的定时器
              clearTimeout(this.timer)
              //定时器  async为异步执行 await等待
              this.timer = setTimeout(async () => {
                const res = await axios({
                  url: 'https://applet-base-api-t.itheima.net/api/translate',
                  params: newValue
                })
                this.result = res.data.data
                console.log(res.data.data)
              }, 300)
            }
          } 
        }
      })
    </script>

data中的可以写 test2 || test1 如果test2为空就显示test1

14、存储到用户本地阅览器 && json转换

-- json转换写法
JSON.parse(你的json)
JSON.stringify(你的json)
-- 存储到用户本地阅览器
localStorage.setItem(你的key, 你的value)
localStorage.getItem(你的key)

15、Vue生命周期

Vue生命周期:就是一个Vue实例从创建 到 销毁 的整个过程。

生命周期四个阶段:① 创建 ② 挂载 ③ 更新 ④ 销毁

1.创建阶段:创建响应式数据

2.挂载阶段:渲染模板

3.更新阶段:修改数据,更新视图

4.销毁阶段:销毁Vue实例 vue提供指令app.$destory()

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <div id="app">
    <h3>{{ title }}</h3>
    <div>
      <button @click="count--">-</button>
      <span>{{ count }}</span>
      <button @click="count++">+</button>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        count: 100,
        title: '计数器'
      },
      // 1. 创建阶段(准备数据)
      beforeCreate () {
        console.log('beforeCreate 响应式数据准备好之前', this.count)
      },
      created () {
        console.log('created 响应式数据准备好之后', this.count)
        // this.数据名 = 请求回来的数据
        // 可以开始发送初始化渲染的请求了
      },

      // 2. 挂载阶段(渲染模板)
      beforeMount () {
        console.log('beforeMount 模板渲染之前', document.querySelector('h3').innerHTML)
      },
      mounted () {
        console.log('mounted 模板渲染之后', document.querySelector('h3').innerHTML)
        // 可以开始操作dom了
      },

      // 3. 更新阶段(修改数据 → 更新视图)
      beforeUpdate () {
        console.log('beforeUpdate 数据修改了,视图还没更新', document.querySelector('span').innerHTML)
      },
      updated () {
        console.log('updated 数据修改了,视图已经更新', document.querySelector('span').innerHTML)
      },

      // 4. 卸载阶段
      beforeDestroy () {
        console.log('beforeDestroy, 卸载前')
        console.log('清除掉一些Vue以外的资源占用,定时器,延时器...')
      },
      destroyed () {
        console.log('destroyed,卸载后')
      }
    })
  </script>
</body>
</html>

16、获取焦点


<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>示例-获取焦点</title>
  <!-- 初始化样式 -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset.css@2.0.2/reset.min.css">
  <!-- 核心样式 -->
  <style>
    html,
    body {
      height: 100%;
    }
    .search-container {
      position: absolute;
      top: 30%;
      left: 50%;
      transform: translate(-50%, -50%);
      text-align: center;
    }
    .search-container .search-box {
      display: flex;
    }
    .search-container img {
      margin-bottom: 30px;
    }
    .search-container .search-box input {
      width: 512px;
      height: 16px;
      padding: 12px 16px;
      font-size: 16px;
      margin: 0;
      vertical-align: top;
      outline: 0;
      box-shadow: none;
      border-radius: 10px 0 0 10px;
      border: 2px solid #c4c7ce;
      background: #fff;
      color: #222;
      overflow: hidden;
      box-sizing: content-box;
      -webkit-tap-highlight-color: transparent;
    }
    .search-container .search-box button {
      cursor: pointer;
      width: 112px;
      height: 44px;
      line-height: 41px;
      line-height: 42px;
      background-color: #ad2a27;
      border-radius: 0 10px 10px 0;
      font-size: 17px;
      box-shadow: none;
      font-weight: 400;
      border: 0;
      outline: 0;
      letter-spacing: normal;
      color: white;
    }
    body {
      background: no-repeat center /cover;
      background-color: #edf0f5;
    }
  </style>
</head>

<body>
<div class="container" id="app">
  <div class="search-container">
    <img src="https://www.itheima.com/images/logo.png" alt="">
    <div class="search-box">
      <input type="text" v-model="words" id="inp">
      <button>搜索一下</button>
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      words: ''
    },
    // 核心思路:
    // 1. 等input框渲染出来 mounted 钩子
    // 2. 让input框获取焦点 inp.focus()
    mounted () {
      document.querySelector('#inp').focus()
    }
  })
</script>

</body>

</html>


二、使用脚手架

ps:生命周期钩子函数执行

在这里插入图片描述

1、安装脚手架Vue CLI

  1. 全局安装(只需安装一次即可) yarn global add @vue/cli 或者 npm i @vue/cli -g
  2. 查看vue/cli版本: vue --version
  3. 创建项目架子:vue create project-name(项目名不能使用中文)
  4. 启动项目:yarn serve 或者 npm run serve(命令不固定,找package.json)

2、项目目录介绍和运行流程

在这里插入图片描述

3、运行流程

在这里插入图片描述
其中.$mount("#app")就是el的另外一种写法

3、组件部分组成

语法高亮插件
在这里插入图片描述

  • 三部分构成

    • template:结构 (有且只能一个根元素)
    • script: js逻辑
    • style: 样式 (可支持less,需要装包)
  • 让组件支持less

    (1) style标签,lang=“less” 开启less功能

    (2) 装包: yarn add less less-loader -D 或者npm i less less-loader -D

4、普通组件的注册使用-局部注册

在app.Vue

<template>
  <div class="App">
    <HmMain></HmMain>
    <!-- 如果 HmFooter + tab 出不来 → 需要配置 vscode
         设置中搜索 trigger on tab → 勾上
    -->
  </div>
</template>

<script>

import HmMain from './components/HmMain.vue'

export default {
  components: {
    // '组件名': 组件对象 也可以简写  这里面没有HmHeader 这里是说下这种写法
    HmHeader: HmHeader,
    HmMain
  }
}
</script>

<style>
.App {
  width: 600px;
  height: 700px;
  background-color: #87ceeb;
  margin: 0 auto;
  padding: 20px;
}
</style>

在components中新建文件HmMain.vue

<template>
  <div class="hm-main">
    我是hm-main
  </div>
</template>

<script>
export default {

}
</script>

<style>
.hm-main {
  height: 400px;
  line-height: 400px;
  text-align: center;
  font-size: 30px;
  background-color: #f79646;
  color: white;
  margin: 20px 0;
}
</style>

5、普通组件的注册使用-全局注册

全局注册的组件,在项目的任何组件中都能使用

步骤

  1. 创建.vue组件(三个组成部分)
  2. main.js中进行全局注册
  3. 使用方式当成HTML标签直接使用<组件名></组件名>

注意:组件名规范 —> 大驼峰命名法, 如 HmHeader

语法:在main.js中
Vue.component('组件名', 组件对象)

例子

在main.js中

// 导入需要全局注册的组件
import HmButton from './components/HmButton'
Vue.component('HmButton', HmButton)

components下创建插件HmButton

<template>
  <button class="hm-button">通用按钮</button>
</template>

<script>
export default {

}
</script>

<style>
.hm-button {
  height: 50px;
  line-height: 50px;
  padding: 0 20px;
  background-color: #3bae56;
  border-radius: 5px;
  color: white;
  border: none;
  vertical-align: middle;
  cursor: pointer;
}
</style>

使用

<HmButton></HmButton>

6、scoped解决样式冲突

写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。

  1. 全局样式: 默认组件中的样式会作用到全局,任何一个组件中都会受到此样式的影响
  2. 局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件

原理:

  1. 当前组件内标签都被添加data-v-hash值 的属性
  2. css选择器都被添加 [data-v-hash值] 的属性选择器
    在这里插入图片描述

使用

<template>
  <div class="base-one">
    BaseOne
  </div>
</template>

<script>
export default {

}
</script>
<style scoped>
</style>

7、data必须是一个函数

<template>
  <div class="base-count">
    <button @click="count--">-</button>
    <span>{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      count: 100,
    }
  },
}
</script>

<style>
.base-count {
  margin: 20px;
}
</style>

8、父向子通信代码示例

完整语法:

  1. default和required一般不同时写(因为当时必填项时,肯定是有值的)
  2. default后面如果是简单类型的值,可以直接写默认。如果是复杂类型的值,则需要以函数的形式return一个默认值
props: {
  校验的属性名: {
    type: 类型,  // Number String Boolean ...
    required: true, // 是否必填
    default: 默认值, // 默认值
    validator (value) {
      // 自定义校验逻辑
      return 是否通过校验
    }
  }
},

实例

<script>
export default {
  // 完整写法(类型、默认值、非空、自定义校验)
  props: {
    w: {
      type: Number,
      //required: true,
      default: 0,
      validator(val) {
        // console.log(val)
        if (val >= 100 || val <= 0) {
          console.error('传入的范围必须是0-100之间')
          return false
        } else {
          return true
        }
      },
    },
  },
}
</script>

简单写法

父组件App.vue

<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是APP组件
    <!-- 1.给组件标签,添加属性方式 赋值 -->
    <Son :title="myTitle"></Son>
  </div>
</template>

<script>
import Son from './components/Son.vue'
export default {
  name: 'App',
  data() {
    return {
      myTitle: '学前端,就来黑马程序员',
    }
  },
  components: {
    Son,
  },
}
</script>

<style>
</style>

子组件Son.vue

<template>
  <div class="son" style="border:3px solid #000;margin:10px">
    <!-- 3.直接使用props的值 -->
    我是Son组件 {{title}}
  </div>
</template>

<script>
export default {
  name: 'Son-Child',
  // 2.通过props来接受
  props:['title']
}
</script>

<style>

</style>

9、子向父

父组件:App.vue

<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是APP组件
    <!-- 2.父组件对子组件的消息进行监听 -->
    <Son :title="myTitle" @changTitle="handleChange"></Son>
  </div>
</template>

<script>
import Son from './components/Son.vue'
export default {
  name: 'App',
  data() {
    return {
      myTitle: '学前端,就来黑马程序员',
    }
  },
  components: {
    Son,
  },
  methods: {
    // 3.提供处理函数,提供逻辑
    handleChange(newTitle) {
      this.myTitle = newTitle
    },
  },
}
</script>

<style>
</style>

子组件Son.vue

<template>
  <div class="son" style="border: 3px solid #000; margin: 10px">
    我是Son组件 {{ title }}
    <button @click="changeFn">修改title</button>
  </div>
</template>
<script>
export default {
  name: 'Son-Child',
  props: ['title'],
  methods: {
    changeFn() {
      // 通过this.$emit() 向父组件发送通知
      this.$emit('changTitle','传智教育')
    },
  },
}
</script>
<style>
</style>

10、list集合操作

  • 计算:reduce()
  • 过滤:filter()
  • 新增:unshift()
  • 查看是否包含:incloudes()

11、非父子通信-event bus 事件总线

非父子组件之间,进行简易消息传递。(复杂场景→ Vuex)

新增加一个utils包EventBus.js

import Vue from 'vue'

const Bus  =  new Vue()

export default Bus

BaseA.vue

<template>
  <div class="base-a">
    我是A组件(接受方)
    <p>{{msg}}</p>  
  </div>
</template>

<script>
import Bus from '../utils/EventBus'
export default {
  data() {
    return {
      msg: '',
    }
  },
  created() {
 	 // 监听
    Bus.$on('sendMsg', (msg) => {
      // console.log(msg)
      this.msg = msg
    })
  },
}
</script>

<style scoped>
.base-a {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>

BaseB.vue

<template>
  <div class="base-b">
    <div>我是B组件(发布方)</div>
    <button @click="sendMsgFn">发送消息</button>
  </div>
</template>

<script>
import Bus from '../utils/EventBus'
export default {
  methods: {
    sendMsgFn() {
    //发送
      Bus.$emit('sendMsg', '今天天气不错,适合旅游')
    },
  },
}
</script>

<style scoped>
.base-b {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>

12、第二种Bus写法

main.js种 在vue对象种增加 beforeCreate()

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this
  }
})

使用

//监听  this.refresh是函数
this.$bus.$on("todo1", this.refresh);
//发送
this.$bus.$emit("todo1");

13、非父子通信-provide&inject

跨层级共享数据
在这里插入图片描述

语法

  1. 父组件 provide提供数据
export default {
  provide () {
    return {
       // 普通类型【非响应式】
       color: this.color, 
       // 复杂类型【响应式】
       userInfo: this.userInfo, 
    }
  }
}

  1. 子/孙组件 inject获取数据
export default {
  inject: ['color','userInfo'],
  created () {
    console.log(this.color, this.userInfo)
  }
}

ps:

  • provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
  • 子/孙组件通过inject获取的数据,不能在自身组件内修改

14、v-model原理 使用$event

v-model本质上是一个语法糖。例如应用在输入框上,就是value属性 和 input事件 的合写

<template>
  <div id="app" >
    <input v-model="msg" type="text">
    <input :value="msg" @input="msg = $event.target.value" type="text">
  </div>
</template>

提供数据的双向绑定

  • 数据变,视图跟着变 :value
  • 视图变,数据跟着变 @input

$event 用于在模板中,获取事件的形参

15、如何自定义双向绑定父子传值 使用$event

  • 父组件App.vue
<template>
  <div class="app">
    <BaseSelect
      :selectId="selectId"
      @changeCity="selectId = $event"
    ></BaseSelect>
  </div>
</template>

<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
  data() {
    return {
      selectId: '102',
    }
  },
  components: {
    BaseSelect,
  },
}
</script>

<style>
</style>

  • 子组件BaseSelect.vue
<template>
  <div>
    <select :value="selectId" @change="selectCity">
      <option value="101">北京</option>
      <option value="102">上海</option>
      <option value="103">武汉</option>
      <option value="104">广州</option>
      <option value="105">深圳</option>
    </select>
  </div>
</template>

<script>
export default {
  props: {
    selectId: String,
  },
  methods: {
    selectCity(e) {
      this.$emit('changeCity', e.target.value)
    },
  },
}
</script>

<style>
</style>

16、使用v-model简写15种的绑定 ::: v-model => :value 和 @input

<template>
  <div class="app">
    <BaseSelect
      v-model="selectId"
    ></BaseSelect>
  </div>
</template>

<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
  data() {
    return {
      selectId: '102',
    }
  },
  components: {
    BaseSelect,
  },
}
</script>

<style>
</style>

  • 子组件BaseSelect.vue
<template>
  <div>
    <select :value="value" @change="selectCity">
      <option value="101">北京</option>
      <option value="102">上海</option>
      <option value="103">武汉</option>
      <option value="104">广州</option>
      <option value="105">深圳</option>
    </select>
  </div>
</template>

<script>
export default {
  props: {
    value: String,
  },
  methods: {
    selectCity(e) {
      this.$emit('input', e.target.value)
    },
  },
}
</script>

<style>
</style>

17、sync修饰符

可以实现 子组件父组件数据双向绑定,简化代码
简单理解:子组件可以修改父组件传过来的props值

本质:.sync修饰符 就是 :属性名@update:属性名 合写

语法:

  • 父组件
//.sync写法
<BaseDialog :visible.sync="isShow" />
--------------------------------------
//完整写法
<BaseDialog 
  :visible="isShow" 
  @update:visible="isShow = $event" 
/>
  • 子组件
props: {
  visible: Boolean
},

this.$emit('update:visible', false)

使用.sync写法例子:

  1. 在App.vue中
<template>
  <div class="app">
    <button @click="openDialog">退出按钮</button>
    <!-- isShow.sync  => :isShow="isShow" @update:isShow="isShow=$event" -->
    <BaseDialog :isShow.sync="isShow"></BaseDialog>
  </div>
</template>

<script>
import BaseDialog from './components/BaseDialog.vue'
export default {
  data() {
    return {
      isShow: false,
    }
  },
  methods: {
    openDialog() {
      this.isShow = true
      // console.log(document.querySelectorAll('.box')); 
    },
  },
  components: {
    BaseDialog,
  },
}
</script>

<style>
</style>

  1. 子组件BaseDialog.vue
<template>
  <div class="base-dialog-wrap" v-show="isShow">
    <div class="base-dialog">
      <div class="title">
        <h3>温馨提示:</h3>
        <button class="close" @click="closeDialog">x</button>
      </div>
      <div class="content">
        <p>你确认要退出本系统么?</p>
      </div>
      <div class="footer">
        <button>确认</button>
        <button>取消</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    isShow: Boolean,
  },
  methods:{
    closeDialog(){
      this.$emit('update:isShow',false)
    }
  }
}
</script>

<style scoped>
.base-dialog-wrap {
  width: 300px;
  height: 200px;
  box-shadow: 2px 2px 2px 2px #ccc;
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: 0 10px;
}
.base-dialog .title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 2px solid #000;
}
.base-dialog .content {
  margin-top: 38px;
}
.base-dialog .title .close {
  width: 20px;
  height: 20px;
  cursor: pointer;
  line-height: 10px;
}
.footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 26px;
}
.footer button {
  width: 80px;
  height: 40px;
}
.footer button:nth-child(1) {
  margin-right: 10px;
  cursor: pointer;
}
</style>

18、ref 和 $refs 获取实例数据

利用ref 和 $refs 可以用于 获取 dom 元素 或 组件实例

  1. 查找范围 → 当前组件内(更精确稳定)

  2. ref写在上面,其中可以获取出来组件的方法

例子 其中ref在父组件引用子组件标签中

在这里插入图片描述
调用其方法

在这里插入图片描述

19、异步更新 $nextTick

等待组件dom更新完成后再执行

<template>
  <div class="app">
    <div v-if="isShowEdit">
      <input type="text" v-model="editValue" ref="inp" />
      <button>确认</button>
    </div>
    <div v-else>
      <span>{{ title }}</span>
      <button @click="editFn">编辑</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      title: '大标题',
      isShowEdit: false,
      editValue: '',
    }
  },
  methods: {
    editFn() {
        // 显示输入框
        this.isShowEdit = true  
        // 获取焦点
		this.$nextTick(() => {
  			this.$refs.inp.focus()
		})        
    }  },
}
</script> 

20、自定义指令 (获取焦点)

在生命周期的函数中加入你的代码处理
概念:自己定义的指令,可以封装一些DOM操作,扩展额外的功能

  • 内置指令:v-html、v-if、v-bind、v-on… 这都是Vue给咱们内置的一些指令,可以直接使用
  • 自定义指令:同时Vue也支持让开发者,自己注册一些指令。这些指令被称为自定义指令
    每个指令都有自己各自独立的功能
1. 全局指令
//在main.js中 加上这段话就行
Vue.directive('指令名', {
   inserted (el) {
    // 可以对 el 标签,扩展额外功能
    el.focus()
  }
})
2. 局部指令
//在Vue组件的配置项中 与函数同级别
directives: {
  指令名: {
    inserted () {
      // 可以对 el 标签,扩展额外功能
      el.focus()
    }
  }
}
3. 使用上述两个的

注意:在使用指令的时候,一定要先注册再使用,否则会报错
使用指令语法: v-指令名。如:
注册指令时不用v-前缀,但使用时一定要加v-前缀

<input type="text"  v-focus/>  

21、自定义指令如何传值 binding.value

1.在绑定指令时,可以通过“等号”的形式为指令 绑定 具体的参数值

<div v-color="color">我是内容</div>

2.通过 binding.value 可以拿到指令值,指令值修改会 触发 update 函数

directives: {
  color: {
    inserted (el, binding) {
      el.style.color = binding.value
    },
    update (el, binding) {
      el.style.color = binding.value
    }
  }
}

22、自定义指令封装v-loading

材料 下述代码中用到了

在这里插入图片描述

<template>
  <div class="main">
    <div class="box" v-loading="isLoading">
      <ul>
        <li v-for="item in list" :key="item.id" class="news">
          <div class="left">
            <div class="title">{{ item.title }}</div>
            <div class="info">
              <span>{{ item.source }}</span>
              <span>{{ item.time }}</span>
            </div>
          </div>

          <div class="right">
            <img :src="item.img" alt="">
          </div>
        </li>
      </ul>
    </div>
    <div class="box2" v-loading="isLoading2"></div>
  </div>
</template>

<script>
// 安装axios =>  yarn add axios
import axios from 'axios'

// 接口地址:http://hmajax.itheima.net/api/news
// 请求方式:get
export default {
  data () {
    return {
      list: [],
      isLoading: true,
      isLoading2: true
    }
  },
  async created () {
    // 1. 发送请求获取数据
    const res = await axios.get('http://hmajax.itheima.net/api/news')
    
    setTimeout(() => {
      // 2. 更新到 list 中,用于页面渲染 v-for
      this.list = res.data.data
      this.isLoading = false
    }, 2000)
  },
  directives: {
    loading: {
      inserted (el, binding) {
        binding.value ? el.classList.add('loading') : el.classList.remove('loading')
      },
      update (el, binding) {
        binding.value ? el.classList.add('loading') : el.classList.remove('loading')
      }
    }
  }
}
</script>

<style>
// 转圈的样式
.loading:before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: #fff url('./loading.gif') no-repeat center;
}

.box2 {
  width: 400px;
  height: 400px;
  border: 2px solid #000;
  position: relative;
}



.box {
  width: 800px;
  min-height: 500px;
  border: 3px solid orange;
  border-radius: 5px;
  position: relative;
}
.news {
  display: flex;
  height: 120px;
  width: 600px;
  margin: 0 auto;
  padding: 20px 0;
  cursor: pointer;
}
.news .left {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-right: 10px;
}
.news .left .title {
  font-size: 20px;
}
.news .left .info {
  color: #999999;
}
.news .left .info span {
  margin-right: 20px;
}
.news .right {
  width: 160px;
  height: 120px;
}
.news .right img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
</style>

23、插槽

1、默认插槽
<template>
  <div>
    <!-- 2. 在使用组件时,组件标签内填入内容 -->
    <MyDialog>
      <div>你确认要删除么</div>
    </MyDialog>

    <MyDialog>
      <p>你确认要退出么</p>
    </MyDialog>
  </div>
</template>

<script>
import MyDialog from './components/MyDialog.vue'
export default {
  data () {
    return {

    }
  },
  components: {
    MyDialog
  }
}
</script>

<style>
body {
  background-color: #b3b3b3;
}
</style>

组件

<template>
  <div class="dialog">
    <div class="dialog-header">
      <h3>友情提示</h3>
      <span class="close">✖️</span>
    </div>

    <div class="dialog-content">
      <!-- 1. 在需要定制的位置,使用slot占位 -->
      <slot></slot>
    </div>
    <div class="dialog-footer">
      <button>取消</button>
      <button>确认</button>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {

    }
  }
}
</script>

<style scoped>
* {
  margin: 0;
  padding: 0;
}
.dialog {
  width: 470px;
  height: 230px;
  padding: 0 25px;
  background-color: #ffffff;
  margin: 40px auto;
  border-radius: 5px;
}
.dialog-header {
  height: 70px;
  line-height: 70px;
  font-size: 20px;
  border-bottom: 1px solid #ccc;
  position: relative;
}
.dialog-header .close {
  position: absolute;
  right: 0px;
  top: 0px;
  cursor: pointer;
}
.dialog-content {
  height: 80px;
  font-size: 18px;
  padding: 15px 0;
}
.dialog-footer {
  display: flex;
  justify-content: flex-end;
}
.dialog-footer button {
  width: 65px;
  height: 35px;
  background-color: #ffffff;
  border: 1px solid #e1e3e9;
  cursor: pointer;
  outline: none;
  margin-left: 10px;
  border-radius: 3px;
}
.dialog-footer button:last-child {
  background-color: #007acc;
  color: #fff;
}
</style>

2、后背内容(不传默认为什么值)

同上1、代码一样只是组件中的改成我是后背内容,当没有值的时候显示出来

3、具名插槽(给一个组件多个插槽,不同地方加不同的内容)

缩写v-slot:写起来太长,vue给我们提供一个简单写法 v-slot: —> #

语法

  • 多个slot使用name属性区分名字

在这里插入图片描述

  • template配合v-slot:名字来分发对应标签
    在这里插入图片描述

案例

<template>
  <div>
    <MyDialog>
      <!-- 需要通过template标签包裹需要分发的结构,包成一个整体 -->
      <template v-slot:head>
        <div>我是大标题</div>
      </template>
      
      <template v-slot:content>
        <div>我是内容</div>
      </template>

      <template #footer>
        <button>取消</button>
        <button>确认</button>
      </template>
    </MyDialog>
  </div>
</template>

<script>
import MyDialog from './components/MyDialog.vue'
export default {
  data () {
    return {

    }
  },
  components: {
    MyDialog
  }
}
</script>

<style>
body {
  background-color: #b3b3b3;
}
</style>

  • 组件
<template>
  <div class="dialog">
    <div class="dialog-header">
      <!-- 一旦插槽起了名字,就是具名插槽,只支持定向分发 -->
      <slot name="head"></slot>
    </div>

    <div class="dialog-content">
      <slot name="content"></slot>
    </div>
    <div class="dialog-footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {

    }
  }
}
</script>

<style scoped>
* {
  margin: 0;
  padding: 0;
}
.dialog {
  width: 470px;
  height: 230px;
  padding: 0 25px;
  background-color: #ffffff;
  margin: 40px auto;
  border-radius: 5px;
}
.dialog-header {
  height: 70px;
  line-height: 70px;
  font-size: 20px;
  border-bottom: 1px solid #ccc;
  position: relative;
}
.dialog-header .close {
  position: absolute;
  right: 0px;
  top: 0px;
  cursor: pointer;
}
.dialog-content {
  height: 80px;
  font-size: 18px;
  padding: 15px 0;
}
.dialog-footer {
  display: flex;
  justify-content: flex-end;
}
.dialog-footer button {
  width: 65px;
  height: 35px;
  background-color: #ffffff;
  border: 1px solid #e1e3e9;
  cursor: pointer;
  outline: none;
  margin-left: 10px;
  border-radius: 3px;
}
.dialog-footer button:last-child {
  background-color: #007acc;
  color: #fff;
}
</style>

4、作用域插槽(给组件的值传到插槽里面)

定义slot 插槽的同时, 是可以传值的。给 插槽 上可以 绑定数据,将来 使用组件时可以用

语法

    1. 给 slot 标签, 以 添加属性的方式传值
<slot :id="item.id" msg="测试文本"></slot>
    1. 所有添加的属性, 都会被收集到一个对象中
{ id: 3, msg: '测试文本' }
    1. 在template中, 通过 #插槽名= "obj" 接收,默认插槽名为 default
<MyTable :list="list">
  <template #default="obj">
    <button @click="del(obj.id)">删除</button>
  </template>
</MyTable>

案例

<template>
  <div>
    <MyTable :data="list">
      <!-- 3. 通过template #插槽名="变量名" 接收 -->
      <template #default="obj">
        <button @click="del(obj.row.id)">
          删除
        </button>
      </template>
    </MyTable>
    
    <MyTable :data="list2">
      <template #default="{ row }">
        <button @click="show(row)">查看</button>
      </template>
    </MyTable>
  </div>
</template>

<script>
import MyTable from './components/MyTable.vue'
export default {
  data () {
    return {
      list: [
        { id: 1, name: '张小花', age: 18 },
        { id: 2, name: '孙大明', age: 19 },
        { id: 3, name: '刘德忠', age: 17 },
      ],
      list2: [
        { id: 1, name: '赵小云', age: 18 },
        { id: 2, name: '刘蓓蓓', age: 19 },
        { id: 3, name: '姜肖泰', age: 17 },
      ]
    }
  },
  methods: {
    del (id) {
      this.list = this.list.filter(item => item.id !== id)
    },
    show (row) {
      // console.log(row);
      alert(`姓名:${row.name}; 年纪:${row.age}`)
    }
  },
  components: {
    MyTable
  }
}
</script>

  • 组件
<template>
  <table class="my-table">
    <thead>
      <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>年纪</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(item, index) in data" :key="item.id">
        <td>{{ index + 1 }}</td>
        <td>{{ item.name }}</td>
        <td>{{ item.age }}</td>
        <td>
          <!-- 1. 给slot标签,添加属性的方式传值 -->
          <slot :row="item" msg="测试文本"></slot>

          <!-- 2. 将所有的属性,添加到一个对象中 -->
          <!-- 
             {
               row: { id: 2, name: '孙大明', age: 19 },
               msg: '测试文本'
             }
           -->
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    data: Array
  }
}
</script>

<style scoped>
.my-table {
  width: 450px;
  text-align: center;
  border: 1px solid #ccc;
  font-size: 24px;
  margin: 30px auto;
}
.my-table thead {
  background-color: #1f74ff;
  color: #fff;
}
.my-table thead th {
  font-weight: normal;
}
.my-table thead tr {
  line-height: 40px;
}
.my-table th,
.my-table td {
  border-bottom: 1px solid #ccc;
  border-right: 1px solid #ccc;
}
.my-table td:last-child {
  border-right: none;
}
.my-table tr:last-child td {
  border-bottom: none;
}
.my-table button {
  width: 65px;
  height: 35px;
  font-size: 18px;
  border: 1px solid #ccc;
  outline: none;
  border-radius: 3px;
  cursor: pointer;
  background-color: #ffffff;
  margin-left: 5px;
}
</style>

24、路由的使用

1. 使用
  1. 安装:下载 VueRouter 模块到当前工程,版本3.6.5
yarn add vue-router@3.6.5
  1. 代码引用: main.js中引入VueRouter
import VueRouter from 'vue-router'
  1. 安装注册
Vue.use(VueRouter)
  1. 创建路由对象
const router = new VueRouter()
  1. 注入,将路由对象注入到new Vue实例中,建立关联
new Vue({
  render: h => h(App),
  router:router
}).$mount('#app')
  1. 当我们配置完以上5步之后 就可以看到浏览器地址栏中的路由 变成了 /#/的形式。表示项目的路由已经被Vue-Router管理了
    在这里插入图片描述

代码示例

  • main.js中
// 路由的使用步骤
// 5个基础步骤
// 1. 下载 v3.6.5
// yarn add vue-router@3.6.5
// 2. 引入
// 3. 安装注册 Vue.use(Vue插件)
// 4. 创建路由对象
// 5. 注入到new Vue中,建立关联

import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

const router = new VueRouter()

new Vue({
  render: h => h(App),
  router
}).$mount('#app')
  • 创建需要的组件 (views目录),const router = new VueRouter()中配置路由规则 ps:如果报错那就是命名规则需要两个单词拼成在对应的组件中 加上name
    在这里插入图片描述
    //报错Find.vue组件中name属性
    在这里插入图片描述
  • 配置导航,配置路由出口(路径匹配的组件显示的位置) App.vue中配置
<div class="footer_wrap">
  <a href="#/find">发现音乐</a>
  <a href="#/my">我的音乐</a>
  <a href="#/friend">朋友</a>
</div>
<div class="top">
  <router-view></router-view>
</div>
2. 路由封装
    1. 创建新目录router,创建新文件index.js
      在文件中写入
import Vue from 'vue'
import Router from 'vue-router'
// @ 是从src目录下去找
import Find form '@/view/Find.vue'
import My form '@/view/My.vue'
import My form '@/view/Friend.vue'

Vue.use(Router)

const router = new VueRouter({
	routes:[
		{path: '/find', component: Find},
		{path: '/my', component: My},
		{path: '/my', component: My}
	]

})
export default router
    1. main.js中
import Vue from 'vue'
import App from './App'
import router from './router/index.js'


new Vue({
  el: '#app',
  render: h => h(App),
  router
})

  • 配置导航,配置路由出口(路径匹配的组件显示的位置) App.vue中配置
<div class="footer_wrap">
  <a href="#/find">发现音乐</a>
  <a href="#/my">我的音乐</a>
  <a href="#/friend">朋友</a>
</div>
<div class="top">
  <router-view></router-view>
</div>
3. router-link (上面2已经对路由做了封装,这边就不加2的步骤了)

vue-router 提供了一个全局组件 router-link (取代 a 标签)

  • 能跳转,配置 to 属性指定路径(必须) 。本质还是 a 标签 ,to 无需 #
  • 能高亮,默认就会提供高亮类名,可以直接设置高亮样式ps:也就是点到谁,谁身上加个高亮类class
    在这里插入图片描述

router-link-active
模糊匹配(用的多)
to=“/my” 可以匹配 /my /my/a /my/b …
只要是以/my开头的路径 都可以和 to="/my"匹配到

router-link-exact-active
精确匹配
to=“/my” 仅可以匹配 /my

代码示例

<template>
  <div>
    <div class="footer_wrap">
      <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/friend">朋友</router-link>
    </div>
    <div class="top">
      <!-- 路由出口 → 匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
.footer_wrap {
  position: relative;
  left: 0;
  top: 0;
  display: flex;
  width: 100%;
  text-align: center;
  background-color: #333;
  color: #ccc;
}
.footer_wrap a {
  flex: 1;
  text-decoration: none;
  padding: 20px 0;
  line-height: 20px;
  background-color: #333;
  color: #ccc;
  border: 1px solid black;
}
// 给点击的高亮class加样式
.footer_wrap a.router-link-active {
  background-color: purple;
}
.footer_wrap a:hover {
  background-color: #555;
}
</style>
4. 自定义路由类名

我们可以在创建路由对象时,额外配置两个配置项即可。 linkActiveClasslinkExactActiveClass
在router/index.js 修改VueRouter

const router = new VueRouter({
  routes: [...],
  linkActiveClass: "类名1",
  linkExactActiveClass: "类名2"
})

5. 路由跳转传参
  1. 查询参数传参 (比较适合传多个参数)

    1. 跳转:to=“/path?参数名=值&参数名2=值”
    2. 获取:$route.query.参数名
  2. 动态路由传参 (优雅简洁,传单个参数比较方便)

    1. 配置动态路由:path: “/path/:参数名”
    2. 跳转:to=“/path/参数值”
    3. 获取:$route.params.参数名

    注意:动态路由也可以传多个参数,但一般只传一个

  1. 查询参数传参
  • 跳转
<router-link to="/path?参数名=值"></router-link>
  • 如何接受参数
    固定用法:
//在标签中
{{ $route.query.参数名 }}
//在js中
this.$route.query.参数名
  1. 动态路由传参
  • 修改路由
const router = new VueRouter({
  routes: [
    ...,
    { 
    //如果参数名后面没有加?代表的是必穿,如果不穿的话就是空白
      path: '/search/:参数名', 
      component: Search 
    }
  ]
})

  • 跳转
<router-link to="/search/参数值"></router-link>
  • 接收
//在标签中
{{ $route.params.参数名 }}
//在js中
this.$route.params.参数名
6. Vue路由-重定向(首页空白)

网页打开时, url 默认是 / 路径,未匹配到组件时,会出现空白 重定向 → 匹配 / 后, 强制跳转 /home 路径

语法:
在路由中

{ path: 匹配路径, redirect: 重定向到的路径 },
比如:
const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home'},
 	 ...
  ]
})
7、404展示配置

path: "" (任意路径) – 前面不匹配就命中最后这个 一定要放在最后面,他是重上往下匹配的,所以组后匹配

在路由中

import NotFind from '@/views/NotFind'

const router = new VueRouter({
  routes: [
    ...
    { path: '*', component: NotFind } //最后一个
  ]
})


8、模式设置
  • hash路由(默认) 例如: http://localhost:8080/#/home
  • history路由(常用) 例如: http://localhost:8080/home (以后上线需要服务器端支持,开发环境webpack给规避掉了history模式的问题)
const router = new VueRouter({
    mode:'histroy', //默认是hash
    routes:[]
})
9、编程式导航-两种路由跳转方式
1、path路径跳转语法

用法
在点击事件方法中

//简单写法
this.$router.push('路由路径')

//完整写法
this.$router.push({
  path: '路由路径'
})

传参

//简单写法
// this.$router.push(`/search?key=${this.inpValue}`)
 // this.$router.push(`/search/${this.inpValue}`)
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2')
//完整写法

      // this.$router.push({
      //   path: '/search',
      //   query: {
      //     key: this.inpValue
      //   }
      // })
      // this.$router.push({
      //   path: `/search/${this.inpValue}`
      // })
this.$router.push({
  path: '/路径',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

2、name命名路由跳转

用法
路由规则,必须配置name配置项

const router = new VueRouter({
  routes: [
    { name: '路由名', path: '/test', component: Test} //最后一个
  ]
})

通过name来跳

this.$router.push({
  name: '路由名'
})

传参
通过name来跳


      //    this.$router.push({
      //        name: '路由名'
      //        query: { 参数名: 参数值 },
      //        params: { 参数名: 参数值 }
      //    })


//query传参
this.$router.push({
  name: '路由名字',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

//动态路由传参 (需要配动态路由)
this.$router.push({
  name: '路由名字',
  params: {
    参数名: '参数值',
  }
})

3、当路由跳转传参有空白的话

就v-if=“test.id” 看看传参是否传过来
返回

this.$router.go(-1)
//或者
this.$router.back()
10、缓存组件(跳转数据会重新加载)

在路由跳转的标签<router-view></router-view>
在这里插入图片描述
缓存后c和m还是会执行一次,但是des不会执行,a和deact会相对应去执行

在这里插入图片描述

11、二级路由配置

当在页面中点击链接跳转,只是部分内容切换时,我们可以使用嵌套路由

语法

  • 在一级路由下,配置children属性即可
  • 配置二级路由的出口
  1. 在一级路由下,配置children属性
    注意:一级的路由path 需要加 / 二级路由的path不需要加 /
const router = new VueRouter({
  routes: [
    {
      path: '/home',
      component: Layout,
      children:[
        //children中的配置项 跟一级路由中的配置项一模一样 
        {path:'xxxx',component:xxxx.vue},
        {path:'xxxx',component:xxxx.vue},
      ]
    }
  ]
})

  1. 配置二级路由的出口
    Layout.vue
<template>
  <div class="h5-wrapper">
    <div class="content">
      <router-view></router-view>
    </div>
  ....
  </div>
</template>

25、 VueCli 自定义创建项目

  1. 创建项目
vue create hm-exp-mobile
  1. 选项
Vue CLI v5.0.8
? Please pick a preset:
  Default ([Vue 3] babel, eslint)
  Default ([Vue 2] babel, eslint)
> Manually select features     选自定义
  1. 手动选择功能
    在这里插入图片描述
  2. 选择vue的版本
  3.x
> 2.x
  1. 是否使用history模式
    在这里插入图片描述
  2. 选择css预处理
    在这里插入图片描述
  3. 选择eslint的风格 (eslint 代码规范的检验工具,检验代码是否符合规范)
    比如:const age = 18; => 报错!多加了分号!后面有工具,一保存,全部格式化成最规范的样子
    在这里插入图片描述
  4. 选择校验的时机 (直接回车)
    在这里插入图片描述
  5. 选择配置文件的生成方式 (直接回车)
    在这里插入图片描述
  6. 是否保存预设,下次直接使用? => 不保存,输入 N
    在这里插入图片描述
  7. 等待安装,项目初始化完成
    在这里插入图片描述

26、vuex

Vuex 是一个 Vue 的 状态管理工具,状态就是数据。

1. 配置:
    1. 安装仓库
# 在vue2中对应的插件版本为3,在vue3的话对应的插件是4
yarn add vuex@3 或者 npm i vuex@3
    1. 新建 store/index.js 专门存放 vuex
      在这里插入图片描述
// 导入 vue
import Vue from 'vue'
// 导入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 进行插件的安装初始化
Vue.use(Vuex)

// 创建仓库 store
const store = new Vuex.Store()

// 导出仓库
export default store

    1. 在 main.js 中导入挂载到 Vue 实例上
import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

    1. 测试打印Vuex
created(){
  console.log(this.$store)
}
2. 核心概念 - state 状态

State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储。
打开项目中的store.js文件,在state对象中可以添加我们要共享的数据。

1.变量使用

提供数据

// 创建仓库 store
const store = new Vuex.Store({
  // state 状态, 即数据, 类似于vue组件中的data,
  // 区别:
  // 1.data 是组件自己的数据, 
  // 2.state 中的数据整个vue项目的组件都能访问到
  state: {
    count: 101
  }
})

使用数据

获取 store:
 1.Vue模板中获取 this.$store
 2.js文件中获取 import 导入 store

模板中:     {{ $store.state.xxx }}
组件逻辑中:  this.$store.state.xxx
JS模块中:   store.state.xxx

1、模板中使用

<h1>state的数据 - {{ $store.state.count }}</h1>

2、组件逻辑中使用

<h1>state的数据 - {{ count }}</h1>

// 把state中数据,定义在组件内的计算属性中
  computed: {
    count () {
      return this.$store.state.count
    }
  }

3、js文件中使用

//main.js

import store from "@/store/index"

console.log(store.state.count)
2. 通过辅助函数 - mapState获取 state中的数据
  1. 导入mapState (mapState是vuex中的一个函数)
import { mapState } from 'vuex'

  1. 采用数组形式引入state属性
mapState(['count']) 

上面代码的最终得到的是 类似于

count () {
    return this.$store.state.count
}
  1. 利用展开运算符将导出的状态映射给计算属性
  computed: {
    ...mapState(['count'])
  }

使用计算函数


 <div> state的数据:{{ count }}</div>
3. 开启严格模式及Vuex的单项数据流

非严格模式 修改数据

button @click="handleAdd">+ 1</button>
methods:{
	 handleAdd (n) {
      // 错误代码(vue默认不会监测,监测需要成本)
       this.$store.state.count++
      // console.log(this.$store.state.count) 
    },
}

开启严格模式
通过 strict: true 可以开启严格模式,开启严格模式后,直接修改state中的值会报错
在这里插入图片描述

3.严格模式去正确修改数据定义mutations
const store  = new Vuex.Store({
  state: {
    count: 0
  },
  // 定义mutations
  mutations: {
    // 方法里参数 第一个参数是当前store的state属性
    // payload 载荷 运输参数 调用mutaiions的时候 可以传递参数 传递载荷
    //有参传入的话就可以写成 addCount (state, name)
    addCount (state) {
      state.count += 1
    }
  }
})

使用

//有参传入this.$store.commit('addCount', "张三")
this.$store.commit('addCount')
4. Vuex的双向绑定

vuex遵循单向流,不呢个使用v-model

<input :value="count" @input="handleInput" type="text">

export default {
  methods: {
    handleInput (e) {
      // 1. 实时获取输入框的值
      const num = +e.target.value
      // 2. 提交mutation,调用mutation函数
      this.$store.commit('changeCount', num)
    }
  }
}
5. 辅助函数- mapMutations

可以使用vuex方法

import  { mapMutations } from 'vuex'
methods: {
    ...mapMutations(['addCount'])
}

上述等同

methods: {
      // commit(方法名, 载荷参数)
      addCount () {
          this.$store.commit('addCount')
      }
 }

此时,就可以直接通过this.addCount调用了

<button @click="addCount">值+1</button>
6. actions异步

state是存放数据的,mutations是同步更新数据 (便于监测数据的变化, 更新视图等, 方便于调试工具查看变化),
actions则负责进行异步操作 actions中需要调用mutations中的方法

说明:mutations必须是同步的
需求: 一秒钟之后, 要给一个数 去修改state

const store  = new Vuex.Store({
  state: {
    count: 0
  },
  // 定义mutations
   // 3. actions 处理异步
  // 注意:不能直接操作 state,操作 state,还是需要 commit mutation
  actions: {
    // context 上下文 (此处未分模块,可以当成store仓库)
    // context.commit('mutation名字', 额外参数)
    setUserSecond(context, num) {
      // 这里是setTimeout模拟异步,以后大部分场景是发请求 changeCount为你mutations中的方法
      setTimeout(() => {
        context.commit('changeCount', num)
      }, 1000)
    }
  }
})

使用

<button @click="updateUser2">一秒后更新信息</button>

methods:{
    updateUser2 () {
      // 调用action dispatch
      this.$store.dispatch('setUserSecond', {
        name: 'xiaohong',
        age: 28
      })
    },
}

mapActions映射

<button @click="setUserSecond({ name: 'xiaoli', age: 80 })">一秒后更新信息</button>

import  { mapActions} from 'vuex'
methods:{
  ...mapActions(['setUserSecond'])
}
7. getters函数

用来计算


const store  = new Vuex.Store({
  state: {
    count: 0
  },
  // 定义mutations
   // 4. getters 类似于计算属性
  getters: {
    // 注意点:
    // 1. 形参第一个参数,就是state
    // 2. 必须有返回值,返回值就是getters的值
    filterList (state) {
      return state.list.filter(item => item > 5)
    }
  }
})

使用

{{ $store.getters.filterList }}

mapState使用

import  { mapState } from 'vuex'
computed : {
	...mapState(['filterList'])
}

{{filterList}}
8、分模块开发
import user from './modules/user'
import setting from './modules/user'

const store = new Vuex.Store({
  // 严格模式 (有利于初学者,检测不规范的代码 => 上线时需要关闭)
  strict: true,
  // 5. modules 模块
  modules: {
    user,
    setting
  }
})


新建文件夹modules在store下面分别新建user.js、user.js
setting.js

// setting模块
const state = {
  theme: 'light', // 主题色
  desc: '测试demo'
}
const mutations = {
  setTheme (state, newTheme) {
    state.theme = newTheme
  }
}
const actions = {}
const getters = {}

export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

user.js

// user模块
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutations = {
  setUser (state, newUserInfo) {
    state.userInfo = newUserInfo
  }
}
const actions = {
  setUserSecond (context, newUserInfo) {
    // 将异步在action中进行封装
    setTimeout(() => {
      // 调用mutation   context上下文,默认提交的就是自己模块的action和mutation
      context.commit('setUser', newUserInfo)
    }, 1000)
  }
}
const getters = {
  // 分模块后,state指代子模块的state
  UpperCaseName (state) {
    return state.userInfo.name.toUpperCase()
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}
1、直接使用
  1. state --> $store.state.模块名.数据项名
  2. getters --> $store.getters[‘模块名/属性名’]
  3. mutations --> $store.commit(‘模块名/方法名’, 其他参数)
  4. actions --> $store.dispatch(‘模块名/方法名’, 其他参数)
2、映射模块
 ...mapState(['count', 'user', 'setting']),

{{ user.userInfo.name }}
3、映射模块直接拿出来
...mapState('user',['count', 'user', 'setting']),

{{ userInfo.name }}
4、使用其他方法替代映射

在这里插入图片描述

5、 调用修改方法(除了…mapxxx(‘user’,[‘count’, ‘user’, ‘setting’]) 方法不需要改变原来的写法,其他都要)

如1中的原生写法

27、兼容适配

官方说明:地址

yarn add postcss-px-to-viewport@1.1.1 -D

项目根目录, 新建postcss的配置文件postcss.config.js

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 375,
    },
  },
};

viewportWidth:设计稿的视口宽度

  1. vant-ui中的组件就是按照375的视口宽度设计的
  2. 恰好面经项目中的设计稿也是按照375的视口宽度设计的,所以此时 我们只需要配置375就可以了
  3. 如果设计稿不是按照375而是按照750的宽度设计,怎么设计

28、封装axios

文档地址

使用

安装

npm install --save axios vue-axios

全局

在main.js中引入

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)

使用

this.axios({
        method:'get',
        url:'http://localhost:8090/regist?mail='+this.mail+'&password='+this.password,

      }).then(function (response) {
        console.log(response.data)
      });

局部工具类

新建模块utils ,新建js文件request.js

import axios from 'axios'
const instance = axios.create({
// 这个地址是服务端接口地址
  baseURL: 'https://some-domain.com/api',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});


// 添加请求拦截器
instance.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
	if (response.data.status != 200) {
		//抛出异常   |这里给个弹框省略了
		return Promise.reject(response.data.messsage)
	}
    return response.data;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });

export default instance

使用

import request from '@/utils/request'

mothods :{

	async getCode () {
	//		https://some-domain.com/api/getCode  为接口返回值base64, key
		const { data: {base64, key} } = await request.get('/getCode')
		this.key = key
		this.base64 = base64
	}
}


提取复用 (将局部封装)

新建api模块,新增Login.js

import request from '@/utils/request'

export const getCode = ()=>{
	return request.get('/getCode')
}

使用

import { getCode  } from '@/api/Login'

mothods :{

	async getCode () {
	//		https://some-domain.com/api/getCode  为接口返回值base64, key
		const { data: {base64, key} } = await getCode()
		this.key = key
		this.base64 = base64
	}
}

29、路由守卫

在router模块中的index.js中增加

//全局前置路由,配合浏览器localStorage进行鉴权操作
const urls = ['/pay','/myOrder']
router.beforeEach((to, from, next) =>{
    
    // console.log(to, from);
    
    if(!urls.incloudes(to.path)){   
    	//非鉴权
     	next();  
     	return
    }
    //如果没有token并且还是鉴权路径next('/login')
    next('/login')
})

// 全局后置路由,简单写一个弹窗,在进入每一个路由组件后弹出一句话
router.afterEach(() =>{
    alert('欢迎你!!');
})

30:打包和打包优化

1.打包
npm build
//npm build指向的是vue-cli-service build

本地访问dist需要在vue.config.js中
在这里插入图片描述

2. 打包优化路由懒加载

默认一进页面就把所有路由给加载

模块router下的index.js文件下修改

原本导对应路由导入:
在这里插入图片描述
修改成异步懒加载路由导入:
在这里插入图片描述
ps:路由懒加载要放在正常导包下面

31、个人记录

package.json
–mode是为了指定.env.development 文件

"scripts": {
    "dev": "vue-cli-service serve --mode development"
  }

.env.development

NODE_ENV = 'xxxx'

使用

process.env.NODE_ENV
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张航柯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值