days to study vue2+react

day01

1.介绍

1.课程

1.内容:

​ vue(vue-router vuex element-ui axios …) 【10+5 2个项目(app 后台管理)】-js

​ react(react-router-dom redux react-redux hooks …) 【6+3 1个项目 app】-js

2.课程内容 vue

1.官网:

https://cn.vuejs.org/

2.SPA:single page application 单页面应用

MPA:多个url–》多个HTML文件 多页面应用

[优点:有利于SEO优化,缺点:会出现白屏,用户体验度不好]

SPA:对个url—>1个html

[优点:用户体验好 缺点:首屏加载速度慢,不利于SEO优化]

2.核心

数据驱动 组件系统

3.安装

1.cdn【不推荐 项目中绝对不用】
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
2.npm 【学习理论过程会使用】
npm init 
npm i vue //安装vue 

//安装淘宝镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
3.脚手架方式【day4 工作时候用】

4.Vue实例

5.数据绑定

js :事件 innerHTML value src className style.color …

jq:事件 html() val() attr() class css …

Vue:非表单元素(div span) 表单元素(input textarea select …) 媒体元素(img video )–给属性赋值 样式。。。。

1.非表单元素【div p span】 {{}} vHtml vText
{{}}     优点:简单方便  缺点:1.不能解析标签;2.首屏可能会出现闪屏
v-html   优点 :可以解决闪屏问题  可以解析标签  【主要用于详情】
v-text   优点:可以解决闪屏问题  缺点:不能解析标签

首屏建议使用v-text|v-html,其他屏你喜欢用什么就用什么。

2.表单元素【input】vModel

v-model

<div>
  账号:<input type="text" v-model="name">
</div>
3.属性绑定【媒体元素】 vBind,简写 :
<div id="app">
  <!-- v-bind 动态绑定属性,不仅可以绑定已知属性,自定义属性也可以绑定,简写为 :   -->
  <img v-bind:src="img" alt="">

  <a v-bind:href="website.url">
    <img v-bind:src="website.img" alt="" v-bind:title="website.name">
  </a>

  <a :href="website.url" :aaa="name">
    <img :src="website.img" alt="">
  </a>
</div>
4.条件渲染
1.v-if
<!-- v-if="boolean" 真就出现,假就消失 -->
<h3 v-if="1===2">社会很单纯</h3>
<h3 v-if="x>5">复杂的是人</h3>
2.v-show
<!-- v-show="boolean" 真就出现,假就消失 -->
<h3 v-show="1===2">社会很单纯</h3>
<h3 v-show="x>5">复杂的是人</h3>
3.v-else
<!-- v-if和v-else必须挨着 -->
<div v-if="comments.length===0">暂无评论</div>
<div v-else>{{comments}}</div>
4.v-if VS v-show
相同点:true 出现,false消失
不同点:false情况下,v-if采用的 惰性加载;v-show采用的是display:none.
使用场景:如果频繁切换,那么就使用v-show;如果不频繁切换,建议使用v-if
5.列表渲染 v-for
1.遍历数组
<!-- 遍历数组 -->
<ul>
  <li v-for="(item,index) in comments">{{index}}---{{item}}</li>
</ul>
comments: ["物美价廉", "很好", "推荐"],
2.遍历json
<!-- 遍历json -->
<div v-for="(value,key) in json">{{key}}:{{value}}</div>
data: {
  comments: ["物美价廉", "很好", "推荐"],
    json: {
      name: "妲己",
      age: 20,
      sex: "女"
    },
}
3.key
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
6.动态类名

1.变量

<!-- 1.:class="变量" -->
<div :class="color">打疫苗了吗?</div>

2.【三元】

<!-- 2.:class="[三元]" 只有2种情景 -->
<div :class="[isOrange?'orange':'lime']">绿水青山就是金山银山</div>

3.json

 <!-- 3. :class="{类名1:true,类名2:false,.....}" 多种情景-->
<div :class="{pink:true,lime:true,whilte:false,blue:true}">大力改革教育</div>
7.动态行间样式
<!-- 理论 :style="{json}"-->
<div :style="{color:color,backgroundColor:'pink'}">天道酬勤,人道酬善,商道酬信。</div>

6.复习知识:

1.toFixed()

7.作业:

1.今天的案例练习3遍;

2.作业文件夹中的2个作业

day02

Vue 不支持 IE8 及以下版本

0.案例

bootstrap官网:

http://bootcss.com

1.双for

<div class="item" v-for="item in movies" :key="item.id">
  <h3>name:{{item.name}}</h3>
  <p>导演:
    <span v-for="i in item.directors" :key="i.id">{{i.name}}&nbsp;&nbsp;</span>
  </p>
  <p>主演:
    <span v-for="i in item.actors" :key="i.id">{{i.name}}&nbsp;&nbsp;</span>
  </p>
</div>

2.表单

表单数据,建议定义一个json,key和后端要求的字段一直,目的是为了方便和后端交互

1.指令 v-model
2.单选框
<!-- 单选框,value是选中后的值 -->
<div>性别:
  <input type="radio" v-model="user.sex" name="sex" :value="0">男
  <input type="radio" v-model="user.sex" name="sex" :value="1">女
</div>
3.多选框

对于checkbox来说,如果初始值是数组,最后就是数组;否则都是boolean

<div>
  爱好:
  <input type="checkbox" :value="1" v-model="user.hobby">唱歌
  <input type="checkbox" :value="2" v-model="user.hobby">跳舞
  <input type="checkbox" :value="3" v-model="user.hobby">写代码
  <input type="checkbox" :value="4" v-model="user.hobby">打游戏
</div>

<div>
  <input type="checkbox" v-model="user.isAgree">是否同意
</div>
user: {
  "name": "11",
  "pass": "22",
  "tel": "322",
   "sex": 1,
  "hobby": [2, 3, 4],
   "job": 2,
  "snacks": [1, 2],
    "des": "123123",
   "isAgree": true
}
4.下拉菜单
<!-- option中间的给用户看的,value是程序取得数据 -->
<div>
  职业:
  <select v-model="user.job">
    <option value="" disabled>--请选择--</option>
    <option :value="1">php工程师</option>
    <option :value="2">web工程师</option>
    <option :value="3">java工程师</option>
  </select>
</div>
<!-- 下拉菜单复选,需要设置multiple  -->
<div>
  零食:
  <select v-model="user.snacks" multiple>
    <option value="" disabled>--请选择--</option>
    <option :value="1">辣条</option>
    <option :value="2">薯片</option>
    <option :value="3">瓜子</option>
    <option :value="4">快乐水</option>
  </select>
</div>
5.多行文本框
<div>
  备注:
  <textarea cols="30" rows="10" v-model="user.des"></textarea>
</div>
6.表单修饰符
<!-- .lazy 失去光标,才去修改data中的数据 -->
<input type="text" v-model.lazy="name">
<div>name:{{name}}</div>

<!-- .number 将输入的内容由string转为number -->
<input type="number" v-model.number="age">
<button v-on:click="logage()">打印年龄</button>

<!-- .trim 输入框前后去空格 -->
<input type="text" v-model.trim="msg">{{msg}}
7.事件

单选、多选、下拉菜单都是onchange,不可以使用onclick

8.MVVM模式
MVVM M-模型(model) V-视图(view) VM-视图模型(viewModel)
模型 通过 视图模型(控制器) 决定了视图的展示;
视图可以通过 视图模型(控制器) 改变模型的数据
视图模型是 视图 和模型之间的桥梁
 vue是MVVM模式框架,适合操作数据多的情况使用
 jq:dom操作方便、动画、链式操作 
官网:jq【维护】
管理系统:vue  【内部使用 不需要推广,数据多,改版】(必然是你)
商城类:数据多,动画多 vue+jq
app:(不一定是你) 改版+数据+动画 vue+jq
活动页:【好看、一次性使用】html+css+jq(是你)

3.事件绑定

1.如何绑定事件
<button v-on:click="fn1()">点击触发</button>
<!-- 如果没有参数,()可以省略 -->
<button v-on:click="fn1">点击触发</button>

<!-- v-on 可以简写为@  -->
<button @click="fn1">点击触发</button>
<div>n:{{n}}</div>

<!-- 如果逻辑只有一句话,可以在html中实现 -->
<button @click="n=n+2">+2</button>
2.事件传参
<button @click="add(3,5)">3+5</button>
3.事件对象Event如何获取?
<!-- 显式获取:$event  -->
<button @click="getEvent($event)">获取event</button>

<!-- 隐式获取:调用不能写(),默认参数就是event  -->
<button @click="getEvent">获取event</button>

<!-- 如果出了event,还有其他参数,只能使用显式传参 -->
<button @click="getEvent2($event,10)">获取event,10</button>
4.阻止默认事件 阻止事件传播
//阻止默认事件
e.preventDefault()
//阻止传播
e.stopPropagation()
5.事件修饰符
.prevent 阻止默认 
.stop 阻止传播 
.self 触发的目标元素是自己才执行
.once 一次性触发
.capture 捕获
.up 上键 
.down 下键
.left 左键
.right 右键
 .enter 回车键
 .13

4.$set

1.数组变了,页面不渲染,怎么办?
1.arr.splice(index,1,newObj)
2.Vue.set(arr,index,newObj)
3.this.$set(arr,index,newObj)
4.取回来数据,先操作,再赋值给数组。
2.json变了,页面不渲染,怎么办?
1.Vue.set(this.json,"y",40)
2.this.$set(this.json,"y",40)
3.this.json={
  ...this.json,
  y:40
}

5.复习内容

1.阻止默认 阻止传播
if(e.preventDefault){
  e.preventDefault()
}else{
  e.returnValue=false
}

6.作业

1.练习3遍

2.作业文件夹的2个作业

day03

1.生命周期

8个生命周期函数是自动执行的函数,称为钩子函数

beforeCreate  //创建之前,什么都没有
created //创建之后,数据初始化完成,但是el还是undefined
            //作用:二次修改初始值
beforeMount //2.挂载期:如果有el或者$mount(),才自动触发
            //挂在之前:找到了要解析的模板,但是{{}} v-指令都还没有解析
mounted //挂载完成:页面完成解析,DOM节点加载完成 *****
            //作用:获取dom,添加动画,开启计时器,给window或者document绑定事件
beforeUpdate  //3.更新期
            //数据变化了,但是页面准备重新渲染之前,注意,此时取到 的数据是新值
updated  //页面更新完成
beforeDestroy //4.销毁期
            //作用:善后工作:1.清除计时器;2.清除window|document上的事件
destroyed //销毁完成,数据不会实时渲染,变成了纯粹的html,css

