vue(插槽、vue-router、导航、路由守卫、axios、vuex)

day 01

slot插槽

  • Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 元素作为承载分发内容的出口内容定义在父组件中,子组件可以拿去渲染
匿名插槽
  • template没有slot或者v-slot,匿名操作,直接插入子组件
具名插槽
  • 多个内容进行分发,使用slot=“插槽名”或者 v-slot:插槽名指定插入的插槽名, 使用 name 指定操作的名称
作用域插槽
  • 子组件通过slot组件自定义属性数据传入到父组件的插操内容来使用,父组件使用 slot-scope="传入的数据对象" 或者 v-slot:插槽名=“传入的数据对象”指令的指令值。
  • 父组件:
<template>
  <!-- 数据驱动 -->
  <div id="app">
    <!-- 2.5.xxxx -->
    <v-child :visible="isShow" :list="list">
      <!--匿名插槽 建议插槽内容写在template中 -->
      <template>
        <button @click="submit">确定</button>
      </template>
      <!--具名插槽 2.5.x,slot="插槽名称" -->
      <template slot="footer" >
        <div>
          底部内容
        </div>
      </template>
      <!-- 
        作用域插槽:
        slot-scope="自定义名称"
        自定义名称:包含子组件传入到父组件的数据对象
       -->
      <template slot="box" slot-scope="scoped">
        <span>
          <button @click="edit(scoped.index)">编辑</button>
          <button @click="del(scoped.index)">删除 </button>
        </span>
      </template>
    </v-child>

    <!-- 2.6.xxxx -->
    <v-child :visible="isShow" :list="list">
      <!-- 建议插槽内容写在template中,匿名插槽的内容 -->
      <template>
        <b @click="isShow=false">bbbb</b>
      </template>
      <!--具名插槽: v-slot:插槽名称 -->
      <template v-slot:footer>
        <div>
          底部内容
        </div>
      </template>
      <!--  
        v-slot:插槽名称 = "包含子组件传入到父组件的数据对象"
      -->
      <template v-slot:box="scoped">
        <span>
          <button>编辑</button>
          <button @click="del(scoped.index)">删除</button>
        </span>
      </template>
    </v-child>
  </div>
</template>

<script>
import vChild from "./components/child";
export default {
     
  components: {
     
    vChild
  },
  data(){
     
    return{
     
      isShow:true,
      list:[{
     
        title:"title1"
      },{
     
        title:"title2"
      }]
    }
  },
  methods:{
     
    submit(){
     
      this.isShow=false
    },
    del(i){
     
      this.list.splice(i,1)
    },
    edit(i){
     	
    }	    
  }
};
</script>
  • 子组件:
	<template>
	  <div class="list" v-show="visible">
	    <h2>标题</h2>
	    <!-- 匿名插槽 -->
	    <slot></slot>
	    <div>其他内容</div>
	    <ul>
	      <li v-for="(item,index) in list" :key="index">
	        <span>{
     {
     item.title}}</span>
	        <!-- 以自定义属性形式向父组件传入数据 -->
	        <slot name="box" :index="index" :item="item"></slot>
	      </li>
	    </ul>
	    <!-- name = 具名插槽的名称 -->
	     <slot name="footer"></slot>
	  </div>
	</template>
	<script>
	export default {
     
	  props:{
     
	    visible:Boolean,
	    list:Array
	  }
	};
	</script>

vue-router

  • Vue Router 是 Vue.js 官方的路由管理器,用来构建单页面应用。(多个页面只需要一次请求)
  • 地址变化,js动态生成html内容
  • 使用:
    • 1.创建项目时,第五项选择安装路由
    • 2.创建一个路由组件
    • 3.src/router/index.js 引入路由组件,定义使用
	import Vue from 'vue'
	import Router from 'vue-router'
	
	// 引入路由组件,@->src目录的绝对地址
	import Index from '@/pages/Index'
	import About from "@/pages/about"
	// 安装路由
	Vue.use(Router)
	
	let router = new Router({
     
	  // 定义 路由
	  routes: [
	    // 每一个对象就是一个路由
	    {
     
	      path: '/',//路由路径
	      component: Index,//该路由对应的渲染组件
	    },{
     
	      path:"/about",
	      component:About
	    }
	  ]
	})
	export default router

