vue 学习相关笔记大全

与三阶段无关

框架是什么?

封装与业务(功能)无关的代码块 简化了我们对于某些功能的代码量 但是我们需要记一套当前框架的语法

淘宝镜像

npm的服务器在国外 咱们国内下载的时候很慢

淘宝就自建了一个服务器 每个10分钟 就把npm的服务器里面所有的内容拉取一次 我们就可以通过淘宝这个国内的服务器来下载 速度更高

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

如果下载的时候报错 1.把你的网线拔了 用手机WIFI

html标签的属性是什么?

写在html开标签中的那些 英文单词

作用:用来扩展html标签的功能

语法:写在html的开标前中 并且 属性="属性值"

bootstrap

原型设计

墨刀 慕客 蓝湖 等工具

http状态码

状态码共分为5大类 100多个状态码 每一个状态码就是对当前请求的时候所产生的的问题进行描述 前端只需要查看这个状态码 就可以去判断请求发生了什么

1xx 请求相关信息

2xx 请求成功

3xx 重定向

4xx 客户端错误

5xx 服务器端错误

echarts v5 大数据可视化

echarts是一个开源的js‘图表库 就是完成大数据可视化 也是下载企业中比较主流的图表库

兼容绝大多数浏览器

高度定制化我们数据可视化图表

可以高效的完成 折线图 柱状图 散点图 K线图等待

npm 获取

npm install echarts --save

基本实现

<template>
  <div>
      <h1>echarts数据统计</h1>
      <!-- echarts的容器 需要设置高度与宽度-->
      <div id="main" ref="demoecharts">
​
      </div>
  </div>
</template>
​
<script>
// 1.先引用   把from 后面的东西全部引进来  然后起个别名 as 后面就是别名
​
import * as echarts from "echarts"
export default {
 mounted(){
  
​
    var myChart = echarts.init(this.$refs.demoecharts);
// 绘制图表  下面的内容先参考官网完成
myChart.setOption({
  title: {
    text: 'ECharts 入门示例'
  },
  tooltip: {},
  xAxis: {
    data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
  },
  yAxis: {},
  series: [
    {
      name: '销量',
      type: 'bar',
      data: [5, 20, 36, 10, 10, 20]
    }
  ]
});
​
​
 }
  
​
}
</script>
​
<style>
#main{
  width: 500px;
​
  height: 500px;
}
</style>

title 配置

Documentation - Apache ECharts

<template>
  <div>
    
      <!-- echarts的容器 需要设置高度与宽度-->
      <div id="main" ref="demoecharts">
​
      </div>
  </div>
</template>
​
<script>
// 1.先引用   把from 后面的东西全部引进来  然后起个别名 as 后面就是别名
​
import * as echarts from "echarts"
export default {
 mounted(){
  
​
    var myChart = echarts.init(this.$refs.demoecharts);
// 绘制图表  下
// setOption 是一个方法 就是用来设置当前echarts的各种配置的一个方法 
myChart.setOption({
  title: {//设置标题的
    text: '我是主标题!!',//主标题
    // show:false //是否显示标题
    link:"http://www.baidu.com",   //设置点击标题之后的超链接地址
    target:"self",  //当点击跳转之后是否打开新页面
    subtext:"我是副标题",
    sublink:"http://www.qq.com",
    // 标题的背景色
     backgroundColor:"gray"
  },
  tooltip: {},
  xAxis: {
    data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
  },
  yAxis: {},
  series: [
    {
      name: '销量',
      type: 'bar',
      data: [5, 20, 36, 10, 10, 20]
    }
  ]
});
 }
  
​
}
</script>
​
<style>
#main{
  width: 500px;
​
  height: 500px;
}
</style>

tooltip

设置提示框组件的一个属性

Documentation - Apache ECharts

<template>
  <div>
    
      <!-- echarts的容器 需要设置高度与宽度-->
      <div id="main" ref="demoecharts">
​
      </div>
  </div>
</template>
​
<script>
// 1.先引用   把from 后面的东西全部引进来  然后起个别名 as 后面就是别名
​
import * as echarts from "echarts"
export default {
 mounted(){
  
​
    var myChart = echarts.init(this.$refs.demoecharts);
// 绘制图表  下
// setOption 是一个方法 就是用来设置当前echarts的各种配置的一个方法 
myChart.setOption({
  title: {//设置标题的
    text: '我是主标题!!',//主标题
    // show:false //是否显示标题
    link:"http://www.baidu.com",   //设置点击标题之后的超链接地址
    target:"self",  //当点击跳转之后是否打开新页面
    subtext:"我是副标题",
    sublink:"http://www.qq.com",
    // 标题的背景色
     backgroundColor:"gray"
  },
  // 提示框设置
  tooltip: {
    // show:false,//显示或隐藏   默认显示
    trigger:"axis",  //触发类型  默认必须在图形中才能触发 axis可以按照坐标轴触发
    axisPointer:{  //畜类类型之后的显示选项 必须要把trigger 设置成axis擦可以进行设置
      type:"shadow"  //cross显示一个十字准星的样式  shadow 图形被阴影包裹
    },
     triggerOn:"click"
​
  },
  xAxis: {
    data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
  },
  yAxis: {},
  series: [
    {
      name: '销量',
      type: 'bar',
      data: [5, 20, 36, 10, 10, 20]
    }
  ]
});
​
​
 }
  
​
}
</script>
​
<style>
#main{
  width: 500px;
​
  height: 500px;
}
</style>

series 系列

系列 在echarts中 系列的意思是指 一组数值生成他们所映射的图

legend 图例

对当前的系列进行标记 它可以通过点击来控制显示对应的内容

es6 解构赋值与...扩展运算符

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // es6解构赋值
        // 数组解构
        let arr=[111,2222,3333,4444,5555,666]
        
         //  let [a,b,c,d,e,f]=arr
        // 缺省赋值
      let [,,,a,b,c] =arr
       console.log(a)
​
​
​
​
        // 对象解构
        let obj={
            name:"xixi",
            age:18,
            sex:"男",
            love:"女"
        }
       let {name,age,sex,love}=obj
       console.log(love)
​
​
​
​
       let arrb=[1,[2,3,[4,5,[6,[7,[8,[9,]]],[10]]]]]
       let [,[,,[,,[,[,[,[,]]],[bb]]]]] =arrb
       console.log(bb)
    </script>
</body>
</html>

vue

什么是vue?

vue是现今最为主流的MVVM 框架 作者:尤雨溪

vue是 渐进式自低向上增量开发 的MVVM框架

渐进式:渐进式的框架 就是一种可以更好的方便的与其他框架整合的模式(只会做职责之内的事情)

自低向上增量开发: 从简单到复杂的一个过程 vue在编写的时候 先把基本页面写好 在逐渐完成逻辑

MVVM是什么?

M model 模型===数据

V view 视图=== 用户能够看见的界面

VM viewModel 视图模型===连接视图于模型之间的桥梁

vue的特点

1.轻量级

2.高效率

3.上手快

4.文档全面

vue--Helloword

1.把cmd的路径切换到你要写项目的位置

初始化不要忘 npm init -y

初始化不要忘 npm init -y

初始化不要忘 npm init -y

初始化不要忘 npm init -y

2.下载

今后所有的依赖全部要被npm(包管理工具)所管理

npm install --save vue\

3.编写如下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 1.先引包 -->
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <!-- M        model          模型===数据
​
         V         view              视图=== 用户能够看见的界面
​
         VM      viewModel  视图模型===  连接视图于模型之间的桥梁
        
    -->
​
​
    <!-- 2.创建视图层v -->
    <div id="demodiv">
        {{text}}-----{{num}}
    </div>
    <script>
        // 3.创建vm层 vm层就是vue实例 注意大小写
        new Vue({
            // 4连接视图
            el:"#demodiv",
            // 5.创建模型层m
            data:{
                text:"我是字符串",
                num:18,
                bool:true,
                arr:[1111,2222,3333],
                obj:{
                    name:"xioxio"
                }
            }
        })
    </script>
​
</body>
</html>

vm :vue实例对象

{{}}是什么?

模板语法/模板插值/双大括号赋值/。。。。。。。。

{{表达式}}作用就是把表达式(就是通过计算可以返回结果的公式)放到页面中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <h1>{{text}}</h1>
        <h1>{{num+1}}</h1>
        <h1>{{bool?"你好":"你坏"}}</h1>
        <h1>{{str.toUpperCase()}}</h1>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"我是字符串",
                num:1,
                bool:true,
                str:"abcdefghijk"
            }
        })
    </script>
</body>
</html>

指令

就是在vue中 使用v-前缀的html特殊属性

作用 : 就是在vue中扩展标签的功能

语法 : 写在开标前中 并且 v-指令名="指令值“

v-model指令

作用: 在表单元素中完成数据的双向绑定

双向绑定:

视图变模型变

在表单元素中 使用v-model绑定的变量 如果在视图中改变了 那么vm就会得到通知 就会通知模型也自动发生改变

模型变视图绑定的数据也随之发生改变

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <h1>v-model指令</h1>
        <!-- 视图变模型变
        在表单元素中  使用v-model绑定的变量  如果在视图中改变了  那么vm
        就会得到通知   就会通知模型也自动发生改变   -->

        <!-- 模型变视图绑定的数据也随之发生改变 -->
        <input type="text" v-model="text"/>
        <h1>{{text}}</h1>
        <hr>
        如果给复选框绑定一个任意的数据  那么当复选框勾选或者取消的时候会把这个变量改变布尔值
        <input type="checkbox" v-model="ck"/>
        <h1>{{ck?"你勾选了":"你取消了"}}</h1>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"",
                ck:"我是一个变量"
            }
        })
    </script>
</body>
</html>

双向绑定的原理

双向绑定是通过数据劫持发布者订阅者模式实现的

数据劫持:数据拦截 就是当数据改变的时候 vm会通过js的一个Object.defineProperty()来监控数据的改变(在初始化的时候 Object.defineProperty()这个方法会把data里面的所有数据监听起来 当监听的数据改变了 那么这个方法就会触发 通知另一方进行改改变

发布者订阅者模式:就是一个一对多的关系 一个发布者可以对应无数个订阅者 如果发布者变了 所有的订阅者都会随之发生改变

v-show指令

作用: 就是切换一个元素的显示或者隐藏(是通过css的方式来控制的)

语法:v-show="true显示/false隐藏"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <h1>v-show</h1>
        <input type="checkbox" v-model="bool">

        <h1 v-show="bool">我是测试站位的</h1>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
               bool:true
            }
        })
    </script>
</body>
</html>

vue原理:

Object.defineProperty

​​

         }
    })
    delete person.age
    person.age
    person.age = 30

</script>

</body> </html>

数据代理:

vm是vue实例化的对象 vm身上的属性都直接去使用。我们不用vm.属性名 而是直接在页面上使用,vm使用数据代理的方式,搁在了vm身上,我们可以直接通过vm.属性名的方式去访问,但是vm身上的属性可以被直接使用,所以我们直接使用属性名

v-on指令

作用:就是给html绑定事件的

语法: v-on:事件不加on="函数 简写: @事件名不加on="fun() “ 函数写在 与el data 等同级的位置 methods来进行表示

v-for指令

作用: 便利数据 并且在页面中进行数据的展示 语法: v-for="( v 便利出来的值, i 便利出来的下标) in 你要便利的数据"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <ul>
            <li v-for="(v,i) in arr">
                {{v}}---{{i}}
            </li>
        </ul>

        <table border="1">
            <tr v-for="(v,i) in obj">
                <td>{{v.name}}</td>
                <td>{{v.age}}</td>
            </tr>
        </table>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                arr:["EZ","VN","MF","NOC"],
                obj:[
                    {name:"xixi1",age:181},
                    {name:"xixi2",age:182},
                    {name:"xixi3",age:183},
                    {name:"xixi4",age:184},
                    {name:"xixi5",age:185},
                    {name:"xixi6",age:186}
                ]
            },
            methods:{

            }
        })
    </script>
</body>
</html>

if 全家桶

v-if

作用:对当前的页面dom内容进行添加或者移除

语法: v-if="true添加/false移除"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        v-if与v-show的区别
        v-show是使用css来显示和隐藏的  在频繁切换的时候性能损耗低   初始化的时候性能损耗高
        v-if是对dom进行添加或者是移除  在频繁切换的时候性能损耗高    初始化的时候性能损耗低
        
        <input type="checkbox" v-model="bool">
        <h1 v-show="bool">我是v-show</h1>
        <h1 v-if="bool">我是v-if</h1>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
              bool:false
            },
            methods:{

            }
        })
    </script>
</body>
</html>

v-else

不能单独使用 必须配合v-if使用 与v-if之间不能有第三者

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <h1 v-if="bool">你好</h1>
        <!-- <p>我是占位的if 与 else之间不能有第三者</p> -->
        <h1 v-else>你坏</h1>
    </div>
    <script>

        new Vue({
            el:"#demodiv",
            data:{
              bool:false
            },
            methods:{

            }
        })
    </script>
</body>
</html>

v-else-if

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
       <h1 v-if="num==1">第1个</h1>
       <h1 v-else-if="num==2">第2个</h1>
       <h1 v-else-if="num==3">第3个</h1>
       <h1 v-else-if="num==4">第4个</h1>
       <h1 v-else>什么都不是</h1>
    </div>
    <script>



        new Vue({
            el:"#demodiv",
            data:{
              num:7
            },
            methods:{

            }
        })
    </script>
</body>
</html>

v-bind(初学者最容易忘记的一个指令)

作用: 给html的属性插变量

语法: v-bind:属性="变量" 简写: :属性="变量"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <a v-bind:href="ahref">{{text}}</a>
        <a :href="ahref">{{text}}</a>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"点我去百度",
                ahref:"http://www.baidu.com"
            },
            methods:{

            }
        })
    </script>
</body>
</html>

v-html

作用: 把字符串html在页面解析

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <div>
            {{text}}
        </div>
        <div v-html="text">

        </div>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"<em>我是一个斜体么么哒</em>"
            },
            methods:{

            }
        })
    </script>
</body>
</html>

v-once

作用: 一次性插值 v-once一旦绑定,数据就不会改变

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <input type="text" v-model="text">
        <h1>{{text}}</h1>
        <h1 v-once>{{text}}</h1>
        <h1>{{text}}</h1>
        <h1>{{text}}</h1>
        <h1>{{text}}</h1>
        <h1>{{text}}</h1>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"我是默认值"
            },
            methods:{

            }
        })
    </script>
</body>
</html>

v-text

作用:就是解析变量到页面展示 同{{}}作用一样

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <h1>{{text}}</h1>
        <h1 v-text="text"></h1>


    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"你好么么哒~!!!!"
            },
            methods:{

            }
        })
    </script>
</body>
</html>

扩展--- 屏幕闪烁问题

当用户网络不好的时候 由于加载速度太慢可能在用户的视图界面展示出很多的{{}}用户体验非常不好 然后等数据加载过来之后 页面突然间变好了 那么这个过程我们叫屏幕闪烁问题

1.使用v-text 替换{{}}绑定数据 (太麻烦了不推荐)

2.使用v-cloak指令来完成(用来保持元素在页面关联实例结束之后进行编译)

watch

监听data的数据 当data的数据改变了 那么watch就说收到信息从而处理一些逻辑

语法:

写在与el data methods 等同级的位置j

watch:{

你要监听的数据(){

你要处理的逻辑

}

}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
    <div id="demodiv">
        <input type="text" v-model="text">
        <h1>{{text}}</h1>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"你好"
            },
            methods:{

            },
            // 监听
            watch:{
                text(newval,oldval){
                    console.log("aaaaa")
                    console.error(newval)//新值
                    console.error(oldval)//旧值
                }
            }
        })
    </script>
</body>
</html>

watch首次加载会触发吗/页面刷新完会触发吗?

watch首次加载页面不触发

那我如果想让他页面在加载完毕就直接触发怎么办?

我们会在上脚手架之后在给大家说

computed 计算属性

引子

你在{{}}中写数据的逻辑处理是没问题的 但是 视图层是展示数据的地方 如果我们在视图层中写了太多了逻辑 导致代码的可维护性降低

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
    <div id="demodiv">
        <!-- 你在{{}}中写数据的逻辑处理是没问题的  但是  视图层是展示数据的地方   
        如果我们在视图层中写了太多了逻辑   导致代码的可维护性降低 -->
        <h1>{{text}}</h1>
        <h1>{{text.toUpperCase()}}</h1>
        <h1>{{text.toUpperCase().substr(1,5)}}</h1>

    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"abcdefgijk"
            },
            methods:{

            },
            watch:{
              
            }
        })
    </script>
</body>
</html>

计算(数据处理)属性(vue实例中的一个属性) 就是可以对data中的数据进行一些数据处理 一条数据在不同位置展示出不同形态的时候

语法 :

写在与el data methods watch等同级的位置

computed:{

处理完的数据变量(){

return 你的操纵数据的逻辑

}

}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
    <div id="demodiv">
        <!-- 你在{{}}中写数据的逻辑处理是没问题的  但是  视图层是展示数据的地方   
        如果我们在视图层中写了太多了逻辑   导致代码的可维护性降低 -->
        <h1>{{text}}</h1>
        <h1>{{text.toUpperCase()}}</h1>
        <h1>{{text.toUpperCase().substr(1,5)}}</h1>

        <h1>计算属性的方式对text进行处理</h1>
        <h1>{{uppertext}}</h1>
        <h1>{{subtext}}</h1>

    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"abcdefgijk"
            },
            methods:{

            },
            watch:{
              
            },
            computed:{
                uppertext(){
                    // 必须
                    return this.text.toUpperCase()
                },
                subtext(){
                    return this.text.toUpperCase().substr(1,6)
                }
            }
        })
    </script>
</body>
</html>

计算属性computed与方法methods的区别?

计算属性是依赖缓存的 只要处理一次数据 这个数据不改变 那么之后的所有调用都是从缓存中读取 更加节省资源

方法 憨憨的只要调用就执行

计算属性与watch有什么区别?

watch 是监听data的数据 当data的数据改变了 那么watch就会去处理一些逻辑

computed 依赖data数据 当data数据改变了 那么计算属性会重新计算返回新的结果

vue事件对象

谁触发这个事件 事件对象就是谁 $event来表示事件对象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <input type="text" @keydown="fun($event)">
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{

            },
            methods:{
                fun(e){
                    if(e.keyCode==13){
                        console.log("回车被按下了");
                    }
                }
            }
        })
    </script>
</body>
</html>

修饰符

就是用来简化我们对于事件处理的一些操作

v-on:事件.修饰符="函数"

按键修饰符

简化了我们对于 键盘相关事件的处理

.up .down .enter .ctrl .space

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <input type="text" @keydown.ctrl="fun()">
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{

            },
            methods:{
                fun(){
                   console.log("我被按下了"); 
                }
            }
        })
    </script>
</body>
</html>

事件修饰符

在没有冲突的情况下 修饰符是可以串联的

对于我们经常处理的事件进行处理 、

prevent 阻止事件默认行为

stop 阻止事件冒泡

captrue 设置事件为捕获方式

self 只会触发自己范围内的事件 不包含子组件

once 事件只会触发一次

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <style>
        .fu{
            width: 400px;
            height: 400px;
            background-color: pink;
        }
        .zi{
            width: 100px;
            height: 100px;
            background-color: green;
        }
    </style>
</head>
<body>
    <div id="demodiv">
        <div class="fu" @click="fu()">
            <div class="zi" @click.stop.once="zi()"></div>
        </div>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{

            },
            methods:{
                fu(){
                    console.log("fuffufufuf");
                },
                zi(){
                    console.log("zizizizzizi");
                },
            }
        })
    </script>
</body>
</html>

生命周期的钩子函数

钩子函数

在程序运行过程中被自动执行的函数叫钩子函数

生命周期

vue实例从创建到销毁的过程

生命周期的钩子函数

写在与el data methods 等同级的位置

实例创建之前 beforeCreate ()

实例创建之后 created()

模板渲染之前 beforeMount()

模板渲染之后 mounted()

数据更新之前 beforeUpdate()

数据更新之后 updated()

实例销毁之前 beforeDestory()

实例销毁之后 destoryed()

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
</head>
<body>
    <div id="demodiv">
        <h1>{{text}}</h1>
        <button @click="text='我变了'">点我修改</button>
    </div>
    <script>
        new Vue({
            el:"#demodiv",
            data:{
                text:"你好"
            },
            methods:{
                
            },
            watch:{

            },
            computed:{

            },
         
            beforeMount(){
                console.log("模板渲染之前")
            },
            mounted(){
                console.log("模板渲染之后");
            },


            beforeCreate(){
                console.log("实例创建之前");
            },
            created(){
                console.log("实例创建之后");
            },

            beforeUpdate(){
                    console.log("数据更新之前");
            },
            updated(){
                console.log("数据更新之后")
            },
            beforeDestory(){
                console.log("实例销毁之前");
            },
            destoryed(){
                console.log("实例销毁之后");
            }
        })
    </script>
</body>
</html>

页面第一次加载执行那些生命周期的钩子?

执行了 实例创建 与 模板渲染的 着4个钩子

生命周期钩子函数的作用?

在程序运行的时候给我们提供了一个自动执行的场所

生命周期共有几个阶段?

4大阶段实例创建 模板渲染 数据更新 实例销毁

8个钩子

Dom在那个阶段加载完成?

mounted阶段 已经把dom加载完毕

简述每个生命周期的钩子函数/请你给我说下vue创建的过程原理

beforeCreate () 在这个阶段数据观测(数据的创建 双向绑定的劫持)与事件的初始化还没有开始

created() 数据观测已经完成 vue的属性方法等这些内容已经准备好了 但是 这个时候dom还没有开始加载(页面还没有内容显示)

beforeMount() 页面的dom准备开始编译渲染了 也开始准备把页面进行编译了 但是还没有挂在到el上(页面还没有显示)

mounted() 页面已经显示,挂载在 页面中了 并且dom内容也已经加载完成了

beforeUpdate() 已经把要更新的内容准备好了 并且也准备开始更新了

updated() 更新完成 并且也显示在页面中了

beforeDestory() 开始准备销毁 但是此时vue还能用

destoryed() 啥都没有了

vue-cli

准备

电脑上面必须有node 如果有 打开cmd 输入 node空格-v查看下版本 如果版本是12以下

建议 删掉你的node重新安装一个高一点的版本 最少12以上

建议电脑上都安装以下淘宝镜像 并且把npm的镜像源切换到淘宝上 :

npm config set registry https://registry.npm.taobao.org

创建

我们现在要常见的脚手架是4.0的版本

1.全局下载脚手架 npm install -g @vue/cli

2.查看版本 vue --version

以上这两部 只需要执行一次即可 除非你重新装系统了 或者重新安装node了 否则执行一次就好 今后就不需要了

3.把cmd的路径切换到你要下载的文件夹路径之下

4.开始创建项目 vue create 你的项目名 然后稍等选择第三项自定义 剩下的 眼睛一闭 一路回车

5.cd到你的项目路径下

拿到空项目怎么办?

1.删除src文件夹下 components下的 Helloword.vue文件

2.删除app.vue文件夹下的指定内容

<template>
  <div id="app">
    <!-- 1.删除如下内容 -->
    <!-- <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/> -->
  </div>
</template>

<script>
// 2.删除如下内容
// import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    // 3.删除如下内容
    // HelloWorld
  }
}
</script>

<style>
/* 4.删除如下内容 */
/* #app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
} */
</style>

3.我们需要创建自己的文件

在components文件夹下 创建一个我们自己的 xx.vue文件写入如下内容

<template>
  <div>
      写我们自己组件的内容
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

4.app.vue中使用我们创建的组件

<template>
  <div id="app">
    <!-- 3.使用 -->
    <Home/>
  </div>
</template>

<script>
// 1.引用  引用单文件组件的时候首字母大写
import Home from "./components/home.vue"

export default {
  name: 'App',
  components: {
    // 2.调用
    Home
  }
}
</script>

<style>

</style>

.vue文件是什么?

.vue文件 全名是 单文件组件

一个文件包含三个内容

template 你写html的地方

script 你写js的地方

style 你写css的地方

vue中的属性在.vue中怎么写?

data

data的写法是有变化的

vue的组件data为什么是一个函数而不是对象?

<template>
  <div>
      我是妹子的区域{{text}}
  </div>
</template>

<script>
export default {
    data(){
        return {
            text:"我是字符串"
        }
    }
}


</script>

<style>

</style>

剩下的属性没有区别

<template>
  <div>
      我是妹子的区域{{text}}
      <input type="text" v-model="text">
      <button @click="fun()">点我调用函数</button>
  </div>
</template>

<script>
export default {
    data(){
        return {
            text:"我是字符串"
        }
    },
    methods:{
        fun(){
            console.log("你好");
        }
    },
    watch:{
        text(newval){
            console.log(newval)
        }
    }
    // 计算属性也和本地模式相同
}


</script>

<style>

</style>

组件

组件就是把一个页面拆分成多个小区域 单独来编写 效率高了 复用性也提高了 降低了测试难度

组件的本质是:自定义标签

全局组件---component

全局组件引用一次之后 在任何地方都可以直接使用了

基本语法

1.创建组件

2.在main.js中配置全局组件

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

Vue.config.productionTip = false

// 全局组件
// 1.引用
import Demo from "./components/demo.vue"
// 2.调用
// Vue.component("组件的名字随便起",你引用的组件)
Vue.component("Demo",Demo)

// 全面引用elementui
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);
// 全面引用elementui


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

3.在任何的组件中直接可以使用

ui库/组件的二次封装

局部组件components

使用局部组件

1.引用

2.调用

3.使用

父子组件

就是组件与组件之间相互引用的关系 引用者就是父组件 被引用者就是子组件

父子组件作用域

父组件的数据子组件能直接读取吗?

子组件不能直接读取父组件的数据

子组件的数据父组件能直接读取吗?

父组件不能直接读取子组件的数据

组件是一个完整的独立的个体 他自己的数据默认情况下只能自己使用不能被其他组件使用

组件传值

正向传值---props

你问你父亲要---正向传值

props就是可以接收组件外部传递进来的数据

语法:写在与el data methods 等同级的位置

props:[ "接收数据的变量1","接收数据的变量2",.........n ]

<template>
  <div>
      zizizizi----{{xiaoming}}
  </div>
</template>

<script>
export default {
  // 定义props设置接收变量
  props:["xiaoming"]
}
</script>

<style>

</style>

父组件就可以给这个接收变量上传递数据了

<template>
  <div>
      fuffufufufufufu
      <!-- 在子组件被调用的地方 把props当成属性来进行传值 -->
      <Zi :xiaoming="text"></Zi>
  </div>
</template>

<script>
import Zi from "./zi.vue"
export default {
  data(){
    return {
      text:"我是父组件的数据"
    }
  },
    components:{
        Zi
    }
}
</script>

