Vue——May(2)

VUE

  • 一、Vue技术
    • 1.1 代理
      • (1)配置方式1
      • (2)配置方式2
    • * 案例github用户搜索
      • (1)引入bootstrap样式
      • (2)请求数据
      • (3)传到list组件上
      • (4)设置成动态样式
      • (5)完善
    • 1.2 vue-resource
    • 1.3 插槽
      • (1)默认插槽
      • (2)具名插槽
      • (3)作用域插槽
  • 二、vuex
    • * 求和案例
    • 2.1 vuex 工作原理图
      • (1)安装
      • (2)创建
    • * 求和案例(vuex优化版)
    • 2.2 getters
    • 2.3 mapState与mapGetters
    • 2.4 mapActions与mapMutations
    • * 人员列表案例
    • 2.5 vuex模块化+namespace
  • 三、vue-router
    • 3.1 对路由的理解
    • 3.2 路由的使用
    • 3.3 嵌套(多级)路由
    • 3.4 路由传参
    • 3.5 命名路由
    • 3.6 params参数
    • 3.7 路由的props配置
    • 3.8 router-link的replace
    • 3.9 编程式路由导航
    • 3.10 缓存路由组件
    • 3.11 新生命周期钩子(路由独有)
    • 3.12 路由守卫
      • (1)全局前置beforeEach
      • (2)全局后置afterEach
      • (3)独享守卫beforeEnter
      • (4)组件内守卫
    • 3.13 hash与history模式
      • (1)node express搭建微型服务器
      • (2)创建server.js
      • (3)启动服务器
      • (4)创建static文件
      • (5)重新启动node
      • (6)细节
  • 四、element-ui
    • 4.1 完整引入
      • (1)安装
      • (2)引入
      • (3)使用
    • 4.2 按需引入
  • 五、vue3
    • 5.1 创建Vue3.0工程
      • (1)用vue-cil创建
      • (2)用Vite创建
    • 5.2 分析工程结构
      • * 开发者工具
    • 5.3 常用组合式API
    • 5.4 setup
      • (1)返回值
      • (2)注意点
      • (3)props
      • (4)context
      • (5)slot
    • 5.5 ref函数
      • (1)接受基本数据类型
      • (2)接受对象类型
    • 5.6 reactive函数
      • (1)处理对象
      • (2)处理数组
      • (3)对比reactive与ref
    • 5.7 响应式原理
      • (1)vue2的
      • (2)vue3的
      • (3)reflect
    • 5.8 computed
    • 5.9 watch
      • (1)监视ref定义的响应式数据
      • (2)监视reactive定义的
      • (3)是否加。value
      • (4)watchEffect
    • 5.10 生命周期
    • 5.11自定义hook函数
      • * 应用
    • 5.12 toRef与toRefs
      • (1) toRef
      • (2) toRefs
    • 5.13 shaowReactive与shallowRef
      • (1)shaowReactive
      • (2)shallowRef
    • 5.14 readonly与shallowReadonly(数据不改变)
      • (1)readonly
      • (2) shallowReadonly
    • 5.15 toRaw与markRaw
      • ??
      • markRaw(数据改变,但不是响应式)
    • 5.16 customRef
    • 5.17 _provide与inject
    • 5.18 响应数据的判断
    • 5.19 碎片组件组件Fragment
    • 5.20 传送组件teleport
    • 5.21 异步组件Suspense
      • (1) 异步引入
      • (2)Suspense
      • (3)利用promise实现异步
      • (4)利用async
    • 5.22 Vue3其他变化

一、Vue技术

1.1 代理

在这里插入图片描述

可以发送aja请求的几种方式

Vue——May(2)_第1张图片

引入axios
在这里插入图片描述

跨域
协议名,主机名,端口号须一致,才是同源的,否则。。

解决跨域的方法

  1. 后端人员用cors,配置响应头
  2. jsonp,前后端共同解决,且只能解决get类型的
  3. 配置代理服务器——因为两台服务器之间,只受协议有关

(1)配置方式1

Vue——May(2)_第2张图片

开代理服务器

  1. nginx
  2. vue-cli
// App.vue

<template>
  <div>
    <button @click="getStu">获取学生信息</button>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "App",
  methods: {
    getStu() {
      axios.get("http://localhost:8080/students").then(
        //成功的回调
        (response) => {
          console.log("请求成功!", response.data);
        },
        (error) => {
          console.log("请求失败了,失败的原因是:", error.message);
        }
      );
    },
  },
};
</script>

//vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  devServer: {
    proxy: 'http://localhost:5000'
  }
})

缺点:1. 不能配置多个服务器
2. 原服务器有的,不会向新服务器请求

在这里插入图片描述

(2)配置方式2

Vue——May(2)_第3张图片
在这里插入图片描述

//App.vue

methods: {
    getStu() {
    //注意前缀的位置
      axios.get("http://localhost:8080/fang/students").then(
        //成功的回调
        (response) => {
          console.log("请求成功!", response.data);
        },
        (error) => {
          console.log("请求失败了,失败的原因是:", error.message);
        }
      );
    },
  },


//vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  devServer: {
    proxy:{
        //请求前缀
      '/fang': {
          target: 'http://localhost:5000',
          // 重写路径 
          // 匹配以/fang开头的,并将/fang转换为空
          pathRewrite: {
              '^/fang': ''
          },
          //欺骗5000服务器,说自己来自5000
          //用于控制请求头中的host值
          changeOrigin: true
      }
    }
  }
})



不加pathRewrite会出现以下问题Vue——May(2)_第4张图片

//App.vue

<template>
  <div>
    <button @click="getStu">获取学生信息</button>
    <button @click="getCar">获取车辆信息</button>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "App",
  methods: {
    getStu() {
      axios.get("http://localhost:8080/fang/students").then(
        //成功的回调
        (response) => {
          console.log("请求成功!", response.data);
        },
        //失败的回调
        (error) => {
          console.log("请求失败了,失败的原因是:", error.message);
        }
      );
    },
    getCar() {
      axios.get("http://localhost:8080/hai/cars").then(
        //成功的回调
        (response) => {
          console.log("请求成功!", response.data);
        },
        //失败的回调
        (error) => {
          console.log("请求失败了,失败的原因是:", error.message);
        }
      );
    },
  },
};
</script>

//vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  devServer: {
    proxy:{
      '/fang': {
          target: 'http://localhost:5000',
          pathRewrite: {
              '^/fang': ''
          },
          changeOrigin: true
      },
      '/hai':{
        target:'http://localhost:5001',
        pathRewrite:{'^/hai': ''}
      }
    }
  }
})

Vue——May(2)_第5张图片

不加前缀就不走代理

<template>
  <div>
    <button @click="getStu">获取学生信息</button>
    
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "App",
  methods: {
    getStu() {
      //不加前缀就不走代理
      axios.get("http://localhost:8080/students").then(
        //成功的回调
        (response) => {
          console.log("请求成功!", response.data);
        },
        //失败的回调
        (error) => {
          console.log("请求失败了,失败的原因是:", error.message);
        }
      );
    },
  },
};
</script>

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  devServer: {
    proxy:{
      '/fang': {
          target: 'http://localhost:5000',
          pathRewrite: {
              '^/fang': ''
          },
      },
    }
  }
})


直接获取8080端口的index.html文件
Vue——May(2)_第6张图片

* 案例github用户搜索

(1)引入bootstrap样式

引入bootstrap时,会有多余的字体样式,删除即可。
Vue——May(2)_第7张图片

引入方式1:
在src下的assets里创建css文件,放入bootstrap.css文件
在App.vue中引入
import './assets/css/bootstrap.css'Vue——May(2)_第8张图片
引入方式2:
文件放置在public下的css里的bootstrap.css文件
Vue——May(2)_第9张图片
在index.html文件里引入

(2)请求数据

//Search.vue

  <template>
    <section class="jumbotron">
    <h3 class="jumbotron-heading">Search Github Users</h3>
    <div>
      <input
        type="text"
        placeholder="enter the name you search"
        v-model="keyWord"
      />&nbsp;
      <button @click="searchUser">Search</button>
    </div>
  </section>
  </template>
  
  <script>
  import axios from 'axios'
  export default {
  name:'Search',
  data(){
    return {
      keyWord:''
    }
  },
  methods:{
    searchUser(){
      axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
        response=>{
          console.log('请求成功',response.data)
          console.log('请求成功',response.data.items)
        },
        error=>{}
      )
    }
  }
  }
  </script>
  
  <style>
  
  </style>

Vue——May(2)_第10张图片

(3)传到list组件上


  <template>
  <section class="jumbotron">
    <h3 class="jumbotron-heading">Search Github Users</h3>
    <div>
      <input
        type="text"
        placeholder="enter the name you search"
        v-model="keyWord"
      />&nbsp;
      <button @click="searchUser">Search</button>
    </div>
  </section>
</template>
  
  <script>
import axios from "axios";
export default {
  name: "Search",
  data() {
    return {
      keyWord: "",
    };
  },
  methods: {
    searchUser() {
      axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
        (response) => {
          console.log("请求成功");
          this.$bus.$emit("UsersList", response.data.items);
        },
        (error) => {
          console.log("请求失败");
        }
      );
    },
  },
};
</script>
  
  <style>
</style>

//List.vue

<template>
  <div class="row">
    <div class="card">
      <a href="https://github.com/xxxxxx" target="_blank">
        <img style="width: 100px" />
      </a>
      <p class="card-text">xxxxxx</p>
    </div>
    <div class="card">
      <a href="https://github.com/xxxxxx" target="_blank">
        <img style="width: 100px" />
      </a>
      <p class="card-text">xxxxxx</p>
    </div>
    <div class="card">
      <a href="https://github.com/xxxxxx" target="_blank">
        <img style="width: 100px" />
      </a>
      <p class="card-text">xxxxxx</p>
    </div>
    <div class="card">
      <a href="https://github.com/xxxxxx" target="_blank">
        <img style="width: 100px" />
      </a>
      <p class="card-text">xxxxxx</p>
    </div>
    <div class="card">
      <a href="https://github.com/xxxxxx" target="_blank">
        <img style="width: 100px" />
      </a>
      <p class="card-text">xxxxxx</p>
    </div>
  </div>
</template>

<script>
export default {
  name: "List",
  data() {
    return {
      users: [],
    };
  },
  //接收数据的组件
  mounted() {
    this.$bus.$on("UsersList", (users) => {
      console.log("我是list组件,我收到数据:", users);
      this.users = users;
    });
  },
};
</script>