2.watch 监听器 侦听器

监听数据的改变,只能监听data中的数据和computed中的数据

new Vue({
	watch:{
		kw(newV,oldV){
      	//逻辑
    },
    
    json:{
      handler(){
        console.log("json变了");
      },
      deep:true
    }
	},
  
})

注意:不建议使用深度监听

1.深度监听会造成页面卡顿;
2.可以使用事件代替监听
jsonp
1.创建一个script标签
	var os=document.createElement("script")
2.给script一个src,src就是地址
	os.src="http://suggestion.baidu.com/su?cb=qwer&wd=123"
3.将script插入到页面
	document.body.appendChild(os)
4.回调函数处理数据
	function qwer(d){
    	//d就是后端返回的数据
  }

3.filter 过滤器

1.作用:

转换数据

2.使用

| 管道符

3.注册方式

全局注册

// 全局过滤器:所有的vue实例都可用
Vue.filter("filterTel",(tel)=>{
	return tel.slice(0,3)+"****"+tel.slice(7)
})

局部注册

new Vue({
  el: "#app",
 
  //局部过滤器:只有当前vue实例可用
  filters: {
    filterPrice(a) {
      return a.toFixed(2)
    },

  }
})

4.computed 计算属性

new Vue({
	computed:{
		allPrice(){
			return 10
		}
	}
})

5.computed VS watch

watch:
	一个数据影响多个数据的改变
	没有调用,自动执行
	可以操作异步
	
computed:
	多个数据影响一个数据
	手动调用,当做属性调用
	做同步
	可以有缓存

6.面试题

1.常用指令
2.SPA优缺点?
3.v-if和v-show的区别?
4.v-for的key的作用
5.数组变了,页面不渲染怎么办?
6.json变了,页面不渲染怎么办?
7.vue生命周期有哪些?
8.vue一进来页面自动执行的生命周期有哪些?
9.一进来页面要发ajax在哪个阶段?
10.表单修饰符?
11.事件修饰符?
12.MVVM
13.MVVM和jq有什么区别?什么情况下使用?
14.watch和computed区别?
15.jsonp原理?
16.vue有哪些bug?如何解决?
	1.{{}}闪屏 v-text
	2.数组变了,不渲染,4种方式解决
	3.json变了,不渲染,3中解决
	4.watch深度监听会卡顿,用事件代替
	5.v-for和v-if同时在一个节点上,渲染效率低,建议使用computed解决

7.复习

1.slice substr substring 区别
2.Date 
3.padStart padEnd indexOf includes 
4.Math.random() Math.max() Math.min()
5.arr: forEach vs map. every(some) filter reduce find findIndex

8.作业

1.练习 3遍

2.作业文件夹中的作业

1.淘宝搜索
2.购物车

day04

1.动画

1.使用场景
1.v-if
2.v-show
3.动态组件
4.路由
2. 6个状态
进来之前 enter
进来过程 enter-active
进来完成 enter-to
离开之前 leave
离开过程 leave-active
离开完成 leave-to
3.使用

1.transition 将内容包裹;2.设置name属性

<transition name="aa">
	<div class="box" v-if="isshow"></div>
</transition>

3.3.设置6个状态

.aa-enter{
  left: 0px;
}
.aa-enter-active,.aa-leave-active{
  transition: all 0.3s;
}
.aa-enter-to{
  left: 500px;
}
.aa-leave{
  opacity: 1;
  transform: scale(1,1);
}

.aa-leave-to{
  opacity: 0.1;
  transform: scale(0.1,0.1);
}
4.animate.css

官网:https://animate.style/

1.安装
npm i animate.css
2.引入
 <link rel="stylesheet" href="./node_modules/animate.css/animate.css">
3.使用
 <transition enter-active-class="animate__animated animate__fadeInDown"
            leave-active-class="animate__animated animate__bounceOutLeft">
            <div class="box" v-if="isshow"></div>
        </transition>
4.注意:

​ 1.一般情况下只写进来动画,不写离开,否则,太花里胡哨。

2.组件基础

1.什么是组件?

可复用的vue实例。(公共的html+css+js)

2.组件注册

1.全局注册

//全局组件:所有的vue实例都可以调用
Vue.component("hello",{
  template:"<div>我是hello</div>"
})

2.局部注册

new Vue({
  el:"#app",

  //局部组件:只有当前vue实例可用
  components:{
    websitenav:{
      template:"<div> <div>特色主题</div><div>行业频道</div><div>更多精选</div></div>"
    }
  }
})

3.大部分使用局部,少数使用全局。

3.组件命名

components: {
  // 1.不能以已经存在的标签命名。比如:div input span ... 
  // 2.也不能以已经存在的标签的大写命名。比如:DIV INPUT ... 
  // 3.如果组件名中间包含了大写(除首字母之外),使用的时候需要变成-小写 ,烤串写法 
  WebsiteNavComponenT: {
    template: "<div>这是第一个组件</div>"
  },
    // 4.建议名称中包含大写,目的是为了方便调用
    vHello: {
      template: "<div>hello</div>"
    }

}

4.template

1.template 可以借助template标签来实现组件的模板

vSecond:{
  template:"#second"
},
<template id="second">
  <div>
    <h3>this is second</h3>
  </div>
</template>

2.template只能有1个根节点。因为一个vue实例只能作用在1个节点上,如果有很多节点,只能作用在满足条件的第一个节点上

3.脚手架

//只执行一次
npm i webpack -g //全局安装webpack
npm i @vue/cli -g //全局安装vue脚手架

创建项目:

vue create demo //demo是你的项目名称
1. manually select features
2.只选择了babel
3.package.json
4.未来是否都是这样? n

项目启动

cd demo
npm run serve

目录:

-demo	
	-node_modules	依赖包
	-public 静态资源
		index.html 唯一的页面
		favicon.ico 小图标
	.gitignore 上传码云不需要传的文件
	.babel.config.js babel 配置
	package.json 项目命令 依赖包
	readme.md 项目说明
	-src 你的代码
		main.js 入口文件
		App.vue 根组件

vue的插件:

vuter vetur

4.作业

1.练习3遍

2.作业文件夹中的作业

day05

组件高阶

1.组件通信***

组件关系:父子关系 非父子

父传子:子组件取了父组件的数据

场景:父组件控制数据。子组件控制的结构

语法:父组件通过自定义属性传值,子组件通过props接收,然后使用

父组件:
 <v-child :img="img" :title="name"></v-child>

子组件:

export default {
    props:["img","title"]
}
props验证:
export default {
    // props验证
    props:{
        price:{
            //必填
            required:true,
            //类型
            type:Number
        },
        tel:{
            //默认值
            default(){
                return '110'
            }
        }
    }
}
子传父:子组件调用了父组件的方法

场景:子组件要修改父组件的值。

使用:父组件绑定自定义事件,子组件通过$emit()触发自定义事件

父组件:
 <!-- 子传父:父组件绑定自定义事件,子组件通过$emit()触发自定义事件 -->
    <v-child  @aa="changeWang" @bb="changeName($event)"></v-child>
子组件:
methods:{
  cwang(){
    //通知父组件触发changeWang  
    this.$emit("aa")
  },
  changeName(name){
    // 自定义事件传参,只能传递1个参数,接收方通过event对象来接收
    this.$emit("bb",name)
  }
},
非父子传值
EventBus【了解】–练习1遍

1.在main.js中给vue原型上添加一个vue实例

// 1.在vue的原型链上挂了一个EventBus,值是一个vue实例
Vue.prototype.EventBus=new Vue();

2.接收方绑定自定义事件

mounted(){
        this.EventBus.$on("sendA",(e)=>{
            console.log(e);
            this.a=e;
        })
    },

3.发送方触发自定义事件:

 //发数据
  this.EventBus.$emit("sendA",this.name)
vuex
本地存储
cookie

2.ref

注意:ref一定要在mounted之后使用

1.获取到原生的DOM节点
<!-- 1.通过ref获取DOM节点 -->
<div class="boxDiv" ref="div"></div>
 this.$refs.div.style.background='blue';
 this.$refs.div.innerHTML="123"
2.父组件获取子组件的实例
 <!-- 2.父组件获取子组件的实例 -->
   <v-child ref="child"></v-child>
 // this.$refs.child.name="貂蝉"
 this.$refs.child.changeName('貂蝉')

3.is

1.解决了标签固定搭配问题

<!-- 1.解决标签固定搭配问题 -->
<ul>
  <li is="v-one"></li>
</ul>
<table>
  <tr is="v-one"></tr>
</table>

2.实现动态组件

<button @click="showCom='v-one'">one</button>
<button @click="showCom='v-two'">two</button>


<div :is="showCom"></div>

4.脚手架上使用动画

1.安装

npm i animate.css --save

2.main.js引入

import "animate.css"

3.使用

<transition
            enter-active-class="animate__animated animate__bounceInLeft"
            >
  <div :is="showCom"></div>
</transition>

5.scoped

样式只在当前文件夹中起作用。 建议每个组件都加scoped.

<style scoped>
h3{
    color:blue;
}
.boxDiv{
    width: 100px;
    height: 100px;
    background: red;
    color: #fff;
}
</style>

6.jquery

1.安装

npm i jquery --save

2.引入

 import $ from "jquery"

3.使用[注意:1.mounted之后再使用;2.注意this指向,最好使用箭头函数]

$(".show").click(()=>{
  $(".red").fadeIn(400)
})
$(".hide").click(()=>{
  $(".red").fadeOut(400)
})

7.slot 插槽

1.匿名插槽

所有嵌套的内容都会出现在匿名插槽中

<v-one>
  <div>善上若水,水善利万物而不争</div>
</v-one>

one组件:

<div class="box">
      <h3>this is one</h3>
      <!--匿名插槽: 内置组件 -->
      <slot></slot>
  </div>
2.具名插槽

two组件

<div class="box">
    <!-- 具名插槽 -->
    <slot name="top"></slot>
    <h3>this is two</h3>
    <slot name="bottom"></slot>
  </div>
<v-two>
  <ol slot="top">
    <li>锦瑟无端五十弦</li>
    <li>一弦一柱思华年</li>
  </ol>
  <ul slot="bottom">
    <li>床前明月光</li>
    <li>疑是地上霜</li>
  </ul>
</v-two>

<!-- 2.具名插槽。如果嵌套的内容没有定前往哪个插槽,那么哪个插槽都不会展示 -->
<v-two>
  <div>手机</div>
</v-two>
3.作用域插槽

作用域插槽:父组件调用了子组件,子组件展示数据,结构又不确定,那么使用插槽;但是这段不确定的结构中有使用到数据,需要slot传递回来,父组件使用。