<style>

</style>

props验证

就是对父组件传递过来的数据 进行校验 判断它的数据是否是我们预先定义好想要的

语法

props:{

你的props变量:数据类型 //验证一种类型

你的props变量:[ 第一个数据类型,第二个数据类型,......n ] //验证一种类型

你的props变量: {

type:你的数据类型,

default: 你的默认值

} // 设置默认值

}

注意:props验证不会对页面的展示造成影响 他只会在控制台 给我们一个警告提示 提示我们数据类型有问题

<template>
  <div>
      <h1>我是子组件-----{{title+1}}</h1>
  </div>
</template>

<script>
export default {
    // props:["title"],
    // props验证
    props:{
        // title:Number,//只能验证一种
        // title:[String,Number] //验证多种类型
        title:{
            type:String,
            default:"我是title的默认值么么哒^_!!!"
        }
    }
}
</script>

<style>

</style>

逆向传值

2.你父亲问你要---逆向传值

注意:在vue中逆向传值是不被允许的

实现:

(1)把数据放在自定义事件上(抛出自定义事件)

$emit( “自定义事件名随便写”, 自定义事件绑定的数据 ) 自定义事件

<template>
  <div>
      ziziziziz
      <!-- 1.逆向传值需要通过事件来触发才行 -->
      <button @click="fun()">点我逆向传值</button>
  </div>
</template>

<script>
export default {
    data(){
        return {
            text:"我是子组件的数据呵呵!!!!"
        }
    },
    methods:{
        fun(){
            // 2.通过自定义事件来抛出子组件的数据
            this.$emit("emitdemo",this.text)
        }
    }
}
</script>

<style>

</style>

(2)接收自定义事件传递的参数(接收自定义事件的数据)\

<template>
  <div>
      fuffufufufufu
      <!-- (3) 绑定子组件的自定义事件  但是 父组件调用的时候函数不加()不加()不加()
      函数不加()不加()不加()
      函数不加()不加()不加()
      函数不加()不加()不加()
      函数不加()不加()不加()
      函数不加()不加()不加()
      函数不加()不加()不加()
      函数不加()不加()不加()
       -->
      <Ez @emitdemo="fun"/>
  </div>
</template>

<script>
import Ez from "./emitzi.vue"
export default {
    components:{
        Ez
    },
    methods:{
        fun(text){
            console.log("我是父组件的函数---"+text)
        }
    }
}
</script>

<style>

</style>

注意

emit() 大家要记住它的作用是自定义事件

同胞传值--*事件总线eventbus

你兄弟问你要---同胞/同级传值

就是在两个需要传递数据的组件之上 的一个空的vue实例 那么这个实例就用来作为传递数据的桥梁 进行兄弟传值

使用

1.创建这个*事件总线(vue实例)在src创建一个文件夹用来容纳eventbus

import Vue from "vue"
export default new Vue

2.在需要传递的组件中进行抛出

只需要在这个实例上绑定一个自定义事件

*事件总线.$emit(事件名,数据)

<template>
  <div>
      aaaaaaaaaaaaaaa
      <button @click="fun()">点我传递数据给b</button>
  </div>
</template>

<script>
// 1.引用*事件总线
import EventBus from "@/eventbus/index.js"
export default {
    methods:{
        fun(){
            // 2使用*事件总线抛出数据
            EventBus.$emit("demoa","你好我是a的数据")
        }
    }

}
</script>

<style>

</style>

3.在需要使用接收数据的组件中进行接收

$on() 监听实例上的自定义事件

<template>
  <div>
      bbbbbbbbbbb
  </div>
</template>

<script>
// 1.引用*事件总线
import EventBus from "@/eventbus/index.js"
export default {
    mounted(){
        // 2.监听实例上的自定义事件
        // EventBus.$on("你要监听的事件名",(xx就是那边抛出来的数据)=>{})
        EventBus.$on("demoa",(val)=>{
            console.log(val);
        })
    }
}
</script>

<style>

</style>

跨组件传值--vuex

你爷爷问你要---跨组件传值

vuex 就是状态数据管理工具 把一个项目中的数据全部放到vuex中 统一管理起来 那么这样子一来 那个组件想用数据 那么这个组件就直接去vuex中取就可以了 在也不需要一层一层的数据传递了

创建好vuex的项目之后 会多一个store的文件夹 ---- 》存储vuex的内容文件夹

import Vue from 'vue'//引用vue
import Vuex from 'vuex'//引用vuex

Vue.use(Vuex)//在vue中使用vuex

export default new Vuex.Store({//创建store实例 vuex的实例
  state: {
      
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
}) 

vuex的5大属性

state

就是数据源 用来存储vuex数据的属性

import Vue from 'vue'//引用vue
import Vuex from 'vuex'//引用vuex

Vue.use(Vuex)//在vue中使用vuex

export default new Vuex.Store({//创建store实例 vuex的实例
  state: { //数据源   vuex的所有数据都写在state当中即可
    name:"xixi",
    age:18,
    arr:[1111,22222,33333],
    obj:{
      sex:"男"
    }
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }    
}) 

使用vuex的数据

在想使用的组件中 直接使用 this.$store.state.xxx 直接进行使用

使用vuex的数据

可以使用计算属性的方式进行取值

<template>
  <div class="about">
    <h1>This is an about page---{{this.$store.state.text}}--{{newtext}}</h1>
  </div>
</template>
<script>
export default {
  computed:{
    // 从vuex中取出数据
    newtext
      
      return this.$store.state.text
    }
  }
}
</script>

modules

modules就是vuex中的模块 在传统的项目中由于项目的体积逐渐变大 今后会有一个问题 就是store文件夹下的index.js中state的内容会越来越多 导致后期维护或者代码编写的时候 难度加大

所以 在这个时候我们可以把vuex查分成一个个个的小模块分别管理 降低了代码的复杂度

使用;

1.在store下创建一个文件夹‘ 用来存储模块

2.然后在模块文件夹中创建文件并写入如下内容

let homem={
    state:{
        hometext:"我是home数据",
    }
}
export default homem

3.关联模块

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

Vue.use(Vuex)

// 1.先引用模块
import aboutm from "./modules/aboutm.js"
import homem from "./modules/homem.js"

export default new Vuex.Store({
  state: {
   
   
  },
  mutations: {
  },
  actions: {
  },
  modules: { // 2。关联模块
    aboutm,
    homem
  }
})

注意注意:

当我们在vuex中使用模块的时候 获取数据 就需要变成 this.$store.state.模块名.你的数据

<template>
  <div class="about">
    <h1>This is an about page---{{this.$store.state.homem.hometext}}--{{newtext}}</h1>
  </div>
</template>
<script>
export default {
  computed:{
    // 从vuex中取出数据 不要忘了.模块名
    newtext(){
      return this.$store.state.aboutm.abouttext
    }
  }
}
</script>

mutations 修改数据

在vuex中 数据state的修改 不能直接改 必须要通过 mutations来对state进行修改

1.触发修改的这个动作 this.$store.commit("你要触发的修改动作名")

<template>
  <div>
      <h1>数据修改--{{newnum}}</h1>
      <button @click="fun()">点我修改数据</button>
  </div>
</template>

<script>
export default {
    methods:{
        fun(){
            // 触发mutations的修改  vuex的commit传递的是一个字符串
            // 这个字符串就是你要触发修改的动作名 但是 在工作中通常大家都会把
            // 动作名写成大写 用来区分和数据的不同
            this.$store.commit("DATA_NUM_UPDATE")
        }
    },
    computed:{
        newnum(){
            return this.$store.state.commitm.num
        }
    }
}
</script>

<style>

</style>

2.在vuex中编写修改动作

let commitm={
    state:{
        num:666
    },
    mutations:{//vuex的数据修改的位置 mutaions中是一个个的函数  每个函数就是一个修改的动作
        // state这个形参就是上面那个数据源
        DATA_NUM_UPDATE(state){
            state.num=9527
        }

    }
}
export default commitm

payload载荷

在上面的数据修改中 我们是吧mutations中的修数据写死了 不太灵活 所以我们可以使用payload接收commit中的数据

1。在触发动作的时候 给commit传递第二个参数

<template>
  <div>
      <h1>数据修改--{{newnum}}</h1>
      <button @click="fun()">点我修改数据</button>
      <button @click="funb()">点我修改数据2</button>

  </div>
</template>

<script>
export default {
    methods:{
        fun(){
            // 触发mutations的修改  vuex的commit传递的是一个字符串
            // 这个字符串就是你要触发修改的动作名 但是 在工作中通常大家都会把
            // 动作名写成大写 用来区分和数据的不同
            this.$store.commit("DATA_NUM_UPDATE")
        },
        funb(){
            // 第一个参数是你调用mutations的名字
            // 第二个参数  就是你传递给mutations的数据
            this.$store.commit("DATA_NUM_UPDATEB",{text:9529})
        }
    },
    computed:{
        newnum(){
            return this.$store.state.commitm.num
        }
    }
}
</script>

<style>

</style>

2.在mutations中定义payload接收

let commitm={
    state:{
        num:666
    },
    mutations:{//vuex的数据修改的位置 mutaions中是一个个的函数  每个函数就是一个修改的动作
        // state这个形参就是上面那个数据源
        DATA_NUM_UPDATE(state){
            state.num=9527
        },
        // payload就是接收commit的第二个参数
        DATA_NUM_UPDATEB(state,payload){
            state.num=payload.text
        }

    }
}
export default commitm

刷新丢失

vuex在数据修改之后刷新页面会丢失

   // 解决数据刷新丢失
    created () {
    //在页面加载时读取sessionStorage里的状态信息
    if (sessionStorage.getItem("store") ) {
        this.$store.replaceState(Object.assign({}, this.$store.state,JSON.parse(sessionStorage.getItem("store"))))
    } 

    //在页面刷新时将vuex里的信息保存到sessionStorage里
    window.addEventListener("beforeunload",()=>{
        sessionStorage.setItem("store",JSON.stringify(this.$store.state))
    })
  }

actions

actions 就是vuex中的异步触发器 (在actions中用来写异步操作)

1.页面触发actions

this.$store.dispatch("你要触发actions的名字")

<template>
  <div>
      <h1>异步请求闭环操作</h1>
  </div>
</template>

<script>
export default {
    mounted(){
        // 调用actions触发异步请求
        this.$store.dispatch("AXIOS_LINK")
    }
}
</script>

<style>

</style>

2.在actions中进行异步操作

// 引用请求封装
import getlink from "@/api/getapi.js"
let actionsm={
    state:{

    },
    mutations:{

    },
    // 就是放置异步操作的
    // acitons中就是一个个的方法  每个方法是一个异步操作
    actions:{
        AXIOS_LINK(){
            // 放置异步操作
            getlink("/mock/data").then((ok)=>{
                console.log(ok);
            })

        }
    }
}

getters

getters就是vuex中的计算属性

1.在state mutations等同级的位置编写

  // 计算属性
    getters:{
        // 这个state的形参就是上面的数据源
        newtext(state){
            return state.text.toUpperCase()
        }
    }

2.在想使用的组件内 使用 this.$store.getters.xxx

{{this.$store.getters.newtext}}

slot插槽/槽口

引子

<template>
  <div>
      fuffufufufufufu
     
      <!-- 组件的本质是什么?
      自定义标签
      标签的分类有哪些?
      单标签双标签
      为什么会有单双两种标签?
      双标签通常需要向其内部插入内容才会显示
       -->
       <Sz></Sz>
       <!-- 默认情况下在组件调用的时候  插内容是不显示的 -->
       <Sz>
           <h1>能在里面写东西吗?</h1>
       </Sz>
  </div>
</template>

<script>
import Sz from "./slotzi.vue"
export default {
    components:{
        Sz
    }
}
</script>

<style>

</style>

slot是什么?

用来混合父组件的内容于子组件自己的模板(就是可以在组件被调用的时候向其内部插入新的dom)

在组件使用的时候 再次提升了组件的可复用性

<template>
  <div>
    ziziziziziz
    <!-- 设置了槽口  从而在组件调用的时候  在开关标签内插入新的dom内容 -->
    <slot></slot>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

具名槽口

带有名字的槽口

使用:

1.需要给槽口起名字 name属性起名字

   <slot name="demo"></slot>

2.在插入新的dom的时候 使用 slot属性 来进行插入

  <h1 slot="demo">能在里面写东西吗?1</h1>[]

扩展----项目怎么启动

只需要进入项目找到项目根路径下的 package.json文件 中的scripts 节点 即可知道

但是注意 所有的名字 启动都要是npm run 你设置的名字

但是只有一个 是start 的启动是可以 npm start 或者 npm run start

路由

就是通过路由技术可以让我们完成 单页面应用 (SPA)

通过路由可以让我们进行根据url的切换从而程序切换不同的页面

单页面应用spa 所谓的单页面应用不是 只会显示一个页面 而是 运行的时候只会有一个html文件在运行 其中的组件页面切换的时候 实质上 是一个组件页面显示 需要切换的时候 把这个显示的组件页面 替换成另一个 而基本的浏览器上的html文件并不发生改变

好处 : 用户的体验非常的好 在切换页面的时候没有等待 很丝滑

缺点: 首次加载 的时候 可能会因为第一次加载内容太多 造成页面白屏问题

路由创建

脚手架创建方式

就是在创建项目的时候 使用光标上下按键找到router 并且使用空格键选中 来进行项目的创建、

大家会发现 项目src下多了两个文件夹

router文件夹-----》j就是写路由配置的文件夹

views文件夹------》就是页面组件文件夹

拿到带路由的空项目

1.删除views页面文件夹中默认的内容 并且根据我们的需求添加创建对应的.vue文件

2.需要去配置路由 router文件夹下的index.js

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

// 1.把你要用的组件页面 先引进来
import Fenlei from "../views/fenlei.vue"
import Gouwu from "../views/gouwu.vue"
import Home from "../views/home.vue"
import Jingxi from "../views/jingxi.vue"
import Wode from "../views/wode.vue"

Vue.use(VueRouter)
// 2.配置规则
const routes = [
  {
    path: '/home',//   配置url路径
    name: 'Home',//给当前规则起个名字
    component: Home//设置引用的组件
  },
  {
    path: '/fenlei',//   配置url路径
    name: 'Fenlei',//给当前规则起个名字
    component: Fenlei//设置引用的组件
  },
  {
    path: '/gouwu',//   配置url路径
    name: 'Gouwu',//给当前规则起个名字
    component: Gouwu//设置引用的组件
  },
  {
    path: '/jingxi',//   配置url路径
    name: 'Jingxi',//给当前规则起个名字
    component: Jingxi//设置引用的组件
  },
  {
    path: '/wode',//   配置url路径
    name: 'Wode',//给当前规则起个名字
    component: Wode//设置引用的组件
  },

]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

3.设置路由导航

4.设置路由出口

路由导航

就是控制页面切换的标签或者功能 等同于原来你们写的超链接,router-link只会跳转刷新组件,也可以会局部的去刷新

声明式导航

router-link to属性开控制跳转路径

      <router-link to="/home">Home</router-link> |
      <router-link to="/shop">Shop</router-link> |
      <router-link to="/user">user</router-link> |
      <router-link to="/phone">phone</router-link> |

声明式导航动态类名

当我们写完了上面的routerlink运行之后 在开发者工具中查看编译后的routerlink会发现到我们点击切换路由 或者手动修改url地址 匹配到指定路由的时候 会给router-link导航动态的添加一个类名 router-link-active的名字 这个类名是vue自动添加的 方便我们设置选中时候的样式

编程式导航

使用js的方式完成路由页面的跳转

this.$router.push("/你去的地方")

<template>
  <div>
      home
      <button @click="fun()">点我去shop</button>
  </div>
</template>

<script>
export default {
    methods:{
        fun(){
            // 编程式导航   
            this.$router.push("/shop")
        }
    }
}
</script>

<style>

</style>

this.$router.replace() 替换

this.$router.go() 正数前进 负数后退

路由出口

router-view

标识路由页面显示的位置 必须有 只是在教授加创建的方式中 router-view会被自动创建

手工创建方式

多级路由

它的创建流程和一级基本相同 唯独就是在配置规则的时候有点区别

1.创建二级路由的组件页面

2.配置路由规则

(1)先引用

(2)配置规则 你要找到是那个一级路由的二级子路由 就在对应的一级路由规则中使用 children来进行配

{
    path: '/shop',
    name: 'Shop',
    component: Shop,
    // 配置二级路由
    children:[
      {
        path: '/era',
        name: 'Era',
        component: Era
      },
      {
        path: '/erc',
        name: 'Erc',
        component: Erc
      },
      {
        path: '/erd',
        name: 'Erd',
        component: Erd
      },
      {
        path: '/ere',
        name: 'Ere',
        component: Ere
      },
    ]

3.设置导航

4.千万要记住 不要忘了设置路由出口 router-view

4.千万要记住 不要忘了设置路由出口 router-view

4.千万要记住 不要忘了设置路由出口 router-view

4.千万要记住 不要忘了设置路由出口 router-view

4.千万要记住 不要忘了设置路由出口 router-view

4.千万要记住 不要忘了设置路由出口 router-view

4.千万要记住 不要忘了设置路由出口 router-view

4.千万要记住 不要忘了设置路由出口 router-view

<template>
  <div>
      shop

      <router-link to="/era">era</router-link>&nbsp;&nbsp;
      <router-link to="/erc">erc</router-link>&nbsp;&nbsp;
      <router-link to="/erd">erd</router-link>&nbsp;&nbsp;
      <router-link to="/ere">ere</router-link>&nbsp;&nbsp;


      <!-- 设置二级路由的出口 -->
      <router-view></router-view>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

小扩展

之前的二级路由规则在写的时候 path是 /二级路由

{
        path: '/erd',
        name: 'Erd',
        component: Erd
      },

但是 这个path还可以写成 不带/的写法

{
        path: 'erd',    不带/
        name: 'Erd',
        component: Erd
      },

如果不带/ 那么在路由导航设置的时候 路径必须是 /一级路由/二级路由,注意点就是在声明式的路由跳转的时候,一定一定要带一级路由,如果不带一级路由的话,在router路由规则中path是无法相对应的路径

     <router-link to="/shop/era">era</router-link>&nbsp;&nbsp;
      <router-link to="/shop/erc">erc</router-link>&nbsp;&nbsp;
      <router-link to="/shop/erd">erd</router-link>&nbsp;&nbsp;
      <router-link to="/shop/ere">ere</router-link>&nbsp;&nbsp;

路由模式

hash 模式 默认模式(不写模式就是hash模式)

history 模式

区别

区别 hash history
#号 带# 不带#
兼容性 兼容低版本浏览器 是H5新特性 所以不支持低版本浏览器
刷新丢失 刷新没事 在上线之后刷新会丢失

某些app中在嵌入页面的时候 由于安全机制 是禁止路径中带特殊符号的

const router = new VueRouter({
  mode: 'history', //设置路由模式
  base: process.env.BASE_URL,
  routes
})                                                                      

路由传参 动态路由匹配

把数据从一个组件页面传递到另外一个组件页面中

query方式

语法:

1.发送数据

声明式

<!-- <router-link v-bind:to="{name:'你要去的页面规则的name',query:{发送数据的key:发送数据的val}}">点我把数据发送到user页面</router-link> -->
<router-link v-bind:to="{name:'User',query:{xiaoming:'我是声明式导航发送的数据么么哒'}}">点我把数据发送到user页面</router-link>

方式2

<!-- 方式2 -->
<router-link v-bind:to="{path:'你要去的页面路径',query:{发送数据的key:发送数据的val}}">点我把数据发送到user页面</router-link>

编程式

// this.$router.push({name:'你要去的页面规则的name',query:{发送数据的key:发送数据的val}})
// this.$router.push({path:'你要去的页面路径',query:{发送数据的key:发送数据的val}})
this.$router.push({path:'/user',query:{xiaohong:"我是编程式发送的query数据"}})

2.接收数据

在你需要接收数据的页面中 直接接收

<template>
  <div>
      user
      <!-- 接收数据 -->
      <h1>{{this.$route.query.xiaoming}}</h1>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

params方式

语法:

1.在路由规则中设置接收参数

     {
        path: 'erc/:xiaoming',//在要接收参数的路由规则中设置接收参数
        name: 'Erc',
        component: Erc
      },

2.发送数据

<!-- 声明式  params方式只能使用name  不能使用path-->
      <!-- <router-link :to="{name:'你去的路由规则的name',params:{发送数据的key和规则中设置的相同:数据}}"></router-link> -->
      <router-link :to="{name:'Erc',params:{xiaoming:'我是params声明式的数据'}}"></router-link>

编程式

就是把声明式to里面的东西 写在push()当中

3.接收数据

this.$route.params.xxxx

query与params的区别

1.用法上 query2步 params3步 query发送数据的时候可以使用name也可以使用path 但是params只能使用name

2.url展示上

params方式: 只展示数据

http://localhost:8080/shop/erc/我是数据

query方式: key和val都展示

http://localhost:8080/all?newsid=3456456

route与router区别

router 是vue路由对象 router是一个全局对象 他包含了所有的路由信息

route 是当前页面路由对象

路由懒加载

懒(懒惰)加载 按需加载 我们需要了你在加载

如果不使用路由懒加载技术 可能会造成首页白屏问题(如果项目的页面很多 那么第一次加载的时候就会把着很多个页面都要进行编译 浪费很长时间 那么这样一来就可能 造成用户首先看到的就是白屏 等页面全部加载完 用户才能看到界面 用户体验非常的不好)

方式

异步组件方式

(resolve) => require(['要使用组件的路径'], resolve)

import 导入方式

() => import('要使用组件的路径')

重定向 redirect

重新定位方向

设置在路由规则的所有规则之后

 // 设置路由重定向
  {
    path:"/",
    redirect:"/home"
  }

404 页面

// 一定要在所有配置规则的最下面
  {
    path: '*',
    name: 'no',
    component: () => import('../views/no.vue')
  },

路由守卫 导航守卫 路由钩子

就是在路由跳转的时候 提供一个自动执行的函数 可以在今后用于对用户状态进行判断的一个作用

全局守卫

就是对全部路由在跳转的时候 都会触发的钩子函数

全局前置守卫

to 你要去哪里的路由信息

from 你从那个路由来的信息

next() 下一步

router.beforeEach((to,from,next)=>{

你要执行的逻辑

})

router.beforeEach((to,from,next)=>{
      console.log(to);
      console.log(from);

      if(to.path=="/home"||to.path=="/phone"){
        next()
      }else{
        alert("您没有登录请您登录后访问vip页面!!!")
         // 下一步必须写
        next(false)
      }

全部后置守卫

router.afterEach()

  router.afterEach((to,from)=>{
        console.log(to);
        console.log(from);
      })

路由独享

只对当前路由生效

beforEnter((to,from,next)=>{

})

  {
    path: '/phone',
    name: 'phone',
    component: () => import('../views/phone.vue'),
    // 路由独享守卫写在那个路由规则中
    beforeEnter:(to,from,next)=>{
      console.log(to);
      console.log(from);
      alert("您没有登录")
      next(false)
    }
  },

组件内

进入组件的时候生效 写在你想生效的组件里面

beforeRouteEnter

离开组件的时候生效

beforeRouteLeave

    beforeRouteEnter(to,from,next){
        console.log(to);
        console.log(from);
        
        console.log("进入组件的时候生效");
        next()
    },
    beforeRouteLeave(to,from,next){
        if(confirm("确定离开吗?")){
            next()
        }else{
            next(false)
        }
    }
}

路径 @

无论在那个路径下 只要使用@就代表src路径

前后台交互

什么是前台什么是后台

前台就是用户能看到的东西(展示数据的)

后台就是支持前台的数据源(处理与提供数据的)

前后台交互的方式

1.原生ajax

2.jquery ajax 就是对XHR对象进行的封装

3.axios 就是对XHR对象进行的封装 在封装的过程中使用了 promise来进行封装让他更加符合当下主流语法

4.fetch 是es7中最新推出的请求 他与XHR无关 是es中推出的新数据获取方式 (react详细说)

axios使用

axios是一个第三方的数据请求 所以我们可以在vue中使用 也可在其他地方进行使用

1.下载 npm install --save axios

2.在你想使用的位置 引用

3.使用

axios.get()

axios.post()

axios({

url:"xxxxxx",

method:"方式"

}).then((成功形参)=>{

成功怎么处理

}) .catch((失败的形参)=>{

失败了怎么处理

})

<template>
  <div>
      <h1>axios数据请求</h1>
      <button @click="fun()">点我发送请求</button>
  </div>
</template>

<script>
// 1.引用
import axios from "axios"
export default {
    methods:{
        fun(){
            // 2.编写请求
            axios({
                url:"/data/mock",
                method:"GET"
            }).then((ok)=>{
                console.log(ok)
            }).catch((err)=>{
                console.log(err)
            })
        }
    }
}
</script>

<style>

</style>

拦截器

拦截器 就是对我们的请求 以及 响应 进行拦截

拦截请求

在每次发送请求之前 拦截到这个请求 并且携带一些参数 减少每次发送请求都要携带参数的复杂度

响应拦截

响应拦截的作用 就是在接收到响应信息之后 进行一些后续的操作

使用:

1.在src下创建一个utils 的文件夹( 工具文件夹其中放的内容 通常都是一些工具库 存放的就是一些没有也没有关系但是有了 可以让程序更加高效执行的代码 )

2.随便创建一个文件用来存放拦截器代码(service.js request.js)

// 编写拦截器代码
import axios from "axios"
// 创建axios
const service = axios.create()

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

// 添加响应拦截器
service.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

 //   暴漏
export default service

请求封装

1.在src下新建一个api文件夹

就是用与存放数据请求封装的 使用promise进行封装

// 咱们请求数据是需要通过拦截器去请求的所以先引用拦截器
import service from "@/util/service.js"

let apilink=(url)=>{
    return new Promise((resolve,reject)=>{
            url,
            method:"get"
        }).then((ok)=>{
            resolve(ok) 
        }).catch((err)=>{
            reject(err)
        })
    })
}

export default apilink

跨域

为什么造成跨域?

同源策略 端口 域名 协议 不同的时候 因为这个是浏览器的安全机制

解决跨域的方式:

1.jsonp

2.代理跨域(vue,react)就是使用脚手架中的一个叫devServer的微型服务器帮助我们获取数据 那么这样一来就绕开了浏览器 没有了浏览器就没有同源策略这个概念 所以就没有了跨域问题

实现:

(1)在根路径下创建vue.config.js

(2) 写入如下内容

module.exports={
    devServer:{
        open:true,//自动开启浏览器
        port:8888, //设置端口
        proxy:{ //设置devServe解决跨域问题
            "/api":{
                // 我们需要告诉devserver帮我们解决那个地址的跨域
                target:"http://www.weather.com.cn",
                changeOrigin:true,
                ws:true,
                // 替换地址
                pathRewrite:{
                    "^/api":""
                }
            }
        }
    }
}