<style>
.album {
  min-height: 50rem; /* Can be removed; just added for demo purposes */
  padding-top: 3rem;
  padding-bottom: 3rem;
  background-color: #f7f7f7;
}

.card {
  float: left;
  width: 33.333%;
  padding: 0.75rem;
  margin-bottom: 2rem;
  border: 1px solid #efefef;
  text-align: center;
}

.card > img {
  margin-bottom: 0.75rem;
  border-radius: 100px;
}

.card-text {
  font-size: 85%;
}
</style>

//App.vue
import Search from "./components/Search.vue";

//main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  beforeCreate(){
    //安装全局事件总线
    Vue.prototype.$bus=this
  }
}).$mount('#app')

Vue——May(2)_第11张图片

(4)设置成动态样式

//List.vue
<template>
  <div class="row">
    <div class="card" v-for="user in users" :key="user.login">
      <a :href="user.html_url" target="_blank">
        <img 
        :src="user.avatar_url"
        style="width: 100px" />
      </a>
      <p class="card-text">{{ user.login }}</p>
    </div>
    
  </div>
</template>

Vue——May(2)_第12张图片

(5)完善

  1. 欢迎
  2. 加载
  3. 请求错误的信息
//list.vue

<template>
  <div class="row">
    <!-- 展示用户列表 -->
    <div
      class="card"
      v-for="user in info.users"
      :key="user.login"
      v-show="info.users.length"
    >
      <a :href="user.html_url" target="_blank">
        <img :src="user.avatar_url" style="width: 100px" />
      </a>
      <p class="card-text">{{ user.login }}</p>
    </div>
    <!-- 展示欢迎词 -->
    <h1 v-show="info.isFirst">欢迎!</h1>
    <!-- 展示加载中 -->
    <h1 v-show="info.isloading">加载中</h1>
    <!-- 展示错误信息 -->
    <h1 v-show="info.errorMsg">{{ info.errorMsg }}</h1>
  </div>
</template>

<script>
export default {
  name: "List",
  data() {
    return {
      info: {
        isFirst: true,
        isloading: false,
        errorMsg: "",
        users: [],
      },
    };
  },
  //接收数据的组件
  mounted() {
    this.$bus.$on("updateList", (dataObj) => {
      this.info=dataObj
    });
  },
};
</script>

<style>
.album {
  min-height: 50rem; /* Can be removed; just added for demo purposes */
  padding-top: 3rem;
  padding-bottom: 3rem;
  background-color: #f7f7f7;
}

.card {
  float: left;
  width: 33.333%;
  padding: 0.75rem;
  margin-bottom: 2rem;
  border: 1px solid #efefef;
  text-align: center;
}

.card > img {
  margin-bottom: 0.75rem;
  border-radius: 100px;
}

.card-text {
  font-size: 85%;
}
</style>

//search.vue


  <template>
  <section class="jumbotron">
    <h3 class="jumbotron-heading">Search Github Users</h3>
    <div>
      <input
        type="text"
        placeholder="enter the name you search"
        v-model="keyWord"
      />&nbsp;
      <button @click="searchUser">Search</button>
    </div>
  </section>
</template>
  
  <script>
import axios from "axios";
export default {
  name: "Search",
  data() {
    return {
      keyWord: '',
    };
  },
  methods: {
    searchUser() {
      //请求前更新list数据
      this.$bus.$emit("updateList",{isFirst:false,isloading:true,errorMsg:'',users:[]});
      axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
        (response) => {
          console.log("请求成功");
          //请求成功后
          this.$bus.$emit("updateList",{isloading:false,errorMsg:'',users:response.data.items});
        },
        (error) => {
          console.log("请求失败");
          //请求失败后
          this.$bus.$emit("updateList",{isloading:false,errorMsg:error.message,users:[]});
        }
      );
    },
  },
};
</script>
  
  <style>
</style>


Vue——May(2)_第13张图片

Vue——May(2)_第14张图片

1.2 vue-resource

发送ajax资源的库
Vue——May(2)_第15张图片

在search.vue的更改部分

 // axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
      this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
        (response) => {...

在main.js部分

import Vue from 'vue'
import App from './App.vue'
import vueResource from 'vue-resource'
Vue.config.productionTip = false
Vue.config.productionTip=false
Vue.use(vueResource)
new Vue({
  render: h => h(App),
  beforeCreate(){
    //安装全局事件总线
    Vue.prototype.$bus=this
  }
}).$mount('#app')

1.3 插槽

在这里插入图片描述

(1)默认插槽

Vue——May(2)_第16张图片

在这里插入图片描述

app中的子组件的内容都是先在app中解析,再放到子组件中(category.vue)

//app.vue


<template>
  <div class="container">
    <Category title="美食" >
      <img src="./assets/monkey.png" alt="">
    </Category>
    <Category title="书籍" >
      <ul>
        <li v-for="(g,index) in books" :key="index">{{ g }}</li>
      </ul>
    </Category>
    <Category title="城市" >
      <video controls src="./vedio/1.mp4"></video>
    </Category>
  </div>
</template>

<script>
import Category from ".//components/Category.vue";
export default {
  name: "App",
  components: {
    Category,
  },
  data(){
    return {
      foods:['包子','豆奶','蛋糕'],
      books:['a书','b书','c书'],
      cities:['徐州','铜山','鼓楼']
    }
  }
};
</script>
<style>

.container{
  display: flex;
}
</style>

//category.vue
<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  props:['title'],
};
</script>

<style>
.category {
    text-align: center;
  background-color: rgb(187, 245, 245);
  width: 200px;
  height: 300px;
  margin-right: 15px;
}
h3 {
  text-align: center;
  background-color: orange;
}
video{
    width: 100px;
  height: 200px;
}
</style>

Vue——May(2)_第17张图片

(2)具名插槽

一种是slot=' '另一种是v-slot=' '(只能在template标签上使用)

Vue——May(2)_第18张图片

//app.vue

<template>
  <div class="container">
    <Category title="美食" >
      <img slot="1" src="./assets/monkey.png" alt="">
      <a href="#" slot="3">更多美食...</a>
      <p slot="2">~~~~~~~</p>
    </Category>
    <Category title="书籍" >
      <ul slot="2">
        <li v-for="(b,index) in books" :key="index">{{ b }}</li>
      </ul>
      <p slot="1">~~~~~~~</p>
      <a href="#" slot="3">更多书籍...</a>
    </Category>
   
  </div>
</template>

<script>
import Category from ".//components/Category.vue";
export default {
  name: "App",
  components: {
    Category,
  },
  data(){
    return {
      foods:['包子','豆奶','蛋糕'],
      books:['a书','b书','c书'],
      cities:['徐州','铜山','鼓楼']
    }
  }
};
</script>
<style>

.container{
  display: flex;
}
</style>

//category.vue

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <slot name="1"></slot>
    <slot name="2"></slot>
    <slot name="3"></slot>
    
  </div>
</template>

<script>
export default {
  name: "Category",
  props:['title'],
};
</script>

<style>
.category {
    text-align: center;
  background-color: rgb(244, 216, 187);
  width: 200px;
  height: 300px;
  margin-right: 15px;
}
h3 {
  text-align: center;
  background-color: orange;
}
video{
    width: 100px;
  height: 200px;
}
</style>

Vue——May(2)_第19张图片

(3)作用域插槽

也可以有名字
在这里插入图片描述
Vue——May(2)_第20张图片
在这里插入图片描述

二、vuex

Vue——May(2)_第21张图片

什么时候使用vuex
Vue——May(2)_第22张图片

Vue——May(2)_第23张图片
Vue——May(2)_第24张图片

* 求和案例

//count.vue

<template>
  <div>
    <h2>当前和为{{ sum }}</h2>
    <select v-model="n">
        <!-- 注意要给value前加上:否则将是字符串形式 -->
      <option :value="1">n=1</option>
      <option :value="2">n=2</option>
      <option :value="3">n=3</option>
    </select>
    <button @click="rise">+n</button>
    <button @click="down">-n</button>
    <button @click="odd">sum为奇数时+n</button>
    <button @click="delay">延迟1+n</button>
  </div>
</template>
  
  <script>
export default {
  name: "Count",
  data() {
    return {
      sum: 0,
      n: 1,//当前要加的数字
    };
  },
  methods: {
    rise() {
        this.sum+=this.n
    },
    down() {
        this.sum-=this.n
    },
    odd() {
        if(this.sum%2){
            this.sum+=this.n
        }
    },
    delay() {
        setTimeout(()=>{
            this.sum+=this.n
        },500)
    },
  },
};
</script>
  
  <style>
</style>
  

Vue——May(2)_第25张图片

2.1 vuex 工作原理图

Vue——May(2)_第26张图片

(1)安装

Vue——May(2)_第27张图片
否则会报错

在这里插入图片描述

(2)创建

引入项目时,注意顺序,所有import都要比js中的源代码,先执行
Vue——May(2)_第28张图片
报错:vuex要在store之前引入
按如下代码写,即可解决问题

//  src/main.js

import Vue from 'vue'
import App from './App.vue'
import store from './store/index'
Vue.config.productionTip = false
new Vue({
  render: h => h(App),
  store,
}).$mount('#app')
// console.log(vm)


// src/store/index.js

//创建Vuex的核心store
//引入Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//创建actions对象 用于响应组件中的动作
const actions={}
//创建mutations对象,用于存储对象
const mutations={}
//准备state用于存储数据
const state={}
//创建store
export default new Vuex.Store({
    // actions:actions
    //重名可以省略
    actions,
    mutations,
    state
})
//暴露store
// export default store

现在所有的组件,组件实例都有==$store==属性
Vue——May(2)_第29张图片

const actions={
    //配置与count组件中对应的add
    // add:function(){
    add(context,value){
        console.log('因为rise被点击了,所有actions上的add被触发了',context,value)
        //上下文对象
        // context.commit('add',value)
    }
}

Vue——May(2)_第30张图片

Vue——May(2)_第31张图片

Vue——May(2)_第32张图片

* 求和案例(vuex优化版)

// count.vue

<template>
  <div>
    <h2>当前和为{{ $store.state.sum }}</h2>
    <select v-model="n">
        <!-- 注意要给value前加上:否则将是字符串形式 -->
      <option :value="1">n=1</option>
      <option :value="2">n=2</option>
      <option :value="3">n=3</option>
    </select>
    <button @click="rise">+n</button>
    <button @click="down">-n</button>
    <button @click="odd">sum为奇数时+n</button>
    <button @click="delay">延迟1+n</button>
  </div>