导航组件

声明式导航
  • router-link组件:导航组件
    • to属性指定导航地址
    • to 属性可以是字符串路径,也可以是对象
    • activeClass:激活对应路由时的类名
    • exact:精确匹配(地址相同才匹配
<ul>
  <li>
    <!-- <router-link to="/">首页</router-link> -->
    <router-link :to="{path:'/'}" activeClass="act" exact>首页</router-link>
  </li>
  <li>
    <router-link to="/about" activeClass="act">about</router-link>
  </li>
</ul>
编程式导航
  • this.$router.push/replace/go
  • push推入访问记录,replace替换访问记录
 	methods:{
     
	    // 使用$router进行编程式导航
	    toIndex(){
     
	      // console.log(this.$router)
	      // 跳转到首页 
	      this.$router.push("/")
	    },
	    toAbout(){
     
	      // push:可以回退回来
	      // this.$router.push("/about");
	
	      // replace:不能回退
	      this.$router.replace("/about");
	      
	      // this.$router.replace({
     path:"/about"});
	    },
	    back(){
     
	      // 前进(参数是正数)或者后退(参数是负数)若干个页面
	      this.$router.go(-1)
	    }
	  }

404页面

  	{
     
      // * 任意其他地址
       path:"*",
       component:NotFound
     }

重定向

 	{
     
      path:"*",
      redirect:"/",// redirect:重定向地址 
    }    

day 02

单页面应用优缺点:

  • 优点:
    1. 分离前后端关注点,前端负责界面显示后端负责数据存储和计算
    2. 通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件
    3. 同一套后端程序代码,不用修改就可以用于多种设备客户端
    4. 用户体验好,快,内容的改变不需要重新加载整个页面
  • 缺点:不支持版本低的浏览器
    1. 不利于SEO优化
    2. 第一次加载首页耗时相对长一些
    3. 导航不可用,如果一定要导航需要自行实现前进、后退,需要程序来实现管理