(3)修改你请求的地址为/api

<template>
  <div class="about">
    <h1>请求数据</h1>
    
  </div>
</template>
<script>
import getlink from "@/api/getapi.js"
export default {
  created(){
    // 修改成跨域
    getlink("/api/data/cityinfo/101320101.html").then((ok)=>{
      console.log(ok);
    })
  }
}
</script>

3.反向代理跨域解决

4.后台解决跨域 CORS

get数据发送---params

get发送数据 只需要在axios中使用params属性来进行数据的发送

import service from "@/utils/service.js"

let getlink=(url,params)=>{
    return new Promise((resolve,reject)=>{
        service.request({
            url,
            method:"GET",
            // get发送数据
            // params:{你数据的key:val}
            params
        }).then((ok)=>{
            resolve(ok)
        }).catch((err)=>{
            reject(err)
        })
    })
}
export default getlink

post发送数据

1.使用data发送数据

//service是utils下面的service导出的 
import service from "@/utils/service.js"

let postlink=(url,data)=>{
    return new Promise((resolve,reject)=>{
        service.request({
            url,
            method:"POST",
            // post使用data
            data
        }).then((ok)=>{
            resolve(ok)
        }).catch((err)=>{
            reject(err)
        })
    })
}
export default postlink

2.转换

 fun(){
           let usp = new URLSearchParams()//帮助axios解析post  数据
        //    usp.append("key","val")
           usp.append("uname",this.inputaval)
           usp.append("upwd",this.inputbval)

            postlink("/api/user/denglu",usp).then((ok)=>{
                console.log(ok);
            })
        }

扩展--模拟数据mockjs

在开发的时候 我们有时候写页面正常情况下数据是后端给我们的 但是写的时候 可能后端数据还没有写好 为了不影响我们写代码 所以我们可以使用mock模拟数据来先替代后台的数据

1.下载 npm install --save mockjs

2.我们要在新建一个文件夹 mock文件夹 在其中在创建一个data文件夹用来存放模拟数据 在和data同级的位置 创建一个index.js文件用来写mock的代码

3.创建mock代码

// 1.引用
let Mock=require("mockjs")
// 2.使用
// Mock.mock("设置请求地址","请求方式get",require("你的模拟数据地址"))
Mock.mock("/movie/data","get",require("./data/movie.json"))

4.千万不要忘了

4.千万不要忘了

4.千万不要忘了

4.千万不要忘了

4.千万不要忘了

4.千万不要忘了

4.千万不要忘了

在main.js引用mock

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

Vue.config.productionTip = false

// 因为如果我们只指定了文件夹的话  那么他会自己去这个文件夹下
// 找叫index.js的文件 
require("./mock")


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

扩展--自动开启浏览器

1.在项目的根路径下创建一个vue.config.js的文件

2.在其中编写如下代码

module.exports={
    devServer:{
        open:true,//自动开启浏览器
        port:8888 //设置端口
    }
}

ref

在vue 中是很少会用到dom操作的 但是万一我们要用了呢 虽然可以使用原生js的dom操作方式 但是vue中还给我们提供了一套更加简单的dom操作语法

使用---ref使用在html标签之上

ref给元素上面起一个引用的名字

this.$refs.你的名字 来进行dom的查找

<template>
  <div class="about">
    <!-- 1.ref绑定名字 -->
    <h1 ref="demoh">This is an about page</h1>
    <button @click="fun()">点我修改h1</button>
  </div>
</template>
<script>
export default {
methods:{
  fun(){
    // 2.查找ref的内容进行操作
      this.$refs.demoh.style.color="pink"
  }
}
}
</script>

扩展 ref使用在组件之上

如果我们把ref绑定到了组件之上 那么我们就可以通过ref 进行父组件获取子组件的数据 还可以 让父组件触发子组件的方法

<template>
  <div class="home">
    <Rd ref="demord"/>
    <button @click="fun()">点我查看下</button>
  </div>
</template>

<script>
import Rd from "@/components/refdemo.vue"

export default {
  name: 'Home',
  components: {
   Rd
  },
  methods:{
    fun(){
      // 打印出来之后 是子组件的 vue组件实例 其中包含了子组件的属性与方法等信息
      console.log(this.$refs.demord);
    }
  }
}
</script>

动态组件

让多个组件使用同一个挂载点并且动态切换,就是动态组件

语法:

设置挂载点 使用 <component></component> 来进行表示

其中有个is属性用来承载显示的组件内容

<template>
  <div>
      <h1>动态组件</h1>
    <button @click="com='Demoa'">点我demoa</button>
    <button @click="com='Demob'">点我demob</button>
    <button @click="com='Democ'">点我democ</button>
      <!-- 设置挂在点 -->
      <!-- <component is="一个变量 这个变量保存的就是你要显示的组件名"></component> -->
      <component :is="com"></component>
  </div>
</template>

<script>
import Demoa from "@/components/demoa.vue"
import Demob from "@/components/demob.vue"
import Democ from "@/components/democ.vue"
export default {
    data(){
    return {
        com:"Demob"
    }},
    components:{
        Demoa,
        Demob,
        Democ
    }
}
</script>

<style>

</style>

keep-alive

引子

我们在动态组件或者路由中 每次切换页面或者组件的时候 会发现其中的内容在伴随着切换的时候 丢失了?

原因 是因为 每当你在动态组件或者路由中切换的时候 vue在每次切换的过程中会创建一个新的vue组实例 组件切换状态丢失

我就是不想让他状态丢失怎么办?

keep-alive

语法:

<keep-alive>

你想保存状态的内容

</keep-alive>

保存动态组件的状态 只需要包裹动态组件的挂载点即可

      <keep-alive>
            <component :is="com"></component>
      </keep-alive>

保存路由的状态

  <keep-alive>
       <router-view/>
  </keep-alive>

扩展--keep-alive的属性与钩子

属性

因为keep-alive是以标签的方式出现的 在这个标签中就有两个属性

include 你要缓存谁

exclude 你不想缓存谁

    <keep-alive include="你要缓存的内容1,你要缓存的内容2,.....n">
            <component :is="com"></component>
      </keep-alive>

include与exclude都写了 执行谁

exclude的优先级要大于include

keep-alive的钩子函数

写在被keep-alive管理的组件中

   activated(){
        console.log("进入到被kepp-alive管理的组件时候执行");
    },
    deactivated(){
        console.log("离开被kepp-alive管理的组件时候执行");
    }

自定义指令-directives

在现有内置指令不够用的时候 我们自己来定义 通过我们自己可以给html标签添加一些特殊的功能

语法:

写在与 data methods同级的位置

directives:{

自定义指令的名字:{

自定义指令的钩子函数(){、

}

},

自定义指令的名字b:{

},

}

<template>
  <div class="home">
    <!-- 自定义指令的使用 -->
    <h1 v-red>自定义指令</h1>
  </div>
</template>

<script>


export default {
  name: 'Home',
  components: {
   
  },
  directives:{
    red:{
      // 这个形参就是代表你把这指令插入到那个元素上 这个形参就是代表你把这指令插入到那个元素上
      inserted(el){
        el.style.color="red"
      }
    }
  }
}
</script>

cLass的动态绑定:

​//绑定class样式--字符串写法, 适用于:样式的类名不确定,需要动态指定//绑定class样式----数组写法,适用于:要绑定的样式个数不确定,名字也不确定

{{name}}

//绑定class样式-对象写法,适用于:要绑定的样式个数确定,名字也确定,但要动态决定用不用

{{name}}

}

})

<style> .atguigu{ height"100px" width:"100px" } .atguigu2{ height"100px" width:" 500px" }

自定义指令的钩子函数

bind 就是在指令绑顶到元素之上 只执行一次。

inserted 使我们用的最多的 绑定了指令的元素在插入到页面展示的时候调用

update 所有的组件节点更新的时候触发

componentUpdated 指令所在的节点 以及它的子节点全部更新完的时候调用

unbind 解除指令和元素绑定的时候 只执行一次

扩展----$set

vue中 数据变了 视图没有改变 为什么?怎么解决?

出现的原因是 当vue的data里面声明了已经赋值过得数组或者对象 的时候 我们程序运行中向其内容部插入新属性的时候 就会出现数据变 视图没有改变的问题。

<template>
  <div>
      $set  出现的原因是   当vue的data里面声明了已经赋值过得数组或者对象
       的时候  我们程序运行中向其内容部插入新属性的时候
        就会出现数据变 视图没有改变的问题。 
        <h1>name:{{user.name}}</h1>
        <h1>age:{{user.age}}</h1>

        <button @click="fun()">点我插入新内容</button>
  </div>
</template>

<script>
export default {
    data(){
        return {
            user:{
                name:"xixi"
            }
        }
    },
    methods:{
        fun(){
            this.user.age=18
            console.log(this.user.age)
        }
    }
}
</script>

<style>

</style>

原理 为什么会有这种情况

因为出现这个情况的原因是 因为在javascript中 Object.defineProperty()这个方法 只会监听初始化的数据 如果初始化完成之后 那么数据劫持就已经监听完了 如果中途在添加新属性 就是数据劫持的Object.defineProperty()劫持不到 劫持不到就没有双向绑定 没有双向绑定那么 数据变 视图当然不会变了

解决方案 --- $set()

我就是向在程序运行的时候添加新的属性 并且数据变视图变怎么办?

语法:

this.$set( 你要操纵的数据 , 新增的key, 新增的val )

 		 fun(){
            // this.user.age=18

            // this.$set( 你要操纵的数据 , 新增的key,  新增的val )
            this.$set(this.user,"age",666)
            console.log(this.user.age)
        }

mixin混入

就是对vue的组件中内容进行复用的一个技术

在多个组件中如果出现了相同的属性与方法 我们传统写法是 在每个组件中都写一遍 去使用 但是这样子后期的代码冗余和维护的难度就变大了

为了解决上面组件中出现重复的属性方法等内容 可以使用混入 那么我们组件中多次出现的属性和方法 封装到混入中 今后要使用直接去混入调用即可

局部混入

局部就是只能在当前引用调用的组件中使用

1.新建mixins文件夹用来容纳

2.在其中把我们想封装的方法放到mixins的文件中 原来组件中怎么写 混入文件中就怎么写

let methodmixins={
    methods:{
        fun(){
            alert("您好")
        }
    }
}
export default methodmixins

3.再想使用这个方法的组件中引用调用使用

<template>
  <div>
      demoa
      <!-- 3.想怎么用就怎么用 -->
      <button @click="fun()">点我</button>
  </div>
</template>

<script>
// 1.引用
import methodmixins from "@/mixins/index.js"
export default {
    // 2.调用 与data methods的同级编写一个属性叫mixins
    mixins:[methodmixins]

}
</script>

<style>

</style>

全局混入

全局就是在一个地方引用一次 剩下所有的组件都可以使用

1.来到main.js中进行引用与配置

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

// 1.引用混入
import Mixins from "./mixins/index.js"
// 2.调用
Vue.mixin(Mixins)

Vue.config.productionTip = false

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

elementui

elementui是一个ui框架 面向于pc端的

vue react jquery 这些都统称为js框架

使用

1.下载 npm install element-ui --save

2.main.js中引用

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

Vue.config.productionTip = false


// 全面引用elementui
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);
// 全面引用elementui


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


nexttick

异步说明

vue的响应式并不是在数据改变之后DOM立刻改变,而是等待vue的一定策略来进行DOM的更新

<template>
  <div class="about">
    <h1 ref="demoh">{{name}}</h1>
    <button @click="fun()">点我修改</button>
  </div>
</template>
<script>
export default {
  data(){
    return {
      name:"xixi"
    }
  },
  methods:{
    fun(){
      // vue的响应式并不是在数据改变之后DOM立刻改变,
      // 而是等待vue的一定策略来进行DOM的更新
      this.name="我变了"
      // 在运行之后大家发现vue中的数据在修改之后 下面打印的dom内容
      // 还是原来旧的数据  也就印证了  vue在修改数据之后  dom不是立即修改
      console.log(this.$refs.demoh.innerText)

    }
  }
}
</script>

异步说明的总结

简单来说说 vue的数据在修改之后 视图不会立即更新 是等待一个事件循环中所有的修改都完成了 然后在同一的进行页面更新

执行原理

1.vue先去修改数据 在修改数据的时候是一个同步任务,在修改数据的时候还没有涉及到dom的改变

2.vue会开启一个异步的列队 用来缓冲刚才修改的数据变化

3.当数据都修改完成之后 开始执行这个异步的dom修改

nexttick是什么

等待dom执行完毕之后所运行的一个方法 这个方法中的回调函数会立即执行

使用场景1

在数据修改之后想得到你改变数据的那个dom的新内容就可以使用nexttick

使用场景1

在vue生命周期的created钩子函数进行dom操作?

不可以 因为created是实例创建完成 但是在这个时候dom还没有进行加载所以在这个钩子中是不能进行dom操作的 我想在created中进行dom操作怎么办?可以使用nexttick

<template>
  <div class="home">
      <h1 ref="demoh">{{name}}</h1>
  </div>
</template>

<script>


export default {
  created(){
    // console.log("我想得到dom中的数据"+this.$refs.demoh.innerText)
    // 尝试在dom执行完执行
    this.$nextTick(()=>{
      console.log("我想得到dom中的数据"+this.$refs.demoh.innerText)
    })
  },
  data(){
    return {
      name:"xixi"
    }
  }
}
</script>

使用场景2

<template>
  <div class="about">
    <h1 ref="demoh">{{name}}</h1>
    <button @click="fun()">点我修改</button>
  </div>
</template>
<script>
export default {
  data(){
    return {
      name:"xixi"
    }
  },
  methods:{
    fun(){
      // vue的响应式并不是在数据改变之后DOM立刻改变,
      // 而是等待vue的一定策略来进行DOM的更新
      this.name="我变了"
      // 在运行之后大家发现vue中的数据在修改之后 下面打印的dom内容
      // 还是原来旧的数据  也就印证了  vue在修改数据之后  dom不是立即修改
      console.log(this.$refs.demoh.innerText)

      // 我就是想在数据修改之后立即得到dom信息怎么办?
      this.$nextTick(()=>{
        console.log(this.$refs.demoh.innerText)

      })

    }
  }
}
</script>

vue3.0

vue3.0 是2.0的一个提升 在3.0中完全向下兼容2.0

3.0与2.0有什么区别

1.响应式的区别

2.0中vue的响应式是基于数据劫持(object.defineProperty这个方法来进行劫持的)当前这个方法是兼容主流浏览器的ie9以上都可以使用 他是可以监听数据的变化 从而让vue做出改变

但是他有一个bug object.defineProperty这个方法只能够监听初始化的时候数据 如果程序运行到一半你给data中的对象或者是数组添加新属性的时候 由于他只会在初始化监听 那么你中间添加的内容就无法监听 没有监听到 就没有数据劫持 没有数据劫持 那么久没有双向绑定 没有双向绑定 那么数据变视图就不会发生改变 我们使用$set

3.0中 对2.0的bug进行了解决

3.0中的响应式是采用ES2015中最新的一个规范 Proxy来进行替代的 proxy是惰性监听(他不会在初始化的时候监听数据 而是在你使用数据的时候才回去监听)

proxy是一个比较新的规范 ie还不支持 vue为了解决ie不支持的问题 所以单独给ie进行了适配

传统主流浏览器 使用proxy来进行监听

但是在ie中还是使用2.0中的object.defineProperty

2.底层的变化

3.0底层全部都是使用ts(typescript)编写的 今后3.0可以更好的与ts结合

3.属性声明方式

2.0中都是使用vue属性的方式来创建数据 方法 计算属性等内容的

3.0中 改变了 变成了 api方式进行创建 也就是或vue3.0把变量 方法 计算属性等内容封装成了一个个的方法 使用方法的方式进行调用

脚手架创建

创建的方式和2.0几乎相同 只是在选择版本的时候选择3.0

语法

setup 启动器

setup是一个函数 这个函数处于生命周期的beforecreate 与 created之间

setup 是vue3.0组合式api(CompositionApi)的入口

setup中必须把定义的内容return出去否则无法使用

vue3.0中创建基本数据类型变量

ref来进行创建

ref 接收一个参数为值 然后返回一个响应式的对象

<template>
  <div class="about">
    <h1>This is an about page---{{text}}</h1>
  </div>
</template>
<script>
// 1.引用
import {ref} from "vue"
export default {
  setup() {
    // ref 接收一个参数为值  然后返回一个响应式的对象
    // 2.使用
   let text= ref("我是变量的初始值")
    return {
      text  //3.返回出
    }
  }
}
</script>

基本数据类型的修改

想要修改使用ref创建的值 那么必须必须必须必须 修改他的.value属性

<template>
  <div class="about">
    <h1>This is an about page---{{text}}</h1>
    <button @click="fun()">点我修改</button>
  </div>
</template>
<script>
// 1.引用
import {ref} from "vue"
export default {
  setup() {
    // ref 接收一个参数为值  然后返回一个响应式的对象
    // 2.使用
   let text= ref("我是变量的初始值")

  //  函数直接写在setup方法体内  不要忘了return
   function fun(){
      // 修改ref创建的基本数据类型的变量 必须.value
      text.value="我变了"
   }

    return {
      text,fun  //3.返回出
    }
  }
}
</script>

ref创建多个变量怎么办?

方式:多次创建

      let text=ref("我是初始值")
      let texta=ref("我是初始值1")
      let textb=ref("我是初始值2")
      let textc=ref("我是初始值3")
      let textd=ref("我是初始值4")

reactive来创建复杂数据类型

reactive怎么创建对象 创建数组

reactive是一个方法 其中传递一个对象 或者数据位置 他会返回一个响应式对象

<template>
  <div class="home">
    <h1>{{obj.name}}</h1>
    <button @click="fun()">点我修改</button>
  </div>
</template>

<script>
import {reactive} from "vue"
export default {
  setup(){
    // 创建复杂数据类型reactive
    let obj=reactive({name:"xixi",age:18,sex:"男"})

    // 如果要修改使用reactive创建的复杂数据类型  那么 
    // 不需要不需要不需要  .value可以直接修改
      // 不需要不需要不需要  .value可以直接修改
      // 不需要不需要不需要  .value可以直接修改
      // 不需要不需要不需要  .value可以直接修改
      // 不需要不需要不需要  .value可以直接修改
      // 不需要不需要不需要  .value可以直接修改
      // 不需要不需要不需要  .value可以直接修改
    function fun(){
      obj.name="hahaa"
    }


    return {
      obj,fun
    }
  }
}
</script>

ref与reactive区别

ref使用来创建基本数据类型 ref创建的内容是需要。value的方式来修改

reactive是用来创建复杂数据类型 可以直接修改

dom操作---ref

3.0中进行dom操作 也是用ref来完成

<template>
  <div>
      <h1>我是3.0中测试dom操作的</h1>
        <!-- 2.ref绑定 -->
      <input type="text" ref="inputdom"/>
      <button @click="fun()">点我得到输入框的值</button>
  </div>
</template>

<script>
import {ref} from "vue"
export default {
   setup(){
    // 在使用ref查找dom对象的时候  我们是向ref中传递一个null那么这样一来
    // ref就可以进行dom操作的设置了
    // 1.创建
    let inputdom=ref(null)

    function fun(){
        // 3.获取ref
        console.log(inputdom.value.value)
    }


       return{
           inputdom,fun
       }
   }
}
</script>

<style>

</style>

计算属性---computed

<template>
  <div>
      <h1>计算属性的学习</h1>
      <h1>{{text}}</h1>
      <h1>{{newtext}}</h1>
  </div>
</template>

<script>
// 1.先引用引用计算属性
import {ref,computed} from "vue"
export default {
    setup(){
        let text=ref("abcdefghijk")
        // 2.计算属性的编写  不要忘了在计算属性中必须有return
        // let newtext=computed(()=>{
        //     return 你处理数据的逻辑
        // })
        let newtext=computed(()=>{
            return text.value.toUpperCase()
        })
        return {
            text,newtext
        }
    }
}
</script>

<style>

</style>

watch监听

<template>
  <div>
      <h1>watch的使用</h1>
      <h1>{{text}}</h1>
      <button @click="fun()">点我修改</button>
  </div>
</template>

<script>
import {watch,ref} from "vue"
export default {
    setup(){
        let text=ref("我是测试watch的变量")

        function fun(){
            text.value="我变了"
        }

        // watch(()=>{return 你要监听的变量.value},(newval,oldval)=>{})
        watch(()=>{
            return text.value
        },(a,b)=>{
            console.log(a)
            console.log(b)
        })
        return {
            text,fun
        }
    }
}
</script>

<style>

</style>

生命周期

2.0的生命周期 3.0的生命周期

实例创建之前 beforeCreate () setup

实例创建之后 created() setup

模板渲染之前 beforeMount() onBeforeMount

模板渲染之后 mounted() onMounted

数据更新之前 beforeUpdate() onBeforeUpdate

数据更新之后 updated() onUpdated

实例销毁之前 beforeDestory() onBeforeUnmount

实例销毁之后 destoryed() onUnmounted

git

git 是什么?

开源 分布式的版本控制系统

git的三层结构

git中有三个区域

1.工作区

2.暂存区/缓存区

3.版本库

git使用

初始化

git 项目的路径中如果出现中文 可能会出现一些奇奇怪怪的错误

git init 会在我们项目下面创建一个隐藏文件夹 .git 这个文件夹千万不要删了 或者是修改 因为其中保存这你们之前的版本信息

查看工作区的文件状态

git status

把工作区的文件提交到暂存区

git add .

把暂存区的文件提交到版本库

git commit -m “你本次的提交注释”

查看下我们的提交信息

git log

从工作区直接去交到版本库

要使用如下命令前提是 必须按照上面的流程正常的提交一次(工作区到暂存区 暂存区到版本库)

git commit -am “本次提交的注释”

git修改最近一次提交的注释

有两个--

git commit --amend

把暂存区的内容替换工作区

git checkout空格--空格文件名

拉取指定版本库的内容替换暂存区

git reset commitid

修改当前工作区的文件名

mv 旧名字 新名字

分支

每个版本控制系统中都会有一个分支这种操作 只是git中操纵分的速度更快效率更高

举个例子 加谁我们准备开发一个项目的新功能,但是这个功能需要两周才能开发完 第一周我们写了百分之50 但是如果我们直接提交可能会造成项目报错无法运行 那么这个不完整的项目 也会造成你同事没有办法干活了 但是如果等着你的代码写完你在提交 又会面临着代码丢失的风险

所以有了分支就不怕了 我们可以创建一个属于我们自己的分支 这个分支是独立的 不和其他人共享 那么这样一来我可以把代码传递到我这个分支上 避免了代码的丢失 也不会影响其他同事工作

查看本地分支

git branch

新建分支

git branch 新建的分支名

切换分支

git checkout 要切换的分支名

删除分支

git branch -d 你要删除的分支名

修改分支名

git branch -m 旧名字 新名字

合并分支

要合并 先切换到主分支上

git merge 你要合并的分支

解决冲突

如果今后工作了代码上出现了如下的内容

<<<<<<< HEAD
3333333cssmastre
=======
3333333cssdev
>>>>>>> dev

这个就叫代码冲突了

出现原因

同一行出现了不同的代码 git不知道需要删除谁 或者是保留谁 git就直接用这些符号给我们标识出冲突让我们自行修改

解决方式

你和谁冲突 你就把谁叫过来 两个人一人一杯茶 坐下来慢慢的来删

node

node是什么?

node 就是 基于chrome谷歌浏览器 当中的js解析器(v8引擎) 所延伸出的一个js解析器(可以让js在脱离浏览器的环境下进行单独的运行)

因为node可以在任何操作系统上运行 所以我们就可以让js在任何操作系统上单独运行 (可以让我们的js变成一个后端语言)

node的特点

1.单线程 单线程就可以把计算机的硬件使用率推到100%

2.非阻塞io

3.事件驱动

node怎么运行js

1.cmd cd到指定的文件夹下

2.输入node 文件名 即可运行

express

node的一个企业级框架 在我们实现服务器创建的时候 可以帮助我们节省大量的代码量 因为express中封装这大量的http方法可供我们使用 高效的创建http服务器

使用:

1.下载 npm install --save express

postman

浏览器没有办法测试post 并且 你可能今后回合公司的其他同事有接口上的冲突 可以使用postman接口测试工具来进行接口是否正确的判断

mongodb

为什么要学习数据库?

1.岗位需求

2.得数据得天下

3.数据存储方式的进化

为什么要学习mongodb

因为mongodb是非关系型数据库

mysql是关系型数据库

安装

在安装的时候我们在选择安装路径的过程中 路径上不能有空格特殊符号中文等内容

配置服务 1.新建对应文件夹

在你安装mongodb的文件夹下 根路径下创建data文件夹 然后在data文件夹下创建db文件夹

2.配置服务

使用管理员方式打开cmd

使用管理员方式打开cmd

使用管理员方式打开cmd

使用管理员方式打开cmd

使用管理员方式打开cmd

使用管理员方式打开cmd

使用管理员方式打开cmd

把cmd的路径切换到你刚才安装的mongodb的bin文件夹路径之下

切换盘符 你的盘名:

进入指定文件夹 cd 你的文件夹名

安装服务:

mongod.exe --install --dbpath 你刚才新建的db文件夹的路径 --logpath 你刚才新建的data路径\log.txt

启动mongodb

1.在cmd中路径切换到你mongodb的bin路径下

net start mongodb

2.我的电脑右键-------》管理---》服务和应用程序-----》服务----》在弹出的列表中找到mongodb ----》点击启动即可

mongodb

在存储数据的时候操纵的就是json

db.集合名.insert({key:val})

db.集合名.find()

db.集合名.remove() 全部删除

db.集合名.remove({删除的key,删除的val})

db.集合名.update(

{你要修改的key:你要修改的val},

{$set:{你要修改的key:你修改之后的值}}

)

mongoose操纵mongodb

就是一个node的第三方库 使用他就可以来操纵mongodb

下载: cnpm install --save mongoose@5

设置连接模块

// 使用mongoose操纵mongodb
var mongoose = require('mongoose');
// 连接指定的数据库
mongoose.connect('mongodb://localhost/xixi',{ useNewUrlParser: true,useUnifiedTopology: true });
// 开始连接  把连接状态交给db这个变量保存
var db = mongoose.connection;
// 错误的提示信息
db.on('error', console.error.bind(console, '错误了'));
// 正确的提示信息
db.once('open', function() {
  console.log("连接ok");
});

// 设置数据库操纵对象
// 并且在其中放置你要操作的数据的key与数据类型
var helloSchema = mongoose.Schema({
    name: String,
    age:String
  });

// 设置你的连接集合(mysql中叫表)
var hello = mongoose.model('haha', helloSchema);

module.exports=hello

分页