</template>
  
  <script>
export default {
  name: "Count",
  data() {
    return {
        n: 1,//当前要加的数字
    };
  },
  methods: {
    rise() {
        // this.sum+=this.n
        this.$store.dispatch('add',this.n)
    },
    down() {
        // this.sum-=this.n
        this.$store.dispatch('cut',this.n)
    },
    odd() {
        if(this.$store.state.sum%2){
            // this.sum+=this.n
            this.$store.dispatch('add',this.n)
        }
    },
    delay() {
        setTimeout(()=>{
            // this.sum+=this.n
            this.$store.dispatch('add',this.n)
        },500)
    },
  },
};
</script>
  
  <style>
</style>
  
// store/index.js

//创建Vuex的核心store
//引入Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//创建actions对象 用于响应组件中的动作
const actions={
    //配置与count组件中对应的add
    // add:function(){
    add(context,value){
        // console.log('因为rise被点击了,所有actions上的add被触发了',context,value)
        //上下文对象
        context.commit('ADD',value)
    },
    cut(a,b){
        a.commit('CUT',b)
    },
    
}
//创建mutations对象,用于存储对象
const mutations={
    ADD(state,value){
        // console.log('mutations上的add被触发了',states,b)
        state.sum+=value
    },
    CUT(c,d){
        c.sum-=d
    }
}
//准备state用于存储数据
const state={
    sum: 11,
}
//创建store
export default new Vuex.Store({
    // actions:actions
    //重名可以省略
    actions,
    mutations,
    state
})
//暴露store
// export default store

另一版

//index.js

//创建Vuex的核心store
//引入Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//创建actions对象 用于响应组件中的动作
const actions = {
    //配置与count组件中对应的add
    // add:function(){
    add(context, value) {
        // console.log('因为rise被点击了,所有actions上的add被触发了',context,value)
        //上下文对象
        context.commit('ADD', value)
    },
    cut(a, b) {
        a.commit('CUT', b)
    },
    addOdd(a, b) {
        a.commit('ADDODD', b)
    },
    addDelay(a, b) {
        a.commit('ADDDELAY', b)
    }
}
//创建mutations对象,用于存储对象
const mutations = {
    ADD(state, value) {
        // console.log('mutations上的add被触发了',states,b)
        state.sum += value
    },
    CUT(c, d) {
        c.sum -= d
    },
    ADDODD(c, d) {
        if (c.sum % 2) {
            c.sum += d
        }
    },
    ADDDELAY(e, f) {
        setTimeout(()=>{
            e.sum += f
        },500)
    }
}
//准备state用于存储数据
const state = {
    sum: 11,
}
//创建store
export default new Vuex.Store({
    // actions:actions
    //重名可以省略
    actions,
    mutations,
    state
})
//暴露store
// export default store


//count.vue

<template>
  <div>
    <h2>当前和为{{ $store.state.sum }}</h2>
    <select v-model="n">
        <!-- 注意要给value前加上:否则将是字符串形式 -->
      <option :value="1">n=1</option>
      <option :value="2">n=2</option>
      <option :value="3">n=3</option>
    </select>
    <button @click="rise">+n</button>
    <button @click="down">-n</button>
    <button @click="odd">sum为奇数时+n</button>
    <button @click="delay">延迟1+n</button>
  </div>
</template>
  
  <script>
export default {
  name: "Count",
  data() {
    return {
        n: 1,//当前要加的数字
    };
  },
  methods: {
    rise() {
        // this.sum+=this.n
        this.$store.dispatch('add',this.n)
    },
    down() {
        // this.sum-=this.n
        this.$store.dispatch('cut',this.n)
    },
    odd() {
        // if(this.$store.state.sum%2){
        //     // this.sum+=this.n
        //     this.$store.dispatch('add',this.n)
        // }
        this.$store.dispatch('addOdd',this.n)
    },
    delay() {
        // setTimeout(()=>{
        //     // this.sum+=this.n
        //     this.$store.dispatch('add',this.n)
        // },500)
        this.$store.dispatch('addDelay',this.n)
    },
  },
};
</script>
  
  <style>
</style>
  

把业务逻辑写在actions中更好

//创建Vuex的核心store
//引入Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//创建actions对象 用于响应组件中的动作
const actions = {
    //配置与count组件中对应的add
    // add:function(){
    add(context, value) {
        // console.log('因为rise被点击了,所有actions上的add被触发了',context,value)
        //上下文对象
        context.commit('ADD', value)
    },
    cut(a, b) {
        a.commit('CUT', b)
    },
    addOdd(a, b) {
        if (a.state.sum % 2) {
            a.commit('ADDODD', b)
        }
    },
    addDelay(a, b) {
        setTimeout(() => {
            a.commit('ADDDELAY', b)
        }, 500)
    }
}
//创建mutations对象,用于存储对象
const mutations = {
    ADD(state, value) {
        // console.log('mutations上的add被触发了',states,b)
        state.sum += value
    },
    CUT(c, d) {
        c.sum -= d
    },
    ADDODD(c, d) {
        c.sum += d
    },
    ADDDELAY(e, f) {
        e.sum += f
    }
}
//准备state用于存储数据
const state = {
    sum: 11,
}
//创建store
export default new Vuex.Store({
    // actions:actions
    //重名可以省略
    actions,
    mutations,
    state
})
//暴露store
// export default store

业务逻辑没有的,就不写dispatch,直接写mutations。

<template>
  <div>
    <h2>当前和为{{ $store.state.sum }}</h2>
    <select v-model="n">
        <!-- 注意要给value前加上:否则将是字符串形式 -->
      <option :value="1">n=1</option>
      <option :value="2">n=2</option>
      <option :value="3">n=3</option>
    </select>
    <button @click="rise">+n</button>
    <button @click="down">-n</button>
    <button @click="odd">sum为奇数时+n</button>
    <button @click="delay">延迟1+n</button>
  </div>
</template>
  
  <script>
export default {
  name: "Count",
  data() {
    return {
        n: 1,//当前要加的数字
    };
  },
  methods: {
    rise() {
        // this.$store.dispatch('add',this.n)
        this.$store.commit('ADD',this.n)
    },
    down() {
        // this.$store.dispatch('cut',this.n)
        this.$store.commit('CUT',this.n)
    },
    odd() {
        // if(this.$store.state.sum%2){
        //     // this.sum+=this.n
        //     this.$store.dispatch('add',this.n)
        // }
        this.$store.dispatch('addOdd',this.n)
    },
    delay() {
        // setTimeout(()=>{
        //     // this.sum+=this.n
        //     this.$store.dispatch('add',this.n)
        // },500)
        this.$store.dispatch('addDelay',this.n)
    },
  },
};
</script>
  
  <style>
</style>
  
//index.js

//创建Vuex的核心store
//引入Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//创建actions对象 用于响应组件中的动作
const actions = {
    //配置与count组件中对应的add
    // add:function(){
    
    addOdd(a, b) {
        if (a.state.sum % 2) {
            a.commit('ADDODD', b)
        }
    },
    addDelay(a, b) {
        setTimeout(() => {
            a.commit('ADDDELAY', b)
        }, 500)
    }
}
//创建mutations对象,用于存储对象
const mutations = {
    ADD(state, value) {
        // console.log('mutations上的add被触发了',states,b)
        state.sum += value
    },
    CUT(c, d) {
        c.sum -= d
    },
    ADDODD(c, d) {
        c.sum += d
    },
    ADDDELAY(e, f) {
        e.sum += f
    }
}
//准备state用于存储数据
const state = {
    sum: 11,
}
//创建store
export default new Vuex.Store({
    // actions:actions
    //重名可以省略
    actions,
    mutations,
    state
})
//暴露store
// export default store

在这里插入图片描述

2.2 getters

Vue——May(2)_第33张图片
Vue——May(2)_第34张图片
Vue——May(2)_第35张图片

2.3 mapState与mapGetters

在这里插入图片描述

Vue——May(2)_第36张图片

Vue——May(2)_第37张图片
Vue——May(2)_第38张图片
名字相同时,用数组写法,可以省略

2.4 mapActions与mapMutations

需要直接传入参
在这里插入图片描述
Vue——May(2)_第39张图片
在这里插入图片描述
Vue——May(2)_第40张图片

* 人员列表案例

//index.js

//创建Vuex的核心store
//引入Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//创建mutations对象,用于存储对象
const mutations = {
    ADD_PERSON(state, personObj) {
        state.personList.unshift(personObj)
    }
}
//准备state用于存储数据
const state = {
    sum: 0,
    personList: [
        { id: '001', name: 'lucy' }
    ]
}
//创建store
export default new Vuex.Store({
    // actions:actions
    //重名可以省略
    mutations,
    state,
})
//暴露store
// export default store

//Person.vue

<template>
  <div>
    <h2>人员列表</h2>
    <input type="text" v-model="name" placeholder="请输入名字" />
    <button @click="add">添加</button>
    <ul>
      <!-- <li v-for="p in $store.state.personList" :key="p.id">*****</li> -->
      <li v-for="p in personList" :key="p.id">{{ p.name }}</li>
    </ul>
  </div>
</template>

<script>
import { nanoid } from "nanoid";
export default {
  name: "Person",
  computed: {
    personList() {
      return this.$store.state.personList;
    },
  },
  data() {
    return {
      name: "",
    };
  },
  methods: {
    add() {
      const personObj = { id: nanoid(), name: this.name };
      this.$store.commit("ADD_PERSON", personObj);
      this.name = "";
    },
  },
};
</script>
//count.vue

<template>
  <div style="color:red">下方组件的总人数是{{ personList.length }}</div>
</template>
  <script>
import { mapState } from "vuex";

export default {
  name: "Count",
  computed: {
    ...mapState(["personList"]),
  },
};
</script>
  

Vue——May(2)_第41张图片

hhhhjjjjLMalscjmdlk

2.5 vuex模块化+namespace

在这里插入图片描述

Vue——May(2)_第42张图片
Vue——May(2)_第43张图片
Vue——May(2)_第44张图片
在这里插入图片描述

在这里插入图片描述

三、vue-router

vue的一个插件库

Vue——May(2)_第45张图片

key-value
路径-组件

Vue——May(2)_第46张图片