父组件:
<v-three :arr="arr1">
  <!-- v-slot 2.x 3.x都支持 -->
  <template v-slot="props">
<span v-for="item in props.showArr" :key="item">{{item}}</span>
  </template>
</v-three>

<v-three :arr="arr2">
  <!-- slot-scope 2.x的写法,以后就弃用 -->
  <template slot-scope="props">
<button v-for="item in props.showArr" :key="item">{{item}}</button>
  </template>
</v-three>

子组件(three)

<template>
  <div class="box">
    <h3>展示的组件</h3>
    
    <slot :showArr="showArr" a="1" b="2"></slot>

    <h3>展示完成</h3>
  </div>
</template>

<script>
// arr=["11","22","33"]
export default {
  props: ["arr"],
  computed:{
      showArr(){
          return this.arr.slice(0,3)
      }
  }
};
</script>

8.面试题

1.data为什么是个函数?

2.如何实现动态组件?

3.组件之间如何通信?

4.ref的作用是什么?

5.is的作用是什么?

9.作业

1.练习3遍

2.作业文件夹中的作业【尽量做】

day06

1.作业

查看作业笔记.md

2.混入

作用:将两个组件公共的逻辑提取。

语法:

export default {
    data(){
        return {
            isshow:false
        }
    },
    methods:{
        show(){
            this.isshow=true
        },
        hide(){
            this.isshow=false
        }
    }

}

组件使用:

import toggle from "./toggle"
export default {
    mixins:[toggle]
}

3.缓存组件

1.如果想要数据缓存,可以加keep-alive

<keep-alive>
  <v-home v-if="n==1"></v-home>
</keep-alive>

2.加上了keep-alive,mounted只会执行一次,beforeDestroy不会再重复出发了

3.加上了keep-alive会多2个钩子函数:activated(激活) deactivated(失活)

activated() {
  window.onclick = () => {
    console.log("window scroll");
  };
},
  deactivated() {
    window.onclick = null;
  },

4.路由

将url映射到组件上。

1.一级路由规则
routes=[
	 //一级路由
  { path: '/login', component: login },
  {
    path: "/index", component: index,
  },
  {
    path: "/detail", component: detail
  },
  { path: "/list", component: list },
]
2.一级重定向
{ path: "*", redirect: "/login" }
3.嵌套路由
{
    path: "/index", component: index,
    //二级路由路由规则path没有"/" ,重定向是""
    children: [
      {
        path: "home",
        component: home,
      },
      {
        path: "cate",
        component: cate
      },
      {
        path: "shop",
        component: shop
      },
      {
        path: "mine",
        component: mine
      },
      {
        path:"",
        redirect: "home"
      }
    ]

  },
4.内置组件
<router-link></router-link>
<router-view></router-view>
5.路由导航高亮效果
<footer>
  <router-link to="/index/home" active-class="select">首页</router-link>
  <router-link to="/index/cate" active-class="select">分类</router-link>
  <router-link to="/index/shop" active-class="select">购物车</router-link>
  <router-link to="/index/mine" active-class="select">我的</router-link>
</footer>
6.编程式导航
this.$router.push("/search") //是添加了新的历史记录
this.$router.replace("/search") //是使用新的记录替换当前记录
this.$router.go(-1) //返回

5.作业

1.练习2遍;

2.作业文件夹中的作业 1

3.app 至少一遍

day07

1.路由传参

<router-link to="/detail?id=1&name=2">手机</router-link>
this.$route.query.id //1
this.$route.query.name //2

2.命名路由

{ 
    path: '/search', 
    component: search,
    // 命名路由
    name:"搜索",

  },
router-link :to="{name:'搜索'}">前往搜索</router-link>

3.命名视图 -了解

<router-view class="con"></router-view>
<!-- 命名视图 了解 -->
<router-view name="view2"></router-view>
//一级路由
  {
    path: '/login',
    // component: login,
    components:{
      default:login,
      view2:test
    },
    name: "登录",
    // 路由元信息
    meta: {
      title: "登录"
    }
  },

4.动态路由

1.传递参数

<router-link to="/detail/1/手机">手机</router-link>

2.修该路由规则

{
	path:"/detail/:id/:name"
}

3.取值

this.$route.params.id //1
this.$route.params.name //手机

5.路由模式

const router = new VueRouter({
  
  // history 不带#,hash是带#
  mode:"history",//hash history  ,默认是hash
  

})

hash VS history

hash  http://baidu.com/#/login
	1.前进 后退 刷新  都ok
	2.#/login 是hash值,hash值是不会影响请求
	3.采用的是window.onhashchange=()=>{} 原理实现,是可以兼容IE678的
history http://baidu.com/login
	1.前进 后退 ok
	刷新:如果后端有这个路由,就会直接展示后端数据到页面;如果后端没有这个路由,404.
	2. /login 是会影响请求
	3.采用的是HTML5新增的API interface (pushState replaceState)
	4.如果想使用history模式,需要后端配合。

6.导航守卫*

守卫|路由钩子函数:

如果没有设置守卫,可以自由出入;如果设置了守卫,需要守卫允许,才可以进入或者离开

全局守卫
	router.beforeEach()
	rotuer.afterEach()
路由独享守卫
	beforeEnter()
组件内部守卫:
	beforeRouteEnter()
	beforeRouteUpdate()
	beforeRouteLeave()
登录拦截如何实现??

如果用户没有登录,只能访问登录或者注册路由;如果登录了,才可以访问所有的路由。

1.在登录成功的时候,设计一个标识,用来延段是否登录。 【login.vue】

 methods:{
    login(){
      //name="admin" pass="123"
      if(this.user.name=="admin"&&this.user.pass=="123"){
        //设计一个标记
        localStorage.setItem("islogin",true)
        this.$router.push("/index/home")
      }else{
        alert("账号密码错误")
      }
    }
  }

2.在全局前置守卫做拦截,【router/index.js】

//全局前置守卫:进入每一个路由都会执行
//to:前往的路由数据 from是从哪来的路由数据  next允许不允许进入
router.beforeEach((to, from,next)=>{
  // 1.如果去的登录页,直接进
  if(to.path==="/login"||to.path=="/register"){
    next()
    return;
  }
  // 2.如果去的不是登录,判断islogin有没有。如果有,进
  let islogin=localStorage.getItem("islogin");// "true" null
  if(islogin){
    next()
    return;
  }
  // 3.如果没有,去登录
  next("/login")
})

7.懒加载

let index=()=>import("../pages/index/index.vue")
let list=()=>Promise.resolve(import("../pages/list/list.vue"))

8.滚动处理

 //滚动处理
  scrollBehavior (to, from, savedPosition){

    //如果之前savedPosition没有,滚动到{x:0,y:0}
    //如果之前savedPosition有,滚动到savedPosition
    if(savedPosition){
      return savedPosition
    }else{
      return {x:0,y:0}
    }
  }

9.路由元信息

{
    path: '/login', component: login, name: "登录",
    // 路由元信息
    meta: {
      title: "登录"
    }
  },
<div class="header">{{$route.meta.title}}</div>

10.作业

1.app 练习2遍

day07

1.路由传参

<router-link to="/detail?id=1&name=2">手机</router-link>
this.$route.query.id //1
this.$route.query.name //2

2.命名路由

{ 
    path: '/search', 
    component: search,
    // 命名路由
    name:"搜索",

  },
router-link :to="{name:'搜索'}">前往搜索</router-link>

3.命名视图 -了解

<router-view class="con"></router-view>
<!-- 命名视图 了解 -->
<router-view name="view2"></router-view>
//一级路由
  {
    path: '/login',
    // component: login,
    components:{
      default:login,
      view2:test
    },
    name: "登录",
    // 路由元信息
    meta: {
      title: "登录"
    }
  },

4.动态路由

1.传递参数

<router-link to="/detail/1/手机">手机</router-link>

2.修该路由规则

{
	path:"/detail/:id/:name"
}

3.取值

this.$route.params.id //1
this.$route.params.name //手机

5.路由模式

const router = new VueRouter({
  
  // history 不带#,hash是带#
  mode:"history",//hash history  ,默认是hash
  

})

hash VS history

hash  http://baidu.com/#/login
	1.前进 后退 刷新  都ok
	2.#/login 是hash值,hash值是不会影响请求
	3.采用的是window.onhashchange=()=>{} 原理实现,是可以兼容IE678的
history http://baidu.com/login
	1.前进 后退 ok
	刷新:如果后端有这个路由,就会直接展示后端数据到页面;如果后端没有这个路由,404.
	2. /login 是会影响请求
	3.采用的是HTML5新增的API interface (pushState replaceState)
	4.如果想使用history模式,需要后端配合。

6.导航守卫*

守卫|路由钩子函数:

如果没有设置守卫,可以自由出入;如果设置了守卫,需要守卫允许,才可以进入或者离开

全局守卫
	router.beforeEach()
	rotuer.afterEach()
路由独享守卫
	beforeEnter()
组件内部守卫:
	beforeRouteEnter()
	beforeRouteUpdate()
	beforeRouteLeave()
登录拦截如何实现??

如果用户没有登录,只能访问登录或者注册路由;如果登录了,才可以访问所有的路由。

1.在登录成功的时候,设计一个标识,用来延段是否登录。 【login.vue】

 methods:{
    login(){
      //name="admin" pass="123"
      if(this.user.name=="admin"&&this.user.pass=="123"){
        //设计一个标记
        localStorage.setItem("islogin",true)
        this.$router.push("/index/home")
      }else{
        alert("账号密码错误")
      }
    }
  }

2.在全局前置守卫做拦截,【router/index.js】

//全局前置守卫:进入每一个路由都会执行
//to:前往的路由数据 from是从哪来的路由数据  next允许不允许进入
router.beforeEach((to, from,next)=>{
  // 1.如果去的登录页,直接进
  if(to.path==="/login"||to.path=="/register"){
    next()
    return;
  }
  // 2.如果去的不是登录,判断islogin有没有。如果有,进
  let islogin=localStorage.getItem("islogin");// "true" null
  if(islogin){
    next()
    return;
  }
  // 3.如果没有,去登录
  next("/login")
})

7.懒加载

let index=()=>import("../pages/index/index.vue")
let list=()=>Promise.resolve(import("../pages/list/list.vue"))

8.滚动处理

 //滚动处理
  scrollBehavior (to, from, savedPosition){

    //如果之前savedPosition没有,滚动到{x:0,y:0}
    //如果之前savedPosition有,滚动到savedPosition
    if(savedPosition){
      return savedPosition
    }else{
      return {x:0,y:0}
    }
  }

9.路由元信息