1.limit() 读取指定数量的数据

2.skip() 跳过多少条数据

RESTfulAPI

就是一个在定义接口的时候规范 让我们在发送请求的时候 使用动作+操作的接口 更加清楚的描述当前我们这个接口是干什么的

原有的请求 GET POST 语义化不够明确

在有了RESTfulAPI之后 可以让我们的接口更加具有语义化

GET 读取后台内容

POST 发送内容

PUT 更新修改内容 (更新全部)

PATCH 更新修改内容(局部更新)

DELETE 删除

前端发送数据的方式和GET方式一样

表格的修改与删除

图片上传

node中图片上传依赖于 multer这个模块来实现图片上传

下载 npm install --save multer

前台部分

form表单方式上传图片

    <form action="http://localhost:3000/user/imgup" method="POST" enctype="multipart/form-data">
      <!-- name属性就是form表单方式提交数据的时候  写key的位置 -->
          <input type="file" name="logo"/>
          <input type="submit" value="图片上传">
      </form>

ajax方式上传图片

 //创建FormData对象
        var data = new FormData();
        let imgdata= this.$refs.demoinput.files[0]//得到当前表单元素选中的图片
        // 把得到的图片传递给formdata对象
        // data.append("key","val")
        data.append("logo",imgdata)
        // 通过封装好的ajax把这个带有图片的数据传递给后台
        postlink("/api/user/imgup",data).then((ok)=>{
          console.log(ok)
        })

接口文档

接口文档 就是后端与前端在沟通接口的时候 一个笔记 一个描述性的东西(有的公司是一个word 有的公司是一个在线地址网页 有的公司是一个txt文档 更过分的可能就是给你微信发一个信息)

数据加密

传统我们写完的项目 我们敏感数据都是以明文存储在数据库中的

明文方式不太安全 所以我们可以使用加密的方式 对数据进行存储

md5加密

crypto加密模块来进行加密

下载 npm install --save crypto

使用 不许要记

crypto.createHash('MD5').update("你要加密的数据").digest("hex")

就是在用户注册的时候 之前我们使用的是把前台发送过来的数据 直接存到数据库中

但是现在有了加密 我们可以把前台的数据 现在后台加密 把加密之后的数据入库

之前的登录就是把前台发送过来的数据数据进行直接数据库查询

我们在加密之后需要 在登录的时候 也罢前台的数据先加密 然后 那加密之后的数据去数据库比对即可

token

是你们今后工作的时候前后台交互中很重要的一个知识点 主要就是用来保存 用户的登录信息 一个网站在登录之后 在进入到这个网站其他的页面中 那么在这个时候我们也许要知道用户是否登录过没有 我们如何在一个页面登录过之后 这个项目下其他页面都要知道用户是否登录过

node中使用token

jwt jsonwebwebtoken

使用jwt就可以在后台用户登录成功之后生成token

下载 npm install --save jsonwebtoken

token的使用流程

前台

1

当用户登录的时候 发送给后台一个请求 里面携带者你的用户名和密码

前台接收到 并且把这个token 保存在本地存储或者cookie中

2

在用户登录页面登录程程后 跳转到其他页面比如首页 首页也需要知道用户是否登录过()

前台从本地存储或者cookie中吧这个保存的token取出来 把这个token发送给后台

我们可以使用 路由守卫来进行token的获取与请求的发送 因为路由守卫在跳转路由的时候就会自动触发 尤其是全局前置守卫 他会在项目的任何页面跳转的时候都进行触发 所以我们可以在全局前置守卫中进行本地存储的获取 与token的发送 那么这样一来 项目中有一万个页面都不怕

就根据后台的错误信息或者失败信息做出相应的反应

后台

1

后台接收到我们发送的用户名和密码 先去数据库判断当前用户的用户名和密码是否在库中存在 存在就是登录成功 不存在就是登录失败、

如果用户是登录成功的状态 那么在这个时候 我们就可以在后台部分生成一个token(用来保存用户登录状态的加密字符串)吧这段内容 和用户是否登录成功的信息 一并返回给前台

生成token

使用到jwt中的 sign方法(你要生成token的数据,秘钥自己写越乱越好)生成token

2 需要接收前台发送过来的token 并且把这个token 解析出来 判断其中的用户登录状态是否正确

verify( 你要解析的token,秘钥和加密的时候必须一样,回调 )解析token

如果正确或者错误都需要给前台一个相应

webpack

webpack是什么?

webpack就是一个模块打包工具 他可以可以分析我们的项目结构 找到js模块以及其他浏览器不能识别的东西(scss less ts等内容)把他们转换打包成为浏览器可以识别的内容

webpack能干什么?

为不怕吃苦可以根据我们的入口的依赖 加载当前项目中的所有js模块 然后把他们合并成一个标准的js 让我们的浏览器可以征程的识别我们的代码

webpack与gulp/grunt 有什么区别

webpack与其他两个没有太大的可比性

gulp/grunt 是一个可以优化前端开发流程的一个工具 webpack是一个模块化解决方案

gulp或者grunt的工作流程 在一个配置文件中指明对某写文件的编译 压缩等任务 这个工具可以按照我们的配置一步步的帮助我们根据这个文职文件来完成

webpack 把一个项目当成一个整体 我们只需要制定一个主入口文件 webpack就会自动的根据这个入口文件找到我们所有的依赖自动完成对项目的处理

webpack的学习内容

webpack分为4大部分 入口 出口 加载器loaders 与 插件plugin

webpack基本使用

安装webpack

webpack 核心文件

webpack-cli 执行webpack的命令行

全局安装 : npm install -g webpack@4.38.3 webpack-cli@3

基本使用

创建一个es6模块化

a.js

export let name="xixxi"
export let age = 18



b.js
import {name,age} from "./a.js"

console.log(name)
console.log(age)

index.html

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

上面模块化的代码 浏览器是不能识别的 因为浏览器不认识模块化项目

所以我们就要想办法让浏览器认识

可以使用webpack进行模块化项目打包

webpack

在之前的es6模块化语法之上 在当前路径下cmd输入如下内容

webpack 你要打包的文件 -o 输出的文件(名字自己起)

输入上面的命令大家会发现webpack帮助我们自动的打包成了一个文件 那么浏览器就可以直接运行这个文件了

设置打包模式

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

当我们正常的打包之后 会发现在控制台有一堆黄色的警告 就是提示我们让我们来设置打包模式

production 默认模式 生产环境 会压缩我们的代码

development 开发环境 不会压缩我们的代码

设置打包模式 : webpack --mode 你要设置的打包模式 你要打包的文件 -o 输入的文件

简便的打包命令

简单的打包命令 我们需要对文件与文件夹有所要求

要求

1.模块代码的文件夹的名字必须叫src

2.其次你要打包的入口文件必须叫index.js

简单的打包命令 webpack --mode 你要设置的打包模式

会把我们打包生成的内容 放到一个dist文件夹下

配置文件

配置文件叫做 webpack.config.js

入口 : entry

出口:output

module.exports={
    // 入口
    entry:"./src/index.js",
    // 出口
    output:{
        // 输出文件夹路径  __dirname代表当前运行文件的文件夹绝对路径
        path:__dirname+"/dist",
        // 文件名
        filename:"outindex.js"
    }
}

输入webpack 即可直接打包

一次打包多个文件

如果我们有多个入口文件怎么办?

module.exports={
    // 入口
    // entry:"./src/index.js",
    // 多个入口文件
    entry:{
        aa:"./src/index.js",
        bb:"./src/indexb.js"
    },
    // 出口
    output:{
        // 输出文件夹路径  __dirname代表当前运行文件的文件夹绝对路径
        path:__dirname+"/dist",
        // 文件名
        // filename:"outindex.js"
        // 多个入口文件  我们就需要设置对应的出口文件
        // [name]最终会被入口文件的key所替代
        filename:"new[name].js"
    }
}

配置打包模式

mode 配置打包模式

module.exports={
    // 入口
    // entry:"./src/index.js",
    // 多个入口文件
    entry:{
        aa:"./src/index.js",
        bb:"./src/indexb.js"
    },
    // 出口
    output:{
        // 输出文件夹路径  __dirname代表当前运行文件的文件夹绝对路径
        path:__dirname+"/dist",
        // 文件名
        // filename:"outindex.js"
        // 多个入口文件  我们就需要设置对应的出口文件
        // [name]最终会被入口文件的key所替代
        filename:"new[name].js"
    },
    mode:"development"//打包模式
}

plugin 插件

plugin 主要适用于扩展webpack的功能 通过插件可以让webpack完成与构建项目无关的事情

plugin 在配置的时候 接收一个数组 数组的每一项 就是一个插件

clean-webpack-plugin

clean-webpack-plugin - npm

在打包之后清空之前打包的内容

下载: npm install --save-dev clean-webpack-plugin

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports={
    // 入口
    // entry:"./src/index.js",
    // 多个入口文件
    entry:{
        ooo:"./src/index.js",
        pppp:"./src/indexb.js"
    },
    // 出口
    output:{
        // 输出文件夹路径  __dirname代表当前运行文件的文件夹绝对路径
        path:__dirname+"/dist",
        // 文件名
        // filename:"outindex.js"
        // 多个入口文件  我们就需要设置对应的出口文件
        // [name]最终会被入口文件的key所替代
        filename:"new[name].js"
    },
    mode:"development",//打包模式
    plugins: [//插件
        new CleanWebpackPlugin(),
    ],
}

html-webpack-plugin

就是把打包之后的内容自动添加到指定的html中

在使用html-webpack-plugin的时候建议在本地再次安装webpack

npm install --save webpack@4.39.3 webpack-cli@3

下载 html-webpack-plugin npm install --save html-webpack-plugin@4

引用使用

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports={
    // 入口
    // entry:"./src/index.js",
    // 多个入口文件
    entry:{
        cc:"./src/index.js",
        dd:"./src/indexb.js"
    },
    // 出口
    output:{
        // 输出文件夹路径  __dirname代表当前运行文件的文件夹绝对路径
        path:__dirname+"/dist",
        // 文件名
        // filename:"outindex.js"
        // 多个入口文件  我们就需要设置对应的出口文件
        // [name]最终会被入口文件的key所替代
        filename:"new[name].js"
    },
    mode:"development",//打包模式
    plugins: [//插件
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin()
    ],
}

指定模板

原有的html是自动生成的 我们也可以手工定义一个模板

新建一个public的文件夹 在其中创建一个index.html的文件

并且在plugin中进行template的配置即可

  plugins: [//插件
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template:"./public/index.html",
            title:"你好么么哒"
        })
    ],

devserver

开发中的微型服务器

npm install -g webpack-dev-server@3

npm install --save webpack-dev-server@3

在项目的路径下直接输入webpack-dev-server即可用开发服务器运行项目

当项目启动的时候我们是输入webpack-dev-server来进行启动的 如何修改他的启动别名

是在项目的package.json文件中 scripts节点中进行修改 如果这个文件没有这个节点那是因为我们没有初始化 npm init

  "scripts": {
    "serve":"webpack-dev-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

loader 加载器

loader就是把浏览器不认识的东西翻译成浏览器可以认识的东西 ts less等内容翻译成js css

css-loader与style-loader

style-loader就是引用css

css-loader就是把css动态插入到模板中

npm install --save css-loader@3 style-loader@1

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports={
    // 入口
    // entry:"./src/index.js",
    // 多个入口文件
    entry:{
        cc:"./src/index.js",
        dd:"./src/indexb.js"
    },
    // 出口
    output:{
        // 输出文件夹路径  __dirname代表当前运行文件的文件夹绝对路径
        path:__dirname+"/dist",
        // 文件名
        // filename:"outindex.js"
        // 多个入口文件  我们就需要设置对应的出口文件
        // [name]最终会被入口文件的key所替代
        filename:"new[name].js"
    },
    mode:"development",//打包模式
    plugins: [//插件
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template:"./public/index.html",
            title:"你好么么哒"
        })
    ],
    module: {//loader加载器
        rules: [
          {
            test: /\.css$/i,
            use: ["style-loader", "css-loader"],
          },
        ],
      },
}

less-loader

npm install --save less less-loader@6

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports={
    // 入口
    // entry:"./src/index.js",
    // 多个入口文件
    entry:{
        cc:"./src/index.js",
        dd:"./src/indexb.js"
    },
    // 出口
    output:{
        // 输出文件夹路径  __dirname代表当前运行文件的文件夹绝对路径
        path:__dirname+"/dist",
        // 文件名
        // filename:"outindex.js"
        // 多个入口文件  我们就需要设置对应的出口文件
        // [name]最终会被入口文件的key所替代
        filename:"new[name].js"
    },
    mode:"development",//打包模式
    plugins: [//插件
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template:"./public/index.html",
            title:"你好么么哒"
        })
    ],
    module: {//loader加载器
        rules: [
          {
            test: /\.css$/i,
            use: ["style-loader", "css-loader"],
          },

          {
            test: /\.less$/i,
            loader: [
              // compiles Less to CSS
              'style-loader',
              'css-loader',
              'less-loader',
            ],
          },
        ],
      },
}

sass-loader

sass-loader | webpack 中文文档

npm install sass-loader@8 sass webpack --save-dev

webpack打包原理

webpack就是把一个项目当场一个整体 我们通过给定一个主文件 webpack就通过这个文件开始去寻找他所有的依赖 自动根据loaders处理这些文件 最终把项目翻译成浏览器可以直接识别的内容

工作流程

1.解析配置参数 webpack.config.js中的文件进行解析 分析主相关的配置文件

2.注册配置中的插件 做出反应

3.根据项目中的entry入口 分析并找出所有的依赖

4.根据loader的配置 进行文件的转换

5.根据出口文件 把打包好的内容生成到指定文件夹中

react

什么是react

react是FaceBook推出一款用于构建用户界面的javascript库

特点

声明式设计---我们只需要关心显示内容 react就会自动的把内容展示到页面上

高效

灵活-- react可以吧已知的第三库进行很好的整合

组件化

单向数据流--- 主要是指 数据从父节点传递到子节点

react的发展史

2013年6月推出的

2013年9月 就受到追捧

2015年 推出了一个reactNative的跨平台开发

cra---------create-react-app

在前两天 create-react-app更新了

他里面对node的版本有要求了 node的版本不能低于14了

win7系统node的版本不能大于12 npm install -g create-react-app@4

1.全局下载 npm install -g create-react-app

2.查看版本 create-react-app --version

3.cd 到指定文件夹下

4.创建项目 create-react-app 项目名

5.cd到你创建的项目下

6.npm start 启动项目

拿到空项目怎么办?

1.在src下新建一个文件夹 components

2.删除app.js与app.css

3.在components文件夹中创建属于我们自己的组件 xxx.jsx

4.在文件中创建一个基本的组件

import React, { Component } from 'react'

export default class home extends Component {
    render() {
        return (
            <div>
                我是一个组件
            </div>
        )
    }
}

5.在index.js下进行引用使用

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Home from './components/home.jsx';//把路径切换成我们自己创建的组件
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    {/* 修改使用 */}
    <Home />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

jsx

jsx== javascript and xml 他是一个新的语法扩展 在react中使用jsx的语法来进行页面内容的描述

jsx 当遇见< 当html解析遇见{}当js解析

import React, { Component } from 'react'

export default class home extends Component {

    render() {
        let text="你好"//创建了一个变量
        return (
            <div>
                我是一个组件---{text}
            </div>
        )
    }
}


jsx语法注意

1.jsx中html的标签必须严格按照w3c的规范来编写

			<div>
               <input type="text" />
            </div>

2.jsx中怎么注释

注释是

{/*我是注释的内容*/}

 			<div>
                {/* 我是一个注释 */}
                
               <input type="text" />
            </div>

3.多行html

在react的组件中多行html必须有一个父容器包裹

import React, { Component } from 'react'

export default class demo extends Component {
    render() {
        return (
            // 多行标签必须有一个父容器包裹
            <div>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
            </div>
          
        )
    }
}

空标签

大家发现刚才上面写的内容 我们的最外层的div是多余的

空标签 在页面是不进行展示的 它的作用仅仅就是用来描述多行标签的一个包裹作用

写法1:

<></>

import React, { Component } from 'react'

export default class demo extends Component {
    render() {
        return (
            // 空标签1
            <>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
            </>
          
        )
    }
}

写法2:

Fragment空标签

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

export default class demo extends Component {
    render() {
        return (
            // 空标签2
            <Fragment>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
            </Fragment>
          
        )
    }
}

4 {}中还可以写什么?

我们可以在jsx的{}中放置任何表达式

import React, { Component } from 'react'

export default class demob extends Component {
    render() {
        let a=3
        let b=5
        let fun=()=>{
            return "我是一个函数"
        }
        let bool=false
        return (
            <div>
                <h1>我是测试大括号的例子</h1>
                <h1>进行运算符的操作--{a+b}</h1>
                <h1>进行函数的调用 --- {fun()}</h1>
                <h1>进行复杂的运算符 --- {bool?"你好":"你坏"}</h1>
            </div>
        )
    }
}

5.属性插变量

import React, { Component } from 'react'

export default class democ extends Component {
    render() {
        let text="点我去百度"
        let ahref="http://www.baidu.com"
        return (
            <>
                <h1>属性插变量</h1>
                {/* react中属性插变量   属性={你要插的值} */}
                <a href={ahref}>{text}</a>
            </>
        )
    }
}

6.怎么设置行内样式

import React, { Component } from 'react'

export default class demod extends Component {
    render() {
        return (
            <div>
                {/* jsx中写行内样式 style={{对象的key:val}} */}
                {/* 如果样式是多个单词 那么必须使用小驼峰命名把  把-去掉 后面的单词大写 */}
                <h1 style={{color:'red',backgroundColor:'yellow'}}>设置行内样式</h1>
            </div>
        )
    }
}

便利列表

使用 原生js的数组 map方法

import React, { Component } from 'react'

export default class demoe extends Component {
    render() {
        let arr=["小明","小白","小黑","小花"]
        let obj=[
            {name:"xixi1",age:181},
            {name:"xixi2",age:182},
            {name:"xixi3",age:183},
            {name:"xixi4",age:184},
            {name:"xixi5",age:185}
        ]
        return (
            <div>
                <h1>便利数据</h1>
                <ul>
                    {
                        arr.map((v,i)=>{
                            return (
                                <li key={i}>{i}----{v}</li>
                            )
                        })
                    }
                </ul>
                <hr />
                <h1>便利表格</h1>
                <table border="1">
                    <tbody>
                  {
                      obj.map((v,i)=>{
                          return (
                              <tr key={i}>
                                  <td>{v.name}</td>
                                  <td>{v.age}</td>
                              </tr>
                          )
                      })
                  }
                    </tbody>
                </table>
            </div>
        )
    }
}

组件

扩展

模块与模块化

模块:用来封装可以重复使用的js代码块

模块化:整个项目都是使用模块的方式来完成的

组件与组件化

组件: 用来封装重复使用的ui代码块

组件化:整个项目都是使用组件的方式来完成的

组件的基本改变

组件就是把ui部分拆分成一个个独立的并且可以重复使用的部件 在吧这些部件拼装在一起 形成一个页面

组件的分类

函数组件/无状态组件

1.函数组件的首字母必须大写首字母必须大写首字母必须大写首字母必须大写首字母必须大写首字母必须大写

2.函数中必须有一个return return 一段jsx

// 我是函数组件的例子
function Fun(){
    return (
        <>
            <h1>我是一个函数组件</h1>
        </>
    )
}
export default Fun

父子组件

组件的使用

1.引用

2.使用

父组件
// 1.引用子组件
import Zi from "./zi.jsx"
function Fu(){
    return (
        <div>
            我是一个父组件
            {/* 2.使用 */}
            <Zi/>
        </div>
    )
}
export default Fu



子组件
function Zi(){
    return (
        <>
            <h1>我是一个子组件</h1>
        </>
    )
}
export default Zi

类组件

1.在类中必须必须必须有一个render的方法 其中必须要有一个return 一段jsx

2.这个类要继承React.Component

import React, { Component } from 'react'

export default class demob extends Component {
    render() {// 就是渲染的意思  执行这个render他就会执行jsx
        return (
            <div>
                
            </div>
        )
    }
}

父子组件

父组件

import React, { Component } from 'react'
// 1.引用子组件
import Zi from "./zi.jsx"
export default class fu extends Component {
    render() {
        return (
            <div>
                我是一个父组件
                {/* 2.使用 */}
                <Zi/>
            </div>
        )
    }
}

子组件

import React, { Component } from 'react'

export default class zi extends Component {
    render() {
        return (
            <div>
                我是一个子组件
            </div>
        )
    }
}

props

props就是组件对外的接口 可以使用props 从组件外部向内部进行数据的传递 从而完成父子组件的传值

props是只读的 他不能被修改

函数组件/无状态

1.把props当成函数的形参传递

2.在子组件设置接收数据

3.在父组件的传递

子组件

// 1.在子组件的形参中 注入props
function Zi(props){
    return (
        <>
                        {/* 2.使用props */}
            <h1>我是一个子组件---{props.title}</h1>
        </>
    )
}
export default Zi
父组件


import Zi from "./zi.jsx"
function Fu(){
    return (
        <div>
            我是一个父组件
            {/* 3.在父组件给子组件的props传递数据 */}
            <Zi title="我是正向传值的数据"/>
        </div>
    )
}
export default Fu

类组件

1.子组件使用this.props.xxx接收

import React, { Component } from 'react'

export default class zi extends Component {
    render() {
        return (
            <div>
                {/* 1.直接使用this.props来进行接收 */}
                我是一个子组件---{this.props.num}
            </div>
        )
    }
}

2.父组件传递

import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    render() {
        return (
            <div>
                我是一个父组件
                {/* 2.给子组件传递数据 */}
                <Zi num="我是num"/>
            </div>
        )
    }
}

props验证

注意:

自 React v15.5 起,React.PropTypes 已移入另一个包中。请使用 prop-types 库 代替。

在cra脚手架中 prop-types的库默认是帮助我们下载好的 我们可以直接使用

函数组件

1.子组件引用prop-types库 并且设置验证

// 1.引用
import PropTypes from 'prop-types';
function Zi(props){
    return (
        <div>
            我是子组件--{props.name}
        </div>
    )
}

// 设置   注意单词大小写
Zi.propTypes={
    name:PropTypes.string
}
export default Zi

2.父组件正常传递

import Zi from "./zi.jsx"
function Fu(){
    let num="666"
    return (
        <div>
            我是父组件
            <Zi name={num}/>
        </div>
    )
}
export default Fu

类组件

子组件

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class zi extends Component {
    static propTypes = {
        name: PropTypes.string
    }

    render() {
        return (
            <div>
                ziziziz--{this.props.name}
            </div>
        )
    }
}


父组件
import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    render() {
        return (
            <div>
                fuffufufu
                <Zi name={"666"}/>
            </div>
        )
    }
}


props公司写法

import React, { Component } from 'react'

export default class zi extends Component {
    render() {
        // 因为你会发现在页面中有大量的this.props看上去非常的麻烦 很冗余
        // 所以我们需要想办法把这个this.props省略掉
        // this.props既然能.出name age等内容  那么久表示这个this.props
        // 是一个对象  既然是个对象 那么我们就可以使用es6解构赋值的语法
        // 快速的取出每个值
       let {name,age,sex,love}=this.props
        return (
            <div>
                zizizizizizizi
                {/* <h1>{this.props.name}</h1>
                <h1>{this.props.age}</h1>
                <h1>{this.props.sex}</h1>
                <h1>{this.props.love}</h1> */}
                {/* 解构赋值减少了代码量页面中也没有那么多this。props */}
                <h1>{name}</h1>
                <h1>{age}</h1>
                <h1>{sex}</h1>
                <h1>{love}</h1>
            </div>
        )
    }
}

父组件传递的时候使用扩展运算符简化了传递的过程

import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    render() {
        let obj={
            name:"xixi1",
            age:181,
            sex:"男1",
            love:"女"
        }
        return (
            <div>
                fuffufufufu
                {/*
                 这种写法 就是子组件需要多少个参数  那么父组件就给他
                传递多少个  没问题 这种写法没问题  但是不够巧妙 
                并且页面中有很多的冗余代码 
                
                为了解决这个问题  我们就可以使用es6  ...扩展运算符的方式

                */}
                {/* <Zi name="xixi" age="18" sex="男" love="女" /> */}
                <Zi {...obj}/>
            </div>
        )
    }
}

this.props.children

思考一个问题

在react中组件的本质是自定义标签 在自定义标签的开关标签中我们能插入新的内容吗?

默认情况下 在组件被调用的时候 是不能插入内容的 你写了他也不显示

 				默认页面是不能插入的 
                <Zib>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                </Zib>

this.props.children

他表示的就是当前组件的所有子节点

在组件中设置this.props。children之后

                <div>
                    zibbbbbbb

                    {this.props.children}
                </div>

就可以向组件内部插入新内容了

   				 <Zib>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                </Zib>

state---状态机

状态(数据/变量)机(机制)

react中创建数据 都是在state中进行创建的

在react中我们开发者只会关心数据 数据改变了页面也随之发生改变

创建状态

思考 函数组件能使用状态吗?

函数组件不能使用状态(后面咱们到react高级部分学到hook就可以让函数组件使用状态 但是现在不行)

import React, { Component } from 'react'

export default class demoi extends Component {
    // 我们要使用状态  必须把状态创建在class的constructor中
    constructor(){
        //es6的继承规则中  不管子类写不写constructor 在实例化的时候都会补上constructor
       // 如果我们写了constructor  那么在其中必须写super() 调用父类的构造方法  那么这个时候子类才会有			//自己的this
        super()

        // 创建状态
        this.state={
            name:"xixi",
            arr:[1111,22222,3333],
            obj:{name:"hehe",age:99}
        }
    }
    render() {
        return (
            <div>
                <h1>状态的使用</h1>
            </div>
        )
    }
}

使用状态

this.state.xxx

  render() {
        return (
            <div>
                {/* 状态的使用直接使用  this.state.xxx */}
                <h1>状态的使用---{this.state.name}</h1>
            </div>
        )
    }

修改状态

在react中状态的修改必须必须必须使用this.setState({你要修改谁:修改成什么})

import React, { Component } from 'react'

export default class demoi extends Component {
    // 我们要使用状态  必须把状态创建在class的constructor中
    constructor(){
        super()

        // 创建状态
        this.state={
            name:"xixi",
            arr:[1111,22222,3333],
            obj:{name:"hehe",age:99}
        }
    }

    fun=()=>{
        // 修改状态
        this.setState({
            name:"我变了"
        })
    }

    render() {
        return (
            <div>
                {/* 状态的使用直接使用  this.state.xxx */}
                <h1>状态的使用---{this.state.name}</h1>
                <button onClick={this.fun}>点我修改</button>
            </div>
        )
    }
}

修改数据的时候能不使用setState吗?