3.1 对路由的理解

Vue——May(2)_第47张图片
Vue——May(2)_第48张图片

3.2 路由的使用

使用vue2只能安装vue-router@3
使用vue3只能安装vue-router@4
Vue——May(2)_第49张图片

  1. src\router\index.js路径下创建路由
//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../components/About.vue'
import Home from '../components/Home.vue'
//创建一个路由
export default new VueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home
        }
    ]
})
  1. src\main.js下应用路由插件,加入路由配置项
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
//引入路由器
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
  render: h => h(App),
  router:router
}).$mount('#app')

  1. src\App.vue下,实现切换(active-class),展示指定位置(router-view
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>

<router-view></router-view>

整体视图

Vue——May(2)_第50张图片

*注意点

  1. 路由组件,一般组件
    Vue——May(2)_第51张图片
  2. 不用的路由组件,会被销毁
    Vue——May(2)_第52张图片
  3. 所有路由组件的$router是同一个,route不是同一个
    在这里插入图片描述

3.3 嵌套(多级)路由

Vue——May(2)_第53张图片

//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
//创建一个路由
export default new VueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home,
            //二级路由
            children:[
                {
                    //不加 / 符号
                    path:'news',
                    component:News
                },
                {
                    path:'message',
                    component:Message
                },
            ]
        }
    ]
})

在这里插入图片描述

//   src/pages/Home.vue

<template>
  <div>
    <ul class="nav nav-tabs">
      <li>
        <router-link class="list-group-item" active-class="active" to="/home/news" href="./home-news.html">News</router-link>
      </li>
      <li>
        <router-link class="list-group-item" active-class="active" to="/home/message" href="./home-message.html">Message</router-link>
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Home",
};
</script>

<style>
</style>

3.4 路由传参

query参数

Vue——May(2)_第54张图片

Vue——May(2)_第55张图片
传递动态参数

//字符串写法
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link>

//对象写法
        <router-link :to="{
            path:'/home/message/detail',
            query:{
                id:m.id,
                title:m.title
            }
        }">
            {{ m.title }} 
        </router-link>

接受参数

<template>
  <ul>
    <li>消息编号{{$route.query.id}}</li>
    消息详情{{ $route.query.title }}
  </ul>
</template>

<script>
export default {
name:'Detail'
}
</script>

Vue——May(2)_第56张图片

3.5 命名路由

作用:跳转时,简化代码
Vue——May(2)_第57张图片

3.6 params参数

在这里插入图片描述

使用params传参,路径必须写name
Vue——May(2)_第58张图片

在index。js下
加粗样式
Vue——May(2)_第59张图片
在message.vue下Vue——May(2)_第60张图片
在detail。vue下
Vue——May(2)_第61张图片

3.7 路由的props配置

Vue——May(2)_第62张图片

//D:\web\practice\route\src\pages\Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
        <router-link :to="{
            // path:'/home/message/detail',
            name:'xiangqing',
            query:{
              id:m.id,
              title:m.title
            }
        }">
            {{ m.title }} 
        </router-link>
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Message",
  data() {
    return {
      messageList: [
        { id: 1, title: "消息001" },
        { id: 2, title: "消息002" },
        { id: 3, title: "消息003" },
      ],
    };
  },
};
</script>
//D:\web\practice\route\src\pages\Detail.vue

<template>
  <ul>
    <li>消息编号:{{id}}</li>
    消息详情:{{ title }}
  </ul>
</template>

<script>
export default {
name:'Detail',
props:['title','id'],
}
</script>

<style>

</style>
//D:\web\practice\route\src\router\index.js

//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from  '../pages/Detail'
//创建一个路由
export default new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home,
            //二级路由
            children:[
                {
                    //不加 / 符号
                    path:'news',
                    component:News
                },
                {
                    path:'message',
                    component:Message,
                    children:[
                        {
                            name:'xiangqing',
                            path:'detail',
                            component:Detail,
                            // 回调函数,定义了不调用,却执行了
                            props($route){
                                return {
                                    id:$route.query.id,
                                    title:$route.query.title
                                }
                            }
                        }
                    ]
                },
            ]
        }
    ]
})

更简便的写法——利用解构赋值

Vue——May(2)_第63张图片
Vue——May(2)_第64张图片

Vue——May(2)_第65张图片

3.8 router-link的replace

Vue——May(2)_第66张图片

对历史记录的操作

  1. push——默认的
  2. replace——无法后退,原来的记录被替换通过以下操作开启

在这里插入图片描述
简写在这里插入图片描述

3.9 编程式路由导航

在这里插入图片描述

//route\src\components\Banner.vue

<template>
  <div class="col-xs-offset-2 col-xs-8">
    <div class="page-header">
      <h2>Vue Router Demo</h2>
      <button @click="back">后退</button>
      <button @click="forward">前进</button>
      <button @click="go">跳转指定步数</button>
    </div>
  </div>
</template>

<script>
export default {
  name: "Banner",
  methods: {
    back() {
      this.$router.back({});
    },
    forward() {
      this.$router.forward({});
    },
    go() {
      this.$router.go(-2)
    },
},
}
</script>

<style>
</style>

Vue——May(2)_第67张图片

//route\src\pages\Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
        <router-link :to="{
            // path:'/home/message/detail',
            name:'xiangqing',
            query:{
              id:m.id,
              title:m.title
            }
        }">
            {{ m.title }} 
        </router-link>
        <button @click="pushShow(m)">push</button>
        <button @click="replaceShow(m)">replace</button>
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Message",
  methods:{
    pushShow(m){
      this.$router.push({
        name:'xiangqing',
            query:{
              id:m.id,
              title:m.title
            }
      })
    },
    replaceShow(m){
      this.$router.replace({
        name:'xiangqing',
            query:{
              id:m.id,
              title:m.title
            }
      })
    }
  },
  data() {
    return {
      messageList: [
        { id: 1, title: "消息001" },
        { id: 2, title: "消息002" },
        { id: 3, title: "消息003" },
      ],
    };
  },
};
</script>

<style>
</style>

Vue——May(2)_第68张图片

3.10 缓存路由组件

即路径切走时,不让组件被销毁
在这里插入图片描述

Vue——May(2)_第69张图片

include里是组件名

//route\src\pages\Home.vue
    <keep-alive include="News">
      <router-view></router-view>
    </keep-alive>

3.11 新生命周期钩子(路由独有)

Vue——May(2)_第70张图片

activated 激活
deactivated 失活

现存问题:希望跳转时后,缓存输入框信息,并且,可以停掉计时器

//route\src\pages\News.vue

<template>
  <div>
    <ul>
      <h3 :style="{ opacity }">欢迎学习vue</h3>
      <li>news001 <input type="text" /></li>
      <li>news002 <input type="text" /></li>
      <li>news003 <input type="text" /></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "News",
  data() {
    return {
      opacity: 1,
    };
  },
  mounted() {
    this.timer = setInterval(() => {
      this.opacity -= 0.01;
      if (this.opacity <= 0) this.opacity = 1;
    }, 10);
  },
  beforeDestroy() {
    clearInterval(this.timer);
  },
};
</script>

Vue——May(2)_第71张图片

  activated() {
    this.timer = setInterval(() => {
      this.opacity -= 0.01;
      console.log('A')
      if (this.opacity <= 0) this.opacity = 1;
    }, 10);
  },
  deactivated() {
    clearInterval(this.timer);
  },

Vue——May(2)_第72张图片

Vue——May(2)_第73张图片
Vue——May(2)_第74张图片

3.12 路由守卫

权限问题

(1)全局前置beforeEach

// route\src\router\index.js

//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from  '../pages/Detail'
//创建一个路由
const router=new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About
        },
        {
            name:'zhuye',
            path:'/home',
            component:Home,
            //二级路由
            children:[
                {
                    name:'xinwen',
                    //不加 / 符号
                    path:'news',
                    component:News
                },
                {
                    name:'xiaoxi',
                    path:'message',
                    component:Message,
                    children:[
                        {
                            name:'xiangqing',
                            path:'detail',
                            component:Detail,
                            //加强版,解构赋值
                            props({query:{id,title}}){
                                return {
                                    id,title
                                }
                            }
                        }
                    ]
                },
            ]
        }
    ]
})
//全局前置路由守卫
//在切换之前,调用下方函数
router.beforeEach((to,from,next)=>{
console.log(to,from)
next()
})
//  改变路由器暴露方式
export default router

Vue——May(2)_第75张图片

设置权限

// route\src\router\index.js

router.beforeEach((to,from,next)=>{
    if(localStorage.getItem('user')==='fang'){
        next() //放权
    }
})

Vue——May(2)_第76张图片
Vue——May(2)_第77张图片

给指定路径加权限


//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from  '../pages/Detail'
//创建一个路由
const router=new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About
        },
        {
            name:'zhuye',
            path:'/home',
            component:Home,
            //二级路由
            children:[
                {
                    name:'xinwen',
                    //不加 / 符号
                    path:'news',
                    component:News
                },
                {
                    name:'xiaoxi',
                    path:'message',
                    component:Message,
                    children:[
                        {
                            name:'xiangqing',
                            path:'detail',
                            component:Detail,
                            //加强版,解构赋值
                            props({query:{id,title}}){
                                return {
                                    id,title
                                }
                            }
                        }
                    ]
                },
            ]
        }
    ]
})
//全局前置路由守卫
//在切换之前,调用下方函数
router.beforeEach((to,from,next)=>{
    if(to.path==='/home/news'||to.path==='/home/message'){
        if(localStorage.getItem('user')==='fang'){
            next() //放权
        }else{
            alert('您不是主人,没有权限进入/home/message及/home/news页面')
        }
    }else{
        next()
    }
})
export default router

Vue——May(2)_第78张图片

这里是引用
路径用name,path都可以

简化写法

使用meta路由元信息

//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from  '../pages/Detail'
//创建一个路由
const router=new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About
        },
        {
            name:'zhuye',
            path:'/home',
            component:Home,
            //二级路由
            children:[
                {
                    name:'xinwen',
                    //不加 / 符号
                    path:'news',
                    component:News,
                    //不要权限即可进入
                    meta:{isAuth:false}
                },
                {
                    //不要权限即可进入
                    meta:{isAuth:false},
                    name:'xiaoxi',
                    path:'message',
                    component:Message,
                    children:[
                        {
                            //需要权限才可以看到
                            meta:{isAuth:true},
                            name:'xiangqing',
                            path:'detail',
                            component:Detail,
                            //加强版,解构赋值
                            props({query:{id,title}}){
                                return {
                                    id,title
                                }
                            }
                        }
                    ]
                },
            ]
        }
    ]
})
//全局前置路由守卫
//在切换之前,调用下方函数
router.beforeEach((to,from,next)=>{
    if(to.meta.isAuth){
        if(localStorage.getItem('user')==='fang'){
            next() //放权
        }else{
            alert('您没有权限进入详情页')
        }
    }else{
        next()
    }
})
export default router

