day01
1.介绍
1.课程
1.内容:
vue(vue-router vuex element-ui axios …) 【10+5 2个项目(app 后台管理)】-js
react(react-router-dom redux react-redux hooks …) 【6+3 1个项目 app】-js
2.课程内容 vue
1.官网:
2.SPA:single page application 单页面应用
MPA:多个url–》多个HTML文件 多页面应用
[优点:有利于SEO优化,缺点:会出现白屏,用户体验度不好]
SPA:对个url—>1个html
[优点:用户体验好 缺点:首屏加载速度慢,不利于SEO优化]
2.核心
数据驱动 组件系统
3.安装
1.cdn【不推荐 项目中绝对不用】
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
2.npm 【学习理论过程会使用】
npm init
npm i vue //安装vue
//安装淘宝镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
3.脚手架方式【day4 工作时候用】
4.Vue实例
5.数据绑定
js :事件 innerHTML value src className style.color …
jq:事件 html() val() attr() class css …
Vue:非表单元素(div span) 表单元素(input textarea select …) 媒体元素(img video )–给属性赋值 样式。。。。
1.非表单元素【div p span】 {{}} vHtml vText
{{}} 优点:简单方便 缺点:1.不能解析标签;2.首屏可能会出现闪屏
v-html 优点 :可以解决闪屏问题 可以解析标签 【主要用于详情】
v-text 优点:可以解决闪屏问题 缺点:不能解析标签
首屏建议使用v-text|v-html,其他屏你喜欢用什么就用什么。
2.表单元素【input】vModel
v-model
<div>
账号:<input type="text" v-model="name">
</div>
3.属性绑定【媒体元素】 vBind,简写 :
<div id="app">
<!-- v-bind 动态绑定属性,不仅可以绑定已知属性,自定义属性也可以绑定,简写为 : -->
<img v-bind:src="img" alt="">
<a v-bind:href="website.url">
<img v-bind:src="website.img" alt="" v-bind:title="website.name">
</a>
<a :href="website.url" :aaa="name">
<img :src="website.img" alt="">
</a>
</div>
4.条件渲染
1.v-if
<!-- v-if="boolean" 真就出现,假就消失 -->
<h3 v-if="1===2">社会很单纯</h3>
<h3 v-if="x>5">复杂的是人</h3>
2.v-show
<!-- v-show="boolean" 真就出现,假就消失 -->
<h3 v-show="1===2">社会很单纯</h3>
<h3 v-show="x>5">复杂的是人</h3>
3.v-else
<!-- v-if和v-else必须挨着 -->
<div v-if="comments.length===0">暂无评论</div>
<div v-else>{{comments}}</div>
4.v-if VS v-show
相同点:true 出现,false消失
不同点:false情况下,v-if采用的 惰性加载;v-show采用的是display:none.
使用场景:如果频繁切换,那么就使用v-show;如果不频繁切换,建议使用v-if
5.列表渲染 v-for
1.遍历数组
<!-- 遍历数组 -->
<ul>
<li v-for="(item,index) in comments">{{index}}---{{item}}</li>
</ul>
comments: ["物美价廉", "很好", "推荐"],
2.遍历json
<!-- 遍历json -->
<div v-for="(value,key) in json">{{key}}:{{value}}</div>
data: {
comments: ["物美价廉", "很好", "推荐"],
json: {
name: "妲己",
age: 20,
sex: "女"
},
}
3.key
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
6.动态类名
1.变量
<!-- 1.:class="变量" -->
<div :class="color">打疫苗了吗?</div>
2.【三元】
<!-- 2.:class="[三元]" 只有2种情景 -->
<div :class="[isOrange?'orange':'lime']">绿水青山就是金山银山</div>
3.json
<!-- 3. :class="{类名1:true,类名2:false,.....}" 多种情景-->
<div :class="{pink:true,lime:true,whilte:false,blue:true}">大力改革教育</div>
7.动态行间样式
<!-- 理论 :style="{json}"-->
<div :style="{color:color,backgroundColor:'pink'}">天道酬勤,人道酬善,商道酬信。</div>
6.复习知识:
1.toFixed()
7.作业:
1.今天的案例练习3遍;
2.作业文件夹中的2个作业
day02
Vue 不支持 IE8 及以下版本
0.案例
bootstrap官网:
1.双for
<div class="item" v-for="item in movies" :key="item.id">
<h3>name:{{item.name}}</h3>
<p>导演:
<span v-for="i in item.directors" :key="i.id">{{i.name}} </span>
</p>
<p>主演:
<span v-for="i in item.actors" :key="i.id">{{i.name}} </span>
</p>
</div>
2.表单
表单数据,建议定义一个json,key和后端要求的字段一直,目的是为了方便和后端交互
1.指令 v-model
2.单选框
<!-- 单选框,value是选中后的值 -->
<div>性别:
<input type="radio" v-model="user.sex" name="sex" :value="0">男
<input type="radio" v-model="user.sex" name="sex" :value="1">女
</div>
3.多选框
对于checkbox来说,如果初始值是数组,最后就是数组;否则都是boolean
<div>
爱好:
<input type="checkbox" :value="1" v-model="user.hobby">唱歌
<input type="checkbox" :value="2" v-model="user.hobby">跳舞
<input type="checkbox" :value="3" v-model="user.hobby">写代码
<input type="checkbox" :value="4" v-model="user.hobby">打游戏
</div>
<div>
<input type="checkbox" v-model="user.isAgree">是否同意
</div>
user: {
"name": "11",
"pass": "22",
"tel": "322",
"sex": 1,
"hobby": [2, 3, 4],
"job": 2,
"snacks": [1, 2],
"des": "123123",
"isAgree": true
}
4.下拉菜单
<!-- option中间的给用户看的,value是程序取得数据 -->
<div>
职业:
<select v-model="user.job">
<option value="" disabled>--请选择--</option>
<option :value="1">php工程师</option>
<option :value="2">web工程师</option>
<option :value="3">java工程师</option>
</select>
</div>
<!-- 下拉菜单复选,需要设置multiple -->
<div>
零食:
<select v-model="user.snacks" multiple>
<option value="" disabled>--请选择--</option>
<option :value="1">辣条</option>
<option :value="2">薯片</option>
<option :value="3">瓜子</option>
<option :value="4">快乐水</option>
</select>
</div>
5.多行文本框
<div>
备注:
<textarea cols="30" rows="10" v-model="user.des"></textarea>
</div>
6.表单修饰符
<!-- .lazy 失去光标,才去修改data中的数据 -->
<input type="text" v-model.lazy="name">
<div>name:{{name}}</div>
<!-- .number 将输入的内容由string转为number -->
<input type="number" v-model.number="age">
<button v-on:click="logage()">打印年龄</button>
<!-- .trim 输入框前后去空格 -->
<input type="text" v-model.trim="msg">{{msg}}
7.事件
单选、多选、下拉菜单都是onchange,不可以使用onclick
8.MVVM模式
MVVM M-模型(model) V-视图(view) VM-视图模型(viewModel)
模型 通过 视图模型(控制器) 决定了视图的展示;
视图可以通过 视图模型(控制器) 改变模型的数据
视图模型是 视图 和模型之间的桥梁
vue是MVVM模式框架,适合操作数据多的情况使用
jq:dom操作方便、动画、链式操作
官网:jq【维护】
管理系统:vue 【内部使用 不需要推广,数据多,改版】(必然是你)
商城类:数据多,动画多 vue+jq
app:(不一定是你) 改版+数据+动画 vue+jq
活动页:【好看、一次性使用】html+css+jq(是你)
3.事件绑定
1.如何绑定事件
<button v-on:click="fn1()">点击触发</button>
<!-- 如果没有参数,()可以省略 -->
<button v-on:click="fn1">点击触发</button>
<!-- v-on 可以简写为@ -->
<button @click="fn1">点击触发</button>
<div>n:{{n}}</div>
<!-- 如果逻辑只有一句话,可以在html中实现 -->
<button @click="n=n+2">+2</button>
2.事件传参
<button @click="add(3,5)">3+5</button>
3.事件对象Event如何获取?
<!-- 显式获取:$event -->
<button @click="getEvent($event)">获取event</button>
<!-- 隐式获取:调用不能写(),默认参数就是event -->
<button @click="getEvent">获取event</button>
<!-- 如果出了event,还有其他参数,只能使用显式传参 -->
<button @click="getEvent2($event,10)">获取event,10</button>
4.阻止默认事件 阻止事件传播
//阻止默认事件
e.preventDefault()
//阻止传播
e.stopPropagation()
5.事件修饰符
.prevent 阻止默认
.stop 阻止传播
.self 触发的目标元素是自己才执行
.once 一次性触发
.capture 捕获
.up 上键
.down 下键
.left 左键
.right 右键
.enter 回车键
.13
4.$set
1.数组变了,页面不渲染,怎么办?
1.arr.splice(index,1,newObj)
2.Vue.set(arr,index,newObj)
3.this.$set(arr,index,newObj)
4.取回来数据,先操作,再赋值给数组。
2.json变了,页面不渲染,怎么办?
1.Vue.set(this.json,"y",40)
2.this.$set(this.json,"y",40)
3.this.json={
...this.json,
y:40
}
5.复习内容
1.阻止默认 阻止传播
if(e.preventDefault){
e.preventDefault()
}else{
e.returnValue=false
}
6.作业
1.练习3遍
2.作业文件夹的2个作业
day03
1.生命周期
8个生命周期函数是自动执行的函数,称为钩子函数
beforeCreate //创建之前,什么都没有
created //创建之后,数据初始化完成,但是el还是undefined
//作用:二次修改初始值
beforeMount //2.挂载期:如果有el或者$mount(),才自动触发
//挂在之前:找到了要解析的模板,但是{{}} v-指令都还没有解析
mounted //挂载完成:页面完成解析,DOM节点加载完成 *****
//作用:获取dom,添加动画,开启计时器,给window或者document绑定事件
beforeUpdate //3.更新期
//数据变化了,但是页面准备重新渲染之前,注意,此时取到 的数据是新值
updated //页面更新完成
beforeDestroy //4.销毁期
//作用:善后工作:1.清除计时器;2.清除window|document上的事件
destroyed //销毁完成,数据不会实时渲染,变成了纯粹的html,css
2.watch 监听器 侦听器
监听数据的改变,只能监听data中的数据和computed中的数据
new Vue({
watch:{
kw(newV,oldV){
//逻辑
},
json:{
handler(){
console.log("json变了");
},
deep:true
}
},
})
注意:不建议使用深度监听
1.深度监听会造成页面卡顿;
2.可以使用事件代替监听
jsonp
1.创建一个script标签
var os=document.createElement("script")
2.给script一个src,src就是地址
os.src="http://suggestion.baidu.com/su?cb=qwer&wd=123"
3.将script插入到页面
document.body.appendChild(os)
4.回调函数处理数据
function qwer(d){
//d就是后端返回的数据
}
3.filter 过滤器
1.作用:
转换数据
2.使用
| 管道符
3.注册方式
全局注册
// 全局过滤器:所有的vue实例都可用
Vue.filter("filterTel",(tel)=>{
return tel.slice(0,3)+"****"+tel.slice(7)
})
局部注册
new Vue({
el: "#app",
//局部过滤器:只有当前vue实例可用
filters: {
filterPrice(a) {
return a.toFixed(2)
},
}
})
4.computed 计算属性
new Vue({
computed:{
allPrice(){
return 10
}
}
})
5.computed VS watch
watch:
一个数据影响多个数据的改变
没有调用,自动执行
可以操作异步
computed:
多个数据影响一个数据
手动调用,当做属性调用
做同步
可以有缓存
6.面试题
1.常用指令
2.SPA优缺点?
3.v-if和v-show的区别?
4.v-for的key的作用
5.数组变了,页面不渲染怎么办?
6.json变了,页面不渲染怎么办?
7.vue生命周期有哪些?
8.vue一进来页面自动执行的生命周期有哪些?
9.一进来页面要发ajax在哪个阶段?
10.表单修饰符?
11.事件修饰符?
12.MVVM
13.MVVM和jq有什么区别?什么情况下使用?
14.watch和computed区别?
15.jsonp原理?
16.vue有哪些bug?如何解决?
1.{{}}闪屏 v-text
2.数组变了,不渲染,4种方式解决
3.json变了,不渲染,3中解决
4.watch深度监听会卡顿,用事件代替
5.v-for和v-if同时在一个节点上,渲染效率低,建议使用computed解决
7.复习
1.slice substr substring 区别
2.Date
3.padStart padEnd indexOf includes
4.Math.random() Math.max() Math.min()
5.arr: forEach vs map. every(some) filter reduce find findIndex
8.作业
1.练习 3遍
2.作业文件夹中的作业
1.淘宝搜索
2.购物车
day04
1.动画
1.使用场景
1.v-if
2.v-show
3.动态组件
4.路由
2. 6个状态
进来之前 enter
进来过程 enter-active
进来完成 enter-to
离开之前 leave
离开过程 leave-active
离开完成 leave-to
3.使用
1.transition 将内容包裹;2.设置name属性
<transition name="aa">
<div class="box" v-if="isshow"></div>
</transition>
3.3.设置6个状态
.aa-enter{
left: 0px;
}
.aa-enter-active,.aa-leave-active{
transition: all 0.3s;
}
.aa-enter-to{
left: 500px;
}
.aa-leave{
opacity: 1;
transform: scale(1,1);
}
.aa-leave-to{
opacity: 0.1;
transform: scale(0.1,0.1);
}
4.animate.css
官网:https://animate.style/
1.安装
npm i animate.css
2.引入
<link rel="stylesheet" href="./node_modules/animate.css/animate.css">
3.使用
<transition enter-active-class="animate__animated animate__fadeInDown"
leave-active-class="animate__animated animate__bounceOutLeft">
<div class="box" v-if="isshow"></div>
</transition>
4.注意:
1.一般情况下只写进来动画,不写离开,否则,太花里胡哨。
2.组件基础
1.什么是组件?
可复用的vue实例。(公共的html+css+js)
2.组件注册
1.全局注册
//全局组件:所有的vue实例都可以调用
Vue.component("hello",{
template:"<div>我是hello</div>"
})
2.局部注册
new Vue({
el:"#app",
//局部组件:只有当前vue实例可用
components:{
websitenav:{
template:"<div> <div>特色主题</div><div>行业频道</div><div>更多精选</div></div>"
}
}
})
3.大部分使用局部,少数使用全局。
3.组件命名
components: {
// 1.不能以已经存在的标签命名。比如:div input span ...
// 2.也不能以已经存在的标签的大写命名。比如:DIV INPUT ...
// 3.如果组件名中间包含了大写(除首字母之外),使用的时候需要变成-小写 ,烤串写法
WebsiteNavComponenT: {
template: "<div>这是第一个组件</div>"
},
// 4.建议名称中包含大写,目的是为了方便调用
vHello: {
template: "<div>hello</div>"
}
}
4.template
1.template 可以借助template标签来实现组件的模板
vSecond:{
template:"#second"
},
<template id="second">
<div>
<h3>this is second</h3>
</div>
</template>
2.template只能有1个根节点。因为一个vue实例只能作用在1个节点上,如果有很多节点,只能作用在满足条件的第一个节点上
3.脚手架
//只执行一次
npm i webpack -g //全局安装webpack
npm i @vue/cli -g //全局安装vue脚手架
创建项目:
vue create demo //demo是你的项目名称
1. manually select features
2.只选择了babel
3.package.json
4.未来是否都是这样? n
项目启动
cd demo
npm run serve
目录:
-demo
-node_modules 依赖包
-public 静态资源
index.html 唯一的页面
favicon.ico 小图标
.gitignore 上传码云不需要传的文件
.babel.config.js babel 配置
package.json 项目命令 依赖包
readme.md 项目说明
-src 你的代码
main.js 入口文件
App.vue 根组件
vue的插件:
vuter vetur
4.作业
1.练习3遍
2.作业文件夹中的作业
day05
组件高阶
1.组件通信***
组件关系:父子关系 非父子
父传子:子组件取了父组件的数据
场景:父组件控制数据。子组件控制的结构
语法:父组件通过自定义属性传值,子组件通过props接收,然后使用
父组件:
<v-child :img="img" :title="name"></v-child>
子组件:
export default {
props:["img","title"]
}
props验证:
export default {
// props验证
props:{
price:{
//必填
required:true,
//类型
type:Number
},
tel:{
//默认值
default(){
return '110'
}
}
}
}
子传父:子组件调用了父组件的方法
场景:子组件要修改父组件的值。
使用:父组件绑定自定义事件,子组件通过$emit()触发自定义事件
父组件:
<!-- 子传父:父组件绑定自定义事件,子组件通过$emit()触发自定义事件 -->
<v-child @aa="changeWang" @bb="changeName($event)"></v-child>
子组件:
methods:{
cwang(){
//通知父组件触发changeWang
this.$emit("aa")
},
changeName(name){
// 自定义事件传参,只能传递1个参数,接收方通过event对象来接收
this.$emit("bb",name)
}
},
非父子传值
EventBus【了解】–练习1遍
1.在main.js中给vue原型上添加一个vue实例
// 1.在vue的原型链上挂了一个EventBus,值是一个vue实例
Vue.prototype.EventBus=new Vue();
2.接收方绑定自定义事件
mounted(){
this.EventBus.$on("sendA",(e)=>{
console.log(e);
this.a=e;
})
},
3.发送方触发自定义事件:
//发数据
this.EventBus.$emit("sendA",this.name)
vuex
本地存储
cookie
2.ref
注意:ref一定要在mounted之后使用
1.获取到原生的DOM节点
<!-- 1.通过ref获取DOM节点 -->
<div class="boxDiv" ref="div"></div>
this.$refs.div.style.background='blue';
this.$refs.div.innerHTML="123"
2.父组件获取子组件的实例
<!-- 2.父组件获取子组件的实例 -->
<v-child ref="child"></v-child>
// this.$refs.child.name="貂蝉"
this.$refs.child.changeName('貂蝉')
3.is
1.解决了标签固定搭配问题
<!-- 1.解决标签固定搭配问题 -->
<ul>
<li is="v-one"></li>
</ul>
<table>
<tr is="v-one"></tr>
</table>
2.实现动态组件
<button @click="showCom='v-one'">one</button>
<button @click="showCom='v-two'">two</button>
<div :is="showCom"></div>
4.脚手架上使用动画
1.安装
npm i animate.css --save
2.main.js引入
import "animate.css"
3.使用
<transition
enter-active-class="animate__animated animate__bounceInLeft"
>
<div :is="showCom"></div>
</transition>
5.scoped
样式只在当前文件夹中起作用。 建议每个组件都加scoped.
<style scoped>
h3{
color:blue;
}
.boxDiv{
width: 100px;
height: 100px;
background: red;
color: #fff;
}
</style>
6.jquery
1.安装
npm i jquery --save
2.引入
import $ from "jquery"
3.使用[注意:1.mounted之后再使用;2.注意this指向,最好使用箭头函数]
$(".show").click(()=>{
$(".red").fadeIn(400)
})
$(".hide").click(()=>{
$(".red").fadeOut(400)
})
7.slot 插槽
1.匿名插槽
所有嵌套的内容都会出现在匿名插槽中
<v-one>
<div>善上若水,水善利万物而不争</div>
</v-one>
one组件:
<div class="box">
<h3>this is one</h3>
<!--匿名插槽: 内置组件 -->
<slot></slot>
</div>
2.具名插槽
two组件
<div class="box">
<!-- 具名插槽 -->
<slot name="top"></slot>
<h3>this is two</h3>
<slot name="bottom"></slot>
</div>
<v-two>
<ol slot="top">
<li>锦瑟无端五十弦</li>
<li>一弦一柱思华年</li>
</ol>
<ul slot="bottom">
<li>床前明月光</li>
<li>疑是地上霜</li>
</ul>
</v-two>
<!-- 2.具名插槽。如果嵌套的内容没有定前往哪个插槽,那么哪个插槽都不会展示 -->
<v-two>
<div>手机</div>
</v-two>
3.作用域插槽
作用域插槽:父组件调用了子组件,子组件展示数据,结构又不确定,那么使用插槽;但是这段不确定的结构中有使用到数据,需要slot传递回来,父组件使用。
父组件:
<v-three :arr="arr1">
<!-- v-slot 2.x 3.x都支持 -->
<template v-slot="props">
<span v-for="item in props.showArr" :key="item">{{item}}</span>
</template>
</v-three>
<v-three :arr="arr2">
<!-- slot-scope 2.x的写法,以后就弃用 -->
<template slot-scope="props">
<button v-for="item in props.showArr" :key="item">{{item}}</button>
</template>
</v-three>
子组件(three)
<template>
<div class="box">
<h3>展示的组件</h3>
<slot :showArr="showArr" a="1" b="2"></slot>
<h3>展示完成</h3>
</div>
</template>
<script>
// arr=["11","22","33"]
export default {
props: ["arr"],
computed:{
showArr(){
return this.arr.slice(0,3)
}
}
};
</script>
8.面试题
1.data为什么是个函数?
2.如何实现动态组件?
3.组件之间如何通信?
4.ref的作用是什么?
5.is的作用是什么?
9.作业
1.练习3遍
2.作业文件夹中的作业【尽量做】
day06
1.作业
查看作业笔记.md
2.混入
作用:将两个组件公共的逻辑提取。
语法:
export default {
data(){
return {
isshow:false
}
},
methods:{
show(){
this.isshow=true
},
hide(){
this.isshow=false
}
}
}
组件使用:
import toggle from "./toggle"
export default {
mixins:[toggle]
}
3.缓存组件
1.如果想要数据缓存,可以加keep-alive
<keep-alive>
<v-home v-if="n==1"></v-home>
</keep-alive>
2.加上了keep-alive,mounted只会执行一次,beforeDestroy不会再重复出发了
3.加上了keep-alive会多2个钩子函数:activated(激活) deactivated(失活)
activated() {
window.onclick = () => {
console.log("window scroll");
};
},
deactivated() {
window.onclick = null;
},
4.路由
将url映射到组件上。
1.一级路由规则
routes=[
//一级路由
{ path: '/login', component: login },
{
path: "/index", component: index,
},
{
path: "/detail", component: detail
},
{ path: "/list", component: list },
]
2.一级重定向
{ path: "*", redirect: "/login" }
3.嵌套路由
{
path: "/index", component: index,
//二级路由路由规则path没有"/" ,重定向是""
children: [
{
path: "home",
component: home,
},
{
path: "cate",
component: cate
},
{
path: "shop",
component: shop
},
{
path: "mine",
component: mine
},
{
path:"",
redirect: "home"
}
]
},
4.内置组件
<router-link></router-link>
<router-view></router-view>
5.路由导航高亮效果
<footer>
<router-link to="/index/home" active-class="select">首页</router-link>
<router-link to="/index/cate" active-class="select">分类</router-link>
<router-link to="/index/shop" active-class="select">购物车</router-link>
<router-link to="/index/mine" active-class="select">我的</router-link>
</footer>
6.编程式导航
this.$router.push("/search") //是添加了新的历史记录
this.$router.replace("/search") //是使用新的记录替换当前记录
this.$router.go(-1) //返回
5.作业
1.练习2遍;
2.作业文件夹中的作业 1
3.app 至少一遍
day07
1.路由传参
<router-link to="/detail?id=1&name=2">手机</router-link>
this.$route.query.id //1
this.$route.query.name //2
2.命名路由
{
path: '/search',
component: search,
// 命名路由
name:"搜索",
},
router-link :to="{name:'搜索'}">前往搜索</router-link>
3.命名视图 -了解
<router-view class="con"></router-view>
<!-- 命名视图 了解 -->
<router-view name="view2"></router-view>
//一级路由
{
path: '/login',
// component: login,
components:{
default:login,
view2:test
},
name: "登录",
// 路由元信息
meta: {
title: "登录"
}
},
4.动态路由
1.传递参数
<router-link to="/detail/1/手机">手机</router-link>
2.修该路由规则
{
path:"/detail/:id/:name"
}
3.取值
this.$route.params.id //1
this.$route.params.name //手机
5.路由模式
const router = new VueRouter({
// history 不带#,hash是带#
mode:"history",//hash history ,默认是hash
})
hash VS history
hash http://baidu.com/#/login
1.前进 后退 刷新 都ok
2.#/login 是hash值,hash值是不会影响请求
3.采用的是window.onhashchange=()=>{} 原理实现,是可以兼容IE678的
history http://baidu.com/login
1.前进 后退 ok
刷新:如果后端有这个路由,就会直接展示后端数据到页面;如果后端没有这个路由,404.
2. /login 是会影响请求
3.采用的是HTML5新增的API interface (pushState replaceState)
4.如果想使用history模式,需要后端配合。
6.导航守卫*
守卫|路由钩子函数:
如果没有设置守卫,可以自由出入;如果设置了守卫,需要守卫允许,才可以进入或者离开
全局守卫
router.beforeEach()
rotuer.afterEach()
路由独享守卫
beforeEnter()
组件内部守卫:
beforeRouteEnter()
beforeRouteUpdate()
beforeRouteLeave()
登录拦截如何实现??
如果用户没有登录,只能访问登录或者注册路由;如果登录了,才可以访问所有的路由。
1.在登录成功的时候,设计一个标识,用来延段是否登录。 【login.vue】
methods:{
login(){
//name="admin" pass="123"
if(this.user.name=="admin"&&this.user.pass=="123"){
//设计一个标记
localStorage.setItem("islogin",true)
this.$router.push("/index/home")
}else{
alert("账号密码错误")
}
}
}
2.在全局前置守卫做拦截,【router/index.js】
//全局前置守卫:进入每一个路由都会执行
//to:前往的路由数据 from是从哪来的路由数据 next允许不允许进入
router.beforeEach((to, from,next)=>{
// 1.如果去的登录页,直接进
if(to.path==="/login"||to.path=="/register"){
next()
return;
}
// 2.如果去的不是登录,判断islogin有没有。如果有,进
let islogin=localStorage.getItem("islogin");// "true" null
if(islogin){
next()
return;
}
// 3.如果没有,去登录
next("/login")
})
7.懒加载
let index=()=>import("../pages/index/index.vue")
let list=()=>Promise.resolve(import("../pages/list/list.vue"))
8.滚动处理
//滚动处理
scrollBehavior (to, from, savedPosition){
//如果之前savedPosition没有,滚动到{x:0,y:0}
//如果之前savedPosition有,滚动到savedPosition
if(savedPosition){
return savedPosition
}else{
return {x:0,y:0}
}
}
9.路由元信息
{
path: '/login', component: login, name: "登录",
// 路由元信息
meta: {
title: "登录"
}
},
<div class="header">{{$route.meta.title}}</div>
10.作业
1.app 练习2遍
day07
1.路由传参
<router-link to="/detail?id=1&name=2">手机</router-link>
this.$route.query.id //1
this.$route.query.name //2
2.命名路由
{
path: '/search',
component: search,
// 命名路由
name:"搜索",
},
router-link :to="{name:'搜索'}">前往搜索</router-link>
3.命名视图 -了解
<router-view class="con"></router-view>
<!-- 命名视图 了解 -->
<router-view name="view2"></router-view>
//一级路由
{
path: '/login',
// component: login,
components:{
default:login,
view2:test
},
name: "登录",
// 路由元信息
meta: {
title: "登录"
}
},
4.动态路由
1.传递参数
<router-link to="/detail/1/手机">手机</router-link>
2.修该路由规则
{
path:"/detail/:id/:name"
}
3.取值
this.$route.params.id //1
this.$route.params.name //手机
5.路由模式
const router = new VueRouter({
// history 不带#,hash是带#
mode:"history",//hash history ,默认是hash
})
hash VS history
hash http://baidu.com/#/login
1.前进 后退 刷新 都ok
2.#/login 是hash值,hash值是不会影响请求
3.采用的是window.onhashchange=()=>{} 原理实现,是可以兼容IE678的
history http://baidu.com/login
1.前进 后退 ok
刷新:如果后端有这个路由,就会直接展示后端数据到页面;如果后端没有这个路由,404.
2. /login 是会影响请求
3.采用的是HTML5新增的API interface (pushState replaceState)
4.如果想使用history模式,需要后端配合。
6.导航守卫*
守卫|路由钩子函数:
如果没有设置守卫,可以自由出入;如果设置了守卫,需要守卫允许,才可以进入或者离开
全局守卫
router.beforeEach()
rotuer.afterEach()
路由独享守卫
beforeEnter()
组件内部守卫:
beforeRouteEnter()
beforeRouteUpdate()
beforeRouteLeave()
登录拦截如何实现??
如果用户没有登录,只能访问登录或者注册路由;如果登录了,才可以访问所有的路由。
1.在登录成功的时候,设计一个标识,用来延段是否登录。 【login.vue】
methods:{
login(){
//name="admin" pass="123"
if(this.user.name=="admin"&&this.user.pass=="123"){
//设计一个标记
localStorage.setItem("islogin",true)
this.$router.push("/index/home")
}else{
alert("账号密码错误")
}
}
}
2.在全局前置守卫做拦截,【router/index.js】
//全局前置守卫:进入每一个路由都会执行
//to:前往的路由数据 from是从哪来的路由数据 next允许不允许进入
router.beforeEach((to, from,next)=>{
// 1.如果去的登录页,直接进
if(to.path==="/login"||to.path=="/register"){
next()
return;
}
// 2.如果去的不是登录,判断islogin有没有。如果有,进
let islogin=localStorage.getItem("islogin");// "true" null
if(islogin){
next()
return;
}
// 3.如果没有,去登录
next("/login")
})
7.懒加载
let index=()=>import("../pages/index/index.vue")
let list=()=>Promise.resolve(import("../pages/list/list.vue"))
8.滚动处理
//滚动处理
scrollBehavior (to, from, savedPosition){
//如果之前savedPosition没有,滚动到{x:0,y:0}
//如果之前savedPosition有,滚动到savedPosition
if(savedPosition){
return savedPosition
}else{
return {x:0,y:0}
}
}
9.路由元信息
{
path: '/login', component: login, name: "登录",
// 路由元信息
meta: {
title: "登录"
}
},
<div class="header">{{$route.meta.title}}</div>
10.作业
1.app 练习2遍
day08
一、课程内容-axios
1.介绍
官网:http://www.axios-js.com/zh-cn/docs/
axios 是一个基于 Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生 XHR 的封装,只不过它是 Promise 的实现版本,符合最新的 ES 规范,它本身具有以下特征:
从浏览器中创建 XMLHttpRequest
⚫ 支持 Promise API
⚫ 客户端支持防止CSRF
⚫ 提供了一些并发请求的接口(重要,方便了很多的操作)
⚫ 从node.js创建http请求
⚫ 拦截请求和响应
⚫ 转换请求和响应数据
⚫ 取消请求
⚫ 自动转换JSON数据
2.配置代理
在你的项目目录下创建一个文件,叫 vue.config.js:
module.exports = {
// 部署应用时的基本 URL
publicPath:"",
// build 时构建文件的目录
outputDir: 'dist',
// build 时放置生成的静态资源 (js、css、img、fonts) 的目录 assetsDir: 'static',
// 指定生成的 index.html
indexPath: 'index.html',
// 设置代理请求
devServer: {
proxy: {
"/api":{
target:"url",
ws:true,
changeOrigin:true
}
}
}
}
注意:前端项目重启
3.安装
npm i axios --save
4.使用
import axios from "axios"
//get
axios({
url:"/后端提供的url",
method:"get",//可以省略,
params:{
id:1
}
}).then(res=>{
//res就是后端返回的数据
})
axios.get(url,{
//配置项
headers:{},
//参数
params:{}
}).then(res=>{})
//post
axios({
url:"/后端提供的url",
method:"post",//不可以省略,
data:{
id:1
}
}).then(res=>{
//res就是后端返回的数据
})
axios.post("url",data,{
//配置项
headers:{
token:1
}
}).then(res=>{})
5.post传参问题
1.没有文件
import qs from "querystring"
export let reqLogin=(user)=>{
console.log(JSON.stringify(user));//'{"phone":"110","password":"123"}'
console.log(qs.stringify(user));//'phone=110&password=123'
return axios({
url: "/api/login",
method: "post",
//没有文件
data: qs.stringify(user),
})
}
2.有文件 FormData
//注册
export let reqRegister=(user)=>{
// 假设:user={name:1,age:2,ava:File}
let data=new FormData()
/*data.append("name",user.name)
data.append("age",user.age)
data.append("ava",user.ava)*/
for(let i in user){
data.append(i,user[i])
}
return axios({
//url请求路径 method请求方式 data参数
url:"/api/register",
method:"post",
data:data
})
}
6.拦截器
//请求拦截:返回的是后端收到的请求
//每次请求都要携带token
axios.interceptors.request.use(config=>{
console.log("此处是请求拦截:");
config.headers.token="123"
console.log(config);
return config
})
//响应拦截:返回的是前端收到的数据
axios.interceptors.response.use(res=>{
//每次请求都要打印
console.log("===此处是响应拦截,本次请求的地址是:"+res.config.url);
console.log(res);
//每次失败都要弹一下失败
if(res.data.code!==200){
alert(res.data.msg)
}
//返回的是给前端的
return res;
})
7.import
// 1个文件,只能有1个export default
// import a from "./a"
export default 10;
// 1个文件可以有很多个export
// import {login,register,home} from "./a"
export let login="123";
export let register=[1,2,3]
export let home=()=>{
return 10;
}
8.封装
1.src/http/http.js 环境配置 请求拦截 响应拦截 get post
//环境配置 请求拦截 响应拦截 get post
// 1.引入依赖包
import axios from "axios"
import Vue from "vue"
import qs from "querystring"
// 2.环境配置
if (process.env.NODE_ENV === "development") {
Vue.prototype.$pre = "http://localhost:3000"
}
if (process.env.NODE_ENV === "production") {
Vue.prototype.$pre = ""
}
// 3.请求拦截
axios.interceptors.request.use(config => {
config.headers.token = "123"
return config;
})
// 4.响应拦截
axios.interceptors.response.use(res => {
//打印
console.log("本次请求地址:" + res.config.url);
console.log(res);
//失败处理
if (res.data.code !== 200) {
alert(res.data.msg)
}
return res;
})
/**
* eg:get("/api/getCate",{}).then(res)
*
* @param {*} url 请求地址
* @param {*} [params={}] 请求参数集合
*/
export function get(url,params={}) {
return axios({
url,
method: "get",
params
})
}
/**
* eg:post("/login",{phone:"123"}).then(res=>{})
*
* @param {*} url 请求地址
* @param {*} [data={}] 请求参数集合,默认是{}
* @param {boolean} [isFile=false] 用来判断是否有文件,有就传true;没有不需要传参
* @returns
*/
export function post(url,data={},isFile=false){
let params=null;
//有文件
if(isFile){
params=new FormData();
for(let i in data){
params.append(i,data[i])
}
}else{
//无文件
params=qs.stringify(data)
}
return axios({
url,
method:"post",
data:params
})
}
2.src/http/api.js
import {get,post} from "./http"
//登录
export const reqLogin=(user)=>post("/api/login",user)
//注册
export const reqRegister=user=>post("/api/register",user)
//分类
export const reqHomeCate=()=>get("/api/getCate")
//列表
export const reqList=(params)=>get("/api/getgoodlist",params)
//详情
export const reqDetail=params=>get("/api/getgoodsinfo",params)
day09
UI框架
1.布局 ;2. 表单; 3.展示数据;4.js反馈; 5.常用的界面组件
1.element-ui 饿了么 PC
1.官网
https://element.eleme.cn/#/zh-CN
2.安装
npm i element-ui -S
3.引入 main.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
4.使用
<el-button type="danger">123</el-button>
2.iview PC端
3.vant 有赞 移动端
https://vant-contrib.gitee.io/vant/#/zh-CN/
day11
vuex
1.Vuex 是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
2.安装
npm i vuex --save
3.创建仓库
1.src/store/index.js
// 1.安装 npm i vuex --save
// 2.引入
import Vue from 'vue'
import Vuex from "vuex"
Vue.use(Vuex)
export default new Vuex.Store({
//公共状态
state: {
name: "妲己",
age: 20
},
//修改状态的方法
mutations: {
changeWang(state) {
state.name = "王昭君"
},
changeAge100(state) {
state.age = 100
}
}
})
2.main.js引入仓库
import store from "./store"
new Vue({
render: h => h(App),
//3.创建仓库
store
}).$mount('#app')
3.组件就可以使用
<div class="box">
<h3>this is a</h3>
<div>name:{{$store.state.name}}</div>
<div>age:{{$store.state.age}}</div>
<button @click="$store.commit('changeWang')">王昭君</button>
<button @click="$store.commit('changeAge100')">age=100</button>
</div>
4.使用
0.仓库
export default new Vuex.Store({
//状态集合:数据
state: {
name: "妲己",
age: "20",
arr: []
},
//将state的数据导出给组件
getters: {
name(state) {
return state.name
},
age(state){
return state.age
},
arr(state){
return state.arr
}
},
//修改状态:只有mutations里面的方法才能修改state
//只能做同步
mutations: {
/**
* @param {*} state 当前仓库的集合
* @param {*} name 你传递的参数
*/
changeName(state, name) {
state.name = name;
},
changeAge(state, age) {
state.age = age;
},
changeArr(state, arr) {
state.arr = arr
}
},
//逻辑 :处理异步,不能直接修改state
actions: {
/*
* @param {*} context 当前仓库本身
*/
changeName2s(context) {
setTimeout(() => {
context.commit("changeName", '西施')
}, 2000)
},
changeName(context, name) {
context.commit("changeName", name)
},
//请求list
reqArr(context) {
setTimeout(() => {
context.commit("changeArr", [1, 2, 3, 4])
}, 300)
}
},
modules: {
}
})
1.取数据
$store.state.name
2.触发mutations
$store.commit('changeName','貂蝉')
3.触发actions
$store.dispatch("changeName",'貂蝉')
4.mutations VS actions
mutations 同步 可以修改state 通过commit() 第一个参数是状态集合(state)
actions 逻辑(同步和异步都可以做) 不能修改state 通过dispatch() 第一个参数是仓库对象(store)
5.从getters中取数据
$store.getters.name
6.流程 单向数据流
state--(getters)-->compoonents--(dispatch)-->actions--(commit)-->mutations--修改-->state--更新-(getters)--->components -...
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8US5FQDY-1659630008219)(/Users/haoliuting/Desktop/0308/day11-vuex/笔记/vuex.png)]
7.辅助函数 mapGetters mapActions
1.数组方式
<script>
import { mapGetters, mapActions } from "vuex";
export default {
// 通过mapGetters可以将getters上的数据成批导入给computed
computed: {
...mapGetters(["name", "age", "arr"]),
a(){
return 10;
}
},
// 通过mapActions可以将actions上方法成批导入给methods
methods: {
...mapActions([
"changeName2s",
"changeName"
]),
fn(){}
},
};
</script>
2.json方式
import { mapGetters, mapActions } from "vuex";
export default {
// 通过mapGetters可以将getters上的数据成批导入给computed
computed: {
...mapGetters({
username:"name",
userage:"age",
arr:"arr"
}),
},
// 通过mapActions可以将actions上方法成批导入给methods
methods: {
...mapActions({
cname:"changeName"
}),
fn(){}
},
};
8.modules 模块
new Vuex.Store({
modules:{
home:{
namespaced:true,//命名空间 有了它,就可以通过“home/list”
state:{},
mutations:{},
actions:{},
getters:{}
}
}
})
9.目录结构
-store
index.js //创建仓库并导出
actions.js //根级别下的actions
mutations.js //根级别下的mutations state getters
-modules //模块
home.js //home模块
shop.js
10.vuex VS 本地存储
vuex 刷新就没有了 操作数据方便 实时渲染
本地存储 刷新还在 操作不方便 不具备实时渲染
本地存储和vuex同步
11.注意
vuex是单项数据流
v-model双向数据绑定.
如果遇到了表单,就使用v-model,此处不能使用vuex.
5.案例
1.创建项目
vuex vue-router css-pre (less)
2.配置代理
3.重置项目
1.App.vue
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
2.views 文件都删除
3.router/index.js 重置
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
]
const router = new VueRouter({
routes
})
export default router
4.components 里的文件都删除
4.http
5.store
-store
index.js //创建仓库并导出
actions.js //根级别下的actions
mutations.js //根级别下的mutations state getters
-modules //模块
home.js //home模块
shop.js
index.js
import Vue from 'vue'
import Vuex from 'vuex'
//引入
import {actions} from "./actions"
import {state,getters,mutations} from "./mutations"
import home from "./modules/home"
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations,
actions,ç
getters,
modules: {
home
}
})
actions.js
export const actions={
}
Mutations.js
export const state={}
export const getters={}
export const mutations={}
Home.js
const state = {}
const getters = {}
const mutations = {
}
const actions = {}
export default {
state,
getters,
mutations,
actions,
namespaced: true
}
6.home下的banner
1.设计状态层 store/modules/home.js
import {reqBanner,reqHomeCate} from "../../http/api"
const state = {
//1.轮播图初始值
banner: [],
}
const getters = {
// 2.导出banner
banner(state) {
return state.banner
},
}
const mutations = {
//3.修改banner
changeBanner(state, banner) {
state.banner = banner;
},
}
const actions = {
//4.请求banner
reqBanner(context){
reqBanner().then(res=>{
//5.触发修改banner
context.commit("changeBanner",res.data.list)
})
},
}
2.home.vue使用
<template>
<div>
<h3>首页</h3>
<img v-for="item in banner" :key="item.id" :src="$pre+item.img" alt="">
</div>
</template>
<script>
import {mapGetters,mapActions} from "vuex"
export default {
computed:{
...mapGetters({
banner:"home/banner",
})
},
methods:{
...mapActions({
reqBanner:"home/reqBanner",
})
},
mounted(){
this.reqBanner()
}
}
</script>
7.home下的cates
1.store/modules/home.js 设计cates
import {reqBanner,reqHomeCate} from "../../http/api"
const state = {
// 6.分类初始值
cates:[]
}
const getters = {
//7.导出cates
cates(state){
return state.cates
}
}
const mutations = {
//8.修改
changeCates(state,cates){
state.cates=cates
}
}
const actions = {
//9.请求分类
reqCates(context){
reqHomeCate().then(res=>{
context.commit("changeCates",res.data.list)
})
}
}
2.组件使用
<template>
<div>
<h3>首页</h3>
<img v-for="item in banner" :key="item.id" :src="$pre+item.img" alt="">
<div v-for="item in cates" @click="$router.push('/list?id='+item.id)" :key="item.catename">{{item.catename}}</div>
</div>
</template>
<script>
import {mapGetters,mapActions} from "vuex"
export default {
computed:{
...mapGetters({
cates:"home/cates"
})
},
methods:{
...mapActions({
reqCates:"home/reqCates"
})
},
mounted(){
this.reqCates()
}
}
</script>
<style>
img{
width: 100px;
height: 100px;
}
</style>
8.登录 登出
1.分析:用户信息很多地方都要用,存vuex方便使用,但是vuex刷新就没有了,所以本地存储同步一份。刷新就将本地存储赋值给vuex。
2、store/mutations.js 初始化数据
export const state={
// 本地存储有值,就取出来给user;没有,user={}
user:sessionStorage.getItem("user")?JSON.parse(sessionStorage.getItem("user")):{}
}
export const getters={
user(state){
return state.user
}
}
export const mutations={
changeUser(state,user){
state.user=user;
}
}
3.store/actions.js 处理逻辑 进行同步
export const actions={
changeUser(context,obj){
// 1.vuex存起来
context.commit("changeUser",obj)
// 2.本地存储 :vuex和本地存储同步
if(obj.token){
sessionStorage.setItem("user",JSON.stringify(obj))
}else{
sessionStorage.removeItem("user")
}
}
}
4.登录成功 login.vue 存值
methods: {
...mapActions({
changeUser: "changeUser",
}),
login() {
reqLogin(this.user).then((res) => {
if (res.data.code == 200) {
//存vuex 存本地存储
this.changeUser(res.data.list);
//跳转
this.$router.push('/home')
}
});
},
},
5.登出 mine,vue
computed: {
...mapGetters({
user:"user"
}),
},
methods: {
...mapActions({
changeUser:"changeUser"
}),
logout(){
this.changeUser({})
}
},
9.登录拦截【router/index.js】
import store from "../store"
//登录拦截
router.beforeEach((to,from,next)=>{
if(to.path=="/"){
next()
return;
}
//判断仓库user是否有token
if(store.getters.user.token){
next()
return;
}
next("/")
})
10.掉线处理[src/http/http.js]
import store from "../store"
import router from "../router"
// 3.请求拦截
axios.interceptors.request.use(config => {
if (config.url !== "/api/login" && config.url !== "/api/register") {
config.headers.authorization = store.getters.user.token
}
return config;
})
// 4.响应拦截
axios.interceptors.response.use(res => {
//打印
console.log("本次请求地址:" + res.config.url);
console.log(res);
//失败处理
if (res.data.code !== 200) {
Toast(res.data.msg)
}
//掉线处理
if(res.data.msg==="登录已过期或访问权限受限"){
router.replace("/login")
}
return res;
})
6.css预处理
1.创建项目 选择了预处理器(less)
2.创建less文件夹,处理预处理
-less
index.less //整合所有的less
color.less //颜色
size.less //大小
table.less //表格
form.less //表单
text.less //文本
3.组件使用
<style lang="less">
@import "../less/index.less";
.title{
font-size: @h3;
color: @primary;
margin: @margin;
}
img{
width: 100px;
height: 100px;
}
</style>
day15
1.vue初探
官网:https://cn.vuejs.org/
介绍:
vue是渐进式 JavaScript 框架
渐进式 :主张最少。
优点:
1.轻量级的数据框架
2.双向数据绑定
3.提供了指令
4.组件化开发
5.客户端路由
6.状态管理
缺点:
1.Vue 底层基于 Object.defineProperty 实现响应式,而这个 api 本身不支持 IE8 及以下浏 览器,所以Vue不支持IE8及其以下浏览器;
2.Vue 打造的是SPA,所以不利于搜索引擎优化(SEO);
3.由于 CSR(客户端渲染)的先天不足,导致首屏加载时间长,有可能会出现闪屏。
核心:
数据驱动 组件系统
MVVM:
M-model模型
V-view视图
VM-viewModel 视图模型
模型(model)通过了视图模型 决定了视图(view)
视图(view) 通过视图模型 修改模型 (model)
视图模型是模型和视图之间的桥梁。
SPA:
single page application 单页面应用
优点:加载快,用户体验好
缺点:不利于SEO,首屏加载时间长
a页面—>index.html/#/a
b页面—>index.html/#/b
MPA:
多页面应用
优点:有利于SEO
缺点:会有白屏,用户体验不好
a页面—>a.html
b页面—>b.html
day16
1.react介绍
1.1简介
React 是Facebook内部的一个JavaScript类库。
React 可用于创建Web用户交互界面。
React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式。
React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。
React 引入了虚拟DOM(Virtual DOM)的机制。
React 引入了组件化的思想。
React 使用Facebook专门为其开发的一套语法糖--JSX。
1.2优缺点
优点:
● React速度很快
react并不直接对DOM进行操作,引入了一个叫做虚拟DOM的概念,安插在javascript逻辑和实际的DOM之间,性能好。
● 跨浏览器兼容
虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。
● 一切皆是组件
代码更加模块化,重用代码更容易,可维护性高。
● 单向数据流
Flux是一个用于在JavaScript应用中创建单向数据层的架构,它随着React视图库的开发而被Facebook概念化。
● 同构、纯粹的javascript
因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。
缺点:
React不适合做一个完成的框架。
React本身只是一个V而已,并不是一个完整的框架,所以如果是大型项目想要一套完整的框架的话,基本都需要加上ReactRouter和Flux才能写大型应用。
1.3 react 解决了什么问题?
1.在组件化方面,react天生组件化,这是React的核心,除了能够在团队内部积累业务组件以外,也能找到众多开源组件的实现。
2.在模块化方便,基于webpack可以使用ES6或者CommonJs的写法实现模块化代码;
3.在开发效率方面,react的代码基本就是组件的组合,分而治之的方式让代码的可读性很高,容易理解。
而且相比于MVC几乎是祛除了Controller的角色,只用关心render函数,不用关心视图局部的修改;
4.在运行效率方面,React实现了Virtual DOM,相比较MVVM框架具有更优的效率;
5.在可维护性方面,React基于flux或者redux的架构设计,确定性的store很容易定位问题,无论是新增业务代码还是查找业务代码都不再是难题;
6.在用户体验方面,基于React很容易实现SPA,提高用户体验。
2.脚手架
//安装脚手架
npm i create-react-app -g
//创建项目
create-react-app demo //demo是项目名
//进入项目
cd demo
//启动
npm start
目录
-demo
-node_modules 依赖包
-public 静态资源
index.html
-src 源代码
index.js // 入口文件
app.js //根组件
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
App.js
function App() {
return (
<div className="App">
123
</div>
);
}
export default App;
3.JSX语法
1.非表单元素 div
{/* {} 可以绑定数据、方法、表达式 */}
<div>姓名:{name}</div>
<div>价格:{price.toFixed(2)}</div>
<div>{1 > 2 ? name : name2}</div>
2.属性绑定:img 属性绑定 <标签 属性名={变量}>
<img src={img} alt="" />
<a href={website.url} aaa={website.name}>
<img src={website.logo} alt="" title={website.name} />
</a>
3.条件渲染
let arr = [1, 2, 3, 4];
let isshow = false;
{/* 条件渲染 {三元判断} ,如果没有内容展示,就定为null*/}
{arr.length > 0 ? <div>有数据</div> : <div>暂无数据</div>}
{isshow ? <div>弹框</div> : null}
4.列表渲染
let news = [
{
id: 1,
name: "7.1建党",
con: "热泪庆祝建党100周年",
},
{
id: 2,
name: "马嘉祺学霸",
con: "分数307",
},
{
id: 3,
name: "上天",
con: "好多人上天",
},
];
{news.map((item) => {
return <div key={item.id}>{item.name}</div>;
})}
5.动态类名
{/* 动态类名 1.使用className代替class; 2.语法 className={三元} */}
<div className={10 > 11 ? "red" : "blue"}>小车车</div>
{news.map((item, index) => {
return (
<div key={item.id} className={index % 2 == 0 ? "red" : "blue"}>
{item.name}
</div>
);
})}
6.动态行间样式
let bg = "pink";
{/* 行间样式 语法:style={json} */}
<h3>行间样式</h3>
<div style={{ background: "#000", color: "#fff" }}>天道酬勤</div>
<div style={{ background: bg }}>商道酬信</div>
7.注释
{/* 注释 */}
- jsx 中遇到< html解析,遇到了{ js解析
- 如果你的js不能直接出一对标签,后缀名改为jsx;
4.组件
1.如何注册?
1.函数注册
function First(props) {
return (
<div className="box">
</div>
);
}
export default First;
2.类定义注册
import React,{Component} from "react"
class Second extends Component{
constructor(){
super();//构造函数必须加super()
}
//渲染钩子函数
render(){
return (
<div className="box">
<h3>this is second -{this.name}--{age}--{this.name2}--{name3}--{this.name4}</h3>
</div>
)
}
}
export default Second;
2.注意点
// 1.一个组件的模板,只能有一个根节点;
// 2.组件名首字母要大写
// 3.可以以已经存在的标签的大写命名。eg:Form
// 4.组件名中间有大写,原样调用即可。eg:WebsiteNav
// 5.注册组件:(1)函数定义组件 (2)类定义
3.类定义 VS 函数定义
(1)类定义组件 有生命周期,函数定义的没有;
(2)类定义有state,函数定义没有;
(3) 对于父组件传递的数据,类定义组件通过this.props接收,函数定义通过props接收。
(4)对于类定义,每调用一次,就会实例化一个对象,而对于函数组件,只是单纯的计算,所以函数的性能高于类定义。
home 类 业务组件
banner 函数 木偶组件
list 函数
5.事件处理
1.如何绑定事件?
{/* 1.如何绑定事件?
(1)箭头函数:不用管this
(2)bind: 第一个参数是调用该函数的对象,一般使用this.
*/}
<button onClick={() => this.fn1()}>箭头函数绑定fn1</button>
<button onClick={this.fn1.bind(this)}>fn1</button>
2.如何传参?
{/* 2.如何传参?
(1)箭头函数:正常传参
(2)bind: 第2个实参对应第1个形参
*/}
<button onClick={() => this.fn2(3, 5)}>箭头函数传参:3+5</button>
<button onClick={this.fn2.bind(this, 10, 20)}>bind传参:10+20</button>
3.event 事件对象如何获取?
{/* 3.event 事件对象如何获取?
(1)显示传参: 箭头函数,event想在哪一位就在哪一位
(2)隐式传参:bind 没有参数的第一位是event. event永远都在最后一位
*/}
<button onClick={(e) => this.getEvent(e)}>箭头函数获取event</button>
<button onClick={this.getEvent.bind(this)}>bind获取event</button>
<button onClick={(e) => this.getEvent2(10, e)}>
箭头函数获取event,10
</button>
<button onClick={this.getEvent2.bind(this, 10)}>bind获取event</button>
4.阻止默认 阻止传播?
{/* 4.阻止默认 阻止传播?
(1)阻止默认:e.preventDefault() 注意:return false 不可以;
(2)阻止传播:e.stopPropagation()
(3)捕获事件:Capture.eg:onClickCapture
*/}
<div className="redBox" onContextMenu={(e) => this.yj(e)}></div>
<div className="outer" onClick={() => this.outerClick()}>
<div className="redBox" onClick={this.innerClick.bind(this)}>
冒泡
</div>
</div>
<div className="outer" onClickCapture={() => this.outerClick2()}>
<div className="redBox" onClickCapture={() => this.innerClick2()}>
捕获
</div>
</div>
6.state
// 1.初始化在constructor
// 2.取值:var { name, age, sex } = this.state;
// 3.如果state要全部传递给子组件,可以使用 <Child {...this.state}></Child>
// 4.修改state数据需要调用setState(),
// 5.setState()的调用会引起render的重新执行,所以render中一定不可以调用setState(),否则会引起死循环
// 6.修改数组:不要直接操作数组。 1.取;2.做;3.放
// 7.修改json:建议使用 ...
// 8.setState()是异步的,如果想要获取修改后的值,需要在回调函数中获取
调用 setState 之后发生了什么?
在代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
7.面试题
1.react的优缺点
2.react中组件如何创建?
3.函数定义组件和类定义组件的区别?
4.setState()中第二个参数为什么是个回调函数?
5.setState()调用后发生了什么?
8.作业:
练习:3遍
day17
1.props
1.取值:let { name, age, sex,json:{x,y} ,changeWang} = this.props;
2.props不仅可以传递属性,也可以传递方法
3.组件嵌套的内容,在this.props.children { this.props.children}
4.super(props)的作用:(1) super() 用了继承,所以需要super();(2)super(props)为了在构造函数中使用this.props。
5.如果想把props全部向下传递,使用{...this.props}
state VS props
state:自己的状态,可以修改,而且需要通过setState()。state变了,页面会重新渲染
props:父组件传递过来的状态,不可以直接修改。props变了,页面会重新渲染
2.组件通信(父子)
父传子
父传子:父组件通过自定义属性传递数据,子组件通过props接收
父组件
<Child name={this.state.name}></Child>
子组件
let { name} = props;
子传父
子传父:父组件通过自定义属性传递了方法,子组件通过props调用
父组件
<Child
changeWang={() => this.changeWang()}
cname={(name) => this.changeName(name)}
></Child>
子组件
export default function Child(props) {
let { changeWang,cname } = props;
return (
<div className="box">
<h3>this is child</h3>
<button onClick={()=>changeWang()}>王昭君</button>
<button onClick={()=>cname('貂蝉')}>貂蝉</button>
</div>
);
}
3.生命周期
1.初始期
(1)constructor:初始化数据
(2)render:渲染DOM
初始期:渲染DOM节点; 更新期:调和过程,进行diff算法,计算出最优的更新方式,局部更新
(3)componentDidMount:渲染完成:计时器、请求打开,window|document添加事件、获取DOM
2.更新期(state、props)
shouldComponentUpdate() : 判断是否更新
(1)没有return内容,报错
(2)return true. 更新流程 shouldComponentUpdate->render->componentDidUpdate
(3) return false.更新流程:shouldComponentUpdate
render() 调和过程,进行diff算法,计算出最优的更新方式,局部更新
componentDidUpdate() 更新完成
3.销毁期
componentWillUnmount() 销毁之前:清除计时器 取消window|document事件
4.表单【受控组件 非受控组件】
1.受控组件
特点:
就地反馈,如:验证
禁用按钮 如:提交
执行特定的输入格式 如:身份证号、银行卡号
取值赋值
特征 | 取值 | 赋值 |
Input type=“text” | e.target.value | value |
Input type=“radio” | e.target.value | checked |
Input type=“checkbox” | e.target.value |e.target.checked | checked |
Select | e.target.value | Value |
Textarea | e.target.value | Value |
//3.修改user
changeUser(e, key) {
let value = e.target.value;
//如果是isAgree,需要用checked取值
if (key === "isAgree") {
value = e.target.checked;
}
//如果是爱好,处理一段逻辑
if (key === "hobby") {
value = this.state.user.hobby;
// 如果点的框选中,user.hobby添加一条数据;如果从选中到取消,删除这条数据
if (e.target.checked) {
value.push(parseInt(e.target.value));
} else {
//将value中数据和parseInt(e.target.value)一样的那条数据删除
value.splice(
value.findIndex((item) => item === parseInt(e.target.value)),
1
);
}
}
//电话号处理
if (key === "tel") {
let {
user: { tel },
} = this.state;
// 原来的是小于3位,+空格;如果原来的是大于3位,不+空格
if (value.length === 3 && tel.length < 3) {
value += " ";
}
// 原来的是小于8位,+空格;如果原来的是大于8位,不+空格
if (value.length === 8 && tel.length < 8) {
value += " ";
}
// 如果大于13位,就是13位
if (value.length > 13) {
value = value.slice(0, 13);
}
}
this.setState({
user: {
...this.state.user,
[key]: value,
},
});
}
2.各个表单使用
<div>
{/* 对于单选框来说,需要自己手动设置value;赋值使用的是checked */}
性别:
<input
type="radio"
name="sex"
onChange={(e) => this.changeUser(e, "sex")}
value="0"
checked={user.sex === "0"}
/>
男
<input
type="radio"
name="sex"
onChange={(e) => this.changeUser(e, "sex")}
value="1"
checked={user.sex === "1"}
/>
女
</div>
<div>
{/* 多选框,取值checked+value;赋值:checked */}
爱好:
{hobbyList.map((item) => (
<label key={item.id}>
<input
type="checkbox"
value={item.id}
onChange={(e) => this.changeUser(e, "hobby")}
checked={user.hobby.includes(item.id)}
/>
{item.name}
</label>
))}
</div>
<div>
{/* select 取值 value;赋值 value */}
职业:
<select onChange={(e) => this.changeUser(e, "job")} value={user.job}>
<option value="" disabled>
--请选择--
</option>
{jobList.map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
</div>
<div>
{/* textarea 取值value;赋值 value */}
描述:
<textarea
ols="30"
rows="10"
onChange={(e) => this.changeUser(e, "des")}
value={user.des}
></textarea>
</div>
3.对比受控和非受控组件
特征 | 受控组件 | 非受控组件 |
一次性检索(例如表单提交) | 是 | 是 |
及时验证 | 是 | 否 |
有条件的禁用提交按钮 | 是 | 否 |
执行输入格式 | 是 | 否 |
一个数据的几个输入 | 是 | 否 |
动态输入 | 是 | 否 |
5.安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
day18
1.ref
React.createRef()
(1)创建一个ref对象;(2)将ref对象绑定到节点(3)操作 this.div.current
1.获取原生DOM节点
constructor() {
super();
// 创建一个ref对象
this.div = React.createRef();
}
<div className="red" ref={this.div}></div>
changeColor(color) {
let div = this.div.current;
div.style.background = color;
div.innerHTML = color;
}
2.获取子组件实例
constructor() {
super();
// 创建一个ref对象
this.child=React.createRef()
}
<Child ref={this.child}></Child>
changeChildName(name){
this.child.current.changeName(name)
}
2.组件优化
1.Fragment
目的:所有的组件都有一个根节点,但是有时候希望这个节点不存在,就可以使用Fragment
1.Fragment
引入 使用
import React, { Component,Fragment } from 'react';
class First extends Component {
render() {
return (
<Fragment>
<h3>fragment</h3>
<p>哈哈</p>
<p>嘿嘿嘿</p>
</Fragment>
);
}
}
export default First;
2.<></>
import React, { Component } from 'react';
class First extends Component {
render() {
return (
<>
<h3>fragment</h3>
<p>哈哈</p>
<p>嘿嘿嘿</p>
</>
);
}
}
export default First;
2.需求:父组件给子组件传了数据,父组件没有传递给子组件的数据变化了,也会引起子组件的重新渲染,我们希望子组件不要渲染,如果是子组件接收的数据 变了,子组件才重新渲染。
1.shouldCompnentUpdate-类定义组件
shouldComponentUpdate(nextProps,nextState){
// 判断旧的props上的name是否和新的props上的name一样。如果一样,就不渲染了
if(this.props.name===nextProps.name){
return false
}
return true;
}
2.pureComponent -类定义组件
1.PureComponent 浅比较,如果传递的数据是引用类型,引用类型的改变,建议需要使用 拷贝
2.PureComponent 如果props发生了改变,会计算,如果是state发生了改变,也会计算。
如果一个组件有state,建议使用Component +shouldComponentUpdate
如果一个组件只有props,建议使用PureComponent 【木偶组件 】
import React, { PureComponent } from 'react';
class Child2 extends PureComponent {
render() {
console.log("child2 render");
let {name,arr}=this.props
return (
<div className="box">
<h3>PureComponent</h3>
<div>name:{name}</div>
<div>arr:{JSON.stringify(arr)}</div>
</div>
);
}
}
export default Child2;
3.React.meno()-函数定义组件
React.memo() 浅比较,如果传递的数据是引用类型,引用类型的改变,建议需要使用 拷贝
React.memo() 本身是一个函数, 参数是个组件,返回一个新的组件,这样的函数叫高阶组件(HOC)
function Child3(props) {
let { name ,arr} = props;
console.log("child3 开始计算");
return (
<div className="box">
<h3>child3</h3>
<div>name:{name}</div>
<div>arr:{JSON.stringify(arr)}</div>
</div>
);
}
export default React.memo(Child3)
3.componentDidCatch 错误边界处理
1.封装了一个组件ErrorBoundary.jsx
import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor(){
super()
//初始认为没有报错
this.state={
hasError:false
}
}
componentDidCatch(){
//此时,有报错了
this.setState({
hasError:true
})
}
render() {
let {hasError}=this.state
return (
<div>
{
hasError?<div>此处有报错!!</div>:this.props.children
}
</div>
);
}
}
export default ErrorBoundary;
2.使用组件ErrorBoundary 包裹可能出错的组件
<ErrorBoundary>
<List></List>
</ErrorBoundary>
4.HOC 高阶组件
HOC 高阶组件:增强原来的组件
1.本身是个函数
2.参数 是个组件
3.返回值也是个组件
封装的withRequest.js
import React, { Component } from "react"
import axios from "axios"
export default url => C => {
return class MyCom extends Component {
constructor() {
super()
this.state = {
arr: []
}
}
componentDidMount() {
axios({
url: url
}).then(res => {
this.setState({
arr: res.data.d
})
})
}
render() {
let { arr } = this.state
return (
<>
<C arr={arr}></C>
</>
)
}
}
}
调用
let RequestList=withRequest("/mock/like.json")(List)
let RequestBanner=withRequest("/mock/banner.json")(Banner)
day19
1.路由
1.路由模式 【hash history】
import {HashRouter,BrowserRouter} from "react-router-dom"
ReactDOM.render(
<HashRouter>
<App />
</HashRouter>
,
document.getElementById('root')
);
2.路由出口【Switch】
import {Switch} from "react-router-dom"
<Switch></Switch>
3.路由规则【Route】
import {Route} from "react-router-dom"
<Route exact strict path="/register" component={Register}></Route>
<Route path="/index" component={Index}></Route>
Route 的属性 :exact[是否精确匹配] 默认:false。 如果要精确匹配,需要设置exact
strict:严格模式。 需要搭配exact使用。 默认是路径后可以加'/',也可以访问,加上严格模式,有'/'就不行
4.重定向【Redirect】
import {Redirect } from "react-router-dom"
{/* 4.重定向 */}
<Redirect to="/"></Redirect>
5.路由导航【Link NavLink(activeClassName activeStyle)】
<Link to="/search">搜索</Link>
<NavLink to="/search">搜索</NavLink>
高亮效果:
<footer className="index-footer">
<NavLink to="/index/home" activeClassName="select">首页</NavLink>
<NavLink to="/index/cate" activeClassName="select">分类</NavLink>
<NavLink to="/index/shop" activeClassName="select">购物车</NavLink>
</footer>
<footer className="index-footer">
<NavLink to="/index/home" activeStyle={{color:"orange"}}>首页</NavLink>
<NavLink to="/index/cate" activeStyle={{color:"orange"}}>分类</NavLink>
<NavLink to="/index/shop" activeStyle={{color:"orange"}}>购物车</NavLink>
</footer>
6.编程式导航【push replace go 】
this.props.history.push("/search"); //添加新的历史记录
this.props.history.replace("/search"); // 用新的历史记录替换当前历史记录
this.props.history.goBack(); //返回
this.props.history.go(-1);// 返回
注意:
1.如果是路由组件,可以直接使用编程式导航;
2.如果不是路由组件,想要使用编程式导航,有2种方式:
① 路由组件传递props给非路由组件;
<GoBack {...this.props}></GoBack>
②使用withRouter
withRouter
import React, { Component } from 'react';
import {withRouter} from "react-router-dom"
class GoBack extends Component {
goBack(){
console.log(this.props);
// this.props.history.goBack()
this.props.history.go(-1)
}
render() {
return (
<button onClick={()=>this.goBack()}>封装的返回</button>
);
}
}
export default withRouter(GoBack);
7.路由传参【?】
<Link to={`/cateList?id=1&name=222`}>{item.name}</Link>
取参数:
1.原生js
componentDidMount(){
let str=this.props.location.search;//"?id=2&name=qqq&age=122" --{id:"2",name:"qqq",age:"122"}
// 1.利用原生js
let substr=str.slice(1);//"id=2&name=qqq&age=122"
let arr=substr.split("&");// ['id=2','name=qqq','age=122']
let result={}
arr.forEach(item=>{
let subArr=item.split("=");//["id","2"]
result[subArr[0]]=subArr[1]
})
console.log(result);
}
2.node questring
import querystring from "querystring"
componentDidMount(){
let str=this.props.location.search;//"?id=2&name=qqq&age=122" --{id:"2",name:"qqq",age:"122"}
// 2.node querystring.parse()
let result=querystring.parse(str.slice(1))
console.log(result);
}
3.URLSearchParams
componentDidMount(){
let str=this.props.location.search;//"?id=2&name=qqq&age=122" --{id:"2",name:"qqq",age:"122"}
// 3.原生js
let params=new URLSearchParams(str);
console.log(params.get("id"));
console.log(params.get("name"));
}
8.动态路由【:】
<Link to={`/detail/1`}>{item.name}</Link>
<Route path="/detail/:id" exact component={Detail}></Route>
let id=this.props.match.params.id
9.全局守卫【登录拦截】
1.登录成功的时候设置一个标识【login.jsx】
login = () => {
let {
user: { phone, password },
} = this.state;
if (phone === "admin" && password === "123") {
//存一个标识,用来判断是否登录
sessionStorage.setItem('islogin',1)
this.props.history.push("/index/home");
} else {
alert("error");
}
};
2.封装了一个PrivateRoute ,判断是否登录,觉得出规则还是重定向
import React, { Component } from 'react';
import {Route,Redirect} from "react-router-dom"
class PrivateRoute extends Component {
render() {
let islogin=sessionStorage.getItem("islogin");//'1' null
return (
<>
{
islogin?<Route {...this.props}></Route>:<Redirect to="/login"></Redirect>
}
</>
);
}
}
export default PrivateRoute;
3.需要拦截的用PrivateRoute写规则【App.jsx】
<Switch>
{/* 路由规则 */}
<Route path="/login" component={Login}></Route>
{/* exact 精确匹配,默认false,如果没设,那么‘/register/a’也会进入'/register'
strict 严格模式,默认false,如果没有设置,那么“/register/”是可以访问的;如果设置了严格模式,只能访问“/register”
strict 需要和exact 一起使用
*/}
<Route path="/register" exact strict component={Register}></Route>
<PrivateRoute path="/index" component={Index}></PrivateRoute>
<PrivateRoute path="/search" component={Search}></PrivateRoute>
<PrivateRoute path="/list" component={List}></PrivateRoute>
{/* exact精确匹配 */}
<PrivateRoute path="/detail/:id" exact component={Detail}></PrivateRoute>
{/* 重定向 */}
{/* <Redirect to="/login"></Redirect> */}
{/* 404 404千万不要设置exact,这句写在最后*/}
<Route path="/" component={NotFound}></Route>
</Switch>
10.路由独享守卫
<Route path="/search" render={(props)=>{
let type=sessionStorage.getItem("type")
if(type==="2"){
return <Search {...props}></Search>
}else{
return <div>你没有权限 !!!!</div>
}
}}></Route>
11.懒加载
1.通过React.lazy()引入组件
let Login=React.lazy(()=>import("./pages/Login/Login"))
let Index=React.lazy(()=>import("./pages/Index/Index"))
2.需要将规则包裹在React.Suspense 组件中,fallback必填
// 2.React.Suspense fallback必须的
<React.Suspense fallback={<div>正在加载。。。</div>}>
{/* 2.路由出口 */}
<Switch>
<Route exact path="/" component={Login}></Route>
<Route path="/index" component={Index}></Route>
</Switch>
</React.Suspense>
2.数据交互
1.下载
npm i axios --save
2.配置代理
package.json 注意:配置完代理需要重启项目
{
"proxy":"http://localhost:3000",
"name": "luyou",
"version": "0.1.0",
}
3.使用
略
3.UI库
1.ant design (PC)
2.ant design mobile (移动端)
1.安装
npm install antd-mobile --save
2.修改index.html
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
if(!window.Promise) {
document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"'+'>'+'<'+'/'+'script>');
}
</script>
3.入口文件引入样式
import 'antd-mobile/dist/antd-mobile.css';
4.使用
4.作业
1.到数据库创建一个数据库,运行orange_data.sql文件
2.修改后端的配置 config/global.js
// 数据库连接参数
exports.dbConfig = {
host: 'localhost', //数据库地址
user: 'root',//数据库用户名
password: '1234',//数据库用户密码
port: 3306,
database: '0308_orange' // 数据库名字
}
3.启动
npm i
npm start //localhost:3000
day20
Redux
1.目的
1.非父子组件通信;
2.组件层和状态层解耦。
2.三大原则
1.单一数据源
2.state是只读的
3.只能通过纯函数修改state
3.目标
React(组件) ----》react-redux《---- redux(状态) ------》redux-thunk 《------ 后端(数据)
4.redux
0.安装
npm i redux --save
1.创建仓库
//引入方法
import { createStore } from "redux"
//初始状态
const initState = {
name: "妲己",
age: 20
}
//reducer用来修改state
/**
*
*
* @param {*} state 上一次修改完成后的值,对于第一次来说,没有上一次,默认是初始值
* @param {*} action 规则-动作
* {type:"changeName",name:"王昭君"}
* {type:"changeAge",age:100}
*/
function reducer(state = initState, action) {
switch (action.type) {
case "changeName":
state.name = action.name;
return state;
case "changeAge":
state.age = action.age;
return state;
default:
return state;
}
}
// 创建仓库
let store = createStore(reducer)
//导出仓库
export default store;
2.组件使用
1.store.getState() 取状态的
2.store.dispatch(action) 派发动作 派发任务
this.un= store.subscribe(callback) 添加订阅者
4.this.un() 取消订阅者
3.action creattors
//action creator [vuex actions] 方便组件派发任务
export let actions = {
changeName: (name) => ({ type: types.CHANGE_NAME, name: name }),
changeAge: (age) => ({ type: types.CHANGE_AGE, age })
}
<button onClick={()=>store.dispatch(actions.changeAge(200))}>age=200</button>
4.action types
// action types
const types = {
// 修改name
CHANGE_NAME: "CHANGE_NAME",
// 修改age
CHANGE_AGE: "changeAge"
}
//action creator [vuex actions] 方便组件派发任务
export let actions = {
changeName: (name) => ({ type: types.CHANGE_NAME, name: name }),
changeAge: (age) => ({ type: types.CHANGE_AGE, age })
}
function reducer(state = initState, action) {
switch (action.type) {
case types.CHANGE_NAME:
state.name = action.name;
return state;
case types.CHANGE_AGE:
return {
...state,
age: action.age
};
default:
return state;
}
}
5.react-redux 关联react和redux
1.安装
npm i react-redux --save
2.通过Provider将store和App关联【入口文件index.js】
import store from "./store"
import {Provider} from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
,
document.getElementById('root')
);
3.容器型组件 connect
import React, { Component } from "react";
//容器型组件:通过connect函数将redux中的数据和方法导入到该组件中
import { connect } from "react-redux";
import { actions } from "../store";
class D extends Component {
render() {
console.log(this.props);
let { name, age, changeName, changeAge } = this.props;
return (
<div className="box">
<h1>this is D --react-redux</h1>
<div>name:{name}</div>
<div>age:{age}</div>
<button onClick={() => changeName("宫本")}>宫本</button>
<button onClick={() => changeAge(300)}>age=300</button>
</div>
);
}
}
let mapStateToProps = (state) => {
return {
name: state.name,
age: state.age,
};
};
let mapDispatchToProps = (dispatch) => {
return {
changeName: (name) => dispatch(actions.changeName(name)),
changeAge: (age) => dispatch(actions.changeAge(age)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(D);
mapStateToProps
参数:store.getState()
返回:一个集合,这个集合会被放到props上
目的:从仓库取数据
mapDispatchToProps
参数:store.dispatch()
返回:一个集合,这个集合会被放到props上
目的:从仓库调用action
4.selectors [vuex getters]
export const getName = state => state.name;
export const getAge = state => state.age
组件使用
let mapStateToProps = (state) => ({
name: getName(state),
age: getAge(state),
});
5.bindActionCreators
目的:集中导入所有的actions到组件
import { actions } from "../store";
import { bindActionCreators } from "redux";
let mapDispatchToProps = (dispatch) => ({
// changeName: (name) => dispatch(changeName(name)),
// changeAge: (age) => dispatch(changeAge(age)),
methods: bindActionCreators(actions, dispatch),
});
组件使用:
let {
methods: { changeName, changeAge },
} = this.props;
6.容器型组件 vs 展示型组件
容器型组件: (1)对redux是有感知的;(2)数据和方法来自store;(3)类定义组件;(4)路由组件
展示型组件:(1)对redux是无感知的; (2) 数据和方法来自父组件;(3)函数定义组件;(4)木偶组件
7.reselect
npm i reselect --save
使用
import { createSelector } from "reselect"
//导出订单
export const getOrders = state => state.orders;
//导出刷选条件
export const getFilter = state => state.filter;
//reselect 可以减少不必要的计算,只有依赖的数据变了,才计算
export const getShowOrder = createSelector(
[getOrders, getFilter],
(orders, filter) => {
console.log("重新计算展示的订单");
return filter === 0 ? orders : orders.filter(item => item.status === filter)
}
)
day22
Hooks 16.8之后
函数定义组件:state 生命周期. hooks只能在函数定义组件中使用,函数组件名首字母要大写。
useState()
useEffect()
useReducer()
useContext()
1.useState() 状态
const [name, setName] = useState("妲己");
2.useEffect() 生命周期
1.useEffect(callback) callback就是componentDidMount +componentDidUpdate.任何数据变了,都会触发componentDidUpdate
useEffect(() => {
document.title = "你点了" + num + "次";
});
2.useEffect(callback,[]) callback是componentDidMount,和后面数组中数据发生改变的时候的更新
如果设置[],那么任何数据的改变,都不会再引起执行,就相当于是componentDidMount()
任何数据变了,都不会再触发componentDidUpdate
useEffect(() => {
setInterval(()=>{
setDate(new Date())
})
document.title = "你点了" + num + "次";
}, []);
3.num变了,才会触发componentDidUpdate
useEffect(() => {
console.log("要重新执行了");
document.title = "你点了" + num + "次";
},[num])
4.useEffect(()=>{ return fn },[]) return的函数就是componentWillUnmount
useEffect(() => {
let time = setInterval(() => {
setDate(new Date());
});
return ()=>{
clearInterval(time)
}
}, []);
3.useReducer()
状态很多,可以使用useReducer()代替useState()
import React,{useReducer} from 'react';
const initState={
name:"妲己",
age:20
}
const reducer=(state,action)=>{
switch(action.type){
case "changeName":
// {type:"changeName",name:'xx'}
return {
...state,
name:action.name
}
case "changeAge":
// {type:"changeAge",age:100}
return {
...state,
age:action.age
}
default:
return state;
}
}
const Reducer = () => {
const [state, dispatch] = useReducer(reducer, initState)
return (
<div>
<div>name:{state.name}</div>
<div>age:{state.age}</div>
<button onClick={()=>dispatch({type:"changeName",name:'王昭君'})}>王昭君</button>
<button onClick={()=>dispatch({type:"changeAge",age:100})}>age=100</button>
</div>
);
}
export default Reducer;
4.useContext()
父组件给子组件传值,子组件没用,子组件传递给了自己的子组件,这样传递,容易出错,可以使用useContext() 让父组件直接将数据传递给子组件的子组件。
1.父组件
import React,{useState} from 'react';
import Child from './Child';
//1.创建一个Context对象
export let MyContext=React.createContext();
const Context = () => {
const [name, setName] = useState('妲己');
const [age, setAge] = useState(20);
return (
<div className="box">
<h3>useContext</h3>
<div>name:{name}</div>
{/* 2.传值 */}
<MyContext.Provider value={ {name,age} }>
<Child ></Child>
</MyContext.Provider>
</div>
);
}
export default Context;
2.子组件 (什么都没做)
import React from 'react';
import ChildChild from './ChildChild';
const Child = () => {
return (
<div className="box">
<h3>子组件</h3>
<ChildChild></ChildChild>
</div>
);
}
export default Child;
3.子组件的子组件
import React, { useContext } from "react";
// 3.引入Context对象
import { MyContext } from "./Context";
const ChildChild = () => {
// 4.取出数据
const { name, age } = useContext(MyContext);
return (
<div className="box">
<h3>child child</h3>
<div>name:{name}</div>
<div>age:{age}</div>
</div>
);
};
export default ChildChild;