不能不使用setState 因为不使用他修改 页面是不会变得

import React, { Component } from 'react'

export default class demoa extends Component {
    constructor(){
        super()

        this.state={
            text:"你好"
        }
    }
    fun=()=>{
        // 使用这种等于号的方式  是可以修改数据的  但是页面不会发生改变
      this.state.text="我没有使用setState修改"
      console.log(this.state.text)
    }
    render() {
        return (
            <div>
                <h1>{this.state.text}</h1>
                <button onClick={this.fun}>修改state的数据</button>
            </div>
        )
    }
}

当我调用了setState之后发生了什么事情

1.setState是异步的

import React, { Component } from 'react'

export default class demob extends Component {
    constructor(){
        super()

        this.state={
            text:"你好"
        }
    }
    fun=()=>{
        // setState是异步的
      this.setState({
          text:"我被改了"
      })
    //   如果setState修改是同步的那么下面的console应该打印出来的就是修改之后的数据
    //   如果setState是异步的那么打印出来的应该就是原始数据
      console.log(this.state.text)
    }
    render() {
        return (
            <div>
                <h1>{this.state.text}</h1>
                <button onClick={this.fun}>修改state的数据</button>
            </div>
        )
    }
}

2 调用了setState之后重新触发了render渲染

import React, { Component } from 'react'

export default class demob extends Component {
    constructor(){
        super()

        this.state={
            text:"你好"
        }
    }
    fun=()=>{
        // setState是异步的
      this.setState({
          text:"我被改了"
      })
    //   如果setState修改是同步的那么下面的console应该打印出来的就是修改之后的数据
    //   如果setState是异步的那么打印出来的应该就是原始数据
      console.log(this.state.text)
    }
    render() {
        // 调用了setState之后页面为什么能改变  原因就是render方法被setState调用了
        console.log("我是render方法")
        return (
            <div>
                <h1>{this.state.text}</h1>
                <button onClick={this.fun}>修改state的数据</button>
            </div>
        )
    }
}

3 因为setState是异步的 那么我们怎么在修改完成之后打印修改之后的结果

this.setState({
          text:"我被改了"
      },()=>{
        //   setState修改成功之后的回调函数
        console.log(this.state.text)
      })

扩展----强制刷新

在react中我们创建变量 并且修改这个变量现在页面做出反应 那么默认情况下我们必须要在state上进行完成 并且修改数据必须要使用setState

我们有没有其他的方式让数据不在state之上并且修改数据还想让页面页同时改变的方法呢?

import React, { Component } from 'react'

export default class demob extends Component {
    constructor(){
        super()
        this.state={
            text:"我是状态的数据"
        }

        // 不想把变量创建在state之上
        this.num=666
    }
    fun=()=>{
        this.setState({
            text:"我被改了"
        })
    }

    funb=()=>{
        this.num=9527
        console.log(this.num);

        // 强制刷新---强制触发render渲染
        this.forceUpdate()
    }
    render() {
        return (
            <div>
                <h1>强制刷新</h1>
                <h1>传统的state-----{this.state.text}</h1>
                <button onClick={this.fun}>点我修改</button>

                <hr />
                <h1>不在state上创建变量----{this.num}</h1>
                <button onClick={this.funb}>点我修改</button>
            </div>
        )
    }
}

事件处理

事件绑定

在react中事件的绑定 使用小驼峰命名法

例:onclick 在react中 onClick

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

事件处理中的一些操作

阻止事件默认行为 阻止冒泡

同原生js一样没有任何改变

阻止事件的默认行为 原生 preventDefault() react也是使用相同的方式完成

函数实参传递

因为在react中函数调用的时候不加() 那我我们如果要传递函数的实参怎么传递?

1.使用bind方式进行传递

 <button onClick={this.fun.bind(this,"我是实参1","我是实参2")}>点我传递函数实参</button>

2.使用箭头函数调用函数进行传递

<button onClick={()=>{this.funb(1111,2222)}}>点我传递实参2</button>

整体代码

import React, { Component } from 'react'

export default class demoe extends Component {
    fun=(num,text)=>{
        console.log(num)
        console.log(text)
    }
    funb=(num,text)=>{
        console.log(num)
        console.log(text)
    }
    render() {
        return (
            <div>
                <h1>函数实参传递</h1>
                {/* bind方式传递实参 */}
                <button onClick={this.fun.bind(this,"我是实参1","我是实参2")}>点我传递函数实参</button>
                
                {/* 使用箭头函数调用函数进行传递 */}
                <button onClick={()=>{this.funb(1111,2222)}}>点我传递实参2</button>
            </div>
        )
    }
}

修改this 指向

1.通过创建箭头函数的方式来修改

funa=()=>{
        this.setState({
            text:"我改了创建箭头函数"
        })
    }

2.通过bind绑定绑定this

<button onClick={this.funb.bind(this)}>通过bind绑定绑定this</button>

3.把函数的调用变成箭头函数调用

 <button onClick={()=>{this.func()}}>把函数的调用变成箭头函数调用</button>

import React, { Component } from 'react'

export default class demof extends Component {
    constructor(){
        super()
        this.state={
            text:'我是text'
        }
    }
    // 现在我们写了一个普通匿名函数
    fun(){
        this.setState({
            text:"我改了"
        })
    }

    // 创建箭头函数
    funa=()=>{
        this.setState({
            text:"我改了创建箭头函数"
        })
    }
    // 通过bind绑定绑定this
    funb(){
        this.setState({
            text:"通过bind绑定绑定this"
        })
    }
    // 把函数的调用变成箭头函数调用
    func(){
        this.setState({
            text:"把函数的调用变成箭头函数调用"
        })
    }
    render() {
        return (
            <div>
                <h1>this指向</h1>
                <h2>{this.state.text}</h2>

                <button onClick={this.fun}>错误的写法</button>
                <button onClick={this.funa}>创建箭头函数来修改</button>
                <button onClick={this.funb.bind(this)}>通过bind绑定绑定this</button>
                <button onClick={()=>{this.func()}}>把函数的调用变成箭头函数调用</button>
            </div>
        )
    }
}

ref

ref 标识组件内部的元素 就给组件的dom元素起个名字

函数组件是不能直接使用ref的

ref写法的分类

1.字符串(官方已经不推荐使用了)

2.回调函数(官方推荐方式)

回调函数的方式 就是在dom节点上挂载一个函数 函数的入参 就是dom节点

import React, { Component } from 'react'

export default class demoj extends Component {
    fun=()=>{
        console.log(this.demoinput.value)
    }
    render() {
        return (
            <div>
                <h1>ref的例子</h1>
                {/* 回调函数的方式创建ref */}
                {/* <input type="text" ref={(text这个变量代表的就是当前这个input标签)=>{this.demoinput(随便创建一个变量)=text}}/> */}
                <input type="text" ref={(text)=>{this.demoinput=text}}/>
                <button onClick={this.fun}>点我得到输入框的值</button>
            </div>
        )
    }
}

3.React.createRef()(16.8版本新增的 官方推荐)

react 16.8新增的一种方式 我们通过初始化createRef从而得到ref对象 插入到页面中

import React, { Component } from 'react'

export default class demoh extends Component {
    constructor(){
        super()
        // 1.创建createRef
        this.inputref=React.createRef()
    }
    fun=()=>{
        // 3.使用
        console.log(this.inputref.current.value)
    }
    render() {
        return (
            <div>
                <h1>createRef</h1>
                {/* 2.绑定 */}
                <input type="text" ref={this.inputref}/>
                <button onClick={this.fun}>点我得到输入框的值</button>
            </div>
        )
    }
}

生命周期

react从创建到销毁的过程那个生命周期。

1.挂载阶段

constructor() react数据初始化

componentWillMount() 用的一般比较少 dom渲染之前调用

render() 执行dom渲染的一个钩子函数

componentDidMount() dom渲染之后调用

2.更新阶段

componentWillReceiveProps() 在组件接收一个新的props的时候被调用 初始化不触发

shouldcomponentUpdate() 判断组件是否要更新 主要是在性能优化的时候使用的 如果在代码中写这个钩 子了 那么必须在其中有一个return 的布尔值的返回值 否则会出问题

componentWillUpdate ()准备更新

render()正在更新

componentDidUpdate()更新完毕

3.销毁阶段

componentWillUnmount()

ajax是在那个钩子中发送的请求

是在componentDidMount()中发送请求 因为这个时候dom已经加载完毕了 所以我们在这个时候可以保证数据可以成功的渲染到页面上 并且如果要修改数据使用setState的时候 也不用怕dom没有加载完毕从而报错

在react16之后 componentWillMount()可能会被执行多次 所以不建议在这个钩子中发送请求

条件渲染

在开发中 创建不同的组件来封装我们需要完成的各个内容 但是我们需要根据程序的状态变化来渲染其中一部分内容

if语句来看进行条件渲染

在react中if条件渲染是最简单的 但是但是但是但是 注意 在jsx不允许出现if

import React, { Component } from 'react'

export default class demo extends Component {
    render() {
        let newhtml=""
        let num=1

        if(num==1){
            newhtml= <h1>吃饭</h1>
        }else if(num==2){
            newhtml= <h1>睡觉</h1>
        }else{
            newhtml=<h1>上厕所</h1>
        }

        return (
            <div>
                    {newhtml}
                
            </div>
        )
    }
}

三元运算符

使用图片

1.把图片放到public文件夹中 直接使用图片名

2.不在public下 我们可以使用require()来进行引用

<img src={require("../assets/2.webp")} />

3.不在public下 我们也可以使用导入式

import React, { Component } from 'react'

// 1.引入图片
import imgA from "../assets/2.webp"
export default class demob extends Component {
    render() {
        return (
            <div>
                {/* <img src="img/1.webp" /> */}



                {/* <img src={require("../assets/2.webp")} /> */}

                {/* 2.使用 */}
                <img src={imgA} />
            </div>
        )
    }
}

组件传值

正向传值

详见上面的props内容

逆向传值

子组件
import React, { Component } from 'react'

export default class zi extends Component {
    render() {
        return (
            <div>
                zizizizziziz
                {/* 1.子组件通过一个事件触发逆向传值 */}
                {/* 2.在事件中接收一个父组件传递过来的函数 */}
                {/* 4.我们通过函数传递实参的方式把数据传递到父组件 */}
                <button onClick={this.props.fufun.bind(this,"我是数据")}>点我进行逆向传值</button>
            </div>
        )
    }
}



父组件
import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    // 5.设置形参接收数据
    demo=(text)=>{
        console.log("父组件",text)
    }
    render() {
        return (
            <div>
                fuffufufufuf
                {/* 3.父组件给子组件传递一个函数 */}
                <Zi fufun={this.demo}/>
            </div>
        )
    }
}


同胞传值/兄弟组件传值

同胞传值 要使用一个插件 叫pubsub-js来完成同胞传值

下载: npm install --save pubsub-js

1.我们需要在传递数据的组件中抛出数据(自定义事件)抛出 publish("事件名",数据)

import React, { Component } from 'react'
// 引入pubsub
import PubSub from "pubsub-js"
export default class zia extends Component {
    fun=()=>{
        // 抛出数据
        PubSub.publish("apao","我是zia组件的数据")
    }
    render() {
        return (
            <div>
                ziaaaaaaaaaa
                <button onClick={this.fun}>点我传递数据到我兄弟b哪里去</button>
            </div>
        )
    }
}

在想接收数据的组件中使用subscribe()监听事件 来进行数据的接收

import React, { Component } from 'react'
// 引用pubsub
import PubSub from "pubsub-js"
export default class zib extends Component {
    // 接收数据 我们是需要自动接收的
    componentDidMount() {
        // PubSub.subscribe("你监听的事件",(你监听的事件,数据)=>{})
        PubSub.subscribe("apao",(a,b)=>{
            console.log(a)
            console.log(b)
        })
    }
    
    render() {
        return (
            <div>
                zibbbbbbb
            </div>
        )
    }
}

跨组件传值

上下文对象context

react的组件中的数据流转 是单向传递的 也就是说数据 是通过props一层一层的进行传递 传递到子组件 孙组件中的 这样子传递很麻烦 如果我们想跃层传值 那么就要使用context上下文对象来进行

context:上下文对象 就可以非常简单的解决跨组件传值的复杂度 高效的完成跨组件的数据通信

1.如果要使用上下文对象 我们必须使用createContext这个方法 来创建出上下文context对象

2.我们就可以使用上下文对象给我们的两个方法

Provider 用来生产数据------》生产者

Consumer 使用数据------------》消费者

1.创建上下文对象文件夹与文件并且写入如下内容

import React, { Component } from 'react'

class MyContext extends Component {
    render() {
        return (
            <div>
                
            </div>
        )
    }
}
export {MyContext}

2.设置上下文对象变成所有组件的父组件

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/demoe/ye.jsx';
import reportWebVitals from './reportWebVitals';

// 引用上下文对象 
import {MyContext} from "./context/index.js"