Vue——May(2)_第79张图片

(2)全局后置afterEach

只有两个参数

给每个路由一个对应的title标题

//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from  '../pages/Detail'
//创建一个路由
const router=new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About,
            meta:{title:'关于'}
        },
        {
            meta:{title:'主页'},
            name:'zhuye',
            path:'/home',
            component:Home,
            //二级路由
            children:[
                {
                    meta:{title:'新闻'},
                    name:'xinwen',
                    //不加 / 符号
                    path:'news',
                    component:News,
                },
                {
                    //不要权限即可进入
                    meta:{
                        title:'消息',
                        isAuth:false
                    },
                    name:'xiaoxi',
                    path:'message',
                    component:Message,
                    children:[
                        {

                            //需要权限才可以看到
                            meta:{
                                isAuth:true,
                                title:'详情'
                            },
                            name:'xiangqing',
                            path:'detail',
                            component:Detail,
                            //加强版,解构赋值
                            props({query:{id,title}}){
                                return {
                                    id,title
                                }
                            }
                        }
                    ]
                },
            ]
        }
    ]
})
//全局前置路由守卫
//在切换之前,调用下方函数
router.beforeEach((to,from,next)=>{
    document.title=to.meta.title
    if(to.meta.isAuth){
        if(localStorage.getItem('user')==='fang'){
            next() //放权
        }else{
            alert('您没有权限进入详情页')
        }
    }else{
        next()
    }
})
export default router

Vue——May(2)_第80张图片

使用beforeEach的bug
Vue——May(2)_第81张图片

优化后

//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from  '../pages/Detail'
//创建一个路由
const router=new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About,
            meta:{title:'关于'}
        },
        {
            meta:{title:'主页'},
            name:'zhuye',
            path:'/home',
            component:Home,
            //二级路由
            children:[
                {
                    meta:{title:'新闻'},
                    name:'xinwen',
                    //不加 / 符号
                    path:'news',
                    component:News,
                },
                {
                    //不要权限即可进入
                    meta:{
                        title:'消息',
                        isAuth:false
                    },
                    name:'xiaoxi',
                    path:'message',
                    component:Message,
                    children:[
                        {

                            //需要权限才可以看到
                            meta:{
                                isAuth:true,
                                title:'详情'
                            },
                            name:'xiangqing',
                            path:'detail',
                            component:Detail,
                            //加强版,解构赋值
                            props({query:{id,title}}){
                                return {
                                    id,title
                                }
                            }
                        }
                    ]
                },
            ]
        }
    ]
})
//全局前置路由守卫
//在切换之前,调用下方函数
router.beforeEach((to,from,next)=>{
    if(to.meta.isAuth){
        //判断是否需要鉴权
        if(localStorage.getItem('user')==='fang'){
            next() //放权
        }else{
            alert('您没有权限进入详情页')
        }
    }else{
        next()
    }
})
router.afterEach((to,from)=>{
    document.title=to.meta.title||'route'
})
export default router

(3)独享守卫beforeEnter

只对新闻做限制

//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from '../pages/Detail'
//创建一个路由
const router = new VueRouter({
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: About,
            meta: { title: '关于' }
        },
        {
            meta: { title: '主页' },
            name: 'zhuye',
            path: '/home',
            component: Home,
            //二级路由
            children: [
                {
                    //需要权限才可以看到
                    meta: {
                        isAuth: true,
                        title: '新闻',
                    },
                    name: 'xinwen',
                    path: 'news',
                    component: News,
                    beforeEnter: (to, from, next) => {
                        if (to.meta.isAuth) {
                            //判断是否需要鉴权
                            if (localStorage.getItem('user') === 'fang') {
                                next() //放权
                            } else {
                                alert('您没有权限进入详情页')
                            }
                        } else {
                            next()
                        }
                    }
                },
                {
                    //不要权限即可进入
                    meta: { title: '消息'},
                    name: 'xiaoxi',
                    path: 'message',
                    component: Message,
                    children: [
                        {
                            meta: {title: '详情'},
                            name: 'xiangqing',
                            path: 'detail',
                            component: Detail,
                            //加强版,解构赋值
                            props({ query: { id, title } }) {
                                return {
                                    id, title
                                }
                            }
                        }
                    ]
                },
            ]
        }
    ]
})
router.afterEach((to, from) => {
    document.title=to.meta.title||'route'
})
export default router

Vue——May(2)_第82张图片

(4)组件内守卫

没有前置后置,==只有 通过路由规则(也就是通过路由组件。而非一般组件)==进入、离开该组件

// route\src\pages\About.vue

<template>
  <div>我是About的内容</div>
</template>

<script>
export default {
  name: "About",
  beforeRouteEnter(to, from, next) {
    if (to.meta.isAuth) {
      //判断是否需要鉴权
      if (localStorage.getItem("user") === "fang") {
        next(); //放权
      } else {
        alert("您没有权限进入about页");
      }
    } else {
      next();
    }
  },
  beforeRouteLeave(to, from, next) {
    next();
  },
};
</script>

<style>
</style>

Vue——May(2)_第83张图片

// route\src\router\index.js

//该文件专门用于创建整个应用的路由
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from '../pages/Detail'
//创建一个路由
const router = new VueRouter({
    routes: [
        {
            name: 'guanyu',
            path: '/about',
            component: About,
            meta: 
            { 
                title: '关于',
                isAuth: true,
            }
        },
        {
            meta: { title: '主页' },
            name: 'zhuye',
            path: '/home',
            component: Home,
            //二级路由
            children: [
                {
                    meta: {title: '新闻',},
                    name: 'xinwen',
                    path: 'news',
                    component: News,
                },
                {
                    meta: { title: '消息'},
                    name: 'xiaoxi',
                    path: 'message',
                    component: Message,
                    children: [
                        {
                            meta: {title: '详情'},
                            name: 'xiangqing',
                            path: 'detail',
                            component: Detail,
                            //加强版,解构赋值
                            props({ query: { id, title } }) {
                                return {
                                    id, title
                                }
                            }
                        }
                    ]
                },
            ]
        }
    ]
})
router.afterEach((to, from) => {
    document.title=to.meta.title||'route'
})

export default router

离开时

  beforeRouteLeave(to, from, next) {
    //不写以下代码则无法出去此页面
    // next();
  },

3.13 hash与history模式

Vue——May(2)_第84张图片