路由模式

  • hash模式
    1. 采用的是window.onhashchange事件实现。
    2. 可以实现前进 后退 刷新
    3. 比如这个URL:http://www.abc.com/#/hello, hash 的值为#/hello。
      它的特点在于:hash 虽然出现URL中,但不会被包含在HTTP请求中,
      对后端完全没有影响,因此改变hash不会重新加载页面
  • history模式:(不带#,直接访问非首页路由,服务器端没有相应的匹配,需要后端的配合)
    1. 采用的是利用了HTML5 History Interface 中新增pushState() 和replaceState() 方法
    2. 可以前进、后退,但是刷新有可能会出现404的报错
    3. 前端的url必须和实际向后端发起请求的url 一致,如http://www.abc.com/book/id 。
      如果后端缺少对/book/id 的路由处理,将返回404错误。

命名路由

		{
     
	      // 命名路由,给路由取名字
	      path: '/menu',
	      name: "menu",
	      component: Menu
	    }

动态路由(冒号传参)

  • 定义: 后面是动态路由参数的名称
	{
     
      path:"/goods/:goodsid",
      component:Goods
    }
  • 获取动态路由参数
// $route:当前路由对象,包含路由信息(动态路由参数)
let goodsid = this.$route.params.goodsid;

关于$route和$router的区别####

  • $router:全局路由器,一般用来导航
  • $route:当前路由对象,包含路由信息(动态路由参数params)

动态路由组件复用,页面不更新问题

  • 动态路由组件被复用不是销毁重建,生命周期只执行一次mounted,请求一次数据,需要手动刷新页面,生命周期重启,页面才会更新,为解决这个问题,应该对当前路由对象进行监听,当对象发生改变,重新获取一次数据
	export default {
     
	  data() {
     
	    return {
     
	      info: {
     }
	    };
	  },
	  // 动态路由组件被复用(/goods/123->/goods/456),组件被复用,而不是销毁重建
	  mounted() {
     
	    this.getDetail();
	  },
	  //   监听 $router的辩护,重新获取数据
	  watch: {
     
	    $route() {
     
	      this.getDetail();
	    }
	  },
	  methods: {
     
	    getDetail() {
     
	      // 获取动态路由参数
	      // $route:当前路由对象,包含路由信息(动态路由参数)
	      let goodsid = this.$route.params.goodsid;
	      console.log(goodsid);
	      $.get(`/static/data/good${
      goodsid}.json`, res => {
     
	        console.log(res);
	        this.info = res;
	      });
	    }
	  }
	};

和命名路由的使用

	// 对象形式传入动态路由参数,必须使用命名路由
      this.$router.push({
     name:"goods",params:{
     goodsid:456}})

使用地址部分的query 传值

	<li><router-link to="/menu?a=1">/menu?a=1</router-link></li>
    <li><router-link to="/menu?a=2">/menu?a=2</router-link></li>	
	console.log(this.$route.query.a)

命名视图

  • 命名视图:一个路由地址对应多个路由组件来渲染
    • key:router-view 的name值
    • value:对应的渲染组件
	{
     
  	  path: '/',
      components: {
     
        /* 
          key:router-view 的name值
          value:对应的渲染组件
        */
        index:Index,
        menu:Menu
      },
    }


	<router-view  name="index" />
	<router-view name="menu" />

嵌套路由

  • 页面完全不相同,这些是不同的一级路由,而页面内容有部分相同的地方,相同的部分在一级路由,不同的部分同一个一级路由下的二级路由
  • 定义嵌套路由:
	{
     
      path: '/',
      component: Layout,
      // 定义嵌套路由
      children: [{
     
        path: "index",//一级路由path + 当前的path (不加 /)
        component: Index
      }, {
     
        path: "menu",
        component: Menu
      },{
     
        path:"*",
        redirect:"/index"
      }]
    }
  • 在一级路由组件 Layout.vue,设置二级路由的视图出口
	<!-- Layout 下面的二级路由 -->
     <router-view></router-view>

路由懒加载

	{
     
      path: "/login",
      component: ()=>(import("@/pages/Login"))
    },
  • 按块打包:
	children: [{
     
        path: "index",//一级路由path + 当前的path (不加 /)
        // 有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。
        component: ()=>(import(/* webpackChunkName: "group-a" */  "@/pages/Index"))
      }, {
     
        path: "menu",
        component:  ()=>(import(/* webpackChunkName: "group-a" */  "@/pages/Menu"))
      },{
     
        path:"*",
        redirect:"/index"
      }]

路由守卫

  • 路由守卫:通过取消或者跳转的形式守卫路由的函数被称为导航钩子(路由守卫钩子)
守卫分类
  • 全局守卫:导航发生就会触发
    • router.beforeEach
    • router.beforeResolve
    • router.afterEach
	// 全局前置守卫(前置:导航被确认前)

	/**
	 * to:即将进入的路由
	 * from:即将离开的路由
	 * next:不加参数 执行下一个守卫,一次导航确保执行next
	 *      地址参数:终止本次导航,开启一轮新导航
	 *      false: 取消本次导航
	*/
	router.beforeEach((to,from,next)=>{
     
	  console.log(to,from)
	  let isLogin = true;
	  // document.title = to.meta.title;
	  // 已登陆
	  if(isLogin){
     
	    // console.log(to.path)
	    if(to.path=="/login"){
     
	      next(false);
	    }else{
     
	      next();
	    }
	  }else{
     
	    // 未登录
	    // console.log("1")
	    // 
	    if(to.path=="/login"){
     
	      next();
	    }else{
     
	      next("/login")
	    }	    
	  }	  
	})
	//所有组件内守卫和异步路由组件被解析之后
	router.beforeResolve((to,from,next)=>{
     
	  console.log("beforeResolve");
	  next();	  
	})
	// 导航被确认后的守卫,没有next
	router.afterEach((to,from)=>{
     
	  console.log("afterEach")	  
	})
  • 路由独享守卫:访问这个路由才触发的守卫
    • beforeEnter
 	{
     
      path: "/login",
      component: ()=>(import( "@/pages/Login")),
      meta:{
     
        title:"登陆页"
      },
      beforeEnter(to,from,next){
     
        console.log("login 专享")
        next();
      }
    },
  • 组件内的守卫
    • beforeRouteEnter:先获取数据再跳转路由,跳转前需要等待获取数据,跳转后立即看到页面
    • beforeRouteUpdate:解决动态路由组件被复用的问题,路由组件被复用触发
    • beforeRouteLeave:离开路由组件(挽留操作)
	beforeRouteEnter(to, from, next) {
     
	    // 这个钩子中访问不到组件实例
	    console.log(this);
	    // next((vm)=>{
     
	    //   console.log(vm);
	    // });
	
	    // let goodsid = this.$route.params.goodsid;
	    // console.log(goodsid);
	    $.get(`/static/data/good${
      goodsid}.json`, res => {
     
	      console.log(res);
	      // vm 就是组件实例,使用回调函数来访问
	      next(vm => {
     
	        vm.info = res;
	      });
	    });
	  }
	  
	// 解决动态路由组件被复用的问题,路由组件被复用触发
    beforeRouteUpdate(to,from,next){
     
        console.log(to.params.cateid)
        next()
    },
    
    // 离开路由组件
   	beforeRouteLeave (to, from, next) {
     
        // ...
        let flag = confirm("确定离开吗?");
        if(flag){
     
            next();
        }else{
     
            next(false)
        }
        
    }

完整的导航解析流程

  • 导航被触发
  • 失活的组件里调用 beforeRouteLeave 守卫。
  • 调用全局beforeEach 守卫。
  • 重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  • 路由配置里调用 beforeEnter
  • 解析异步路由组件。
  • 被激活的组件里调用 beforeRouteEnter
  • 调用全局beforeResolve 守卫 (2.5+)。
  • 导航被确认
  • 调用全局afterEach 钩子。
  • 触发 DOM 更新
  • 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入

day 03

项目

服务器端
  • 解压->npm i->打开navicat,新建一个名为shop_db(与项目的confiig/global.js下的数据库名相匹配)->在表格那里导入数据库表->修改 confiig/global.js->运行:npm start
设置跨域:config/index.js
	// 设置解决跨域问题的代码
    proxyTable: {
     
      // "/api":已 /api开头的请求会被代理进行跨域问题解决
      "/api":{
     
        target:"http://localhost:3000",//代理的目标地址(真实地址)
        changeOrigin:true,//是否跨域
        // pathRewrite:{
     
        //   "^/api":"/api"
        // }
      }
      /* "/test":{
     
        target:"http://localhost:3000",//代理的目标地址(真实地址)
        changeOrigin:true,//是否跨域
        pathRewrite:{
     
          "^/test":"",//去除真实请求中不存在二点 /test
        }
      } */
    },
  • 项目配置相关信息发生变动,必须重新启动项目
axios:npm i axios
  • 官方API
  • Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
axios 方法:
  • get和post请求区别:是method 和参数写法(get->params;post->data)
  • get请求
	axios({
     
        url:"/api/getgoodsinfo",//请求地址,(target地址+ url 才是真正的地址)
        method:"get",//请求方法
        // 参数
        params:{
     
            id:1
        }
    }).then((res)=>{
     
        console.log(res);
    })
  • post请求:
	axios({
     
        url:"/api/userlogin",
        method:"post",
        data:this.info
    }).then(res=>console.log(res))
  • axios.get
	axios.get("/api/getgoodsinfo",{
     
        params:{
     
            id:1
        }
    }).then((res)=>{
     
        console.log(res);     
    })
  • axios.post
axios.post('/api/userlogin',this.info);
  • axios.all
    • 并发:一起发送请求,一起接受数据
  • 定义并发函数
	methods: {
     
	    getBanner() {
     
	      return axios.get("/api/getbanner");
	    },
	    getInfo() {
     
	      return axios.get("/api/getgoodsinfo", {
     
	        params: {
     
	          id: 1
	        }
	      });
	    }
    }
  • 使用all发出请求,spread解析
	axios.all([this.getBanner(), this.getInfo()]).then(axios.spread((res1, res2) => {
          console.log(res1,res2)
      }));

创建实例:

  • 实例可以被认为是axios的独立拷贝,进行单独设置

    // 创建新独立axios 实例
    let instance = axios.create({
    baseURL: “/api”,
    timeout: 5000,
    })

    let instance1 = axios.create({
    baseURL: “/apis”
    })

    instance.get("/getbanner").then(res => console.log(res));

拦截器

  • 发送请求前接受响应前触发的回调函数
	// 设置请求拦截器,回调函数,发送请求时执行的函数
	// config 请求参数
	axios.interceptors.request.use(config => {
     
	    // Do something before request is sent	
	    config.headers.Authorization = localStorage.getItem("token")
	    /* if(!(config.url=="/api/userlogin"&&config.method=="post")){
     
	        config.headers.Authorization = localStorage.getItem("token")
	    } */
	    console.log(config)
	    // return的 config对象,真正发送请求时的请求对象
	    return config;
	}, error => {
     
	    // Do something with request error
	    return Promise.reject(error);
	});
	
	// 响应拦截器
	// response:响应数据
	axios.interceptors.response.use(response => {
     
	    // Do something before response is sent
	    console.log(response)
	
	    return response.data;
	}, error => {
     
	    // Do something with response error
	    return Promise.reject(error);
	});

day 04

Vuex

vue(插槽、vue-router、导航、路由守卫、axios、vuex)_第1张图片

  • Vuex是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  • 使用:npm i vuex
  • 创建: src/store/index.js
	import Vue from "vue"
	import Vuex from "vuex"
	
	// 安装插件
	Vue.use(Vuex);
	
	// store-> vuex 实例
	let store = new Vuex.Store({
     
	    // 定义state(所有组件都可以共享的数据)
	    state:{
     
	        count:1
	    }
	})
	
	export default store
  • main.js
	import Vue from 'vue'
	import App from './App'
    + import store from "./store"
	Vue.config.productionTip = false
	console.log(store)
	/* eslint-disable no-new */
	new Vue({
     
	  el: '#app',
	  + store,// 组件应用vuex实例
	  components: {
      App },
	  template: ''
	})

五大核心:

  • State: Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据 (SSOT (opens new window))”而存在。这也意味着,每个应用将仅仅包含一个 store 实例
  • getters:state 的计算属性
	let store = new Vuex.Store({
     
	    // 定义state(所有组件都可以共享的数据)
	    state:{
     
	        count:1
	    },
	    // state 的计算属性
	    getters:{
     
	        //参数state-> vuex的state
	        sqrt(state){
     
	            return state.count*state.count;
	        }
	    }
	})
  • 组件内使用:作为计算属性来使用
	computed:{
     
	    count(){
     
	      return this.$store.state.count;
	    },
	    sqrt(){
     
	      return this.$store.getters.sqrt
	    }
	  },
  • mutations修改state的唯一办法是通过提交mutation
	// 定义mutation,修改state的唯一办法是通过提交mutation
    mutations:{
     
        // add是mutation的名称,也是函数的名字(当add这个mutation被提交commit时,add函数会执行)
        // 参数state-> vuex的state
        add(state){
     
            console.log("add ")
            // 修改state
            state.count++;
        },
        // payload->负载,提交参数
        minus(state,payload){
     
            state.count-=payload
        },
        // 对象提交方法
        mul(state,payload){
     
            console.log(payload)
            state.count*=payload.n
    }
  • 组件中使用:提交commit名为xxx的mutation
	methods: {
     
	    addFn() {
     
	      // 提交名为add的mutation
	      // commit(mutation名称)
	      this.$store.commit("add");
	    },
	    minus() {
     
	      // commit(mutation名称,负载数据)
	      this.$store.commit("minus",2)
	    },
	    mul(){
     
	      // 提交名为 mul的mutation,数据是 n:2
	      this.$store.commit({
     
	        type:"mul",//mutation名称
	        n:2,//数据
	      })
	    }
	  }
  • actions
    • Action 类似于 mutation(严格模式下不能进行异步操作),不同在于:
      • Action 提交的是 mutation,而不是直接变更状态
      • Action 可以包含任意异步操作。
	//组件中使用
	ADD() {
     
      this.$store.dispatch("ADD");
    }
	
	//在action中提交mutation 
    actions:{
     
        ADD({
     commit},payload){
     
            console.log(context)
            // context.commit("add")
            setTimeout(()=>{
     
                commit("add")
            },1000)
        }
    }
  • 模块化
  • modules/cart.js(模拟购物车模块)
	let state = ()=>{
     return {
     count:100}}//函数返回对象
	let mutations={
     
	    add(state){
     
	        state.count++;
	    }
	}
	let actions={
     
	    ADD({
     commit}){
     
	        commit("add")
	    }
	}
	// cart 模块的配置
	let cart = {
     
	    namespaced:true,//开启命名空间,提交模块的mutation必须带上模块的名称(独立的)(不开启命名空间,某个mutation被提交,所有同名的mutation都会执行,)
	    state,
	    mutations,
	    actions
	}

	export default cart
  • 根模块中来注册
	import cart from "./modules/cart"
	import mutations from "./mutations" 
	let store = new Vuex.Store({
     
	    strict:true,//严格模式
	    // 定义模块
	    modules:{
     
	        // key:模块名称
	        // value:模块配置
	        cart:cart
	    },
	    state:{
     ... },
	    getters:{
     ... },
	    mutations,
	    actions:{
     ...}
	    }
	})
  • 组件中来使用
		computed: {
     
		    /* count1(){
     
		      return this.$store.state.cart.count;
		    }, */
		    // 计算属性count1 返回的是state中 cart模块的count
		    ...mapState({
     count1:state=>state.cart.count}),
	    }
		  methods: {
     
		    ...mapMutations("cart",["add"]),
		    addFn1(){
     
		      // 提交根模块的 mutation add
		       this.$store.commit("add")
		      //只触发cart模块的 add mutation
		      this.$store.commit("cart/add")
		      // this.add()
		    }
		  }

辅助函数

import {
     mapState,mapGetters,mapMutations,mapActions} from "vuex"
  • 辅助生成计算属性mapState,mapGetters
	computed: {
     
    /* count:function() {
     
      return this.$store.state.count;
    }, 
    msg()*{
     
       return this.$store.state.msg;
    }*/
    // 添加 名为count的计算属性,返回state 中count
    // 映射 this.count 为 store.state.count
    ...mapState(["count","msg"]),

    /* sqrt() {
     
      return this.$store.getters.sqrt;
    } */
    // 映射 this.sqrt 为 store.getters.sqrt
    ...mapGetters(["sqrt"])
  },
  • 辅助生成methodsmapMutations,mapActions
	/* add(){
     
      this.$store.commit("add");
    }, */
    ...mapMutations(["add","minus"]),
    addFn() {
     
      // 提交名为add的mutation
      // commit(mutation名称)
      // this.$store.commit("add");
      // this.add 映射提交 名为 add的mutation
      this.add();
    },
    /* minus(n){
     
      this.$store.commit("minus", n);
    }, */
    minusFn() {
     
      // commit(mutation名称,负载数据)
      // this.$store.commit("minus", 2);
      // 提交名为 minus的mutation,payload为2
      this.minus(2)
    },
    mul() {
     
      this.$store.commit("mul",3)
    },
    ...mapActions(["ADD"]),
    /* ADD(){
     
       this.$store.dispatch("ADD");
    }, */
    ADDFn() {
      
      this.ADD();
    }

你可能感兴趣的:(笔记)