ReactDOM.render(
  // 让上下文对象的组件变成所有组件的老大
  <MyContext>
    <App />
  </MyContext>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

3.在上下文对象中设置子节点的接收

import React, { Component } from 'react'

class MyContext extends Component {
    render() {
        return (
            <div>
                {/* 使用this.props.children接收所有的子节点 */}
                {this.props.children}
            </div>
        )
    }
}
export {MyContext}

4.开始创建上下文对象

import React, { Component,createContext } from 'react'
// 创建上下文对象
let context=createContext()
// 取出生产者与消费者
// let Provider=context.Provider
// let Consumer=context.Consumer

   let {Provider,Consumer}=context;


class MyContext extends Component {
    render() {
        return (
            <div>
                 {/* 使用provider生产数据 使用value属性生产数据*/}
                <Provider value={{name:"xixi"}}>
               
                    {/* 使用this.props.children接收所有的子节点 */}
                    {this.props.children}

                </Provider>
            </div>
        )
    }
}
// 暴露消费者   其他想使用的组件才能调用
export {MyContext,Consumer}

5.在想使用数据的组件中直接使用

import React, { Component } from 'react'
// 引用消费者
import {Consumer} from "../../context/index.js"
export default class zi extends Component {
    render() {
        return (
            <div>
                zziziziziziziz
                {/* 使用消费者来获取数据 */}
                <Consumer>

                    {
                        // (形参就是数据)=>{}
                        (value)=>{
                            return (
                                <>
                                <h1>{value.name}</h1>
                                <h1>{value.age}</h1>
                                </>
                                
                            )
                        }
                    }

                </Consumer>
            </div>
        )
    }
}

redux

在传统的跨层级传值中 如果我们需要进行数据的传递 那么我们就需要把数据先通过逆向传值一层层往上传递 在通过正向传值一层层的向下传递

redux就是javascript点的一个状态管理工具 集中的进行react中多个组件的状态管理 方便我们在跨层级使用数据的便捷性

redux是一个专门进行状态管理的js库 但是他是一个第三方的插件 所以 他不是react独享的 他可以在任意地方使用 vue中也可以 只是vue中有vuex 没有人使用redux罢了

redux的三大原则

1.单一数据源 整个redux引用是一个一个store对象

2.state是只读的 不能直接修改 必须通过redux当中的action来进行修改

3.使用纯函数来执行修改 我们如果要修改数据 那么我们就要编写一个个的函数来承载着每一个修改操作

redux 执行流程

1.用户在页面操作需要修改数据状态 发送dispatch 触发修改动作

2.派发动作触发

3.开始进行修改 携带者原始的state状态 在action中进行数据的处理 通过reducer来生成新的结果

4.把修改的状态state返回出来

5.在组件中进行新状态的展示

redux实践

1.下载 npm install --save redux

2.创建对应的文件夹(store)与文件(xxx.js)

基本的redux

数据的创建

// 1.引用redux  并且解构出创建redux的方法 createStore
import {createStore} from "redux"
// 5.创建数据
let data={
    name:"xixi",
    age:18
}
// 4.创建reducer是一个方法  方法中包含了数据 与修改数据的动作
// state数据 action就是今后数据的修改动作
// 6.使用函数形参默认值的方式 把上面的data赋值给state即可
let reducer=(state=data,action)=>{
    return state
}
// 2.创建redux对象
// 7把reducer注入 stote中
let store=createStore(reducer)

// 3.暴露redux对象
export default store

数据的读取

import React, { Component } from 'react'
// 1.引用store对象
import store from "../store/index.js"
export default class demo extends Component {
    constructor(){
        super()
        // 2.把数据redux的数据挂载到state之上   因为方便后期的修改
        // 读取redux的数据使用getState()来进行redux的数据获取
        this.state={
            name:store.getState().name
        }
    }
    render() {
        return (
            <div>
                {/* 3.使用 */}
                <h1>基本的redux数据读取---{this.state.name}</h1>
            </div>
        )
    }
}

数据的修改

1.在组件中通过事件调用一个函数触发修改redux数据的动作

import React, { Component } from 'react'
import store from "../store/index.js"
export default class demob extends Component {
    constructor(){
        super()
        this.state={
            xiaoming:store.getState().age
        }
    }
    fun=()=>{
        // 2.触发redux中修改动作dispatch()调用修改的动作
        // store.dispatch({type:你要触发的修改动作名})
        // 在工作的时候这个动作名大家为了方便和变量进行区分 
        // 所以 工作中一般都是大写 
        store.dispatch({type:"UPDATE_NUM"})
    }
    render() {
        return (
            <div>
                <h1>redux的数据修改--{this.state.xiaoming}</h1>
                {/* 1.事件调用一个函数触发redux的数据修改 */}
                <button onClick={this.fun}>点我修改redux中的age数据</button>
            </div>
        )
    }
}

2.redux中创建对应的修改动作

import {createStore} from "redux"
let data={
    name:"xixiahah",
    age:18
}
let reducer=(state=data,action)=>{
    // action就是数据的修改动作集合  判断action.type是那个名字
    // 从而就知道你要调用那个修改的动作
    switch (action.type) {
        case "UPDATE_NUM":
            console.log({...state,age:666})
            return {...state,age:666}
            break;
    
        default:
            return state
            break;
    }

   
}
let store=createStore(reducer)

export default store

大家发现上面的两步操作 完成之后数据变了 但是组件中的内容并没有改变

原因 虽然redux中的数据改变了 但是组件中的render方法并没有执行 那么页面就不会重新渲染 数据当然不会在页面改变了

3.使用subscribe() 一个监听器 会监听store中的数据 当store中的数据改变的时候 subscribe就会触发

import React, { Component } from 'react'
import store from "../store/index.js"
export default class demob extends Component {
    constructor(){
        super()
        this.state={
            xiaoming:store.getState().age
        }
    }

    // 3.设置监听监听store中的数据改变触发页面中的render渲染
    componentDidMount() {
        store.subscribe(()=>{
            // 会在store数据改变的时候自动触发
            this.setState({
                xiaoming:store.getState().age
            })
        })
    }
    


    fun=()=>{
        // 2.触发redux中修改动作dispatch()调用修改的动作
        // store.dispatch({type:你要触发的修改动作名})
        // 在工作的时候这个动作名大家为了方便和变量进行区分 
        // 所以 工作中一般都是大写 
        store.dispatch({type:"UPDATE_NUM"})
    }


    render() {
        return (
            <div>
                <h1>redux的数据修改--{this.state.xiaoming}</h1>
                {/* 1.事件调用一个函数触发redux的数据修改 */}
                <button onClick={this.fun}>点我修改redux中的age数据</button>
               
            </div>
        )
    }
}

数据修改参数传递

1.在dispatch触发的时候传递第二个参数

 store.dispatch({type:"UPDATE_ADD_NUM",num:10})

2.在redux的action就可以使用 action.xxx来进行接收

  case "UPDATE_ADD_NUM":
            console.log("aaaaaa")
            return {...state,age:state.age+action.num}
            break;

redux拆分的写法

之前的写法都是把内容冗余在一起 导致项目的可维护性就非常低

工作之后都会吧这些内容拆分成一个个的独立文件 方便后期维护与管理

使用actionCreator统一的创建action

今后会有很多个修改的动作在组件里面通过dispatch进行创建 那么项目复杂的时候比较难以维护

可以使用一个独立的文件来管理我们的派发动作

1.在store文件夹下新建一个文件 actionCreator.js

// 封装派发动作

export let UPDATE_NUM=()=>{
    return {type:"UPDATE_NUM"}
}

2.在使用的组件中引用使用

import React, { Component } from 'react'
import store from "../store/oldindex.js"
// 1.引用封装的派发动作
import {UPDATE_NUM} from "../store/actionCreator.js"

export default class demob extends Component {
    constructor(){
        super()
        this.state={
            xiaoming:store.getState().age
        }
    }

    componentDidMount() {
        store.subscribe(()=>{
            this.setState({
                xiaoming:store.getState().age
            })
        })
    }
    
    fun=()=>{
        // 2.使用封装
        store.dispatch(UPDATE_NUM())
    }

    add=()=>{
        store.dispatch({type:"UPDATE_ADD_NUM",num:10})
    }
    
    render() {
        return (
            <div>
                <h1>redux的数据修改--{this.state.xiaoming}</h1>
                {/* 1.事件调用一个函数触发redux的数据修改 */}
                <button onClick={this.fun}>点我修改redux中的age数据</button>
                <button onClick={this.add}>点我让num+1</button>
            </div>
        )
    }
}

拆分动作名

封装的本质就是把 重复使用的东西剥离出来单独创建方便复用

因为在派发动作和store的action中这个动作名出现了多次 为了方便后期好维护所以我们 拆分动作名

1.在store文件夹下创建一个actionType.js的文件用来容纳动作名

// 用来容纳动作名
export const UPDATE= "UPDATE_NUM_A"
export const ADD= "UPDATE_ADD_NUM"

2.在页面调用的动作哪里时候替换我们封装的动作

// 封装派发动作
// 1.引用封装的名字
import {UPDATE,ADD} from "./actionType.js"

export let UPDATE_NUM=()=>{
    // 2.替换原有的字符串
    return {type:UPDATE}
}

export let UPDATE_ADD_NUM=(num)=>{
    return {type:ADD,num}
}

3.在action的switch中还需要替换

import {createStore} from "redux"
// 1.引用封装的动作名
import {UPDATE,ADD} from "./actionType.js"
let data={
    name:"xixiahah",
    age:18
}
let reducer=(state=data,action)=>{
    switch (action.type) {
        // 2.替换动作名的字符串
        case UPDATE:
            console.log({...state,age:666})
            return {...state,age:666}
            break;
        case ADD:
            console.log("aaaaaa")
            return {...state,age:state.age+action.num}
            break;
    
        default:
            return state
            break;
    }

   
}
let store=createStore(reducer)

export default store

reducer-拆分reducer和和并reducer

到此大家发现redux 的index.js中还是包含着项目的所有数据和所有的修改动作

组件多起来之后 还是会发现 数据全部在一起 后期难以维护

我们就要把数据和修改动作拆分成一个个独立的模块

1.新建一个文件夹 reducers 其中创建一个xxx.js用来存放我们的模块

2.把原来写在一起的数据 与数据的修改动作 放置其中

// 创建数据
import {UPDATE_NAME} from "../actionType.js"
let data={
    name:"xixi",
    age:18
}

// 创建数据与数据的修改动作
let demoreducer=(state=data,action)=>{
    // 判断action的type名字
    switch (action.type) {
        case UPDATE_NAME:
            return {...state,age:666}
            break;
    
        default:
            break;
    }
    return state
}


export default demoreducer

3.因为我们把每个组件的数据与修改动作单独管理了 所以我们会有很多个独立小模块的reducer 那么我们就需要把这些独立的小模块的reducer合并成一个

combineReducers()来对多个reducer模块进行合并操作

// 把上面多个reducer模块进行合并的文件
// 1.引用你所要合并的reducer模块
import demoreducer from "./reducers/demom.js"
// 2.创建出合并的方法
import {combineReducers} from "redux"
// 3.开始合并
let reducer=combineReducers({
    demoreducer
})

// 暴露
export default reducer

4.在创建store对象的文件 引用合并reducer文件 把合并之后的reducer传递到 store的创建当中

import {createStore} from "redux"

// 引用合并reducer的文件
import reducer from "./reducer.js"


let store=createStore(reducer)

export default store

5.读取

   constructor(){
        super()
        this.state={
            // 当我们查分模块之后  读取数据应该是 
            // store.getState().模块名.数据
            text:store.getState().demoreducer.age
        }
    }
    componentDidMount() {
        // 监听store的变化  当store改变的时候我们触发一个render方法重新渲染
        store.subscribe(()=>{
            this.setState({
                text:store.getState().demoreducer.age
            })
        })
    }

react-redux

之前上面的redux大家会发现一个问题 就是redux与react之间的耦合度非常高 代码不简洁(组件中有大量的第三方redux的东西 store)

为了解决这个问题 我们可以使用react-redux 这是一个专门为react开发的状态管理工具 使用他就可以非常方便的解决react与redux'之间的耦合度过高的问题

既然我们要学习react-redux 问题 我们之前学的redux'是不是就不用了 是不是就白学了

1.下载 npm install --save react-redux

2.Provider 提供store数据的 通过provider来传递store到所有的子组件身上 去index.js使用provider来进行数据的传递

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/rddemo.jsx';
import reportWebVitals from './reportWebVitals';

// 1.引用provider
import {Provider} from "react-redux"
// 3.引用store对象
import store from "./store/index.js"

ReactDOM.render(
  // 2.使用provider传递store对象
  // 4.传递
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

3在组件中连接react-redux 并且使用数据 connect(他是连接react与react-redux的一个方法 通过这个方法就可以把组件与react-redux连接到一起方便使用数据)

import React, { Component } from 'react'
// 1.引用连接
import {connect} from "react-redux"
class rddemo extends Component {
    render() {
        return (
            <div>
                <h1>react-redux的使用</h1>
                {/* 3.使用数据             */}
                {/* <h1>{this.props.state.模块名.数据}</h1> */}
                <h1>{this.props.state.rddemoreducer.name}</h1>
            </div>
        )
    }
}
// 2.修改暴露到最下面  使用connect来进行连接操作
// state就是react-redux会把store中的数据自动注入到形参中
export default connect(state=>({state}))(rddemo)







数据修改

原来在redux'中数据修改 不仅仅是单纯的修改 我们还需要 使用subscribe来进行监听非常麻烦

但是在react-redux中我们可以直接修改

import React, { Component } from 'react'
// 1.引用连接
import {connect} from "react-redux"
class rddemo extends Component {
    fun=()=>{
        // 4数据的修改
        this.props.dispatch({type:"upname"})
    }
    render() {
        return (
            <div>
                <h1>react-redux的使用</h1>
                {/* 3.使用数据             */}
                {/* <h1>{this.props.state.模块名.数据}</h1> */}
                <h1>{this.props.state.rddemoreducer.name}</h1>
                <button onClick={this.fun}>点我修改</button>
            </div>
        )
    }
}
// 2.修改暴露到最下面  使用connect来进行连接操作
// state就是react-redux会把store中的数据自动注入到形参中
export default connect(state=>({state}))(rddemo)







数据请求

分类

1.jquery 或者是原生的ajax请求

(1)下载 npm install --save jquery

import React, { Component } from 'react'
// 1.引用jquery
import $ from "jquery"
export default class jquerydemo extends Component {
    componentDidMount() {
        $.ajax({
            url:"/api/ceshi",
            type:"GET",
            success:(ok)=>{
                console.log(ok)
            }
        })
    }
    
    render() {
        return (
            <div>
                <h1>使用jquery请求</h1>
            </div>
        )
    }
}

2.axios

3.fetch请求

axios的使用

axios是一个第三方的数据请求库 所以在react中使用的方式和vue中使用的方式是一模一样的

详见vue的笔记

跨域

通过devServer完成代理跨域

1.找到配置文件来进行跨域

node_modules/react-scripts/config/webapckDevServer.config.js 文件下进行配置

2.找到proxy节点替换如下

  // `proxy` is run between `before` and `after` `webpack-dev-server` hooks
    proxy:{ //设置devServe解决跨域问题
      "/api":{
          // 我们需要告诉devserver帮我们解决那个地址的跨域
          target:"http://localhost:3000/",
          changeOrigin:true,
          ws:true,
          // 替换地址
          pathRewrite:{
              "^/api":""
          }
      }
  },

3.修改组件中的请求地址为/api

4.重启项目

修改跨域的文件层级太深了 有点麻烦 有没有办法解决?

使用弹射即可解决

弹射

免责声明

弹射是不可逆的 就是去公司了 不要随便弹射 如果你要弹射 先和项目经理沟通下 在进行

弹射就是把隐藏层级很深的配置文件 通过弹射放到项目的根路径下(好处就是哪些配置文件查找起来方便了 但是缺点 就是这些文件会导致项目的层级看起来很乱)

使用npm run eject 来进行弹射

会出现如下错误

his git repository has untracked files or uncommitted changes:

package.json
M src/index.js
src/api/
src/components/
src/store/
src/util/

Remove untracked files, stash or commit any changes, and try again.npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! myapp@0.1.0 eject: `react-scripts eject`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the myapp@0.1.0 eject script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\xixi\AppData\Roaming\npm-cache\_logs\2022-01-04T03_31_21_115Z-debug.log

出现错误的原因是因为 他需要让我们在git上面进行一次提交

所以我们使用git 先git add . 在 git commit -m 提交一次 在重新npm run eject

fetch

fetch是原生js新推出的一个请求方式 他和jqueryajax 原生ajax axios 最大的区别是 他不在使用xhr 对象

他是一个新的请求方式

但是 由于是比较新的请求方式所以对老旧浏览器的兼容性就不好

import React, { Component } from 'react'

export default class fetdemo extends Component {
    componentDidMount() {
        fetch("/api/ceshi",{method:"GET"})
        .then(req=> req.json())
        .then((ok)=>{console.log(ok)})
    }
    
    render() {
        return (
            <div>
                <h1>使用fetch来进行数据请求</h1>
            </div>
        )
    }
}

fetch vs ajax vs axios区别

原生的ajax 就是对于XMLHttpRequest对象进行数据请求ongoing的一个技术 他是属于原生js的一个技术 对个请求之间如果有先后的顺序 那么就会出现回调地狱

jqueryajax 就是对原生ajax进行一个一个封装 减低了兼容性与语法难度

axios 他是基于promise 本质上就是对XHR对象进行了封装 减低了兼容性与语法难度 但是他是使用promise来进行封装的实现 让他更加符合当下的语法

fetch 他不是依赖于XHR 他是原生js给我们提供的一个最新的数据请求的标准 但是兼容性一般

json-server模拟数据

仅限于2022年1月5日上课的这个时间段内出现的下载失败大家可以切换以下 当前这个修改npm 下载路径从淘宝镜像源切换到npm默认路径 npm config set registry http://registry.npmjs.org/

切换回淘宝镜像 npm config set registry http://registry.npm.taobao.org/

json-server 可以在react中进行模拟数据的实现 同时vue 小程序 都可以完成模拟数据的实现 并且语法都是一样的

1.下载 npm install -g json-server

2.查看版本 json-server --version

3.需要在项目下创建一个mock的文件夹用来容纳模拟数据 在其中创建模拟数据的xxx.json 写入如下内容

{
    "one":[
        111,2222,3333,444,55
    ]
}

4.启动模拟数据

cd到 mock文件夹下

输入 json-server --watch xxx.json --port 端口号

大家会发现基本的模拟数据已经成功了 但是 我们就一个接口 如果进行有很多个接口怎么办?

我们如果需要多个 不能新建很多个json 并且启动很多个服务

就是在你新建的这个json文件里面做手脚

{
    "one":[
        111,2222,3333,444,55,66,77,88,99,321,123
    ],
    "two":{
        "name":"xixi",
        "age":18
    },
    "three":{
        "title":"我是第三个"
    }
}

##

styled--components

什么是 CSS-in-JS?

“CSS-in-JS” 是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义。

style-components 是一个js库 它的主要作用就是使用js写css 因为这样一来就可以解决一些原有css不具备的能力 不能创建变量 函数 循环等等

传统的css痛点

1.全局污染

2.命名混乱

3.样式重用困难

4.代码冗余

使用

1下载 npm install --save styled-components

2设置样式

// css-in-js的方式完成样式的设置
// 1.引用styled
import styled from "styled-components"
// 这里的变量名首字母必须大写
let Democstyled=styled.div`
//想怎么写样式就怎么写
            h1{
                color: red;
            }
`
export default Democstyled

3.使用

import React, { Component } from 'react'
// 1.引用样式
import Democstyled from "./xiaoming/democstyled.js"
export default class democ extends Component {
    render() {
        return (
            <div>
                {/* 2.使用 */}
                <Democstyled>
                    <h1>我是测试styled-componets的组件</h1>
                   
                    <div className='demoh'>
                        <h1>我是占位的</h1>
                    </div>
                </Democstyled>
            </div>
        )
    }
}

扩展 性能优化

下面就是性能非常浪费的代码大家不同联系直接放到你们项目中即可

父组件

import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    constructor(){
        super()
        this.state={
            arr:[
                {title:"111111",ck:false},
                {title:"111112",ck:false},
                {title:"111113",ck:false},
                {title:"111114",ck:false},
                {title:"111115",ck:false},
                {title:"111116",ck:false},
                {title:"111117",ck:false}
   //             大家自行赋值上面的数据几千航即可
                
              
            ]
        }
    }
    fu=(num)=>{
        console.log(num)
        let newarr=this.state.arr

        newarr[num].ck=!newarr[num].ck

        this.setState({
            arr:newarr
        })
    }
    render() {
        return (
            <div>
                {
                    this.state.arr.map((v,i)=>{
                        return (
                            <Zi text={v.title} bool={v.ck} index={i} fun={this.fu}/>
                        )
                    })
                }
                
            </div>
        )
    }
}


子组件
import React, { Component } from 'react'

export default class zi extends Component {
   
    render() {
        console.log("我是子组件")
        let {text,bool,index,fun}=this.props
        return (
            <div>
                <input type="checkbox" onChange={fun.bind(this,index)}/>
                <span style={{backgroundColor:bool?'red':''}}>{text}</span>
            </div>
        )
    }
}

解决性能浪费--性能优化

1.生命周期 --- shouldComponentUpdate() 判断组件是否要更新 减少了不必要的组件渲染 提高了性能

import React, { Component } from 'react'

export default class zi extends Component {

    // 性能优化
    shouldComponentUpdate(nextProps,nextState){
        // 当新传递进来的props更新了  当前组件就重新更新
        // 但是剩下的没有更新  那么当前组件就不渲染更新

        return nextProps.bool!=this.props.bool
        
       
    }
   
    render() {
        console.log("我是子组件")
        let {text,bool,index,fun}=this.props
        return (
            <div>
                <input type="checkbox" onChange={fun.bind(this,index)}/>
                <span style={{backgroundColor:bool?'red':''}}>{text}</span>
            </div>
        )
    }
}

2.纯组件---PureComponent方式优化性能

纯组件是react中性能优化最终要的方法 当组件发生改变更新的时候 组件的props和state如果没有改变 那么当前render就不更新 他只会比较props和state的内存地址 如果地址相同 标识没有改变 那么他就自动让shouldcomponentupdate return false

// 纯组件优化性能
import React, { Component,PureComponent } from 'react'

export default class zi extends PureComponent {

    // 性能优化
    // shouldComponentUpdate(nextProps,nextState){
    //     // 当新传递进来的props更新了  当前组件就重新更新
    //     // 但是剩下的没有更新  那么当前组件就不渲染更新

    //     return nextProps.bool!=this.props.bool
        
       
    // }
   
    render() {
        console.log("我是子组件")
        let {text,bool,index,fun}=this.props
        return (
            <div>
                <input type="checkbox" onChange={fun.bind(this,index)}/>
                <span style={{backgroundColor:bool?'red':''}}>{text}</span>
            </div>
        )
    }
}

3.函数组件中可以使用 React.memo() 来进行性能优化 但是当前这个方法是在函数组件中使用的

扩展 --- cra脚手架如何修改端口

找到 node_modules/react-scripts/scripts/start.js

修改如下内容

const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 你的端口;
const HOST = process.env.HOST || '0.0.0.0';

HOC 高阶组件

在react组件开发项目的过程中 常常有这样一个场景 一些功能需要被复用 这个时候在react中我们就可以使用高阶组件来完成这样的一个需求

HOC高阶组件 ----- 参数是一个组件 返回值还是一个组件

1.创建高阶组件

// 高阶组件
// HOC 高阶组件---- 参数是一个组件  返回值还是一个组件


import React, { Component } from 'react'

let List=(Com)=>{
    return class index extends Component {
        render() {
            return (
                <div>
                    <Com/>
                      <h1>by 2112班</h1>
                </div>
            )
        }
    }

}

export default List



2.使用高阶组件

import React, { Component } from 'react'
// 1.引用
import List from "../hoc/index.js"
class demo extends Component {
    render() {
        return (
            <div>
                <h1>我是测试高阶组件的例子</h1>
              
            </div>
        )
    }
}
// 2.暴露高阶组件
export default List(demo)

路由

根据url地址栏的不同来切换不同的组件页面 实现spa单页面应用 整个项目只有一个完整的页面 同时在切换页面的时候不会刷新地址栏 没有切换白屏的问题 用户体验更好

路由的分类

1.react-router 仅仅给我们提供了一些基本的核心路由功能

2.react-router-dom 除了基本的核心路由功能之外 还加入了很多便捷性的api 方便我们在实现路由功能的时候有更多的辅助性的api

路由模式

HashRouter url中带# 并且上线后刷新不会丢失 兼容性好

BrowerRouter url中不带# 上线后刷新会丢失 兼容性一般

路由的基本使用

1.下载路由模块

npm install --save react-router-dom@5.2.0

2.设置路由模式

在index.js下设置路由模式

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/demo.jsx';
import reportWebVitals from './reportWebVitals';
// 1.引用路由模式
import {BrowserRouter} from "react-router-dom"
ReactDOM.render(
  // 2.包裹根组件
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

3.创建组件页面

在src下新建一个pages的文件夹 用来新建组件页面

4.配置路由规则与出口的设置

1.在src下新建一个router的文件夹

2.创建路由配置文件

3.路由规则与出口

import React, { Component } from 'react'
// 1.引用你的路由页面
import Home from "../pages/home.jsx"
import Phone from "../pages/phone.jsx"
import User from "../pages/user.jsx"
import Shop from "../pages/shop.jsx"
// 2.创建出路由配置  使用Route来进行规则与出口的设置

import {Route} from "react-router-dom"

export default class index extends Component {
    render() {
        return (
            <div>
                {/* 3.使用route配置 */}
                {/* <Route path="/路径" component={你要使用的组件页面}/> */}
                <Route path="/home" component={Home}/>
                <Route path="/phone" component={Phone}/>
                <Route path="/user" component={User}/>
                <Route path="/shop" component={Shop}/>
            </div>
        )
    }
}

4.设置成根组件

在index.js中吧我们刚才创建好的路由配置文件变成根组件即可

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// 把路由的配置文件设置成根组件
// 把路由的配置文件设置成根组件
// 把路由的配置文件设置成根组件
// 把路由的配置文件设置成根组件
// 把路由的配置文件设置成根组件
import App from './router/index.js';
import reportWebVitals from './reportWebVitals';
// 1.引用路由模式
import {BrowserRouter} from "react-router-dom"
ReactDOM.render(
  // 2.包裹根组件
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

路由导航

声明式

Link

使用to属性来设置你的路径的

NavLink

使用to属性来设置你的路径的 会自动在选中的导航中添加一个样式的类名 active

但是这个active的类名 今后可能会在很多地方都会出现这个名字 出现的频率太高了 可能我们在设置样式的时候 会污染其他的内容

可以进行这个动态选中类名的修改 使用activeClassName来进行修改

    			 <NavLink to="/home" activeClassName='xiaoming'>点我home</NavLink>
                <NavLink to="/phone" activeClassName='xiaoming'>点我phone</NavLink>
                <NavLink to="/user" activeClassName='xiaoming'>点我user</NavLink>
                <NavLink to="/shop" activeClassName='xiaoming'>点我shop</NavLink>

注意注意

navlink的声明式 导航 在各位同学课下联系的时候可能会出现 这个类名不出来的情况

如果出现类名出不来的情况 那么 在启动项目的时候 大家不要在vscode内置终端中启动 而是在外部的cmd中来进行项目的启动即可解决

编程式

this.props.history.push("/去哪里")

import React, { Component } from 'react'

export default class home extends Component {
    fun=()=>{
        // 编程式导航
        this.props.history.push("/phone")
    }
    render() {
        return (
            <div>
                home
                <button onClick={this.fun}>点我去phone</button>
            </div>
        )
    }
}

编程式导航常见问题

我们会发现 代码是没有错的 但是当我调用 编程式导航 会在浏览器的控制台出现 如下错误

Uncaught TypeError: Cannot read property 'push' of undefined

j出现上述错误的原因是因为 我们在使用编程式导航的时候 因为这个代码出现在了 没有被路由所管理的页面中 那么这个编程式导航就不能正常使用

如果我就是想在被路由所管理的页面中使用编程式导航怎么办?

那么我们就需要使用withRouter 这个高阶组件来进行完成

1.在不是被路由所管理的页面中引用withRouter这个高阶组件

import React, { Component } from 'react'
// 1.引用withRouter
import {withRouter} from "react-router-dom"
 class demob extends Component {
    fun=()=>{
        this.props.history.push("/home")
    }
    render() {
        return (
            <div>
                <button onClick={this.fun}>点我去home</button>
            </div>
        )
    }
}
// 2.使用高阶组件
export default withRouter(demob)

withRouter是什么?

withRouter是一个高阶组件 它的作用就是因为不是被路由所管理的页面是没有路由个跳转的属性 通过withrouter就可以给当前页面添加路由跳转属性 从而可以使用路由跳转的各种方法

更多的编程式导航

push()

this.props.history.replace()// 替换

this.props.history.goBack()//后退

this.props.history.goForward()//前进

路由更多设置

路由-404页面

就是当用户在访问我们项目的时候 由于在url中输入了我们并没有的页面的时候出现白屏 为了解决这个问题 当用户在url中输入没有的页面时候 我们可以用一个404的错误提示页面告知用户当前路径有问题

1.创建404页面组件

2.配置404页面

 			    <Route path="/home" component={Home}/>
                <Route path="/phone" component={Phone}/>
                <Route path="/user" component={User}/>
                <Route path="/shop" component={Shop}/>

                {/* 路由配置的最下面来进行404页面的配置 */}
                <Route component={No}/>

当我们配置完404之后会发现 这个404页面在所有的页面中都会显示

解决404页面重复出现可以使用下面的技术

Switch 唯一渲染

为了解决react的路由在匹配到内容之后还会向下继续匹配 渲染多个内容的问题 所以我们使用swtich来进行唯一渲染 让react的路由在匹配到指定内容之后不继续向下渲染的问题

1.在配置页面引用Switch

2.使用Switch包裹所有的路由配置从而实现唯一渲染

   {/* 唯一渲染 */}
               <Switch>
                    <Route path="/home" component={Home}/>
                    <Route path="/phone" component={Phone}/>
                    <Route path="/user" component={User}/>
                    <Route path="/shop" component={Shop}/>

                    {/* 路由配置的最下面来进行404页面的配置 */}
                    <Route component={No}/>
                </Switch>

重定向与精准匹配

redirect来实现重定向

<Redirect from="/" to="/去哪里"/>
 			 <Switch>
                    <Route path="/home" component={Home}/>
                    <Route path="/phone" component={Phone}/>
                    <Route path="/user" component={User}/>
                    <Route path="/shop" component={Shop}/>
                    {/* 设置重定向 */}
                    <Redirect from="/" to="/home"/>
                    {/* 路由配置的最下面来进行404页面的配置 */}
                    <Route component={No}/>
                </Switch>

但是我们设置好了重定向之后 会发现404页面的功能就没有了

原因 因为重定向的from里面填写的是/ 这个/会匹配所有路径所以导致404这个匹配项进步去

解决 精准匹配exact 来帮助我们只是/的时候被重定向拦截 其余都不进入重定向

<Redirect from="/" to="/home" exact/>
 				{/* 唯一渲染 */}
               <Switch>
                    <Route path="/home" component={Home}/>
                    <Route path="/phone" component={Phone}/>
                    <Route path="/user" component={User}/>
                    <Route path="/shop" component={Shop}/>
                    {/* 设置重定向 */}
                    <Redirect from="/" to="/home" exact/>
                    {/* 路由配置的最下面来进行404页面的配置 */}
                    <Route component={No}/>
                </Switch>

二级多级路由

react中二级路由没有更多的语法 就是把一级路由的写法在重新写一遍

1.创建二级路由的组件页面

2.在对应的一级路由页面中 直接使用Route进行二级路由的配置

import React, { Component } from 'react'
// 1.引用二级路由页面
import Era from "./era.jsx"
import Erc from "./erc.jsx"
import Erd from "./erd.jsx"
// 2.引用Route
import {Route,NavLink} from "react-router-dom"
export default class phone extends Component {
    render() {
        return (
            <div>
                phone
                {/* 4.设置二级路由的路由导航 */}
                <NavLink to="/phone/era">era</NavLink>&nbsp;&nbsp;
                <NavLink to="/phone/erc">erc</NavLink>&nbsp;&nbsp;
                <NavLink to="/phone/erd">erd</NavLink>&nbsp;&nbsp;

                {/* 3.使用Route来进行二级路由的配置 */}
                {/* <Route path="/一级路由/二级路由" component={你要引用的二级路由页面}/> */}
                <Route path="/phone/era" component={Era}/>
                <Route path="/phone/erc" component={Erc}/>
                <Route path="/phone/erd" component={Erd}/>
            </div>
        )
    }
}

路由传参

params方式

1.在路由规则上配置接收参数

  {/* params传参第一步配置接收参数 */}
 <Route path="/shop/:xiaoming" component={Shop}/>

2.发送数据

声明式

<Link to="/去哪里的路径/你要传递的参数">xxxx</Link>

编程式

this.props.history.push("/去哪里的路径/你要传递的参数")

import React, { Component } from 'react'
import Demob from "../components/demob.jsx"

import {Link} from "react-router-dom"
export default class user extends Component {
    render() {
        return (
            <div>
                user
                {/* 发送数据 */}
                <Link to="/shop/我是传递的数据">点我发送数据给shop页面接收</Link>
                <Demob/>
            </div>
        )
    }
}

3.接收数据

this.props.match.params.xxx 数据的接收

// 接收数据
    componentWillMount() {
        console.log(this.props.match.params.xiaoming)
    }

总结

优势: params方式进行参数传递的时候 刷新地址栏 数据依然存在

缺点: 只能传递字符串 并且参数传递过多的时候 url会非常的难看

state方式

1.发送数据

import React, { Component } from 'react'

export default class user extends Component {
    fun=()=>{
        // state方式发送数据
        // this.props.history.push({pathname:"/路径",state:{发送数据的key:发送数据的value}})
        this.props.history.push({pathname:"/shop",state:{text:"我是传递的数据state"}})
    }
    render() {
        return (
            <div>
                user
                <button onClick={this.fun}>点我传递数据到shop中</button>
            </div>
        )
    }
}

2.接受数据

this.props.location.state.xxx

import React, { Component } from 'react'

export default class shop extends Component {

    render() {
        return (
            <div>
                shop--{this.props.location.state.text}
            </div>
        )
    }
}

总结

有点 在url地址栏中不显示传递的数据 并且传递数据的时候可以传递对象

缺点 刷新地址栏数据会丢失

扩展 --- 路由的render写法

就是在路由跳转显示的时候 可以对显示的路由进行一些验证上的写法

import React, { Component } from 'react'
import Home from "../views/home.jsx"
import Phone from "../views/phone.jsx"
import User from "../views/user.jsx"
import Shop from "../views/shop.jsx"
import No from "../views/no.jsx"



import {Route,NavLink,Switch,Redirect} from "react-router-dom"
export default class index extends Component {
    render() {
        return (
            <div>
                <NavLink to="/home">home</NavLink>&nbsp;
                <NavLink to="/phone">phone</NavLink>&nbsp;
                <NavLink to="/user">user</NavLink>&nbsp;
                <NavLink to="/shop">shop</NavLink>&nbsp;
                
                <Switch>
                    <Route path="/home" component={Home}/>
                    {/* <Route path="/phone" component={Phone}/> */}
                    {/* render写法在路由跳转之前可以做一些验证 */}
                    <Route path="/phone" render={()=>{
                        if(false){
                            return <Phone/>
                        }else{
                            return <Home/>
                        }
                    }}/>


                    <Route path="/user" component={User}/>
                    <Route path="/shop" component={Shop}/>
                    <Redirect from="/" to="/home" exact/>
                    {/* 404 */}
                    <Route component={No}/>
                </Switch>
            </div>
        )
    }
}

状态提升

基本概念

多个组件要反应相同的数据变化的时候 我们就可以使用状态提升(一条数据在改变之后要影响多个组件的展示结果)

如果多个组件要反映出相同的数据变化 那么在这个时候 我们可以把这一个数据方法这些组件的父组件之上 用状态来标识 当状态修改了 通过正向传值把这些数据传递到需要反映变化的组件之上 那么这些组件数据变化就想相同了

父组件
import React,{useState} from "react"
import Zia from "./zia"
import Zib from "./zib"
let Fu:React.FC<{}>=()=>{
    let [text,setText]=useState("你好")

    let fun=()=>{
        setText("你坏")
    }
    return (
        <div>
            <h1>fufufufuff--{text}</h1><button onClick={fun}>点我修改</button>
            <Zia title={text}/>
            <Zib title={text}/>
        </div>
    )
}
export default Fu


子组件
import React from "react"

interface IProps{
    title:String
}
let Zia:React.FC<IProps>=(props)=>{
    return (
        <div>
            <h1>zizizaaaaaaaa---{props.title}</h1>
        </div>
    )
}
export default Zia

TypeScript初体验

ts是由微软开发的一个*开源的编程语言。 他是js的一个超集 本质上ts扩展了js的语法 解决了js上的很多痛点 弱类型 很难模块化 没有类的概念

1.ts是js的超集

2.他对js进行了扩展 加入了很多新的特性

3.ts代码需要通过一个编译器来进行编译 编译成为js 才能运行

4.ts完全兼容js 任何代码都可以在ts中直接写

5.ts的语法更加的严格 ts是在代码编译的时候就能发现问题 减少了出错的几率

开发环境

1.电脑上必须有node

2.全局安装typescript

npm install --save typescript

3.查看版本 tsc -v

4.要写typescript 那么我们在创建文件的时候 要创建一个ts后缀的文件

5.使用ts编译器编译成js

1.cd到指定路径下

2.使用tsc 文件名即可编译

通过配置文件的方式进行运行

上面的运行方式比较麻烦所以我们可以使用配置文件的方式简化我们的运行

上面的写法在每次修改完ts之后 我们都要重新再次编译 很麻烦 所以我们能不能让文件修改完成之后自动的进行编译

1.cd到指定路径下

2.使用tsc -init 初始化一个tsconfig.json的配置文件(用于指定编译的配置项)

3.直接输入tsc 即可

自动编译

需要监听这个tsconfig.json

vscode自动编译

1.点击终端 运行任务

2.在下拉的弹出框中选择typescript

3.在弹出的下拉框中选择 监视 tsconfig.json 即可完成自动编译

命令方式自动编译

在编译文件的时候 使用-w命令 ts编译器就会自动监听数据的变化 从而重新编译当前文件

tsc -w

Ts知识点

变量/常量

注意: let 创建的变量不能重复声明

注意: let const创建的变量是块级作用域

注意:创建变量的时候 除了下划线_和美元$付浩源 不能包含其他的特殊字符 包括空格

类型声明

在ts中有一个非常重要的特点就是类型声明 类型声明所指的就是在创建ts的变量的时候我们需要指定当前变量的数据类型是什么 指定好类型之后ts编译器就会检查我们设置的值 是否符合当前指定的类型 不符合就会报错

语法:

let 变量名:数据类型="值"

数据类型

在ts中支持所有传统的js数据类型

string 字符串

number 数字

boolean 布尔值

// 创建变量
let text:string="我是一个字符串"
let bool:boolean=true

object 对象

array 数组

// 创建对象
let obj:object={
    name:"xixi",
    age:18
}
// 创建数组
let arr:number[]=[1111,22222,33333];
let arrb:Array<string>=["你好","你坏"]

ts新增的数据类型

any类型 在一些情况下 如果我们无法确定你当前这个变量存储的数据类型是什么 那么这个时候我们可以把数据类型设置成any(任意类型)

注意:如果我们把数据类型设置成了any 那么ts编译器就会在解析当前变量的时候 跳过类型验证

// 任意类型
let num:any="什么数据类型都可以存储"

tuple元祖类型 就是已知长度和数据类型的数组

// 元祖
let arrc:[string,number,boolean]=["你好",11,true]

enum 枚举类型 枚举就是给一组数值赋予友好的名字,枚举也可以理解为是一组命名元素的集合给一组数值起个友好的名字

// enum枚举
// enum 名字{数值1,数值2,。。。。。n}
// enum user{xiaoming,xiaohong,xiaobai}
// console.log(user.xiaobai)
// 设置枚举类型的值 设置了数字的值之后   只会影响自身以及后面的内容
// enum user{xiaoming,xiaohong=99,xiaobai}
// console.log(user.xiaoming)

// 给枚举类型设置类字符串类型的值之后   那么他后面的所有值都必须初始化
// 因为后面的值 没有办法给字符串自增1
enum user{xiaoming,xiaohong="你好",xiaobai="设置初始值"}
console.log(user.xiaoming)

void 没有返回值

// void没有返回值
// 函数如果有返回值  那么建议设置返回值类型
function fun():string{
    return "xxx"
}
// 函数如果没有返回值
function funb():void{
    console.log("没有返回值")
}

never 表示永远不会出现的类型

类型别名

类型别名 就是给数据类型起一个友好的名字 使用type关键字来进行类型别名的创建

// 类型别名
type xx=string
let namedemo:xx="xixix"

因为上面的类型别名在开发的时候单独使用的场景很少 通常类型别名都是要配合联合类型 来一起使用的

联合类型

联合类型就表示一个变量如果要存储多个数据类型的时候 传统定义方式是不行的 因为传统的方式当我们指定一个数据类型之后 就没有办法在给当前这个变量设置其他类型了

// 联合类型
type newType=string|number|boolean


let age:newType=true

自动类型判断

ts中拥有自动的类型判断机制 党对变量进行声明和赋值的时候 ts的编译器会自动判断变量的类型并且赋值 (是在我们没有指定数据类型的基础之上才会自动类型判断)

面向对象

面向对象 OOP思想 程序之中所有的操作都是以对象的方式来完成的

举例:

操纵浏览器 使用window对象

操纵网页 使用document对象

操纵控制台 使用console对象

一切操纵都要通过对象 这也就是面向对象

程序是什么?

计算机程序的本质就是对象现实事物的抽象 抽象的反义词就是具体 比如 照片就是对具体的一个物体进行抽象 汽车模型就是对汽车进行抽象 程序也就是对事物进行抽象 通过程序 我们可以标识一个人一个事物等等 一个事物就是一个对象

在程序中所有的对象都被分为两个部分 数据 与 功能 以人举例 人的姓名 年龄 性别 等都属于数据 人可以说话 走路等都属于功能 数据就是属性 功能就是方法

操纵对象 创建对象 首先究竟要拥有对象

定义类对象:

class 类名{
    属性名:数据类型;
    constructor(参数:数据类型){
        this.属性名=参数
    }
    方法(){
        
    }
}

例子:

class User{
    // 创建属性
    name:string;
    age:Number;
    constructor(newname:string,newage:number){
        this.name=newname
        this.age=newage
    }
    showname(){
        console.log("你好我是"+this.name)
    }
}
// 使用类new关键字  可以方便的生产出一个实例对象  这个成产的过程叫实例化
let user= new User("xixi",18)
user.showname()

面向对象的特点

封装

封装:对象的本质上就是属性和方法容器 主要的作用就是用来容纳属性和方法 这就是封装

默认情况下 对象的属性可以在任何地方进行修改

class User{
    // 创建属性
    name:string;
    age:Number;
    constructor(newname:string,newage:number){
        this.name=newname
        this.age=newage
    }
    showname(){
        console.log("你好我是"+this.name)
    }
}
// 使用类new关键字  可以方便的生产出一个实例对象  这个成产的过程叫实例化
let user= new User("xixi",18)
user.showname()


// 对象的属性可以在任何地方进行修改
console.log(user.age)
user.age=666
console.log(user.age)

但是但是 为了确保数据的安全性 在ts中 可以对属性的权限进行设置

三种修饰符

1.public(默认值) 公开的 谁都可以使用 可以在类 子类 对象中进行修改(就算我们没有写public 程序运行的时候也会默认给我们补上)

// public 共有的  任何地方都可以使用修改
class a{
    public name:string="你好"
}

let demoa=new a()
console.log(demoa.name)
demoa.name="xixi"
console.log(demoa.name)

2.protected 受保护的 只能在当前类和子类中使用

// protected受保护的
class a{
    protected name:string="你好"
    showname(){
        console.log(this.name)
    }
}

let demoa=new a()
// 读取
// console.log(demoa.name)//因为name是受保护的 所以不能在类外部使用
demoa.showname()

3.private 私有的 只能在当前类中使用

// private 私有的
class a{
    private name:string="你好"
    private showname(){
        console.log(this.name)
    }
}

let demoa=new a()
// 读取
console.log(demoa.name)//因为name是私有的  不能在类外部使用
demoa.showname()//因为showname是私有的  不能在类外部使用

只读属性

readonly 属性生成之后不能修改

静态属性

static 使用静态属性修饰的内容 不需要实例化就可以直接使用

// static  就可以不用实例化直接使用
class a{
    static namedemo:String="xixi"
}

console.log(a.namedemo)

继承

继承是面向对象中另一个重要的特性 通过继承可以讲别的类中的属性和方法放置到当前类中

// 继承
// 父类
class Fu{
    public name:String="我是父类的属性111"
    public showName(){
        console.log("我是父类的方法")
    }
}

// 子类  实现继承  extends关键字来实现继承
class Zi extends Fu{
    // 既然子类继承了父类  那么就可以在子类中直接使用父类的属性和方法

    zifun(){
        console.log("我是子类我直接使用父类的内容"+this.name)
    }
}

let zi=new Zi()
zi.zifun()

多态/重写

多态的前提是 必须在继承的时候才能实现多态

在发生继承的时候 如果子类中的方法替换了父类中的同名方法 那么这个就叫做多态/重写

// 多态
// 父类
class Fu{
    fufun(){
        console.log("我是父类的方法")
    }
}

// 子类 多态的前提是有继承
class Zi extends Fu{
    // 多态就是子类中的方法和父类中的方法名相同的时候
    // 那么子类的方法就会替换原有相同的那个内容
    fufun(): void {
        console.log("我是子类的方法")
    }
}

let zi=new Zi()
zi.fufun()

接口 interface

接口就是定义规范的一个技术 他主要定义行为和动作 是一个限制性的规范作用

通过接口可以对类中的属性进行格式上的规定

// 接口  接口名首字母大写  建议大家  在定义接口的时候  使用大写I作为开头

interface IUser{
    name:String,//属性
    age:number,
    showname():void//方法
}
// 使用: 约束对象中的数据类型
let obj:IUser={
    name:"你好",
    age:18,
    showname(){
        console.log("你好呀")
    }
}

// 大家会发现我们接口定义了3条数据  那么默认在使用的时候 就必须把这3条都要创建出来

我就是在创建接口的时候创建了多条 但是我只想使用其中一部分怎们办?

可选属性

可选属性的含义就是该属性可以定义但是不使用 有的时候不需要完全匹配一个接口 那么就可以使用可选属性 使用?号来进行表示

interface IUser{
    name?:String,//属性
    age?:number,
    showname():void//方法不行
}
let obj:IUser={
    name:"xixi",
    showname(){
        console.log("不乖")
    }
}

泛型

定义函数或者类的时候 有些情况下 我们没有办法第一时间知道当前数据的数据类型(返回值 参数)这个时候泛型就派上了作用

泛型就是在定义函数接口或者类的时候 我们不先预定指定类型 而是在使用的时候在指定类型的一个特性

// 原来使用数据  都要先定义类型
// class Demo{
//     name:String
//     constructor(newname:string){
//         this.name=newname
//     }
// }

// 如果我们不确定这个数据的类型  那么我们可以使用泛型先创建   在使用的时候在注入数据类型
class Demo<T>{
    name:T
    constructor(newname:T){
        this.name=newname
    }
}

ts+react

创建

使用 cra来进行创建 create-react-app 项目名 --template typescript

组件

创建组件使用.tsx结尾 组件中的内容没有区别

但是在引用组件的时候 不加.tsx结尾的后缀名

传值

正向传值

需要使用接口与泛型配合使用定义数据类型

父组件
import React, { Component } from 'react'
import Zi from "./zi"
export default class demo extends Component {
    render() {
        return (
            <div>
                我是一个组件
                <Zi title="我是正向传值的数据" age="6666"/>
                
            </div>
        )
    }
}


子组件
import React, { Component } from 'react'
// 定义props的数据类型
interface IProps{
    title:String,
    age:string
}

export default class zi extends Component<IProps> {
    render() {
        return (
            <div>
                zzizizizzizizi--{this.props.title}--{this.props.age}
            </div>
        )
    }
}

逆向传值

state

创建状态

import React, { Component } from 'react'
import Zi from "./zi"
export default class demo extends Component {

    public constructor(props:any){
        super(props)
        // 创建状态
        this.state={
            text:"我是state的数据"
        }
    }

    public render() {
        return (
            <div>
                我是一个组件
                <Zi title="我是正向传值的数据" age="6666"/>
                
            </div>
        )
    }
}

import React, { Component } from 'react'
import Zi from "./zi"

interface IState{
    text?:string
}
// 我们使用泛型已经把state的接口类型传递进来了但是为什么还报错呢?
// 原因是因为  当前这个组件  会把泛型中的第一个数据当成props的数据类型
// 而第二个才会当成state的数据类型 
export default class demo extends Component<{},IState> {

    public constructor(props:any){
        super(props)
        // 创建状态
        this.state={
            text:"我是state的数据"
        }
    }

    public render() {
        return (
            <div>
                {/* 使用状态 */}
                我是一个组件---{this.state.text}
                <Zi title="我是正向传值的数据" age="6666"/>
                
            </div>
        )
    }
}

函数组件

react中核心概念就是组件 在react中16.8版本之后 函数组件又重登舞台 因为函数组件默认不能使用状态 ref 生命周期等内容

但是16.8版本之后 react有一个新的概念叫HOOK通过HOOK可以让react使用上述不能使用的内容

// 函数组件的首字母要大写
// let Fun=()=>{
//     return (
//         <div>xxxxxx</div>
//     )
// }

// export default Fun

import React from "react"
// 函数组件的首字母要大写
// FC 是functionComponent的简写
let Fun:React.FC<{}>=()=>{
    return (
        <div>
            你好么么哒
        </div>
    )
}
export default Fun

props

父
// 函数组件的首字母要大写
// let Fun=()=>{
//     return (
//         <div>xxxxxx</div>
//     )
// }

// export default Fun

import React from "react"
import Zi from "./zi"
// 函数组件的首字母要大写
// FC 是functionComponent的简写
let Fun:React.FC<{}>=()=>{
    return (
        <div>
            你好么么哒
            <Zi title="你好我是正向传值"/>
        </div>
    )
}
export default Fun



子
import React from "react"

interface IProps{
    title:String
}

// 传递接口指定类型
let Zi:React.FC<IProps>=(props)=>{
    return (
        <div>
            zizizizizzizizizi--{props.title}
        </div>
    )
}

export default Zi 

HOOK

react HOOK 是react16.8新增的一个特性 主要作用 就是让无状态组件/函数组件 可以使用状态 ref等一些特性

HOOK 不能在 class组件中使用

useState---可以让函数组件使用状态

useState 是reactHOOK给我们提供的 最基本最常用的一个HOOK 主要作用就是用来管理当前本地的状态

useState() 返回值是一个数组(长度为2)数组的第一项标识的是当前的值 数组的第二项标识的时候修改这个值的函数

let [xiaoming , setXiaoming]=useState(初始值)

import React,{useState} from "react"


let Fun:React.FC<{}>=()=>{
    // 函数组件使用useState来进行状态的创建
    let [xiaoming,setXiaoming]=useState("你好")

    let demoFun=()=>{
        // 修改useState创建的状态
        setXiaoming("你坏")
    }
    return (
        <div>
            {/* 使用useState创建的变量 */}
            <h1>useState-----{xiaoming}</h1>
            <button onClick={demoFun}>点我修改xiaoming这个状态</button>
        </div>
    )
}

export default Fun

多个状态呢?

需要给useState()传递一个对象

import React,{useState} from "react"


let Fun:React.FC<{}>=()=>{
    // 创建多个状态
    let [xiaoming,setXiaoming]=useState({
        name:"xixi",
        age:18,
        sex:"nan"
    })

    let upFun=()=>{
        // 修改状态
        setXiaoming({...xiaoming,name:"哈哈"})
    }
  
    return (
        <div>
            {/* 读取 */}
           <h1>useState创建多个状态----{xiaoming.name}---{xiaoming.age}</h1>
           <button onClick={upFun}>点我修改状态</button>
        </div>
    )
}

export default Fun

useRef

hook中让函数组件可以使用ref

import React,{useRef} from "react"
let Fun:React.FC<{}>=()=>{
    // 创建ref
    let u:any=useRef(null)

    let clickFun=():void=>{
        u.current.style.color="red";
        console.log(u.current)
    }
    return (
        <div>
            <h1>useRef的使用</h1>
            {/* 使用ref */}
            <h1 ref={u}>修改我的颜色</h1>
            <button onClick={clickFun}>点我修改上面的颜色</button>
        </div>
    )
}
export default Fun

useEffect

使用useEffect来对react的生命周期进行替代 componentDidMount 与 componentDidUpdate和componentWillUnmount 三个生命周期的组合

import React,{useEffect} from "react"
let Fun:React.FC<{}>=()=>{
    useEffect(()=>{
        console.log("我被调用了")
    })


    return (
        <div>
            <h1>useEffect</h1>    课件休息课件休息课件休息课件休息课件休息课件休息
        </div>
    )
}
export default Fun

useReducer

useReducer和redux没有关系的

主要就是修改数据的 他和useState最大的区别就是在处理多个数据修改的时候降低了复杂度(对一个数据有多个修改的时候 使用useReducer)

基本的数据创建

import React,{useReducer} from "react"

let Fun:React.FC<{}>=()=>{
    let reducer:any=(state:number,action:object)=>{
        return state
    }

    // 1.初始化数据
    // let [代表数据的变量,触发修改动作]=useReducer(reducer(修改数据的动作方法),初始值)
    let [state,dispatch]=useReducer(reducer,66)
    return (
        <div>
            <h1>useReducer---{state}</h1>
        </div>
    )
}
export default Fun

修改数据

import React,{useReducer} from "react"

let Fun:React.FC<{}>=()=>{
    let reducer:any=(state:number,action:any)=>{
        switch(action.type){
            case "ADD":
                return state+1;
            case "DEL":
                return state-1;
            default :
            return state

        }
        
    }

    // 1.初始化数据
    // let [代表数据的变量,触发修改动作]=useReducer(reducer(修改数据的动作方法),初始值)
    let [state,dispatch]:any=useReducer(reducer,66)

    let add=():void=>{
        dispatch({type:"ADD"})
    }
    let del=():void=>{
        dispatch({type:"DEL"})
    }
    return (
        <div>
            <h1>useReducer---{state}</h1>

            <button onClick={add}>+1</button>
            <button onClick={del}>-1</button>
        </div>
    )
}
export default Fun

React hook 10种 Hook - 简书

虚拟DOM

React的高性能体现就是虚拟DOM

浏览器出现的原因就是为了把数据(图片文字等内容展现给用户) 但是随着技术的发展浏览器现在的功能强度和功能性已经和设计之处完全不同了 在浏览器中出现了大量的特效需要展示 游戏需要运行 各种各样的复杂功能接踵而至

慢慢的开发者就觉的浏览器的效率就已经不足以支撑我们当前要运行的内容了 有很大部分原因 就是dom操作影响了性能

传统的dom操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>
        
    </div>
    <script>
        document.getElementsByTagName("div")[0].innerHTML="你好"
        // 从上面的代码大家会发现  浏览器如果要修改一个简单的内容  那么就需要进行一次dom操作
        // 如果要修改很多个  那么就会源源不断的进行dom的修改 一直迭代下去
    </script>
</body>
</html>

react中

对上面大量的dom操作进行了修改和优化

具体实现

react会把浏览器上渲染的dom(真实dom)转换成一个js对象(虚拟dom virtualDOM)react会拿着这个虚拟dom进行数据的修改与数据的更新 把所有要修改 或者是需要更新的内容进行统一的操作 然在一起进行页面的更新

react高性能的原理

在web开发中 我们需要把数据的变化反映到页面中 那么这个时候就不得不进行dom的操作 而负载的dom操作又是影响浏览器性能的最大原因

react中就引用了虚拟dom virtualDOM 可以在浏览器中用js来实现一套DOM的api react在开发的过程中对所有的dom操作都是用这个虚拟dom进行 当数据变化的时候 react就会重新构建dom树 然后react会比较新修改的和原始的dom树得到新的比较结果从而根据这个结果来进行内容的修改(比较的过程就是用diff算法 来尽可能少的比较出差异 对这个差异进行修改) 最终进行页面的render更新

扩展--react最新的核心算法

在react16x之后 发布了一张全新的react算法 叫做 react Fiber 是对核心算法的重新实现 之前使用的都是diff算法 整个更新都是同步的 这样可能也是会造成性能上的损耗 当react要加载或者更新整个组件数据的时候 会多出很多的事情 比如 要调用每个组件的生命周期函数 计算和对比虚拟dom 最后更新 整个过程都是同步的 加载事件耗时太长

Fiber 实现: 分片 会把一个消耗事件很长的过程拆分成一个个的小片端 每个小片段的事件很短 虽然总时间 没有太大变化 但是在每个小片段执行完 都会给其他的任务一个执行的机会 这样一来 线程就不会被独占 其他的任务依然可以有运行机会

umi

Umi,中文可发音为乌米,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。 (通过umi我们可以简化react项目开发中的路由)

umi蚂蚁金服推出的

umi与creact-react-app的区别

cra 他是一个基于webpack的打包方案 在默认的项目中是不包含路由

umi是一个框架 他其中已经内容了路由等功能让我们在开发项目的时候更加高效的完成指定的功能

yarn

yarn就是和npm一样 都是一个包管理工具

yarn是由 facebook推出的一个包管理工具

yarn的安装

npm install -g yarn

yarn和npm 的对照表

功能 npm yarn
初始化 npm init yarn init
安装依赖 npm install yarn install或者 yarn
新增依赖 npm install --save xxx yarn add xxx
全局安装 npm install -g xxx yarn global add xxx
同时下载多个 npm install --save xx1 xx2 yarn add xx1 xx2
删除依赖 npm uninstall --save xxx yarn remove xxx

如果觉得yarn默认下载很慢 那么我们可以把yarn切换成淘宝镜像地址

yarn config set registry https://registry.npm.taobao.org/

umi的创建

npm install -g umi / yarn global add umi 全局安装

umi -v 查看版本

创建一个文件夹并且cd到这个文件夹之下(因为umi在创建项目的时候不会自动生成项目文件夹 所以我们要自己创建)

npm 或 yarn create @umijs/umi-app 项目的创建

npm install 或者 yarn 来进行项目的依赖下载

npm start 或者 yarn start 来进行项目的启动

页面创建

手工创建

我们可以创建类组件 也可以创建函数组件

import React, { Component } from 'react'

export default class home extends Component {
    render() {
        return (
            <div>
                我是一个类组件
            </div>
        )
    }
}


function Shop(){
    return (
        <div>
            我是一个函数组件
        </div>
    )
}
export default Shop

自动创建

我们可以在项目的根路径下输入如下命令 即可自动创建对应的页面

umi g page xxx(页面名)

umi g page xxx(页面名) --typescript --less 创建ts与less的

路由的配置

去项目下的 .umirc 文件进行配置

import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: [
    { path: '/', component: '@/pages/index' },
    // 配置路由
    { path: '/home', component: '@/pages/home' },
    { path: '/shop', component: '@/pages/shop' },
  ],
  fastRefresh: {},
});

路由导航

编程式

history.push

声明式--Link

// 1.引用
import {Link,history} from "umi"
let Fun=()=>{
    let demo=()=>{
        // 编程式导航
        history.push("/shop")
    }
    return (
        <div>
            <h1>我是路由导航</h1>
            {/* 2.使用 */}
            <Link to="/">默认页面</Link>
            <Link to="/home">home页面</Link>
            <Link to="/phone">phone页面</Link>
            <Link to="/shop">shop页面</Link>
            <h1>编程式导航</h1>
            <button onClick={demo}>点我去shop</button>
        </div>
    )
}
export default Fun

重定向与404页面

import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: [
    // 重定向
    {exact:true, path:"/",redirect:"/index"},
    { path: '/index', component: '@/pages/index' },
    // 配置路由
    { path: '/home', component: '@/pages/home' },
    { path: '/shop', component: '@/pages/shop' },
    { path: '/phone', component: '@/pages/phone' },
    // 404页面
    { component: '@/pages/no' },
  ],
  fastRefresh: {},
});