hash部分(#内的部分)不会作为路径传给服务器
在这里插入图片描述

默认为hash

const router = new VueRouter({
    mode:'hash',
  1. hash的兼容性好
  2. 上线问题

Vue——May(2)_第85张图片

打包后的必须要放到服务器上部署

(1)node express搭建微型服务器

  1. 新建一个文件,把它变成合法的包Vue——May(2)_第86张图片

2.Vue——May(2)_第87张图片

3
Vue——May(2)_第88张图片

(2)创建server.js

//  route_node\server.js

//引入express
const express = require('express')
//创建app服务实例对象
const app = express()
//配置后端路由
app.get('/person', (request, response) => {
    //给客户端返回消息
    response.send({
        name: 'fang',
        age: 18
    })
})
//端口监听
app.listen(5005, (err) => {
    if (!err)
        console.log('服务器启动了')
})

(3)启动服务器

Vue——May(2)_第89张图片

成功访问Vue——May(2)_第90张图片

(4)创建static文件

把刚刚npm run build的文件,打包放入route_node\static

Vue——May(2)_第91张图片

route_node\static加上下行代码

app.use(express.static(__dirname+'/static'))

(5)重新启动node

Vue——May(2)_第92张图片

Vue——May(2)_第93张图片

(6)细节

在这里插入图片描述

因为是hash模式,所以后面的不会带给服务器,服务器指定的是/person路径

用history模式也可以,
在刷新完,出现404问题后
需要后端进行相关处理

  1. 在node.js利用库,(根据正则相关)在这里插入图片描述
    Vue——May(2)_第94张图片

四、element-ui

4.1 完整引入

Vue——May(2)_第95张图片

(1)安装

安装包
Vue——May(2)_第96张图片

(2)引入

// elementt\src\main.js

import Vue from 'vue'
//引入element-ui组件库
import ElementUI from 'element-ui';
//引入ElentUI全部样式
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'
//应用ElementUI,且是全局注册
Vue.use(ElementUI);
Vue.config.productionTip = false

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

(3)使用

<template>
  <div>
    <button>原生的按钮</button>

    <hr />
    <h3>引入的按钮</h3>
    <el-row>
      <el-button plain>朴素按钮</el-button>
      <el-button type="primary" plain>主要按钮</el-button>
      <el-button type="success" plain>成功按钮</el-button>
      <el-button type="info" plain>信息按钮</el-button>
      <el-button type="warning" plain>警告按钮</el-button>
      <el-button type="danger" plain>危险按钮</el-button>
    </el-row>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

Vue——May(2)_第97张图片
Vue——May(2)_第98张图片

官方解释
Vue——May(2)_第99张图片

4.2 按需引入

Vue——May(2)_第100张图片


//  elementt\babel.config.js
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset',
    //两种写法都可以
    // ["es2015", { "modules": false }]
    ["@babel/preset-env", { "modules": false }]
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

报错
在这里插入图片描述
elementt\src\App.vue中加入import Vue from "vue";

//  elementt\src\App.vue
<template>
  <div>
    <el-row>
      <el-button icon="el-icon-search" circle></el-button>
      <el-button type="primary" icon="el-icon-edit" circle></el-button>
      <el-button type="success" icon="el-icon-check" circle></el-button>
      <el-button type="info" icon="el-icon-message" circle></el-button>
      <el-button type="warning" icon="el-icon-star-off" circle></el-button>
      <el-button type="danger" icon="el-icon-delete" circle></el-button>
    </el-row>
  </div>
</template>

<script>
import Vue from "vue";
import { Button, Row } from "element-ui";
Vue.component("el-button", Button);
Vue.component("el-row", Row);
export default {
  name: "App",
};
</script>

Vue——May(2)_第101张图片

五、vue3

Vue——May(2)_第102张图片
Vue——May(2)_第103张图片

清空之前的脚手架自定义配置Vue——May(2)_第104张图片
Vue——May(2)_第105张图片
Vue——May(2)_第106张图片

5.1 创建Vue3.0工程

查看vue版本
Vue——May(2)_第107张图片

(1)用vue-cil创建

Vue——May(2)_第108张图片

(2)用Vite创建

Vue——May(2)_第109张图片
Vue——May(2)_第110张图片

Vue——May(2)_第111张图片
Vue——May(2)_第112张图片

Vue——May(2)_第113张图片
Vue——May(2)_第114张图片

5.2 分析工程结构

Vue——May(2)_第115张图片

相比于脚手架,不需要new了

Vue——May(2)_第116张图片

记得添加vue.config.js文件,并加上lintOnSave:false

可以没有根标签Vue——May(2)_第117张图片

* 开发者工具

Vue——May(2)_第118张图片

5.3 常用组合式API

5.4 setup

(1)返回值

Vue——May(2)_第119张图片
Vue——May(2)_第120张图片

返回值是对象

// test\src\App.vue

<template>
  <div>
    <h2>一个人的信息</h2>
    姓名:{{ name }}
    <button @click="sayHello">说话</button>
  </div>
</template>

<script>
export default {
  name: "App",
  setup() {
    let name = "lucy";
    function sayHello() {
      alert(`你好,${name}`);
    }
    return {
      name,
      sayHello,
    };
  },
};
</script>

Vue——May(2)_第121张图片

返回值是渲染函数
vue2中的渲染函数Vue——May(2)_第122张图片

<template>
  <div>
    <h2>一个人的信息</h2>
    姓名:{{ name }}
    <button @click="sayHello">说话</button>
  </div>
</template>

<script>
import { h } from 'vue';

export default {
  name: "App",
  setup() {
    let name = "lucy";
    function sayHello() {
      alert(`你好,${name}`);
    }
    return ()=>{
      //渲染函数有返回值
      //返回h的调用结果
      return h('h2',"学校")
    }
  },
};
</script>
简写

return ()=>h('h2',"学校")

Vue——May(2)_第123张图片


(2)注意点

Vue——May(2)_第124张图片

向vue2兼容,但不推荐混用

如有重名,setup优先

setup收到的参数(props,context)

(3)props

props
传了没接收会有警告(但是在vue2中不会有警告)

(4)context

contextVue——May(2)_第125张图片

//  test\src\App.vue

<template>
  <Demo @fang="showInfo"></Demo>
</template>

<script>
import Demo from "./components/Demo.vue";
export default {
  name: "App",
  setup() {
    function showInfo() {
      alert("hello");
    }
    return {
      showInfo,
    };
  },
  components: { Demo },
};
</script>
//  test\src\components\Demo.vue

<template>
  <button @click="test">触发Demo组件上的fang事件</button>
</template>

<script>
export default {
  name: "Demo",
  emits:['fang'],
  setup(a,context) {
    function test() {
        console.log('aaa')
      context.emit('fang')
      
    }
    return {
      test,
    };
  },
};
</script>

Vue——May(2)_第126张图片

(5)slot

<template>
    <div>
        <button @click="test">触发Demo组件上的fang事件</button>
        <br>
        <slot name='hai'></slot>
    </div>
  
  
</template>

<script>
export default {
  name: "Demo",
  emits:['fang'],
  setup(a,context) {
    function test() {
        console.log(context.slots)
      context.emit('fang')
      
    }
    return {
      test,
    };
  },
};
</script>
<template>
  <Demo @fang="showInfo">
    <template v-slot:hai> 
    <span>haihaihai</span>
  </template>
</Demo>
  
</template>

<script>
import Demo from "./components/Demo.vue";
export default {
  name: "App",
  setup() {
    function showInfo() {
      alert("hello");
    }
    return {
      showInfo,
    };
  },
  components: { Demo },
};
</script>

Vue——May(2)_第127张图片

5.5 ref函数

Vue——May(2)_第128张图片
Vue——May(2)_第129张图片

(1)接受基本数据类型

Vue——May(2)_第130张图片
在这里插入图片描述

Vue——May(2)_第131张图片

<template>
  <div>
    <h2>一个人的信息</h2>
    姓名:{{ name }}
    <button @click="changeInfo">改变信息</button>
  </div>
</template>

<script>
import {ref} from 'vue'
export default {
  name: "App",
  setup() {
    //普通变量
    // let name = "lucy";
    //响应式——通过ref
    let name=ref('lucy')
    function changeInfo(){
      name.value='jack'
    }
    return {
      name,
      changeInfo
    };
  },
};
</script>

Vue——May(2)_第132张图片

在模板中不需要.value,解析时,会自动添加

(2)接受对象类型

对象类型数据,底层用的都是proxy数据处理
在这里插入图片描述

<template>
  <div>
    <h2>一个人的信息<button @click="changeInfo">改变信息</button></h2>
    
    姓名:{{ name }}
    <br>
    工作职位:{{ job.type }}
    <br>
    工作薪水:{{ job.salary }}
  </div>
</template>

<script>
import {ref} from 'vue'
export default {
  name: "App",
  setup() {
    //普通变量
    // let name = "lucy";
    //响应式——通过ref
    let name=ref('lucy')
    let job=ref({
      type:'前端',
      salary:'10k'
    })
    function changeInfo(){
      name.value='jack',
      // job.type.value='后端'//错误写法
      job.value.type='后端'
      job.value.salary='11k'
    }
    return {
      name,
      job,
      changeInfo
    };
  },
};
</script>

Vue——May(2)_第133张图片

理想上Vue——May(2)_第134张图片
实际上
在这里插入图片描述
在这里插入图片描述

5.6 reactive函数

Vue——May(2)_第135张图片

(1)处理对象

要先引入

<template>
  <div>
    <h2>一个人的信息<button @click="changeInfo">改变信息</button></h2>
    
    姓名:{{ name }}
    <br>
    工作职位:{{ job.type }}
    <br>
    工作薪水:{{ job.salary }}
  </div>
</template>

<script>
import {ref,reactive} from 'vue'
export default {
  name: "App",
  setup() {
    let name=ref('lucy')
    let job=reactive({
      type:'前端',
      salary:'10k'
    })
    function changeInfo(){
      name.value='jack',
      //使用reactive得到了简化
      job.type='后端'
      job.salary='11k'
    }
    return {
      name,
      job,
      changeInfo
    };
  },
};
</script>

监测深层对象

<template>
  <div>
    <h2>c的值:{{ a.b.c }}</h2>
    
  </div>
</template>

<script>
import {ref,reactive} from 'vue'
export default {
  name: "App",
  setup() {
    let name=ref('lucy')
    let a=reactive({
      b:{
        c:'找到了'
      },
      d:'1'
    })
    
    return {
      name,
      a,
    };
  },
};
</script>

Vue——May(2)_第136张图片

(2)处理数组

<template>
  <div>
    <h2>{{ number }}</h2>
    <button @click="change">更改数组</button>
  </div>
</template>

<script>
import { ref, reactive } from "vue";
export default {
  name: "App",
  setup() {
    let number = reactive([1, 2, 3]);
    function change() {
      number[0] = 5;
    }
    return {
      number,
      change,
    };
  },
};
</script>

Vue——May(2)_第137张图片

整合写法
Vue——May(2)_第138张图片

(3)对比reactive与ref

Vue——May(2)_第139张图片

5.7 响应式原理

Vue——May(2)_第140张图片

(1)vue2的

Vue——May(2)_第141张图片

劫持到 添加的数据
Vue——May(2)_第142张图片

修改数组数据
Vue——May(2)_第143张图片
在这里插入图片描述

(2)vue3的

测试新增与删除属性

<template>
  <div>
    <h2>姓名:{{person.name}}</h2>
    <h2>年龄:{{person.age}}</h2>
    <h2 v-show="person.sex">性别:{{person.sex}}</h2>
    <button @click="add">新增信息</button>
    <button @click="del">删除信息</button>
  </div>
</template>

<script>
import {  reactive } from "vue";
export default {
  name: "App",
  setup() {
    let person=reactive({
      name:'lucy',
      age:18,
    })
    function add(){
      person.sex='女'
    }
    function del(){
      // person.sex=''
      delete person.sex
    }
    return {
      person,
      add,
      del
    };
  },
};
</script>

Vue——May(2)_第144张图片

在vue2中用Object.defineProperty模拟实现响应式Vue——May(2)_第145张图片
在vue3中实现响应式

Vue——May(2)_第146张图片
Vue——May(2)_第147张图片

手动添加折叠区域
Vue——May(2)_第148张图片
Vue——May(2)_第149张图片

(3)reflect

Vue——May(2)_第150张图片
使用这个封装框架时,不需要捕获错误看到错误
Vue——May(2)_第151张图片

5.8 computed

Vue——May(2)_第152张图片

方法一(vue2)

<template>
  <div>
    <h2>个人信息</h2>
    姓:<input type="text" v-model="person.surName" />
    <br />
    名:<input type="text" v-model="person.lastName" />
    <br>
    全名:{{ fullName }}
  </div>
</template>
<script>
import { reactive } from "vue";
export default {
  name: "Demo",
  setup() {
    let person = reactive({
      surName: "张",
      lastName: "三",
    });
    return {
      person,
    };
  },
  computed:{
    fullName(){
        return this.person.surName+'-'+this.person.lastName
    }
  }
};
</script>

vue3写法

<template>
  <div>
    <h2>个人信息</h2>
    姓:<input type="text" v-model="person.surName" />
    <br />
    名:<input type="text" v-model="person.lastName" />
    <br />
    全名:{{ fullName }}
  </div>
</template>
<script>
import { reactive, computed } from "vue";
export default {
  name: "Demo",

  setup() {
    let person = reactive({
      surName: "张",
      lastName: "三",
    });
    let fullName = computed(() => {
      return person.surName + "-" + person.lastName;
    });
    return {
      person,
      fullName,
    };
  },
};
</script>

简写

<template>
  <div>
    <h2>个人信息</h2>
    姓:<input type="text" v-model="person.surName" />
    <br />
    名:<input type="text" v-model="person.lastName" />
    <br />
    全名:{{ person.fullName }}
  </div>
</template>
<script>
import { reactive, computed } from "vue";
export default {
  name: "Demo",

  setup() {
    let person = reactive({
      surName: "张",
      lastName: "三",
    });
    // let fullName = computed(() => {
    //   return person.surName + "-" + person.lastName;
    // });
    person.fullName=computed(() => {
      return person.surName + "-" + person.lastName;
    });
    return {
      person,
    };
  },
};
</script>

可修改的computed(完整写法)

<template>
  <div>
    <h2>个人信息</h2>
    姓:<input type="text" v-model="person.surName" />
    <br />
    名:<input type="text" v-model="person.lastName" />
    <br />
    全名:<input type="text" v-model="person.fullName">
  </div>
</template>
<script>
import { reactive, computed } from "vue";
export default {
  name: "Demo",

  setup() {
    let person = reactive({
      surName: "张",
      lastName: "三",
    });
    
    person.fullName=computed({
        get(){
            return person.surName + "-" + person.lastName;
        },
        set(value){
            const nameArr=value.split('-')
            person.surName=nameArr[0]
            person.lastName=nameArr[1]
        }
    });
    return {
      person,
    };
  },
};
</script>

Vue——May(2)_第153张图片

5.9 watch

Vue——May(2)_第154张图片

(1)监视ref定义的响应式数据

监视一个

<template>
  <div>
    <h3>当前求和为:{{ sum }}</h3>
    <button @click="sum++">点我+1</button>
  </div>
</template>
<script>
import { reactive, watch,ref } from "vue";
export default {
  name: "Demo",

  setup() {
    let sum=ref(0)
    watch(sum,(newVal,oldVal)=>{
        console.log('sum的值改变',newVal,oldVal)
    })
    return {
      sum
    };
  },
};
</script>

Vue——May(2)_第155张图片

监视多个

<template>
  <div>
    <h3>当前求和为:{{ sum }}</h3>
    <button @click="sum++">点我+1</button>
    <hr>
  <h3>当前的信息为:{{ msg }}</h3>
  <button @click="msg+='!'">修改信息</button>
  </div>
  
</template>
<script>
import { reactive, watch,ref } from "vue";
export default {
  name: "Demo",

  setup() {
    let sum=ref(0)
    let msg=ref('你好')
    watch([sum,msg],(newVal,oldVal)=>{
        console.log('sum的值改变',newVal,oldVal)
    },{immediate:true})
    return {
      sum,
      msg
    };
  },
};
</script>

Vue——May(2)_第156张图片

(2)监视reactive定义的

<template>
  <div>
    <h3>当前求和为:{{ sum }}</h3>
    <button @click="sum++">点我+1</button>
    <hr>
  <h3>当前的信息为:{{ msg }}</h3>
  <button @click="msg+='!'">修改信息</button>
  <hr>
  <h3>人员信息</h3>
  <button @click="updateName">修改姓名</button>
  <button @click="person.age++">增长年龄</button>
  <h4>姓名:{{ person.name }}</h4>
  <h4>年龄:{{ person.age }}</h4>
  </div>
  
</template>
<script>
import { reactive, watch,ref } from "vue";
export default {
  name: "Demo",

  setup() {
    let sum=ref(0)
    let msg=ref('你好')
    let person=reactive({
        name:'lucy',
        age:18
    })
    function updateName(){
        person.name='jack'
    }
    watch(person,(newVal,oldVal)=>{
        console.log('person的值改变',newVal,oldVal)
    },{immediate:true})
    return {
      sum,
      msg,
      person,
      updateName,
    };
  },
};
</script>

Vue——May(2)_第157张图片

监测oldvalue有问题,都显示为newValue

多级嵌套的对象,不用加深度监测属性,也可以监测的到在这里插入图片描述

监视某具体属性,新旧值都显示
Vue——May(2)_第158张图片

Vue——May(2)_第159张图片

这个东西依然是个对象这里是引用

(3)是否加。value

不用加.valueVue——May(2)_第160张图片
在这里插入图片描述

可以加。value

Vue——May(2)_第161张图片

(4)watchEffect

Vue——May(2)_第162张图片

<template>
  <div>
    <h3>当前求和为:{{ sum }}</h3>
    <button @click="sum++">点我+1</button>
    <hr>
  <h3>当前的信息为:{{ msg }}</h3>
  <button @click="msg+='!'">修改信息</button>
  <hr>
  <h3>人员信息</h3>
  <button @click="updateName">修改姓名</button>
  <button @click="person.job.salary++">涨薪</button>
  <h4>姓名:{{ person.name }}</h4>
  <h4>薪资:{{ person.job.salary }}</h4>
  </div>
  
</template>
<script>
import { reactive, watch,ref ,watchEffect} from "vue";
export default {
  name: "Demo",

  setup() {
    let sum=ref(0)
    let msg=ref('你好')
    let person=reactive({
        name:'lucy',
        job:{
            salary:20
        }
    })
    function updateName(){
        person.name='jack'
    }
    watchEffect(()=>{
        //用谁监测谁
        const x1=sum.value
        const x2=person.job.salary
        console.log('a')
    })
    
    return {
      sum,
      msg,
      person,
      updateName,
    };
  },
};
</script>

Vue——May(2)_第163张图片

5.10 生命周期

普通写法
Vue——May(2)_第164张图片

组合式API写法
Vue——May(2)_第165张图片

两个一起用的顺序
Vue——May(2)_第166张图片
组合式 执行时机更快

5.11自定义hook函数

正常写法

Vue——May(2)_第167张图片

<template>
  <h2>当前鼠标的坐标为:x:{{ point.x }},y:{{ point.y }}</h2>
</template>
<script>
import {reactive,onMounted} from "vue";
export default {
  name: "Demo",
  setup(){
    let point=reactive({
      x:0,
      y:0
    })
    onMounted(()=>{
      window.addEventListener('click',(e)=>{
        point.x=e.pageX
        point.y=e.pageY
      })
    })
    return {
      reactive,onMounted,point
    }
  }
};
</script>

Vue——May(2)_第168张图片
Vue——May(2)_第169张图片

可拆卸事件的版本

<template>
  <h2>当前鼠标的坐标为:x:{{ point.x }},y:{{ point.y }}</h2>
</template>
<script>
import { reactive, onMounted, onBeforeUnmount } from "vue";
export default {
  name: "Demo",
  setup() {
    let point = reactive({
      x: 0,
      y: 0,
    });
    function savePoint(e) {
      point.x = e.pageX;
      point.y = e.pageY;
    }

    onMounted(() => {
      window.addEventListener("click", savePoint);
    });
    onBeforeUnmount(() => {
      window.removeEventListener("click", savePoint);
    });
    return {
      reactive,
      onMounted,
      point,
      savePoint,
    };
  },
};
</script>

* 应用

与别人共享这套方法

// test\src\hooks\Demo.vue
<template>
  <h2>当前鼠标的坐标为:x:{{ point.x }},y:{{ point.y }}</h2>
</template>
<script>
import usePoint from "../hooks/usePoint";
export default {
  name: "Demo",
  setup() {
    let point=usePoint();
    return {
      point,
    };
  },
};
</script>
//  test\src\hooks\usePoint.js
import { reactive, onMounted, onBeforeUnmount } from "vue";
export default function () {
    let point = reactive({
        x: 0,
        y: 0,
    });
    function savePoint(e) {
        point.x = e.pageX;
        point.y = e.pageY;
    }
    onMounted(() => {
        window.addEventListener("click", savePoint);
    });
    onBeforeUnmount(() => {
        window.removeEventListener("click", savePoint);
    });
    return point
}

Vue——May(2)_第170张图片

5.12 toRef与toRefs

Vue——May(2)_第171张图片

Vue——May(2)_第172张图片
name1是单纯字符串,name2是响应式数据Vue——May(2)_第173张图片

(1) toRef

<template>
  
  <div>
    {{ name }}
    {{ age }}
    {{ all }}
  </div>
</template>

<script>
import { toRef,reactive } from 'vue'
export default {
name:'Demo',
setup(){
  let person=reactive({
    name:'lucy',
    age:18,
    job:{
      salary:{
        all:3
      }
    }
  })
  
  const name1=person.name
  console.log(name1)
  const name2=toRef(person.name)
  console.log(name2)
  return {
    //告诉person,拿它的name
    //以下同理
    //好处,简化书写
    name:toRef(person,'name'),
    age:toRef(person,'age'),
    all:toRef(person.job.salary,'all')
  }
}
}
</script>

Vue——May(2)_第174张图片

(2) toRefs

<template>
  
  <div>
    {{ name }}
    {{ age }}
    {{ job.salary.all }}
  </div>
</template>

<script>
import { toRefs,reactive } from 'vue'
export default {
name:'Demo',
setup(){
  let person=reactive({
    name:'lucy',
    age:18,
    job:{
      salary:{
        all:3
      }
    }
  })
  return {
    person,
    ...toRefs(person)
  }
}
}
</script>

5.13 shaowReactive与shallowRef

Vue——May(2)_第175张图片

(1)shaowReactive

薪资+ 没有反应

<template>
  
  <div>
    姓名:{{ name }}
    <br>
    年龄:{{ age }}
    <br>
    薪资:
    {{ job.salary.all }}
    <br>
    <button @click="name+='!'">改变名字</button>
    <button @click="age++">年龄+</button>
    <button @click="job.salary.all++">薪资+</button>
  </div>
</template>

<script>
import { toRefs,shallowReactive } from 'vue'
export default {
name:'Demo',
setup(){
  let person=shallowReactive({
    name:'lucy',
    age:18,
    job:{
      salary:{
        all:3
      }
    }
  })
  return {
    person,
    ...toRefs(person)
  }
}
}
</script>

Vue——May(2)_第176张图片

(2)shallowRef

<template>
  <div>
    当前x.y的值是:{{ x.y }}
    <br />
    <button @click="x = { y: 888 }">点我替换x</button>
    <button @click="x.y++">点我x.y++</button>
  </div>
</template>

<script>
import { toRefs, shallowRef } from "vue";
export default {
  name: "Demo",
  setup() {
    let x = shallowRef({
      y: 0,
    });
    return {
      x,
    };
  },
};
</script>

Vue——May(2)_第177张图片

5.14 readonly与shallowReadonly(数据不改变)

Vue——May(2)_第178张图片

(1)readonly

<template>
  
  <div>
    姓名:{{ name }}
    <br>
    年龄:{{ age }}
    <br>
    薪资:
    {{ job.salary.all }}
    <br>
    <button @click="name+='!'">改变名字</button>
    <button @click="age++">年龄+</button>
    <button @click="job.salary.all++">薪资+</button>
  </div>
</template>

<script>
import { readonly ,toRefs,reactive} from 'vue'
export default {
name:'Demo',
setup(){
  let person=reactive({
    name:'lucy',
    age:18,
    job:{
      salary:{
        all:3
      }
    }
  })
  person=readonly(person)
  return {
    person,
    ...toRefs(person),
  }
}
}
</script>

Vue——May(2)_第179张图片

(2) shallowReadonly

<template>
  
  <div>
    姓名:{{ name }}
    <br>
    年龄:{{ age }}
    <br>
    薪资:
    {{ job.salary.all }}
    <br>
    <button @click="name+='!'">改变名字</button>
    <button @click="age++">年龄+</button>
    <button @click="sal">薪资+</button>
  </div>
</template>

<script>
import { shallowReadonly ,toRefs,reactive} from 'vue'
export default {
name:'Demo',
setup(){
  let person=reactive({
    name:'lucy',
    age:18,
    job:{
      salary:{
        all:3
      }
    }
  })
  function sal(){
    person.job.salary.all++
    console.log('更新后的salary值为:',person.job.salary.all)
  }
  person=shallowReadonly(person)
  
  return {
    person,
    ...toRefs(person),
    sal
  }
}
}
</script>

Vue——May(2)_第180张图片

5.15 toRaw与markRaw

??

没有效果

<template>
  <div>
    姓名:{{ name }}
    <br />
    年龄:{{ age }}
    <br />
    薪资:
    {{ job.salary.all }}
    <br />
    <button @click="name += '!'">改变名字</button>
    <button @click="age++">年龄+</button>
    <button @click="job.salary.all++">薪资+</button>
    <br />
    <button @click="rawShow">输出最原始的person</button>
  </div>
</template>

<script>
import { toRefs, reactive, toRaw } from "vue";
export default {
  name: "Demo",
  setup() {
    let person = reactive({
      name: "lucy",
      age: 18,
      job: {
        salary: {
          all: 3,
        },
      },
    });
    function rawShow() {
      let r = toRaw(person);
      console.log(r);
    }
    return {
      person,
      ...toRefs(person),
      rawShow,
    };
  },
};
</script>

Vue——May(2)_第181张图片

markRaw(数据改变,但不是响应式)

<template>
  <div>
    姓名:{{ name }}
    <br />
    年龄:{{ age }}
    <br />
    薪资:
    {{ job.salary.all }}
    <br />
    <button @click="name += '!'">改变名字</button>
    <button @click="age++">年龄+</button>
    <button @click="job.salary.all++">薪资+</button>
    
  </div>
</template>

<script>
import { toRefs, reactive, markRaw } from "vue";
export default {
  name: "Demo",
  setup() {
    let person = reactive({
      name: "lucy",
      age: 18,
      job: {
        salary: {
          all: 3,
        },
      },
    });
    person.age=markRaw(age)
    return {
      person,
    
      ...toRefs(person),
    };
  },
};
</script>

5.16 customRef

做延时显示效果

<template>
  <div>
    <input type="text" v-model="keyWord" />
    <h2>{{ keyWord }}</h2>
  </div>
</template>

<script>
import { customRef } from "vue";
export default {
  name: "Demo",
  setup() {
    //初始化传入的值,是不变的
    function myref(val) {
      //自定义一个ref
      // const x=customRef()
      // return x
      return customRef((track, trigger) => {
        return {
          get() {
            track();
            return val;
          },
          set(newVal) {
            val = newVal;
            setTimeout(() => {
              trigger(); //通知Vue去重新解析模板
            }, 1000);
          },
        };
      });
    }
    // let keyWord=ref('hello'+123)//使用vue提供的ref(精装版)
    //使用程序员自定义的ref
    let keyWord = myref("hello");
    return {
      keyWord,
    };
  },
};
</script>

Vue——May(2)_第182张图片

5.17 _provide与inject

Vue——May(2)_第183张图片

Vue——May(2)_第184张图片

任何后代都可以用inject(注入)

//  test\src\App.vue

<template >
  <div class="app">
    App
    <br />
    <h3>{{ name }}--{{ age }}
    </h3>
    
    <Child />
  </div>
</template>

<script>
import { reactive, toRefs ,provide} from "vue";
import Child from "./components/Child.vue";
export default {
  name: "App",
  setup() {
    let per = reactive({ name: "lucy", age: 18 });
    provide("fang", per);
    return { ...toRefs(per) };
  },
  components: {
    Child,
  },
};
</script>
<style>
.app {
  background-color: rgb(246, 113, 171);
  padding: 10px;
}
</style>
//  test\src\components\Child.vue

<template>
  <div class="child">
    child
    <GrandChild />
  </div>
</template>

<script>
import GrandChild from "./GrandChild.vue";
export default {
  name: "Child",
  components: { GrandChild },
};
</script>

<style>
.child {
  background-color: rgb(153, 153, 220);
  padding: 10px;
}
</style>
//   test\src\components\GrandChild.vue

<template>
  <div class="gchild">GrandChild
  <h3>{{ hai.name }}--{{ hai.age }}</h3>
</div>
</template>

<script>
import { inject } from "vue";
export default {
  name: "GrandChild",
  setup() {
    let hai = inject("fang");
    return { hai };
  },
};
</script>

<style>
.gchild {
  background-color: rgb(237, 192, 227);
  padding: 10px;
}
</style>

Vue——May(2)_第185张图片

5.18 响应数据的判断

Vue——May(2)_第186张图片

<template></template>

<script>
import {
  isProxy,
  isReactive,
  isReadonly,
  isRef,
  reactive,
  readonly,
  ref,
  toRefs,
} from "vue";
export default {
  name: "App",
  setup() {
    let per1 = reactive({ name: "lucy", age: 18 });
    let per2 = readonly(per1);
    let sum = ref(0);

    console.log(isRef(sum));
    console.log(isReactive(per2));
    console.log(isReadonly(per2));
    console.log(isReadonly(sum));
    console.log(isProxy(per2));
    console.log(isProxy(sum));
    return { ...toRefs(per1) };
  },
};
</script>

<style>
</style>

在这里插入图片描述

5.19 碎片组件组件Fragment

碎片标签
Vue——May(2)_第187张图片

5.20 传送组件teleport

传送

//  test\src\components\Dialog.vue

<template>
  <div>
    <button @click="show = true">点我弹窗</button>
    <!-- 要传送的内容 -->
    <!-- to到html结构里面 -->
    <teleport to="body">
      <!-- 遮罩层 -->
      <div class="mask" v-if="show">
        <div class="dia">
          <h3>我是一个弹窗</h3>
          @click="show=true"
          <div>内容</div>
          <div>内容</div>
          <button @click="show = false">关闭弹窗</button>
        </div>
      </div>
    </teleport>
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  name: "Dialog",
  setup() {
    let show = ref(false);
    return {
      show,
    };
  },
};
</script>

<style>
.mask {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.5);
}
.dia {
  position: absolute;
  /* 参考父元素的50% */
  top: 50%;
  left: 50%;
  /* 参考自身的50% */
  transform: translate(-50%, -50%);
  border-radius: 10px;
  widows: 200px;
  padding: 10px;
  height: 150px;
  background-color: rgb(192, 111, 122);
}
</style>

Vue——May(2)_第188张图片

5.21 异步组件Suspense

Vue——May(2)_第189张图片

(1) 异步引入

<template>
  <div class="app">APP
    <Child/>
  </div>
</template>

<script>
//静态引入
// import Child from './components/Child.vue'
//动态(异步)引入
import {defineAsyncComponent} from "vue";
//此处的import是个函数
const Child=defineAsyncComponent(()=>import('./components/Child.vue'))
export default {
  name: "App",
  components:{Child}
};
</script>

<style>
.app{
  background-color: rgb(238, 144, 230);
  padding: 10px;
}
</style>

Vue——May(2)_第190张图片

(2)Suspense

<template>
  <div class="app">
    APP
    <Suspense>
      <!-- default放置应该展现的组件 -->
      <template v-slot:default>
        <Child />
      </template>
      <!-- 放置应急计划(退路) -->
      <template v-slot:fallback>
        <h3>加载中......</h3>
      </template>
    </Suspense>
  </div>
</template>

<script>
import { defineAsyncComponent } from "vue";
//此处的import是个函数
const Child = defineAsyncComponent(() => import("./components/Child.vue"));
export default {
  name: "App",
  components: { Child },
};
</script>

<style>
.app {
  background-color: rgb(238, 144, 230);
  padding: 10px;
}
</style>

Vue——May(2)_第191张图片

(3)利用promise实现异步

可以控制几秒到达

//  test\src\components\Child.vue

<template>
  <div class="child">
    {{ sum }}
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  name: "Child",
  setup() {
    let sum = ref(123);
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ sum });
        //随意设置等待几秒
      }, 500);
    });
  },
};
</script>