{
    path: '/login', component: login, name: "登录",
    // 路由元信息
    meta: {
      title: "登录"
    }
  },
<div class="header">{{$route.meta.title}}</div>

10.作业

1.app 练习2遍

day08

一、课程内容-axios

1.介绍

官网:http://www.axios-js.com/zh-cn/docs/

axios 是一个基于 Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生 XHR 的封装,只不过它是 Promise 的实现版本,符合最新的 ES 规范,它本身具有以下特征:

从浏览器中创建 XMLHttpRequest
⚫ 支持 Promise API
⚫ 客户端支持防止CSRF
⚫ 提供了一些并发请求的接口(重要,方便了很多的操作)

⚫ 从node.js创建http请求

⚫ 拦截请求和响应
⚫ 转换请求和响应数据

⚫ 取消请求
⚫ 自动转换JSON数据

2.配置代理

在你的项目目录下创建一个文件,叫 vue.config.js:

module.exports = {
  // 部署应用时的基本 URL
  publicPath:"",
  // build 时构建文件的目录
  outputDir: 'dist',
  // build 时放置生成的静态资源 (js、css、img、fonts) 的目录 assetsDir: 'static',
  // 指定生成的 index.html
  indexPath: 'index.html',
  // 设置代理请求
  devServer: {
    proxy: {
      "/api":{
        	target:"url",
        	ws:true,
        	changeOrigin:true
      }
    } 
  }
}

注意:前端项目重启

3.安装

npm i axios --save

4.使用

import axios from "axios"

//get
axios({
  url:"/后端提供的url"method:"get",//可以省略,
  params:{
  		id:1
	}
}).then(res=>{
  //res就是后端返回的数据
})

axios.get(url,{
  //配置项
  headers:{},
  //参数
  params:{}
}).then(res=>{})

//post
axios({
  url:"/后端提供的url"method:"post",//不可以省略,
  data:{
  		id:1
	}
}).then(res=>{
  //res就是后端返回的数据
})

axios.post("url",data,{
  //配置项
  headers:{
    token:1
  }
}).then(res=>{})

5.post传参问题

1.没有文件
import qs from "querystring"

export let reqLogin=(user)=>{  
    console.log(JSON.stringify(user));//'{"phone":"110","password":"123"}'
    console.log(qs.stringify(user));//'phone=110&password=123'
    return axios({
        url: "/api/login",
        method: "post",
        //没有文件
        data: qs.stringify(user),
      })
}
2.有文件 FormData
//注册
export let reqRegister=(user)=>{
    // 假设:user={name:1,age:2,ava:File}
    let data=new FormData()
    /*data.append("name",user.name)
    data.append("age",user.age)
    data.append("ava",user.ava)*/

    for(let i in user){
        data.append(i,user[i])
    }

    return  axios({
        //url请求路径 method请求方式 data参数
        url:"/api/register",
        method:"post",
        data:data
    })

}

6.拦截器

//请求拦截:返回的是后端收到的请求
//每次请求都要携带token
axios.interceptors.request.use(config=>{
    console.log("此处是请求拦截:");
    config.headers.token="123"
    console.log(config);


    return config
})

//响应拦截:返回的是前端收到的数据
axios.interceptors.response.use(res=>{
    //每次请求都要打印
    console.log("===此处是响应拦截,本次请求的地址是:"+res.config.url);
    console.log(res);

    //每次失败都要弹一下失败
    if(res.data.code!==200){
        alert(res.data.msg)
    }

    //返回的是给前端的
    return res;
})


7.import

// 1个文件,只能有1个export default 
// import a from "./a"
export default 10;


// 1个文件可以有很多个export
// import {login,register,home} from "./a"
export let login="123"export let register=[1,2,3]
export let home=()=>{
    return 10;
}

8.封装

1.src/http/http.js 环境配置 请求拦截 响应拦截 get post

//环境配置 请求拦截 响应拦截 get post
// 1.引入依赖包
import axios from "axios"
import Vue from "vue"
import qs from "querystring"

// 2.环境配置
if (process.env.NODE_ENV === "development") {
    Vue.prototype.$pre = "http://localhost:3000"
}

if (process.env.NODE_ENV === "production") {
    Vue.prototype.$pre = ""
}

// 3.请求拦截 
axios.interceptors.request.use(config => {
    config.headers.token = "123"
    return config;
})

// 4.响应拦截
axios.interceptors.response.use(res => {
    //打印
    console.log("本次请求地址:" + res.config.url);
    console.log(res);

    //失败处理
    if (res.data.code !== 200) {
        alert(res.data.msg)
    }

    return res;
})
/**
 * eg:get("/api/getCate",{}).then(res)
 *
 * @param {*} url 请求地址
 * @param {*} [params={}] 请求参数集合
 */
export function get(url,params={}) {
    return axios({
        url, 
        method: "get",
        params
    })
}
 
/**
 * eg:post("/login",{phone:"123"}).then(res=>{})
 *
 * @param {*} url 请求地址
 * @param {*} [data={}] 请求参数集合,默认是{}
 * @param {boolean} [isFile=false] 用来判断是否有文件,有就传true;没有不需要传参
 * @returns
 */
export function post(url,data={},isFile=false){
    let params=null;
    //有文件
    if(isFile){
        params=new FormData();
        for(let i in data){
            params.append(i,data[i])
        }
    }else{
        //无文件
        params=qs.stringify(data)
    }

   return axios({
        url,
        method:"post",
        data:params
    })
}

2.src/http/api.js

import {get,post} from "./http"
//登录
export const reqLogin=(user)=>post("/api/login",user)

//注册
export const reqRegister=user=>post("/api/register",user)

//分类
export const reqHomeCate=()=>get("/api/getCate")

//列表
export const reqList=(params)=>get("/api/getgoodlist",params)

//详情
export const reqDetail=params=>get("/api/getgoodsinfo",params)

day09

UI框架

1.布局 ;2. 表单; 3.展示数据;4.js反馈; 5.常用的界面组件

1.element-ui 饿了么 PC

1.官网

https://element.eleme.cn/#/zh-CN

2.安装
npm i element-ui -S
3.引入 main.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
4.使用
<el-button type="danger">123</el-button>

2.iview PC端

http://v1.iviewui.com/

3.vant 有赞 移动端

https://vant-contrib.gitee.io/vant/#/zh-CN/

day11

vuex

1.Vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

2.安装

npm i vuex --save

3.创建仓库

1.src/store/index.js

// 1.安装 npm i vuex --save
// 2.引入
import Vue from 'vue'
import Vuex from "vuex"
Vue.use(Vuex)

export default new Vuex.Store({
    //公共状态
    state: {
        name: "妲己",
        age: 20
    },
    //修改状态的方法
    mutations: {
        changeWang(state) {
            state.name = "王昭君"
        },
        changeAge100(state) {
            state.age = 100
        }
    }
})

2.main.js引入仓库

import store from "./store"
new Vue({
  render: h => h(App),
  //3.创建仓库
  store
}).$mount('#app')

3.组件就可以使用

<div class="box">
      <h3>this is a</h3>
      <div>name:{{$store.state.name}}</div>
      <div>age:{{$store.state.age}}</div>
      <button @click="$store.commit('changeWang')">王昭君</button>
      <button @click="$store.commit('changeAge100')">age=100</button>
  </div>

4.使用

0.仓库
export default new Vuex.Store({
  //状态集合:数据
  state: {
    name: "妲己",
    age: "20",
    arr: []
  },
  //将state的数据导出给组件
  getters: {
    name(state) {
      return state.name
    },
    age(state){
      return state.age
    },
    arr(state){
      return state.arr
    }
  },
  //修改状态:只有mutations里面的方法才能修改state
  //只能做同步
  mutations: {
    /**
     * @param {*} state 当前仓库的集合
     * @param {*} name 你传递的参数
     */
    changeName(state, name) {
      state.name = name;
    },
    changeAge(state, age) {
      state.age = age;
    },
    changeArr(state, arr) {
      state.arr = arr
    }
  },
  //逻辑 :处理异步,不能直接修改state
  actions: {
    /*
     * @param {*} context 当前仓库本身
     */
    changeName2s(context) {

      setTimeout(() => {
        context.commit("changeName", '西施')
      }, 2000)
    },
    changeName(context, name) {
      context.commit("changeName", name)
    },
    //请求list
    reqArr(context) {
      setTimeout(() => {
        context.commit("changeArr", [1, 2, 3, 4])
      }, 300)
    }
  },
  modules: {
  }
})

1.取数据
$store.state.name
2.触发mutations
$store.commit('changeName','貂蝉')
3.触发actions
$store.dispatch("changeName",'貂蝉')
4.mutations VS actions
mutations 同步                     可以修改state 通过commit()   第一个参数是状态集合(state)
actions   逻辑(同步和异步都可以做) 不能修改state 通过dispatch() 第一个参数是仓库对象(store)
5.从getters中取数据
$store.getters.name
6.流程 单向数据流
state--(getters)-->compoonents--(dispatch)-->actions--(commit)-->mutations--修改-->state--更新-(getters)--->components -...

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8US5FQDY-1659630008219)(/Users/haoliuting/Desktop/0308/day11-vuex/笔记/vuex.png)]

7.辅助函数 mapGetters mapActions

1.数组方式

<script>
import { mapGetters, mapActions } from "vuex";
export default {
  // 通过mapGetters可以将getters上的数据成批导入给computed
  computed: {
    ...mapGetters(["name", "age", "arr"]),
    a(){
        return 10;
    }
  },
  //   通过mapActions可以将actions上方法成批导入给methods
  methods: {
      ...mapActions([
          "changeName2s",
          "changeName"
      ]),
      fn(){}
  },
};
</script>

2.json方式

import { mapGetters, mapActions } from "vuex";
export default {
  // 通过mapGetters可以将getters上的数据成批导入给computed
  computed: {
    ...mapGetters({
        username:"name",
        userage:"age",
        arr:"arr"
    }),
   
  },
  //   通过mapActions可以将actions上方法成批导入给methods
  methods: {
      ...mapActions({
          cname:"changeName"
      }),
      fn(){}
  },
};
8.modules 模块
new Vuex.Store({
	modules:{
		home:{
      namespaced:true,//命名空间  有了它,就可以通过“home/list”
      state:{},
      mutations:{},
      actions:{},
      getters:{}
    }
	}
})
9.目录结构
-store
	index.js //创建仓库并导出
	actions.js //根级别下的actions
	mutations.js //根级别下的mutations state getters
	-modules //模块
		home.js //home模块
		shop.js
10.vuex VS 本地存储
vuex 刷新就没有了  操作数据方便  实时渲染
本地存储 刷新还在   操作不方便    不具备实时渲染