二级多级路由

1.创建页面

2.在指定的一级路由规则中 使用routes 进行二级路由的配置

import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: [
    // 重定向
    {exact:true, path:"/",redirect:"/index"},
    { path: '/index', component: '@/pages/index' },
    // 配置路由
    { path: '/home', component: '@/pages/home' },
    { path: '/shop', component: '@/pages/shop' },
    { 
      path: '/phone', 
      component: '@/pages/phone', 
      // 配置二级路由
      routes:[
        { path: '/phone/era', component: '@/pages/er/era' },
        { path: '/phone/erc', component: '@/pages/er/erc' },
      ]
    },
    // 404页面
    { component: '@/pages/no' },
  ],
  fastRefresh: {},
});

3.设置二级路由的出口

this.props.children 来配置二级路由的出口

约定式路由

umi中除了配置式路由以外 同样也支持约定式路由 约定是路由就是不需要我们手工的区在配置文件上 指明路由的相关参数

如果没有routes这个配置项 那么umi会自动进入到约定式路由模式 他会自己分析src/pages文件夹 从而自行生成路由规则

函数组件与类组件的优缺点

函数组件 不会被实例化 整体的渲染速度要比类组件高

函数组件 整个内容被进行剪成了一个jsx 所以语法上更加的简单

函数组件 不需要访问this

函数组件 不能使用state 生命周期 ref等内容 需要用hook替代 语法上我们要学习新内容

dva

dva简化了我们组件之间的数据通信(简化了我们之前在组件传值的时候使用的redux)

通过dva 就可以让我们更好的执行项目中数据传递的一个便捷性

dva=react-router(虽然dva中带有路由功能 可能工作中一般是吧dva和umi配合使用)+redux+redux-saga

简化完成一个redux的闭环 (dva和redux不是一个东西 他是redux的替代产品)

redux-saga

redux-saga是在redux中完成异步操纵的一个技术

创建

npm install -g dva-cli 全局下载

dva -v 查看版本

cd到指定文件夹下

dva new 项目名 创建项目

npm start 启动项目

dva路由

dva中也带路由功能 但是 在实际开发的时候 很多开发者喜欢把umi和dva整合在一起 umi管理路由 dva进行组件传值 各干各的事

1.创建路由组件页面

在src文件夹下的routes文件夹下创建即可

2.配置 在src下找到router.js进行配置

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
// 1.引用
import Home from './routes/home';
import Phone from './routes/phone';

function RouterConfig({ history }) {
  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={IndexPage} />
        {/* 2.配置规则 */}
        <Route path="/home" exact component={Home} />
        <Route path="/phone" exact component={Phone} />
      </Switch>
    </Router>
  );
}

export default RouterConfig;

路由导航

声明式--Link

import {Link} from "dva/router"
export default ()=>{
    return (
        <>
            <Link to="/">默认页面</Link>&nbsp;&nbsp;&nbsp;
            <Link to="/home">home</Link>&nbsp;&nbsp;&nbsp;
            <Link to="/phone">phone</Link>&nbsp;&nbsp;&nbsp;
        </>
    )
}

编程式

编程式的方式和原生react的react-router-dom实现路由的方式是一样的

也要使用withRouter来设置不是被路由所管理的页面 具有路由的跳转对象

import {Link,withRouter} from "dva/router"
let Topbar= (props)=>{
    let fun=()=>{
        // 编程式导航
        props.history.push("/phone")
    }
    return (
        <>
            <Link to="/">默认页面</Link>&nbsp;&nbsp;&nbsp;
            <Link to="/home">home</Link>&nbsp;&nbsp;&nbsp;
            <Link to="/phone">phone</Link>&nbsp;&nbsp;&nbsp;
            <button onClick={fun}>点我进行页面的跳转phone</button>
        </>
    )
}
export default withRouter(Topbar)

dva-Model

model就是用来存放一个个的小模块 通过模块化的管理 可以弱化 组件数据与组件数据的耦合度

1.在src下的models文件夹下创建属于我们自己的数据模块

// 创建了一个phone组件的状态模块

export default {

    namespace: 'phonem',//命名空间  给当前这个模块起个名字必须写
  
    state: {//管理数据的

    },
  };
  

2.使用模块

把模块文件在index.js中进行配置引用

import dva from 'dva';
import './index.css';

// 1. Initialize
const app = dva();

// 2. Plugins
// app.use({});

// 3. Model
// app.model(require('./models/example').default);
app.model(require('./models/phonem.js').default);

// 4. Router
app.router(require('./router').default);

// 5. Start
app.start('#root');

3.组件内使用配置的模块

在组件内使用connect()高阶组件 来进行连接

import React, { Component } from 'react'
import Tb from "../components/topbar"
// 1.引用connect   connect是一个高阶组件
import {connect} from "dva"
 class phone extends Component {
    render() {
        return (
            <div>
                <Tb/>
               phone
               {/* 读取模块中的数据 */}
               {/* {this.props.state.命名空间.数据} */}
               {this.props.state.phonem.name}
            </div>
        )
    }
}
// connect是一个方法   只有这个方法被调用的时候才是一个高阶组件
export default connect(state=>({state}))(phone)

dva-state

state就是dva中的数据源 把所有的数据都存放在state中

// 创建了一个phone组件的状态模块

export default {

    namespace: 'phonem',//命名空间  给当前这个模块起个名字必须写
  
    state: {//管理数据的
        name:"xixi",
        age:18,
        arr:[1111,2222,3333,4444,5555]
    },
  };
  

dva--reducers

在dva的model中 我们使用reducers可以进行数据的修改操作

reducers中包含的就是一个个的函数 每一个函数就是一个修改的动作

export default {

    namespace: 'homem',
  
    state: {
        name:"xixi",
        age:18
    },
    // 数据修改
    reducers:{
        // 每一个函数就是一个数据的修改动作
        demoa(){

        },
        demob(){

        },
    }
  
  };
  

1.创建修改

export default {

    namespace: 'homem',
  
    state: {
        name:"xixi",
        age:18
    },
    // 数据修改
    reducers:{
        // 每一个函数就是一个数据的修改动作
        // state 数据源
        // payload 就是外部传递进来的数据
        demoa(state,payload){
            // 在函数中返回一个修改之后的结果
            return {...state,age:666}
        },
        demob(){

        },
    }
  
  };
  

2.组件中触发这个修改的动作

this.props.dispatch({type:"命名空间的名字/你要调用的reducers的名字"})

import React, { Component } from 'react'
import {connect} from "dva"

class home extends Component {
    fun=()=>{
        // 调用dva中的reducers进行数据的修改
        this.props.dispatch({type:"homem/demoa"})
    }
    render() {
        return (
            <div>
                我是dva---{this.props.state.homem.name}
                {this.props.state.homem.age}
                <button onClick={this.fun}>点我修改age</button>
            </div>
        )
    }
}
export default connect(state=>({state}))(home)