<style>
.child {
  background-color: rgb(153, 153, 220);
  padding: 10px;
}
</style>
//  app组件不变
<template>
  <div class="app">
    APP
    <Suspense>
      <!-- default放置应该展现的组件 -->
      <template v-slot:default>
        <Child />
      </template>
      <!-- 放置应急计划(退路) -->
      <template v-slot:fallback>
        <h3>加载中......</h3>
      </template>
    </Suspense>
  </div>
</template>

<script>
import { defineAsyncComponent } from "vue";
//此处的import是个函数
const Child = defineAsyncComponent(() => import("./components/Child.vue"));
export default {
  name: "App",
  components: { Child },
};
</script>

<style>
.app {
  background-color: rgb(238, 144, 230);
  padding: 10px;
}
</style>

(4)利用async

//  test\src\components\Child.vue
<template>
  <div class="child">
    {{ sum }}
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  name: "Child",
  async setup() {
    let sum = ref(123);
    let p= new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ sum });
      }, 500);
    });
    return await p
  },
};
</script>

<style>
.child {
  background-color: rgb(153, 153, 220);
  padding: 10px;
}
</style>

5.22 Vue3其他变化

Vue——May(2)_第192张图片
Vue——May(2)_第193张图片

Vue——May(2)_第194张图片
Vue——May(2)_第195张图片

你可能感兴趣的:(框架,vue.js,javascript,前端)