本地存储和vuex同步

11.注意

vuex是单项数据流

v-model双向数据绑定.

如果遇到了表单,就使用v-model,此处不能使用vuex.

5.案例

1.创建项目
vuex vue-router css-pre (less)
2.配置代理
3.重置项目

1.App.vue

<template>
  <div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {

}
</script>

2.views 文件都删除

3.router/index.js 重置

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
  
]

const router = new VueRouter({
  routes
})

export default router

4.components 里的文件都删除

4.http
5.store
-store
	index.js //创建仓库并导出
	actions.js //根级别下的actions
	mutations.js //根级别下的mutations state getters
	-modules //模块
		home.js //home模块
		shop.js

index.js

import Vue from 'vue'
import Vuex from 'vuex'


//引入
import {actions} from "./actions"
import {state,getters,mutations} from "./mutations"
import home from "./modules/home"
Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations,
  actions,ç
  getters,
  modules: {
    home
  }
})

actions.js

export const actions={
    
}

Mutations.js

export const state={}
export const getters={}
export const mutations={}

Home.js

const state = {}

const getters = {}

const mutations = {

}

const actions = {}

export default {
    state,
    getters,
    mutations,
    actions,
    namespaced: true
}
6.home下的banner

1.设计状态层 store/modules/home.js

import {reqBanner,reqHomeCate} from "../../http/api"
const state = {
    //1.轮播图初始值
    banner: [],
   
}

const getters = {
    // 2.导出banner
    banner(state) {
        return state.banner
    },
    
}

const mutations = {
    //3.修改banner
    changeBanner(state, banner) {
        state.banner = banner;
    },
    
}

const actions = {
    //4.请求banner
    reqBanner(context){
        reqBanner().then(res=>{
            //5.触发修改banner
            context.commit("changeBanner",res.data.list)
        })
    },
   
}

2.home.vue使用

<template>
  <div>
      <h3>首页</h3>
      <img v-for="item in banner" :key="item.id" :src="$pre+item.img" alt="">
  </div>
</template>

<script>
import {mapGetters,mapActions} from "vuex"
export default {
  computed:{
    ...mapGetters({
      banner:"home/banner",
    })
  },
  methods:{
    ...mapActions({
      reqBanner:"home/reqBanner",
    })
  },
  mounted(){
    this.reqBanner()
  }
}
</script>
7.home下的cates

1.store/modules/home.js 设计cates

import {reqBanner,reqHomeCate} from "../../http/api"
const state = {
   
    // 6.分类初始值
    cates:[]
}

const getters = {
   
    //7.导出cates
    cates(state){
        return state.cates
    }
}

const mutations = {
  
    //8.修改
    changeCates(state,cates){
        state.cates=cates
    }
}

const actions = {

    //9.请求分类
    reqCates(context){
       reqHomeCate().then(res=>{
           context.commit("changeCates",res.data.list)
       })
    }
}

2.组件使用

<template>
  <div>
      <h3>首页</h3>
      <img v-for="item in banner" :key="item.id" :src="$pre+item.img" alt="">
      <div v-for="item in cates" @click="$router.push('/list?id='+item.id)" :key="item.catename">{{item.catename}}</div>
  </div>
</template>

<script>
import {mapGetters,mapActions} from "vuex"
export default {
  computed:{
    ...mapGetters({
    
      cates:"home/cates"
    })
  },
  methods:{
    ...mapActions({
      
      reqCates:"home/reqCates"
    })
  },
  mounted(){
    this.reqCates()
  }
}
</script>

<style>
img{
  width: 100px;
  height: 100px;
}
</style>
8.登录 登出

1.分析:用户信息很多地方都要用,存vuex方便使用,但是vuex刷新就没有了,所以本地存储同步一份。刷新就将本地存储赋值给vuex。

2、store/mutations.js 初始化数据

export const state={
    // 本地存储有值,就取出来给user;没有,user={}
    user:sessionStorage.getItem("user")?JSON.parse(sessionStorage.getItem("user")):{}
}
export const getters={
    user(state){
        return state.user
    }
}
export const mutations={
    changeUser(state,user){
        state.user=user;
    }
}

3.store/actions.js 处理逻辑 进行同步

export const actions={
    changeUser(context,obj){
        // 1.vuex存起来
        context.commit("changeUser",obj)

        // 2.本地存储 :vuex和本地存储同步
        if(obj.token){
            sessionStorage.setItem("user",JSON.stringify(obj))
        }else{
            sessionStorage.removeItem("user")
        }
        
    }
}

4.登录成功 login.vue 存值

 methods: {
    ...mapActions({
      changeUser: "changeUser",
    }),
    login() {
      reqLogin(this.user).then((res) => {
        if (res.data.code == 200) {
          //存vuex 存本地存储
          this.changeUser(res.data.list);
          //跳转
          this.$router.push('/home')
        }
      });
    },
  },

5.登出 mine,vue

computed: {
    ...mapGetters({
        user:"user"
    }),
  },
  methods: {
    ...mapActions({
        changeUser:"changeUser"
    }),
    logout(){
        this.changeUser({})
    }
  },
9.登录拦截【router/index.js】
import store from "../store"

//登录拦截
router.beforeEach((to,from,next)=>{
  if(to.path=="/"){
    next()
    return;
  }
  //判断仓库user是否有token
  if(store.getters.user.token){
    next()
    return;
  }
  next("/")
})
10.掉线处理[src/http/http.js]
import store from "../store"
import router from "../router"
// 3.请求拦截 
axios.interceptors.request.use(config => {
    if (config.url !== "/api/login" && config.url !== "/api/register") {
        config.headers.authorization = store.getters.user.token

    }
    return config;
})

// 4.响应拦截
axios.interceptors.response.use(res => {
    //打印
    console.log("本次请求地址:" + res.config.url);
    console.log(res);

    //失败处理
    if (res.data.code !== 200) {
        Toast(res.data.msg)
    }
    //掉线处理
    if(res.data.msg==="登录已过期或访问权限受限"){
        router.replace("/login")
    }

    return res;
})

6.css预处理

1.创建项目 选择了预处理器(less)

2.创建less文件夹,处理预处理

-less
	index.less //整合所有的less
	color.less //颜色
	size.less //大小
	table.less //表格
	form.less //表单
	text.less //文本

3.组件使用

<style lang="less">
@import "../less/index.less";
.title{
  font-size: @h3;
  color: @primary;
  margin: @margin;
}
img{
  width: 100px;
  height: 100px;
}
</style>

day15

1.vue初探

官网:https://cn.vuejs.org/
介绍:

vue是渐进式 JavaScript 框架

渐进式 :主张最少。

优点:
1.轻量级的数据框架
2.双向数据绑定
3.提供了指令
4.组件化开发
5.客户端路由
6.状态管理
缺点:
1.Vue 底层基于 Object.defineProperty 实现响应式,而这个 api 本身不支持 IE8 及以下浏 览器,所以Vue不支持IE8及其以下浏览器;
2.Vue 打造的是SPA,所以不利于搜索引擎优化(SEO);
3.由于 CSR(客户端渲染)的先天不足,导致首屏加载时间长,有可能会出现闪屏。
核心:
数据驱动 组件系统
MVVM:
M-model模型
V-view视图
VM-viewModel 视图模型
模型(model)通过了视图模型  决定了视图(view)
视图(view)  通过视图模型 修改模型 (model) 
视图模型是模型和视图之间的桥梁。
SPA:

single page application 单页面应用

优点:加载快,用户体验好

缺点:不利于SEO,首屏加载时间长

a页面—>index.html/#/a

b页面—>index.html/#/b
MPA:

多页面应用

优点:有利于SEO

缺点:会有白屏,用户体验不好

a页面—>a.html

b页面—>b.html

day16

1.react介绍

1.1简介

React 是Facebook内部的一个JavaScript类库。
React 可用于创建Web用户交互界面。
React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式。
React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。
React 引入了虚拟DOM(Virtual DOM)的机制。
React 引入了组件化的思想。
React 使用Facebook专门为其开发的一套语法糖--JSX。

1.2优缺点

优点:
● React速度很快
react并不直接对DOM进行操作,引入了一个叫做虚拟DOM的概念,安插在javascript逻辑和实际的DOM之间,性能好。
● 跨浏览器兼容
虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。
● 一切皆是组件
代码更加模块化,重用代码更容易,可维护性高。
● 单向数据流
Flux是一个用于在JavaScript应用中创建单向数据层的架构,它随着React视图库的开发而被Facebook概念化。
● 同构、纯粹的javascript
因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。
缺点:
React不适合做一个完成的框架。
React本身只是一个V而已,并不是一个完整的框架,所以如果是大型项目想要一套完整的框架的话,基本都需要加上ReactRouter和Flux才能写大型应用。

1.3 react 解决了什么问题?

1.在组件化方面,react天生组件化,这是React的核心,除了能够在团队内部积累业务组件以外,也能找到众多开源组件的实现。
2.在模块化方便,基于webpack可以使用ES6或者CommonJs的写法实现模块化代码;
3.在开发效率方面,react的代码基本就是组件的组合,分而治之的方式让代码的可读性很高,容易理解。
而且相比于MVC几乎是祛除了Controller的角色,只用关心render函数,不用关心视图局部的修改;
4.在运行效率方面,React实现了Virtual DOM,相比较MVVM框架具有更优的效率;
5.在可维护性方面,React基于flux或者redux的架构设计,确定性的store很容易定位问题,无论是新增业务代码还是查找业务代码都不再是难题;
6.在用户体验方面,基于React很容易实现SPA,提高用户体验。

2.脚手架

//安装脚手架
npm i create-react-app -g

//创建项目
create-react-app demo //demo是项目名

//进入项目
cd demo 

//启动
npm start
目录
-demo
	-node_modules 依赖包
	-public 静态资源 
		index.html 
	-src	源代码
		index.js // 入口文件
		app.js //根组件
		

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

App.js

function App() {
  return (
    <div className="App">
     123
    </div>
  );
}

export default App;

3.JSX语法

1.非表单元素 div
{/* {} 可以绑定数据、方法、表达式 */}
<div>姓名:{name}</div>
<div>价格:{price.toFixed(2)}</div>
<div>{1 > 2 ? name : name2}</div>

2.属性绑定:img 属性绑定 <标签 属性名={变量}>
<img src={img} alt="" />
      <a href={website.url} aaa={website.name}>
        <img src={website.logo} alt="" title={website.name} />
      </a>