payload的使用‘

payload就是用来接受调用的时候传递进来的数据

1.传递数据的时候 在dispatch的方法中传递第二个参数

import React, { Component } from 'react'
import {connect} from "dva"

class home extends Component {
    fun=()=>{
        // 调用dva中的reducers进行数据的修改
        this.props.dispatch({type:"homem/demoa"})
    }
    funb=()=>{
        // 我给reducers的payload传递数据
        this.props.dispatch({type:"homem/demob",data:{text:"我是传递的数据"}})
    }
    render() {
        return (
            <div>
                我是dva---{this.props.state.homem.name}
                {this.props.state.homem.age}
                <button onClick={this.fun}>点我修改age</button>
                <button onClick={this.funb}>点我传递payload</button>
            </div>
        )
    }
}
export default connect(state=>({state}))(home)

2.在模块中可以使用payload来进行数据的接收

export default {

    namespace: 'homem',
  
    state: {
        name:"xixi",
        age:18
    },
    // 数据修改
    reducers:{
        // 每一个函数就是一个数据的修改动作
        // state 数据源
        // payload 就是外部传递进来的数据
        demoa(state,payload){
            // 在函数中返回一个修改之后的结果
            return {...state,age:666}
        },
        demob(state,payload){
            // console.log(payload.data)
            return {...state,age:payload.data.text}
        },
    }
  
   
  
  };
  

dva--基本的数据请求

1.在services下 设置我们的请求封装

import request from '../utils/request';

export function query() {
  return request('/api/users');
}
//我们自己的请求
export function apilink() {
  return request('http://localhost:3000/user/list');
}



2.在想使用请求的组件中 引用 使用

import React, { Component } from 'react'
import {connect} from "dva"

// 引用请求封装
import {apilink} from "../services/example.js"
class home extends Component {
    fun=()=>{
        // 调用dva中的reducers进行数据的修改
        this.props.dispatch({type:"homem/demoa"})
    }
    funb=()=>{
        // 我给reducers的payload传递数据
        this.props.dispatch({type:"homem/demob",data:{text:"我是传递的数据"}})
    }
    func=()=>{
        // 请求数据
        apilink().then((ok)=>{
            console.log(ok)
        })
    }
    render() {
        return (
            <div>
                我是dva---{this.props.state.homem.name}
                {this.props.state.homem.age}
                <button onClick={this.fun}>点我修改age</button>
                <button onClick={this.funb}>点我传递payload</button>
                <hr />
                <button onClick={this.func}>点我发送请求</button>
            </div>
        )
    }
}
export default connect(state=>({state}))(home)

当我们运行的时候发现请求会跨域

跨域

在dva中设置跨域 我们在webpackrc的文件中进行设置

写的内容同vue于react的跨域内容 使用proxy来进行跨域解决

{
     "proxy":{ //设置devServe解决跨域问题
            "/api":{
                // 我们需要告诉devserver帮我们解决那个地址的跨域
                "target":"http://localhost:3000/",
                "changeOrigin":true,
                "ws":true,
                // 替换地址
                "pathRewrite":{
                    "^/api":""
                }
            }
        }
}

千万不要忘了 把请求地址换成 /api 重启项目

dva --- 异步操作方式effects

effect中是一个个的函数 每个函数就是一个异步操作

扩展---es6 Generator函数

Generator 有的人叫做迭代器函数 / 星号函数

通过generator函数 可以让我们在函数执行的时候 控制其内容的内容 走走停停 让函数交出执行权 可以让函数在调用的时候按照我们的需要执行或者暂停

传统的函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        function fun(){
            console.log(1)
            console.log(2)
            console.log(3)
        }
        fun()
        // 在传统的函数中   只要函数被调用  那么函数中的所有代码
        // 都会从上到下 一次执行一遍
    </script>
</body>
</html>

我现在不想让函数一调用全部都执行 我想让他按照我的命令来执行怎么办?

generator基本概念

可以让我们在函数执行的时候 控制其内容的内容 走走停停 让函数交出执行权 可以让函数在调用的时候按照我们的需要执行或者暂停

要求

1.在生命函数点的时候 在 函数名前面加一个*号 用于区分和普通函数的区别

2.yield 分割线 通过分割线就可以指明我们需要函数在执行中的暂停位置

3.使用next()来执行函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        function *fun(){
            console.log(1)
            yield
            console.log(2)
            yield
            console.log(3)
        }
        // console.log(fun())
        // 我们需要使用next()让他走走停停
        let g=fun()
        g.next()//执行
        g.next()//执行
        g.next()//执行
        
       
    </script>
</body>
</html>

dva异步请求数据

是吧数据请求在effect中进行数据请求 然后把请求来的数据交给 reducers进行修改 最终赋值给state

1.需要在页面有个事件触发 effects进行异步操作

this.props.dispatch({type:"命名空间的名字/你触发effects的名字"})

import React, { Component } from 'react'
import {connect} from "dva"
class phone extends Component {
    componentDidMount() {
        // 触发effects进行数据请求
        this.props.dispatch({type:"homem/demolink"})
    }
    
    render() {
        return (
            <div>
                <h1>dva数据请求闭环操作</h1>
            </div>
        )
    }
}
export default connect(state=>({state}))(phone)

2.就在模块中创建对应的effects

export default {

    namespace: 'homem',
  
    state: {
        name:"xixi",
        age:18
    },
    // 数据修改
    reducers:{
        // 每一个函数就是一个数据的修改动作
        // state 数据源
        // payload 就是外部传递进来的数据
        demoa(state,payload){
            // 在函数中返回一个修改之后的结果
            return {...state,age:666}
        },
        demob(state,payload){
            // console.log(payload.data)
            return {...state,age:payload.data.text}
        },
    },
    // 进行异步数据的操作
    effects:{
        // 对应的都是一个个的方法(Generator函数)  每个方法都是一个异步数据的操作
        *demolink({payload},{put,call}){

        }
    }
   
  
  };
  

3.在刚才创建的effect中进行异步操作

// 引用请求封装
import {apilink} from "../services/example.js"
export default {

    namespace: 'homem',
  
    state: {
        name:"xixi",
        age:18
    },
    // 数据修改
    reducers:{
        // 每一个函数就是一个数据的修改动作
        // state 数据源
        // payload 就是外部传递进来的数据
        demoa(state,payload){
            // 在函数中返回一个修改之后的结果
            return {...state,age:666}
        },
        demob(state,payload){
            // console.log(payload.data)
            return {...state,age:payload.data.text}
        },
    },
    // 进行异步数据的操作
    effects:{
        // 对应的都是一个个的方法(Generator函数)  每个方法都是一个异步数据的操作
        *demolink({payload},{put,call}){
            // 先把请求引用进来
            // 使用请求call 用来包裹数据请求 会返回出成功的结果
          let data=yield call(apilink)
          console.log(data)
          
        }
    }
   
  
  };
  

4.把请求来的数据交给reducers进行state的修改

// 引用请求封装
import {apilink} from "../services/example.js"
export default {

    namespace: 'homem',
  
    state: {
        name:"xixi",
        age:18
    },
    // 数据修改
    reducers:{
        // 每一个函数就是一个数据的修改动作
        // state 数据源
        // payload 就是外部传递进来的数据
        demoa(state,payload){
            // 在函数中返回一个修改之后的结果
            return {...state,age:666}
        },
        demob(state,payload){
            // console.log(payload.data)
            return {...state,age:payload.data.text}
        },
    },
    // 进行异步数据的操作
    effects:{
        // 对应的都是一个个的方法(Generator函数)  每个方法都是一个异步数据的操作
        *demolink({payload},{put,call}){
            // 先把请求引用进来
            // 使用请求call 用来包裹数据请求 会返回出成功的结果
          let data=yield call(apilink)
          console.log(data.data.data)
        //   把请求来的数据交给reducers来修改 我们在effect中调用reducer使用put来触发
          yield put({
              type:"update",//put是调用reducer的  type就是调用的reducer名字
              data:{
                  arr:data.data.data
              }
            })

        }
    }
   
  
  };
  

5.编写对应的reducer 来进行state的修改

// 引用请求封装
import {apilink} from "../services/example.js"
export default {

    namespace: 'homem',
  
    state: {
        name:"xixi",
        age:18,
        arr:[]
    },
    // 数据修改
    reducers:{
        // 每一个函数就是一个数据的修改动作
        // state 数据源
        // payload 就是外部传递进来的数据
        demoa(state,payload){
            // 在函数中返回一个修改之后的结果
            return {...state,age:666}
        },
        demob(state,payload){
            // console.log(payload.data)
            return {...state,age:payload.data.text}
        },

        // 进行state的数据修改
        update(state,payload){
            return {...state,arr:payload.data.arr}
        }
    },
    // 进行异步数据的操作
    effects:{
        // 对应的都是一个个的方法(Generator函数)  每个方法都是一个异步数据的操作
        *demolink({payload},{put,call}){
            // 先把请求引用进来
            // 使用请求call 用来包裹数据请求 会返回出成功的结果
          let data=yield call(apilink)
          console.log(data.data.data)
        //   把请求来的数据交给reducers来修改 我们在effect中调用reducer使用put来触发
          yield put({
              type:"update",//put是调用reducer的  type就是调用的reducer名字
              data:{
                  arr:data.data.data
              }
            })

        }
    }
   
  
  };
  

6页面获取数据

import React, { Component } from 'react'
import {connect} from "dva"
class phone extends Component {
    componentDidMount() {
        // 触发effects进行数据请求
        this.props.dispatch({type:"homem/demolink"})
    }
    
    render() {
        return (
            <div>
                {/* 读取数据 */}
                <h1>dva数据请求闭环操作---{this.props.state.homem.arr}</h1>
            </div>
        )
    }
}
export default connect(state=>({state}))(phone)

antd

antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品(pc端)。

1.下载 yarn add antd npm install --save antd

2.在想使用的页面中引用使用

import React, { Component } from 'react'
import { Button } from 'antd';//引用对应的内容
import 'antd/dist/antd.css';//引用样式
export default class home extends Component {
    render() {
        return (
            <div>
                我是一个组件
                <Button type="primary" ghost>
                Primary
                </Button>
            </div>
        )
    }
}

提交react项目

新建一个文件夹 把项目代码放进去 不要node——models 在吧项目的截图也一并放进去

提交到544078964@qq.com 主题不要忘了写上你的班号和名字

微信小程序

什么是微信小程序

微信小程序就是一个不需要下载安装即可直接使用的应用 这个应用是在微信客户端来进行运行的 它实现了触手可及的一个基本概念 我们可以直接扫一扫 即可直接打开应用

触手可及(单个小程序的体积不会超过2mb) 我们用户不需要在乎安装了太多的应用 用完也不需要关心占用太多的手机内容存 不需要卸载

优势

1.不需要下载安装

2.运行速度 要远远大于浏览器应用

3.创建迅速

4.短小精干 单个程序不会超过2mb

小程序的发展史

2016年 1月 产品经理张小龙

2016年 9月 开始内测

2017年 1月 第一批小程序上线

知识储备

HTML5 CSS3 JS 后端的相关知识

如何开发小程序

1.拥有一个微信号

2.百度搜索微信公众平台 登录进去并且注册小程序开发账号

3.开发工具下载与安装

4.登录微信开发者工具(扫码登录 并且填写appID 开发者的唯一标识)

注意 在创建项目的时候我们要选择 不使用云开发

文件结构

全局文件---app.json全局配置文件

全局配置 | 微信开放文档

pages

用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径(含文件名) 信息。文件名不需要写文件后缀,框架会自动去寻找对应位置的 .json, .js, .wxml, .wxss 四个文件进行处理。

数组的第一项代表小程序的初始页面(首页)。

小程序中新增/减少页面,都需要对 pages 数组进行修改。

 "pages": [
      // 谁的页面配置在第一项谁就是首页
        "pages/shop/shop",
        "pages/home/home"
    
    ],

window 配置

用于设置小程序的状态栏、导航条、标题、窗口背景色。

属性 类型 默认值 描述 最低版本
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如 #000000
navigationBarTextStyle string white 导航栏标题颜色,仅支持 black / white
navigationBarTitleText string 导航栏标题文字内容
navigationStyle string default 导航栏样式,仅支持以下值: default 默认样式 custom 自定义导航栏,只保留右上角胶囊按钮。参见注 2。 iOS/Android 微信客户端 6.6.0,Windows 微信客户端不支持
backgroundColor HexColor #ffffff 窗口的背景色
backgroundTextStyle string dark 下拉 loading 的样式,仅支持 dark / light
backgroundColorTop string #ffffff 顶部窗口的背景色,仅 iOS 支持 微信客户端 6.5.16
backgroundColorBottom string #ffffff 底部窗口的背景色,仅 iOS 支持 微信客户端 6.5.16
enablePullDownRefresh boolean false 是否开启全局的下拉刷新。 详见 Page.onPullDownRefresh
onReachBottomDistance number 50 页面上拉触底事件触发时距页面底部距离,单位为 px。 详见 Page.onReachBottom
pageOrientation string portrait 屏幕旋转设置,支持 auto / portrait / landscape 详见 响应显示区域变化 2.4.0 (auto) / 2.5.0 (landscape)
restartStrategy string homePage 重新启动策略配置 2.8.0
initialRenderingCache string 页面初始渲染缓存配置,支持 static / dynamic 2.11.1
visualEffectInBackground string none 切入系统后台时,隐藏页面内容,保护用户隐私。支持 hidden / none 2.15.0

tabbar

tabbar就是设置页面底部的导航切换 用于快速的页面切换设置

最少2个 最多5个 少于或者大于 就会报错

属性 类型 必填 默认值 描述 最低版本
color HexColor tab 上的文字默认颜色,仅支持十六进制颜色
selectedColor HexColor tab 上的文字选中时的颜色,仅支持十六进制颜色
backgroundColor HexColor tab 的背景色,仅支持十六进制颜色
borderStyle string black tabbar 上边框的颜色, 仅支持 black / white
list Array tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab
position string bottom tabBar 的位置,仅支持 bottom / top
custom boolean false 自定义 tabBar,见详情 2.5.0

其中 list 接受一个数组,只能配置最少 2 个、最多 5 个 tab。tab 按数组的顺序排序,每个项都是一个对象,其属性值如下:

属性 类型 必填 说明
pagePath string 页面路径,必须在 pages 中先定义
text string tab 上按钮文字
iconPath string 图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。 positiontop 时,不显示 icon。
selectedIconPath string 选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片。 positiontop 时,不显示 icon。

页面结构

每个小程序的页面是由4个文件构成 wxml wxss js json

页面中的json文件

局部的页面配置 可以配置局部页面的标题下拉等区域的样式

局部配置的优先级要大于app.json的全局配置 我们可以在局部页面的json中设置当前页面的相关页面表现

页面中的wxml

wxml就是写页面结构的类似于html

微信小程序给我们提供了一套相关的标签(不要在用传统的html标签了)通过微信小程序的标签 可以让我们更加快速的完成指定的功能 微信小程序把他给我们提供的标签称之为 组件

组件--view

等同于html的div

view | 微信开放文档

组件--text

所有文字都用text组件 (没有什么标题 斜体 段落的概念了)

text | 微信开放文档

组件--icon

图标

icon | 微信开放文档

组件--image与swiper

image 图片组件等同于html的img image组件默认宽度320px、高度240px

image | 微信开放文档

swiper 轮播图组件

swiper | 微信开放文档

表单组件--表单组件的全家桶

input | 微信开放文档

组件--- 页面链接

我们在之前学习到的页面间的跳转是使用app.json中的tabbar来完成的 但是tabbar只能最多设置5个页面的跳转 显然不符合我们正常的功能开发要求 5个页面太少 那么如果我有很多个页面 怎么相互跳转呢

navigator

navigator | 微信开放文档

路由导航

上面的navigator就是小程序中路由导航之声明式导航

navigator

属性:

open-type:

navigate 保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。

navigateBack 关闭当前页面,返回上一页面或多级页面。

switchTab 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面

redirect 关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面

reLaunch 关闭所有页面,打开到应用内的某个页面

自定义组件

新建自定义组件

1.在项目下新建一个文件夹 components

2.在components文件夹下就可以新建我们的组件文件夹

3.在新建的组件文件夹中创建对应的4个文件 wxml wxss js json (可以使用鼠标右键 新建component的方式创建更简单)

4.在对应的页面json文件中 设置component:true用来表明是组件(但是我们在使用自动创建的使用已经给我们创建过了)

组件使用

1.在使用的页面中的json文件里进行引用

{
  "usingComponents": {
    "引用的组件名自己气名字":"你要引用的组件路径"
  }
}

2.因为组件的本质是自定义标签 所以我们在wxml中直接使用

<demo></demo>

组件传值

正向传值

1.子组件设置接收参数

// components/demo/demo.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        // 接收参数:数据类型
        text:String
    },

    /**
     * 组件的初始数据
     */
    data: {

    },

    /**
     * 组件的方法列表
     */
    methods: {

    }
})

2.传值

<demo text="我是正向传值"></demo>

逆向传值

1.通过事件触发一个函数来进行逆向传值(小程序的事件是bindtap)

    <button bindtap="fun">点我逆向传值</button>

函数写在组件的methods中

  methods: {
        fun(){
            
        }
    }

2.在函数中使用自定义事件抛出数据

this.triggerEvent("自定义事件的名字","数据")

    methods: {
        fun(){
            // 抛出自定义事件
            this.triggerEvent("zipao","我是逆向传值的数据")
        }
    }

3.接收数据

在想使用的地方 使用bind:自定义事件名="函数"

<demo bind:zipao="fufun"></demo>
// pages/home/home.js
Page({

    // 在页面中创建函数  直接创建与data他们同级即可
    fufun(el){
        console.log(el)
    },

    /**
     * 页面的初始数据
     */
    data: {

    },

    /**
     * 生命周期函数--监听页面加载
     */
    onl oad: function (options) {

    },

    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    onReady: function () {

    },

    /**
     * 生命周期函数--监听页面显示
     */
    onShow: function () {

    },

    /**
     * 生命周期函数--监听页面隐藏
     */
    onHide: function () {

    },

    /**
     * 生命周期函数--监听页面卸载
     */
    onUnload: function () {

    },

    /**
     * 页面相关事件处理函数--监听用户下拉动作
     */
    onPullDownRefresh: function () {

    },

    /**
     * 页面上拉触底事件的处理函数
     */
    onReachBottom: function () {

    },

    /**
     * 用户点击右上角分享
     */
    onShareAppMessage: function () {

    }
})

扩展---behaviors

behaviors 是用于组件间代码共享的特性,类似于一些编程语言中的 “mixins” 或 “traits”。

每个 behavior 可以包含一组属性、数据、生命周期函数和方法。组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。 每个组件可以引用多个 behavior ,behavior 也可以引用其它 behavior

// my-component.js var myBehavior = require('my-behavior') Component({ behaviors: [myBehavior], properties: { myProperty: { type: String } }, data: { myData: 'my-component-data' }, created: function () { console.log('[my-component] created') }, attached: function () { console.log('[my-component] attached') }, ready: function () { console.log('[my-component] ready') }, methods: { myMethod: function () { console.log('[my-component] log by myMethod') }, } }) 在上例中, my-component 组件定义中加入了 my-behavior,

而 my-behavior 结构为:

属性:myBehaviorProperty 数据字段:myBehaviorData 方法:myBehaviorMethod 生命周期函数:attached、created、ready 这将使 my-component 最终结构为:

属性:myBehaviorProperty、myProperty 数据字段:myBehaviorData、myData 方法:myBehaviorMethod、myMethod 生命周期函数:attached、created、ready 当组件触发生命周期时,上例生命周期函数执行顺序为:

[my-behavior] created [my-component] created [my-behavior] attached [my-component] attached [my-behavior] ready [my-component] ready 详细规则参考 同名字段的覆盖和组合规则。

同名字段的覆盖和组合规则 组件和它引用的 behavior 中可以包含同名的字段,对这些字段的处理方法如下:

如果有同名的属性 (properties) 或方法 (methods):

若组件本身有这个属性或方法,则组件的属性或方法会覆盖 behavior 中的同名属性或方法; 若组件本身无这个属性或方法,则在组件的 behaviors 字段中定义靠后的 behavior 的属性或方法会覆盖靠前的同名属性或方法; 在 2 的基础上,若存在嵌套引用 behavior 的情况,则规则为:父 behavior 覆盖 子 behavior 中的同名属性或方法。 如果有同名的数据字段 (data):

若同名的数据字段都是对象类型,会进行对象合并; 其余情况会进行数据覆盖,覆盖规则为:组件 > 父 behavior > 子 behavior 、 靠后的 behavior > 靠前的 behavior。(优先级高的覆盖优先级低的,最大的为优先级最高) 生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用:

对于不同的生命周期函数之间,遵循组件生命周期函数的执行顺序; 对于同种生命周期函数,遵循如下规则: behavior 优先于组件执行; 子 behavior 优先于 父 behavior 执行; 靠前的 behavior 优先于 靠后的 behavior 执行; 如果同一个 behavior 被一个组件多次引用,它定义的生命周期函数只会被执行一次。

作用就是组件之间的代码共享的特性 类似于vue中的mixins

每个behaviors中都可以包含一组属性 方法 生命周期等内容 组件在引用之后 就可以直接使用behaviors中的内容

使用:

1 创建文件夹与文件 用来容纳behaviors

2.在文件中创建属性与方法

// 创建behaviors
let behaviors=Behavior({
    // 创建属性
    // 创建方法等内容

     /**
     * 组件的属性列表
     */
    properties: {
      
    },

    /**
     * 组件的初始数据
     */
    data: {
        num:666
    },

    /**
     * 组件的方法列表
     */
    methods: {
        befun(){
            console.log("我是behaviors中的方法")
        }
    }



})

3.在想使用的组件中 里面的js文件上引用调用 即可直接使用

// components/demo/demo.js
// 1.引用
import behaviors from "../../behaviors/index"

Component({
    // 2.调用
    behaviors:[behaviors],
    /**
     * 组件的属性列表
     */
    properties: {
        // 接收参数:数据类型
        text:String
    },

    /**
     * 组件的初始数据
     */
    data: {

    },

    /**
     * 组件的方法列表
     */
    methods: {
        fun(){
            // 抛出自定义事件
            this.triggerEvent("zipao","我是逆向传值的数据")
        }
    }
})

4.在组件中可以直接使用(想怎么用就怎么用)

wxml事件

事件 | 微信开放文档

忘掉传统的js事件吧 因为在小程序中有属于自己的一套事件逻辑

事件分类

冒泡事件---bind

非冒泡事件---catch

类型 触发条件 最低版本
touchstart 手指触摸动作开始
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如来电提醒,弹窗
touchend 手指触摸动作结束
tap 手指触摸后马上离开
longpress 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 1.5.0
longtap 手指触摸后,超过350ms再离开(推荐使用longpress事件代替)
transitionend 会在 WXSS transition 或 wx.createAnimation 动画结束后触发
animationstart 会在一个 WXSS animation 动画开始时触发
animationiteration 会在一个 WXSS animation 一次迭代结束时触发
animationend 会在一个 WXSS animation 动画完成时触发
touchforcechange 在支持 3D Touch 的 iPhone 设备,重按时会触发 1.9.90

事件对象

属性 类型 说明 基础库版本
type String 事件类型
timeStamp Integer 事件生成时的时间戳
target Object 触发事件的组件的一些属性值集合
currentTarget Object 当前组件的一些属性值集合
mark Object 事件标记数据 2.7.1

函数参数传递

调用小程序的函数时候是不加() 那么他是怎么进行参数传递的呢?

我们要使用自定义属性与事件对象来进行函数的接收

1.在你绑定事件的dom上面 使用data-xxx=“值”来进行自定义属性的设置

 <view wx:for="{{arrb}}" wx:for-item="v" bindtap="fun" data-xiaoming="{{index}}">

2.在函数中使用事件对象 来获取自定义属性的值

  fun(event){
        console.log(event.currentTarget.dataset.你的自定义属性名)
    },

属性插变量

在小程序中 组件的属性如果想插入变量的话

属性="{{变量}}"

列表的便利wx:for

wx:for在便利的时候 下标默认值为index 便利的结果默认值为item

语法:

wx:for="{{你要便利的数据}}"

<view wx:for="{{arr}}">
{{index}}-----{{item}}
</view>

便利多行内容---block

block没有什么别的作用 就是在便利多行数据的时候 对内容块进行包裹 并且block不参与渲染 所以不会在页面中添加无用的 dom元素

<block wx:for="{{arr}}">
    <view>{{index}}</view>
    <view>{{item}}</view>
</block>

列表渲染 wx:for修改默认值

列表渲染的时候 默认值下标index 值item 我如果想修改的话

index的修改 wx:for-index来进行修改

item的修改 wx:for-item来进行修改

    <view class="item" wx:for="{{dataarr}}" wx:for-item="v">
        <image src="{{v.imgurl}}"></image>
        <text>\n{{v.title}}</text>
    </view>

条件渲染---if全家桶

if里面为真执行wx:if 为false 执行wx:else

wx:if来进行条件渲染

wx:else 搭配if来进行使用 如果if不成立执行wx:else

wx:elif 就是多重条件判断

数据请求---request

RequestTask | 微信开放文档

合法域名设置于跳过

当我们正常请求的时候会在小程序中出现如下错误

VM13 asdebug.js:1 Wed Jan 19 2022 09:59:49 GMT+0800 (中国标准时间) request 合法域名校验出错
VM13 asdebug.js:1 如若已在管理后台更新域名配置,请刷新项目配置后重新编译项目,操作路径:“详情-域名信息”
VM13 asdebug.js:1  http://localhost:8888 不在以下 request 合法域名列表中,请参考文档:https://developers.weixin.qq.com/miniprogram/dev/framework/ability/network.html(env: Windows,mp,1.05.2111300; lib: 2.19.4)

因为小程序中我们所有的数据接口地址 今后都需要在位置的服务器中进行先期的验证 通过之后在能使用否则就会有上述的错误提示(目的就是为了防止小程序中出现违反国家法律法规的内容进行显示)

而且 地址不能使用ip地址 或者localhost 这个请求的地址必须是在国家备案过正规的网络地址

跳过域名验证

仅在开发的时候有用 真正上线后还是需要配置你的合法域名

在开发工具的详情---->本地设置----》勾选不校验合法域名这一项即可

数据修改

在小程序中数据的创建是在js文件下 的data中进行创建的 数据的页面展示是使用{{}}来进行展示的

但是数据的修改我们必须要用 this.setData()来进行修改 如果不使用这个 那么页面的内容是不改变的

上一篇:[Excel 技巧] 动态高亮显示选中的单元格所在行(列)


下一篇:1006 Sign In and Sign Out