3.条件渲染
let arr = [1, 2, 3, 4];
let isshow = false;
{/* 条件渲染   {三元判断} ,如果没有内容展示,就定为null*/}
{arr.length > 0 ? <div>有数据</div> : <div>暂无数据</div>}
{isshow ? <div>弹框</div> : null}
4.列表渲染
let news = [
  {
    id: 1,
    name: "7.1建党",
    con: "热泪庆祝建党100周年",
  },
  {
    id: 2,
    name: "马嘉祺学霸",
    con: "分数307",
  },
  {
    id: 3,
    name: "上天",
    con: "好多人上天",
  },
];
{news.map((item) => {
  return <div key={item.id}>{item.name}</div>;
})}
5.动态类名
{/* 动态类名 1.使用className代替class; 2.语法 className={三元} */}
<div className={10 > 11 ? "red" : "blue"}>小车车</div>
{news.map((item, index) => {
  return (
    <div key={item.id} className={index % 2 == 0 ? "red" : "blue"}>
      {item.name}
    </div>
  );
})}

6.动态行间样式
let bg = "pink";
{/* 行间样式 语法:style={json} */}
<h3>行间样式</h3>
<div style={{ background: "#000", color: "#fff" }}>天道酬勤</div>
<div style={{ background: bg }}>商道酬信</div>
7.注释
{/* 注释 */}
  1. jsx 中遇到< html解析,遇到了{ js解析
  2. 如果你的js不能直接出一对标签,后缀名改为jsx;

4.组件

1.如何注册?

1.函数注册

function First(props) {
  return (
    <div className="box">
     
    </div>
  );
}

export default First;

2.类定义注册

import React,{Component} from "react"

class Second extends Component{  
    constructor(){      
        super();//构造函数必须加super()
    }
   
    //渲染钩子函数
    render(){
       
        return (
            <div className="box">
                <h3>this is second -{this.name}--{age}--{this.name2}--{name3}--{this.name4}</h3>
            </div>
        )
    }
}

export default Second;
2.注意点
// 1.一个组件的模板,只能有一个根节点;
// 2.组件名首字母要大写
// 3.可以以已经存在的标签的大写命名。eg:Form
// 4.组件名中间有大写,原样调用即可。eg:WebsiteNav
// 5.注册组件:(1)函数定义组件 (2)类定义
3.类定义 VS 函数定义
(1)类定义组件 有生命周期,函数定义的没有;
(2)类定义有state,函数定义没有;
(3) 对于父组件传递的数据,类定义组件通过this.props接收,函数定义通过props接收。
(4)对于类定义,每调用一次,就会实例化一个对象,而对于函数组件,只是单纯的计算,所以函数的性能高于类定义。
home 类 业务组件
    banner 函数 木偶组件
    list 函数

5.事件处理

1.如何绑定事件?
{/* 1.如何绑定事件? 
        (1)箭头函数:不用管this
        (2)bind: 第一个参数是调用该函数的对象,一般使用this.
        */}
<button onClick={() => this.fn1()}>箭头函数绑定fn1</button>
<button onClick={this.fn1.bind(this)}>fn1</button>
2.如何传参?
{/* 2.如何传参?
        (1)箭头函数:正常传参
        (2)bind: 第2个实参对应第1个形参
        */}
<button onClick={() => this.fn2(3, 5)}>箭头函数传参:3+5</button>
<button onClick={this.fn2.bind(this, 10, 20)}>bind传参:10+20</button>
3.event 事件对象如何获取?
{/* 3.event 事件对象如何获取? 
        (1)显示传参: 箭头函数,event想在哪一位就在哪一位
        (2)隐式传参:bind 没有参数的第一位是event. event永远都在最后一位
        */}
<button onClick={(e) => this.getEvent(e)}>箭头函数获取event</button>
<button onClick={this.getEvent.bind(this)}>bind获取event</button>
<button onClick={(e) => this.getEvent2(10, e)}>
  箭头函数获取event,10
</button>
<button onClick={this.getEvent2.bind(this, 10)}>bind获取event</button>
4.阻止默认 阻止传播?
{/* 4.阻止默认 阻止传播?
        (1)阻止默认:e.preventDefault() 注意:return false 不可以;
        (2)阻止传播:e.stopPropagation()
        (3)捕获事件:Capture.eg:onClickCapture
        */}
<div className="redBox" onContextMenu={(e) => this.yj(e)}></div>

<div className="outer" onClick={() => this.outerClick()}>
  <div className="redBox" onClick={this.innerClick.bind(this)}>
    冒泡
  </div>
</div>

<div className="outer" onClickCapture={() => this.outerClick2()}>
  <div className="redBox" onClickCapture={() => this.innerClick2()}>
    捕获
  </div>
</div>

6.state

// 1.初始化在constructor
// 2.取值:var { name, age, sex } = this.state;
// 3.如果state要全部传递给子组件,可以使用 <Child {...this.state}></Child>
// 4.修改state数据需要调用setState(),
// 5.setState()的调用会引起render的重新执行,所以render中一定不可以调用setState(),否则会引起死循环
// 6.修改数组:不要直接操作数组。 1.取;2.做;3.放
// 7.修改json:建议使用 ...
// 8.setState()是异步的,如果想要获取修改后的值,需要在回调函数中获取
调用 setState 之后发生了什么?

在代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

7.面试题

1.react的优缺点

2.react中组件如何创建?

3.函数定义组件和类定义组件的区别?

4.setState()中第二个参数为什么是个回调函数?

5.setState()调用后发生了什么?

8.作业:

练习:3遍

day17

1.props

1.取值:let { name, age, sex,json:{x,y} ,changeWang} = this.props;
2.props不仅可以传递属性,也可以传递方法
3.组件嵌套的内容,在this.props.children  { this.props.children}
4.super(props)的作用:(1) super() 用了继承,所以需要super();(2)super(props)为了在构造函数中使用this.props。
5.如果想把props全部向下传递,使用{...this.props} 
state VS props
state:自己的状态,可以修改,而且需要通过setState()。state变了,页面会重新渲染
props:父组件传递过来的状态,不可以直接修改。props变了,页面会重新渲染

2.组件通信(父子)

父传子

父传子:父组件通过自定义属性传递数据,子组件通过props接收

父组件
 <Child name={this.state.name}></Child>
子组件
  let { name} = props;
子传父

子传父:父组件通过自定义属性传递了方法,子组件通过props调用

父组件
<Child
          changeWang={() => this.changeWang()}
          cname={(name) => this.changeName(name)}
        ></Child>
子组件
export default function Child(props) {
  let {  changeWang,cname } = props;
  return (
    <div className="box">
      <h3>this is child</h3>
      <button onClick={()=>changeWang()}>王昭君</button>
      <button onClick={()=>cname('貂蝉')}>貂蝉</button>
    </div>
  );
}

3.生命周期

1.初始期
    (1)constructor:初始化数据
    (2)render:渲染DOM
    初始期:渲染DOM节点; 更新期:调和过程,进行diff算法,计算出最优的更新方式,局部更新
    (3)componentDidMount:渲染完成:计时器、请求打开,window|document添加事件、获取DOM
2.更新期(state、props)
    shouldComponentUpdate() : 判断是否更新
        (1)没有return内容,报错
        (2return true. 更新流程 shouldComponentUpdate->render->componentDidUpdate
        (3) return false.更新流程:shouldComponentUpdate
    render() 调和过程,进行diff算法,计算出最优的更新方式,局部更新
    componentDidUpdate() 更新完成
3.销毁期
    componentWillUnmount() 销毁之前:清除计时器 取消window|document事件

4.表单【受控组件 非受控组件】

1.受控组件
特点:
就地反馈,如:验证
禁用按钮 如:提交
执行特定的输入格式 如:身份证号、银行卡号
取值赋值
特征取值赋值
Input type=“text”e.target.valuevalue
Input type=“radio”e.target.valuechecked
Input type=“checkbox”e.target.value |e.target.checkedchecked
Selecte.target.valueValue
Textareae.target.valueValue
 //3.修改user
  changeUser(e, key) {
    let value = e.target.value;
    //如果是isAgree,需要用checked取值
    if (key === "isAgree") {
      value = e.target.checked;
    }
    //如果是爱好,处理一段逻辑
    if (key === "hobby") {
      value = this.state.user.hobby;
      // 如果点的框选中,user.hobby添加一条数据;如果从选中到取消,删除这条数据
      if (e.target.checked) {
        value.push(parseInt(e.target.value));
      } else {
        //将value中数据和parseInt(e.target.value)一样的那条数据删除
        value.splice(
          value.findIndex((item) => item === parseInt(e.target.value)),
          1
        );
      }
    }

    //电话号处理
    if (key === "tel") {
      let {
        user: { tel },
      } = this.state;
      // 原来的是小于3位,+空格;如果原来的是大于3位,不+空格
      if (value.length === 3 && tel.length < 3) {
        value += " ";
      }
      // 原来的是小于8位,+空格;如果原来的是大于8位,不+空格
      if (value.length === 8 && tel.length < 8) {
        value += " ";
      }
    //   如果大于13位,就是13位
      if (value.length > 13) {
        value = value.slice(0, 13);
      }
    }
    this.setState({
      user: {
        ...this.state.user,
        [key]: value,
      },
    });
  }

2.各个表单使用
<div>
          {/* 对于单选框来说,需要自己手动设置value;赋值使用的是checked */}
          性别:
          <input
            type="radio"
            name="sex"
            onChange={(e) => this.changeUser(e, "sex")}
            value="0"
            checked={user.sex === "0"}
          />
          男
          <input
            type="radio"
            name="sex"
            onChange={(e) => this.changeUser(e, "sex")}
            value="1"
            checked={user.sex === "1"}
          />
          女
        </div>
        <div>
          {/* 多选框,取值checked+value;赋值:checked */}
          爱好:
          {hobbyList.map((item) => (
            <label key={item.id}>
              <input
                type="checkbox"
                value={item.id}
                onChange={(e) => this.changeUser(e, "hobby")}
                checked={user.hobby.includes(item.id)}
              />
              {item.name}
            </label>
          ))}
        </div>
        <div>
          {/* select 取值 value;赋值 value  */}
          职业:
          <select onChange={(e) => this.changeUser(e, "job")} value={user.job}>
            <option value="" disabled>
              --请选择--
            </option>
            {jobList.map((item) => (
              <option key={item.id} value={item.id}>
                {item.name}
              </option>
            ))}
          </select>
        </div>
        <div>
          {/* textarea 取值value;赋值 value */}
          描述:
          <textarea
            ols="30"
            rows="10"
            onChange={(e) => this.changeUser(e, "des")}
            value={user.des}
          ></textarea>
        </div>
3.对比受控和非受控组件
特征受控组件非受控组件
一次性检索(例如表单提交)
及时验证
有条件的禁用提交按钮
执行输入格式
一个数据的几个输入
动态输入

5.安装cnpm

npm install -g cnpm --registry=https://registry.npm.taobao.org

day18

1.ref

React.createRef()

(1)创建一个ref对象;(2)将ref对象绑定到节点(3)操作 this.div.current

1.获取原生DOM节点
 constructor() {
    super();
    // 创建一个ref对象
    this.div = React.createRef();
  }
 <div className="red" ref={this.div}></div>
changeColor(color) {
  let div = this.div.current;
  div.style.background = color;
  div.innerHTML = color;
}
2.获取子组件实例
constructor() {
    super();
    // 创建一个ref对象
    this.child=React.createRef()
  }
<Child ref={this.child}></Child>
 changeChildName(name){
    this.child.current.changeName(name)
  }

2.组件优化

1.Fragment

目的:所有的组件都有一个根节点,但是有时候希望这个节点不存在,就可以使用Fragment

1.Fragment

引入 使用

import React, { Component,Fragment } from 'react';

class First extends Component {
    render() {
        return (
            <Fragment>
                <h3>fragment</h3>
                <p>哈哈</p>
                <p>嘿嘿嘿</p>
            </Fragment>
        );
    }
}

export default First;
2.<></>
import React, { Component } from 'react';

class First extends Component {
    render() {
        return (
            <>
                <h3>fragment</h3>
                <p>哈哈</p>
                <p>嘿嘿嘿</p>
            </>
        );
    }
}

export default First;
2.需求:父组件给子组件传了数据,父组件没有传递给子组件的数据变化了,也会引起子组件的重新渲染,我们希望子组件不要渲染,如果是子组件接收的数据 变了,子组件才重新渲染。
1.shouldCompnentUpdate-类定义组件
shouldComponentUpdate(nextProps,nextState){
        // 判断旧的props上的name是否和新的props上的name一样。如果一样,就不渲染了
        if(this.props.name===nextProps.name){
            return false
        }
        return true;
    }
2.pureComponent -类定义组件
1.PureComponent 浅比较,如果传递的数据是引用类型,引用类型的改变,建议需要使用 拷贝 
2.PureComponent 如果props发生了改变,会计算,如果是state发生了改变,也会计算。
如果一个组件有state,建议使用Component +shouldComponentUpdate 
如果一个组件只有props,建议使用PureComponent 【木偶组件 】
import React, { PureComponent } from 'react';
class Child2 extends PureComponent {
    
    render() {
        console.log("child2 render");
        let {name,arr}=this.props
        return (
            <div className="box">
                <h3>PureComponent</h3>
                <div>name:{name}</div>
                <div>arr:{JSON.stringify(arr)}</div>
            </div>
        );
    }
}

export default Child2;
3.React.meno()-函数定义组件
React.memo()  浅比较,如果传递的数据是引用类型,引用类型的改变,建议需要使用 拷贝 
React.memo() 本身是一个函数, 参数是个组件,返回一个新的组件,这样的函数叫高阶组件(HOC
function Child3(props) {
  let { name ,arr} = props;
  console.log("child3 开始计算");
  return (
    <div className="box">
      <h3>child3</h3>
      <div>name:{name}</div>
      <div>arr:{JSON.stringify(arr)}</div>
    </div>
  );
}
export default React.memo(Child3)
3.componentDidCatch 错误边界处理

1.封装了一个组件ErrorBoundary.jsx

import React, { Component } from 'react';

class ErrorBoundary extends Component {
    constructor(){
        super()
        //初始认为没有报错
        this.state={
            hasError:false
        }
    }
    componentDidCatch(){
        //此时,有报错了
        this.setState({
            hasError:true
        })
       
    }
    render() {
        let {hasError}=this.state
        return (
            <div>
                {
                    hasError?<div>此处有报错!!</div>:this.props.children
                }
            </div>
        );
    }
}

export default ErrorBoundary;

2.使用组件ErrorBoundary 包裹可能出错的组件

<ErrorBoundary>
  <List></List>
</ErrorBoundary>
4.HOC 高阶组件
HOC 高阶组件:增强原来的组件
    1.本身是个函数
    2.参数 是个组件
    3.返回值也是个组件

封装的withRequest.js

import React, { Component } from "react"
import axios from "axios"
export default url => C => {
    return class MyCom extends Component {
        constructor() {
            super()
            this.state = {
                arr: []
            }
        }
        componentDidMount() {
            axios({
                url: url
            }).then(res => {
                this.setState({
                    arr: res.data.d
                })
            })
        }
        render() {
            let { arr } = this.state
            return (
                <>
                    <C arr={arr}></C>
                </>
            )
        }
    }
}

调用

let RequestList=withRequest("/mock/like.json")(List)
let RequestBanner=withRequest("/mock/banner.json")(Banner)

day19

1.路由

1.路由模式 【hash history】
import {HashRouter,BrowserRouter} from "react-router-dom"

ReactDOM.render(
  <HashRouter>
    <App />
  </HashRouter>
  ,
  document.getElementById('root')
);
2.路由出口【Switch】
import {Switch} from "react-router-dom"
<Switch></Switch>
3.路由规则【Route】
import {Route} from "react-router-dom"
 <Route exact strict path="/register" component={Register}></Route>
 <Route path="/index" component={Index}></Route>
Route 的属性 :exact[是否精确匹配] 默认:false。 如果要精确匹配,需要设置exact 
strict:严格模式。 需要搭配exact使用。 默认是路径后可以加'/',也可以访问,加上严格模式,有'/'就不行 
4.重定向【Redirect】
import {Redirect } from "react-router-dom"
{/* 4.重定向 */}
 <Redirect to="/"></Redirect>
5.路由导航【Link NavLink(activeClassName activeStyle)】
<Link to="/search">搜索</Link>
<NavLink to="/search">搜索</NavLink>
高亮效果:
<footer className="index-footer">
  <NavLink to="/index/home" activeClassName="select">首页</NavLink>
  <NavLink to="/index/cate" activeClassName="select">分类</NavLink>
  <NavLink to="/index/shop" activeClassName="select">购物车</NavLink>
</footer>
<footer className="index-footer">
  <NavLink to="/index/home" activeStyle={{color:"orange"}}>首页</NavLink>
  <NavLink to="/index/cate" activeStyle={{color:"orange"}}>分类</NavLink>
  <NavLink to="/index/shop" activeStyle={{color:"orange"}}>购物车</NavLink>
</footer>
6.编程式导航【push replace go 】
this.props.history.push("/search"); //添加新的历史记录
this.props.history.replace("/search"); // 用新的历史记录替换当前历史记录
this.props.history.goBack(); //返回
this.props.history.go(-1);// 返回
注意:
1.如果是路由组件,可以直接使用编程式导航;
2.如果不是路由组件,想要使用编程式导航,有2种方式:
	① 路由组件传递props给非路由组件;
		<GoBack {...this.props}></GoBack>
	②使用withRouter

withRouter

import React, { Component } from 'react';
import {withRouter} from "react-router-dom"
class GoBack extends Component {
    goBack(){
        console.log(this.props);
        // this.props.history.goBack()
        this.props.history.go(-1)
    }
    render() {
        return (
            <button onClick={()=>this.goBack()}>封装的返回</button>
        );
    }
}

export default withRouter(GoBack);
7.路由传参【?】
 <Link to={`/cateList?id=1&name=222`}>{item.name}</Link>
取参数:

1.原生js

componentDidMount(){
        let str=this.props.location.search;//"?id=2&name=qqq&age=122" --{id:"2",name:"qqq",age:"122"}
        // 1.利用原生js
        
        let substr=str.slice(1);//"id=2&name=qqq&age=122"
        let arr=substr.split("&");// ['id=2','name=qqq','age=122']
        let result={}
        arr.forEach(item=>{
            let subArr=item.split("=");//["id","2"]
            result[subArr[0]]=subArr[1]  
        })
        console.log(result);
    }

2.node questring

import querystring from "querystring"
componentDidMount(){
        let str=this.props.location.search;//"?id=2&name=qqq&age=122" --{id:"2",name:"qqq",age:"122"}
        
        // 2.node querystring.parse()
        let result=querystring.parse(str.slice(1))
        console.log(result);


    }

3.URLSearchParams

componentDidMount(){
        let str=this.props.location.search;//"?id=2&name=qqq&age=122" --{id:"2",name:"qqq",age:"122"}

        // 3.原生js
        let params=new URLSearchParams(str);
        console.log(params.get("id"));
        console.log(params.get("name"));

    }
8.动态路由【:】
 <Link to={`/detail/1`}>{item.name}</Link>
<Route path="/detail/:id" exact component={Detail}></Route>
let id=this.props.match.params.id

9.全局守卫【登录拦截】

1.登录成功的时候设置一个标识【login.jsx】

 login = () => {
   
    let {
      user: { phone, password },
    } = this.state;
    if (phone === "admin" && password === "123") {
        //存一个标识,用来判断是否登录
        sessionStorage.setItem('islogin',1)
      this.props.history.push("/index/home");
    } else {
      alert("error");
    }
  };

2.封装了一个PrivateRoute ,判断是否登录,觉得出规则还是重定向

import React, { Component } from 'react';
import {Route,Redirect} from "react-router-dom"
class PrivateRoute extends Component {
    render() {
        let islogin=sessionStorage.getItem("islogin");//'1' null
        return (
            <>
                {
                    islogin?<Route {...this.props}></Route>:<Redirect to="/login"></Redirect>
                }
            </>
        );
    }
}

export default PrivateRoute;

3.需要拦截的用PrivateRoute写规则【App.jsx】

<Switch>
  {/* 路由规则 */}
  <Route path="/login" component={Login}></Route>
  {/* exact 精确匹配,默认false,如果没设,那么‘/register/a’也会进入'/register'
        strict 严格模式,默认false,如果没有设置,那么“/register/”是可以访问的;如果设置了严格模式,只能访问“/register”
        strict 需要和exact 一起使用
        */}
  <Route path="/register" exact strict component={Register}></Route>
  <PrivateRoute path="/index" component={Index}></PrivateRoute>


  <PrivateRoute path="/search" component={Search}></PrivateRoute>
  <PrivateRoute path="/list" component={List}></PrivateRoute>

  {/* exact精确匹配 */}
  <PrivateRoute path="/detail/:id" exact component={Detail}></PrivateRoute>
  {/* 重定向 */}
  {/* <Redirect to="/login"></Redirect> */}

  {/* 404 404千万不要设置exact,这句写在最后*/}
  <Route path="/" component={NotFound}></Route>
</Switch>

10.路由独享守卫

 <Route path="/search" render={(props)=>{
          let type=sessionStorage.getItem("type")
          if(type==="2"){
            return <Search {...props}></Search>
          }else{
            return <div>你没有权限 !!!!</div>
          }
          
        }}></Route>

11.懒加载

1.通过React.lazy()引入组件

let Login=React.lazy(()=>import("./pages/Login/Login"))
let Index=React.lazy(()=>import("./pages/Index/Index"))

2.需要将规则包裹在React.Suspense 组件中,fallback必填

// 2.React.Suspense  fallback必须的
    <React.Suspense fallback={<div>正在加载。。。</div>}>
      {/* 2.路由出口 */}
      <Switch>
      
        <Route exact path="/" component={Login}></Route>
        <Route path="/index" component={Index}></Route>
       
      </Switch>
    </React.Suspense>

2.数据交互

1.下载
npm i axios --save
2.配置代理

package.json 注意:配置完代理需要重启项目

{
  "proxy":"http://localhost:3000",
  "name": "luyou",
  "version": "0.1.0",
}
3.使用

3.UI库

1.ant design (PC)
2.ant design mobile (移动端)

1.安装

npm install antd-mobile --save

2.修改index.html

<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
  <script>
    if ('addEventListener' in document) {
      document.addEventListener('DOMContentLoaded', function() {
        FastClick.attach(document.body);
      }, false);
    }
    if(!window.Promise) {
      document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"'+'>'+'<'+'/'+'script>');
    }
  </script>

3.入口文件引入样式

import 'antd-mobile/dist/antd-mobile.css'; 

4.使用

4.作业

1.到数据库创建一个数据库,运行orange_data.sql文件

2.修改后端的配置 config/global.js

// 数据库连接参数
exports.dbConfig = {
    host: 'localhost', //数据库地址
    user: 'root',//数据库用户名
    password: '1234',//数据库用户密码
    port: 3306,
    database: '0308_orange' // 数据库名字
}

3.启动

npm i 
npm start //localhost:3000

day20

Redux

1.目的

1.非父子组件通信;
2.组件层和状态层解耦。

2.三大原则

1.单一数据源
2.state是只读的
3.只能通过纯函数修改state

3.目标

React(组件) ----》react-redux《---- redux(状态) ------》redux-thunk 《------ 后端(数据)

4.redux

0.安装
npm i redux --save
1.创建仓库
//引入方法
import { createStore } from "redux"

//初始状态
const initState = {
    name: "妲己",
    age: 20
}


//reducer用来修改state
/**
 *
 *
 * @param {*} state  上一次修改完成后的值,对于第一次来说,没有上一次,默认是初始值
 * @param {*} action 规则-动作
 *             {type:"changeName",name:"王昭君"}
 *             {type:"changeAge",age:100}
 */

function reducer(state = initState, action) {
    switch (action.type) {
        case "changeName":
            state.name = action.name;
            return state;
        case "changeAge":
            state.age = action.age;
            return state;
        default:
            return state;
    }
}

// 创建仓库
let store = createStore(reducer)

//导出仓库
export default store;
2.组件使用

1.store.getState() 取状态的

2.store.dispatch(action) 派发动作 派发任务

this.un= store.subscribe(callback) 添加订阅者

4.this.un() 取消订阅者

3.action creattors
//action creator [vuex actions] 方便组件派发任务
export let actions = {
    changeName: (name) => ({ type: types.CHANGE_NAME, name: name }),
    changeAge: (age) => ({ type: types.CHANGE_AGE, age })
}
<button onClick={()=>store.dispatch(actions.changeAge(200))}>age=200</button>
4.action types
// action types 
const types = {
    // 修改name
    CHANGE_NAME: "CHANGE_NAME",
    // 修改age
    CHANGE_AGE: "changeAge"
}
//action creator [vuex actions] 方便组件派发任务
export let actions = {
    changeName: (name) => ({ type: types.CHANGE_NAME, name: name }),
    changeAge: (age) => ({ type: types.CHANGE_AGE, age })
}
function reducer(state = initState, action) {
    switch (action.type) {
        case types.CHANGE_NAME:
            state.name = action.name;
            return state;
        case types.CHANGE_AGE:
            return {
                ...state,
                age: action.age
            };
        default:
            return state;
    }
}

5.react-redux 关联react和redux

1.安装
npm i react-redux --save
2.通过Provider将store和App关联【入口文件index.js】
import store from "./store"
import {Provider} from 'react-redux'
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
  ,
  document.getElementById('root')
);

3.容器型组件 connect
import React, { Component } from "react";
//容器型组件:通过connect函数将redux中的数据和方法导入到该组件中
import { connect } from "react-redux";
import { actions } from "../store";
class D extends Component {
  render() {
    console.log(this.props);
    let { name, age, changeName, changeAge } = this.props;
    return (
      <div className="box">
        <h1>this is D --react-redux</h1>
        <div>name:{name}</div>
        <div>age:{age}</div>
        <button onClick={() => changeName("宫本")}>宫本</button>
        <button onClick={() => changeAge(300)}>age=300</button>
      </div>
    );
  }
}
let mapStateToProps = (state) => {
  return {
    name: state.name,
    age: state.age,
  };
};
let mapDispatchToProps = (dispatch) => {
  return {
    changeName: (name) => dispatch(actions.changeName(name)),
    changeAge: (age) => dispatch(actions.changeAge(age)),
  };
};
export default connect(mapStateToProps, mapDispatchToProps)(D);

mapStateToProps
参数:store.getState()
返回:一个集合,这个集合会被放到props上
目的:从仓库取数据
mapDispatchToProps
参数:store.dispatch()
返回:一个集合,这个集合会被放到props上
目的:从仓库调用action
4.selectors [vuex getters]
export const getName = state => state.name;
export const getAge = state => state.age

组件使用

let mapStateToProps = (state) => ({
  name: getName(state),
  age: getAge(state),
});
5.bindActionCreators

目的:集中导入所有的actions到组件

import { actions } from "../store";
import { bindActionCreators } from "redux";
let mapDispatchToProps = (dispatch) => ({
  //   changeName: (name) => dispatch(changeName(name)),
  //   changeAge: (age) => dispatch(changeAge(age)),
  methods: bindActionCreators(actions, dispatch),
});

组件使用:

let {
  methods: { changeName, changeAge },
} = this.props;
6.容器型组件 vs 展示型组件
容器型组件: (1)对redux是有感知的;(2)数据和方法来自store;3)类定义组件;(4)路由组件
展示型组件:(1)对redux是无感知的; (2) 数据和方法来自父组件;(3)函数定义组件;(4)木偶组件
7.reselect
npm i reselect --save

使用

import { createSelector } from "reselect"
//导出订单
export const getOrders = state => state.orders;
//导出刷选条件
export const getFilter = state => state.filter;

//reselect 可以减少不必要的计算,只有依赖的数据变了,才计算
export const getShowOrder = createSelector(
    [getOrders, getFilter],
    (orders, filter) => {
        console.log("重新计算展示的订单");
        return filter === 0 ? orders : orders.filter(item => item.status === filter)
    }
)

day22

Hooks 16.8之后

函数定义组件:state 生命周期. hooks只能在函数定义组件中使用,函数组件名首字母要大写。

useState()   
useEffect()
useReducer()
useContext()

1.useState() 状态

const [name, setName] = useState("妲己");

2.useEffect() 生命周期

​ 1.useEffect(callback) callback就是componentDidMount +componentDidUpdate.任何数据变了,都会触发componentDidUpdate

useEffect(() => {
    document.title = "你点了" + num + "次";
  });

2.useEffect(callback,[]) callback是componentDidMount,和后面数组中数据发生改变的时候的更新

​ 如果设置[],那么任何数据的改变,都不会再引起执行,就相当于是componentDidMount()

​ 任何数据变了,都不会再触发componentDidUpdate

useEffect(() => {
    setInterval(()=>{
        setDate(new Date())
    })
    document.title = "你点了" + num + "次";
  }, []);

3.num变了,才会触发componentDidUpdate

useEffect(() => {
      console.log("要重新执行了");
    document.title = "你点了" + num + "次";
  },[num])

4.useEffect(()=>{ return fn },[]) return的函数就是componentWillUnmount

 useEffect(() => {
    let time = setInterval(() => {
      setDate(new Date());
    });

    return ()=>{
        clearInterval(time)
    }
  }, []);

3.useReducer()

状态很多,可以使用useReducer()代替useState()

import React,{useReducer} from 'react';


const initState={
    name:"妲己",
    age:20
}


const reducer=(state,action)=>{
    switch(action.type){
        case "changeName":
            // {type:"changeName",name:'xx'}
            return {
                ...state,
                name:action.name
            }
        case "changeAge":
            // {type:"changeAge",age:100}
            return {
                ...state,
                age:action.age
            }
        default:
            return state;
    }
}

const Reducer = () => {
    const [state, dispatch] = useReducer(reducer, initState)
    return (
        <div>
            <div>name:{state.name}</div>
            <div>age:{state.age}</div>
            <button onClick={()=>dispatch({type:"changeName",name:'王昭君'})}>王昭君</button>
            <button onClick={()=>dispatch({type:"changeAge",age:100})}>age=100</button>
        </div>
    );
}

export default Reducer;

4.useContext()

父组件给子组件传值,子组件没用,子组件传递给了自己的子组件,这样传递,容易出错,可以使用useContext() 让父组件直接将数据传递给子组件的子组件。

1.父组件
import React,{useState} from 'react';
import Child from './Child';

//1.创建一个Context对象
export let MyContext=React.createContext();
const Context = () => {
    const [name, setName] = useState('妲己');
    const [age, setAge] = useState(20);
    return (
        <div className="box">
            <h3>useContext</h3>
            <div>name:{name}</div>
            {/* 2.传值 */}
            <MyContext.Provider value={ {name,age} }>
                <Child ></Child>
            </MyContext.Provider>
            
        </div>
    );
}

export default Context;

2.子组件 (什么都没做)
import React from 'react';
import ChildChild from './ChildChild';
const Child = () => {
    return (
        <div className="box">
            <h3>子组件</h3>
            <ChildChild></ChildChild>
        </div>
    );
}

export default Child;
3.子组件的子组件
import React, { useContext } from "react";

// 3.引入Context对象
import { MyContext } from "./Context";
const ChildChild = () => {

    // 4.取出数据
  const { name, age } = useContext(MyContext);

  return (
    <div className="box">
      <h3>child child</h3>
      <div>name:{name}</div>
      <div>age:{age}</div>
    </div>
  );
};

export default ChildChild;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值