toxicfy / combing-notes Goto Github PK
View Code? Open in Web Editor NEW📎issue/project笔记存档
📎issue/project笔记存档
目前来说处理数据的方法会很多,包括
ES6
中有迭代器的概念用于统一接口处理处理不同结构;所以这里不会依次罗列,讲的一些是比较常用的,当然语意明确,书写简洁同样是选择方法的指标之一
初始的方法中例如
push
,unshift
等方法是修改原数组返回的是数组的长度;现在解构赋值可以帮我们完成数据的浅拷贝并返回新的数据,要求不改变原数组的推荐使用:
const original = ['node','javascript','http'];
const originalUnshift = [...original, 'vue']; // unshift
const originalPush = ['react',...original]; // push
const originalConcat = original.concat(['typeScript','deno'])
//👍👍推荐:可以在任意位置添加及删除元素(startNum,delNum,addItem)
let list = ['Jan', 'March', 'April', 'June'];
list.splice(1,1,'newItem'); //list: ['Jan', 'newItem', 'April', 'June']
//Array.prototype.includes(searchElement, fromIndex);
let nums = [1, 2, 3],
pets = ['cat', 'dog', 'bat'];
nums.includes(1,2); //false
pets.inclides('cat'); //true
//原来多采用 indexOf() === -1 进行判断
const posts = [
{id: 13, title: 'Title 221'},
{id: 5, title: 'Title 102'},
{id: 131, title: 'Title 18'},
{id: 55, title: 'Title 234'}
];
// 找到id为131的元素
const requiredIndex = posts.findIndex(obj => obj.id === 131);
console.log(requiredIndex); //2
filter、find、map、reduce、every
🔥🔥🔥filter: 筛选数组或类数组,过滤掉不符合筛选条件的元素并返回符合条件的数组
// Array.filter(callback(element,index,prevArr),[,第二个参数是callback绑定的this])
let prevArr, filterArr;
prevArr = [1, 2, 3, 4, 5, 6]
filterArr = prevArr.filter(ele => ele % 3 === 0); //[3,6]
🔥🔥find: 返回数组或类数组中符合条件的第一个元素(参数同上)
let prevObjectList, filterObjectName;
prevObjectList = [
{name: 'tom', age:19},
{name: 'jack', age:12},
{name: 'lucy', age:21},
]
filterObjectName = prevObjectList.find(ele => ele.age < 18).name; //jack
🔥🔥🔥 🔥 map:每个元素都会触发这个函数并返回对应的结果,类似所有元素的一个转换器
let arr, doubleArr;
arr = [1, 2, 3, 4, 6, 7];
doubleArr = arr.map(i => i*2); // doubleArr [2, 4, 6, 8, 12, 14],而不会对Arr造成以影响;
示例:
实现数组的小写且不影响到原来数组:
//这样的代码我们的项目里面也是最多的😐😐😐
let validData, lowercaseEmails,
mixedEmails = ['[email protected]', '[email protected]', '[email protected]'];
function getEmailsInLowercase(emails) {
lowercaseEmails = [];
for (let i = 0; i < emails.length; i++) {
lowercaseEmails.push(emails[i].toLowerCase());
}
return lowercaseEmails;
}
validData = getEmailsInLowercase(mixedEmails);
更清晰的一些:
//我们的代码中也有,替换循环(主要为使用forEach和map)😅😅😅
let validData, lowercaseEmails,
mixedEmails = ['[email protected]', '[email protected]', '[email protected]'];
function getEmailsInLowercase(emails) {
lowercaseEmails = [];
//简洁性更强,同时避免了添加索引记录访问位置
emails.map( email => {
lowercaseEmails.push(email.toLowerCase());
});
return lowercaseEmails;
}
validData = getEmailsInLowercase(mixedEmails);
但实际上这依旧是不ok
的,主要是命令式编程涉及的细节太多了;细节如下:我需要取出列表中第一个元素,然后把它转换成小写,再存储到另一个列表中,最后返回这个列表;
实际上我们并不关心这个过程,而应该是:“我有混合大小写的邮件列表,我需要一个全是小写的邮件列表,这要写一个能够进行小写转换的函数“,这样可读性才会提高;
let mixedEmails = ['[email protected]', '[email protected]', '[email protected]'], validData;
function downcase(str) {
return str.toLowerCase();
}
validData = mixedEmails.map(downcase);
🔥🔥🔥 🔥 reduce: 将N个数据进行处理后返回一个实例:这个用的也超多
//Array.prototype.reduce(callback [, initialValue (初始值)])
const posts = [
{id: 1, upVotes: 2},
{id: 2, upVotes: 89},
{id: 3, upVotes: 1}
];
const totalUpvotes = posts.reduce((totalUpvotes, currentPost) =>
totalUpvotes + currentPost.upVotes, //reducer 回调函数
0 // 初始化投票数为0
);
console.log(totalUpvotes)//输出投票总数:92
//其中callback()中第一个参数是上一次调用回调时返回的值,第二个值是数组中正在处理的元素;
🔥🔥 every: 有些判断场合用起来不错
//每个元素都满足条件才会返回true:
let judge = [1, 2, 3, 4, 5].every( val => val > 1); // false
目前已经在公司正式工作一个月了,但是在开发过程中没有严格的代码的规范,所以在这里主要是结合这个月中写代码的一些不好的情况做了一个规范的整理,希望可以督促自己改良代码风格
javascript
部分全局变量的问题在于,你的JavaScript
应用程序和web
页面上的所有代码都共享了这些全局变量,他们住在同一个全局命名空间,所以当程序的两个不同部分定义同名但不同作用的全局变量的时候,命名冲突在所难免。
避免创建隐式全局变量
可移植性同样是避免全局变量的原因:比如把一个方法移植到另一个命名空间里面(或者是另一个文件中)来完成功能整合的时候, 可能我们关注的只是方法名没有重复,但是内部创建的隐式全局变量可能会造成很大的影响
function sum(x, y) {
// 不推荐写法: 隐式全局变量
result = x + y;
// let result = x + y;
return result;
}
此段代码中的result
变量没有声明。代码照样运作正常,但在调用函数后你最后的结果就多一个全局命名空间,这可能是一个问题的根源
var a = b = 0;
// => 从右到左的赋值的规则 var a = (b = 0);
b = 0;
var a = b;
提议:
let
、val
语句进行的批量声明变量function myFunc() {//function body
var a = null,
b = Math.random(),
c = "value";
//优点:单一的地方去寻找功能所需要的所有局部;
// 避免在定义之前使用的逻辑错误(提升可以访问,但是赋值为undefined)
}
for (let i = 0, max = myArray.length; i < max; i++) {
// 使用myArray[i]
}
js
是非常动态的脚本语言,当缺乏注释,在重构代码(或者说阅读其他人代码)时,经常遇到的一个问题是在运行到这里时,这个应该是什么类型,这种状态下取什么值?本质上来说还是程序往往是围绕数据编程
所以通常应该记录是函数(它们的功能简述
和参数
以及返回值
)或是其中特殊的;仅需要注释和函数属性名来理解代码内容;
最好在时间充裕且重要部分写行注释,
最重要的习惯,然而也是最难遵守的,就是保持注释的及时更新,因为过时的注释比没有注释更加的误导人。
命名方式:使用普通函数使用小驼峰式的命名,造函数(class)使用大驼峰式命名;
建议规则:前缀为动词,
can
(返回一个布尔值判断是否可以进行)has/is
(返回一个布尔值判断是否存在/为某个值)get/set
(返回目标对象)//是否可以删除
function canDelete(){
return true;
}
//获取姓名
function getName{
return this.name
}
//创建类
class PersonHabitAll{
constructor(sleepTime,diet){
this.sleepTime = sleepTime;
this.diet = diet;
}
}
maxNumberOfTeamMembers
这样也不易读取不好的变量名
id
num1
/num2
好的变量名
userId
/fileId
maxNum
/minNum
up
/down
、begin
/end
、opened
/closed
、visible
/invisible
、scource
/target
,对仗明确可以让人很清楚地知道两个变量的意义和用途命名方法 : 全部大写
命名规范 : 使用大写字母和下划线来组合命名,下划线用以分割单词
const COUNT = 100;
const REQUEST_URL = '192.168.10.9';
ps:这个还是很有必要的,我们常见的情况是给一个函数传递不同的的时候传递一个数字作为不同的分类,这个时候往往会出现必须写较多的注释才能缕清结构,所以建议创建成常量,这个在build
的很多文件中都有体现;
//var function
//let const
//这样的代码实际上会让人感觉很困惑,这些都是什么
someThingHandler.fnTrigger("handlerName", 3, true);
//所以可以使用常量的方式进行书写
const ACTIVE_STATUS = {
ACTIVE_DEFAULT: 0,
DOT_ACTIVE: 1,
LINE_ACTIVE: 2,
FACE_ACTIVE: 3,
//...
}
someThingHandler.fnTrigger("handlerName", ACTIVE_STATUS.FACE_ACTIVE, true);
假值的判断
在我们使用判断某个状态的时候,不仅仅是通过类似canDoSomething
的函数直接拿到返回值的情况,往往也会遇到判断某个数据类型进行作为判断语句:下面就是所有在javascript
中判断为否的情况:
false
/null
/undefined
/0
/NaN
/'' (空字符串)
三元条件判断
if (x === ACTIVE_STATUS.ACTIVE_DEFAULT) {
return false;
} else {
return true;
}
//推荐使用
return x === ACTIVE_STATUS.ACTIVE_DEFAULT ? false : true
隐式转换
这个本身没有什么问题,但是因为目前来说我们对隐式转换并不是所有的都能掌握,所以还是尽量在做判断条件的时候进行避免这种情况,使用===
也可以直接了解到该值的数据类型
null == undefined //true
'' == '0' //false
0 == '' //true
0 == '0' //true
new String("abc") == "abc" //true
new Boolean(true) == true //true
true == 1 //true
在自己对转换有有一定的掌控力的时候吧,就可以自己使用进行转换( ps:如果有类型转换自己做类型转换,不要让别人去猜这里面可能类型转换),可以更加方便
//字符串转换为整型
let value = "11";
let newPrice = +value;
value === 11 //true
//判断其他类型为boolean,使用 !!
if(!!someTypeValue) {
//dosomething
}
在代码的编写中会频繁的遇到拷贝的情况:由于基本数据类型值不可变,每次赋值都是创建一个新的数据,所以对于基本类型就不需要注意太多(当然需要注意比较是值的比较,只要它们的值相等就认为它们就是相等);
这里主要提一下引用类型
var a = [666,999];
a[1] = 2333;
console.log(a[1]); // 2333
var a = [666,999];
var b = [666,999];
a === b //false
继续看的话:
var a = {}; // a保存了一个空对象的实例
var b = a; // a和b都指向了这个空对象
a.name = 'tom';
console.log(a.name); // 'tom'
console.log(b.name); // 'tom'
console.log(a == b);// true
实际上这个只是把a的引用复制了一份并传递给b,使得b同样指向了a所指向的那个引用类型(对象),这实际也不是拷贝,只是传递地址;
在js中的拷贝改变新数据都不会使得原数据一同改变,只是在于深浅,也就是常说的深拷贝和浅拷贝;
它们的区别也就在于属性的复制层数
浅拷贝的话会将各个属性进行依次复制,并不会进行递归复制,也就是说只会赋值目标对象的第一层属性;
深拷贝的话不只拷贝目标对象的第一层属性,而是递归拷贝目标对象的所有属性;
这里贴出jq-extend的源码和注释
jQuery.extend = jQuery.fn.extend = function() {
var options,
name,
src,
copy,
copyIsArray,
clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// 如果第一个参数是布尔值,则为判断是否深拷贝的标志
if (typeof target === "boolean") {
deep = target;
// 跳过 deep 标志,留意上面 i 的初始值为 1 ,重新给目标进行赋值
target = arguments[i] || {};
// i 自增 1,== 2
i++;
}
// 判断 target 是否为 object / array / function 以外的类型
if (typeof target !== "object" && !isFunction(target)) {
target = {}; // 如果是其它类型,则强制重新赋值为新的空对象
}
// 如果只传入1个参数;或者是传入2个参数,第一个参数为 deep ,第二个为 target
// 所以 length 的值可能为 1 或 2,但无论是 1 或 2,下段 for 循环只会运行一次
if (i === length) {
// 将 jQuery 本身赋值给 target
target = this;
// i 自减 1,可能的值为 0 或 1
i--;
}
for (; i < length; i++) {
// 以下拷贝操作,只针对非 null 或 undefined 的 arguments[i] 进行
if ((options = arguments[i]) != null) {
// Extend the base object
for (name in options) {
src = target[name];
copy = options[name];
// 避免死循环的情况
if (target === copy) {
continue;
}
// Recurse if we're merging plain objects or arrays
// 如果是深拷贝,且copy值有效,且copy值为纯object或纯array
if (
deep &&
copy &&
(jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))
) {
if (copyIsArray) {
// 数组情况
copyIsArray = false;
clone = src && Array.isArray(src) ? src : [];
} else {
// 对象情况
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// 克隆copy对象到原对象并赋值回原属性,而不是重新赋值
// 递归调用
target[name] = jQuery.extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
//返回修改后的目标
return target;
};
当然我们想要实现拷贝的话有几种方案
//object
let copy = Object.assign({}, obj);
//Array.prototype.concat()/Array.prototype.slice()
let newArr = oldArr.concat()
let newArr2 = oldArr2.slice()
//使用函数
function shallowClone (source){
if(!source || typeof source != 'object'){
return;
}
var targetObj = source.constructor === Array ? [] : {};
for(var keys in source) {
if(source.hasOwnProperty(keys)){//此处避免遍历到原型上的方法
targetObj[keys] = source[keys];
}
}
return targetObj;
}
//JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象,通过这两个方法,也可以实现对象的深复制
var dest = JSON.parse(JSON.stringify(target));
//jquery
let obj2 = $.extend(true, {}, obj1);
//lodash
var lodash = require('lodash');
var oldObj = {
a: [1],
b: { f: { g: 1 } },
};
var newObj = lodash.cloneDeep(oldObj);
说了这么多关于拷贝,实际是在我们进行引用类型赋值的时候要知道一个=
仅仅是指针的复制,和原对象依旧共用一个内存地址,修改新的内容也会修改原对象,这点需要注意,如果想要不互相影响,就使用拷贝的方式。
CSS
部分仅和样式相关的避免使用驼峰命名和
_
的方式命名,而是用-
进行单词的连接;
ID和class的名称总是使用可以反应元素 目的和用途 的名称
//应当避免名字去反映自己写的css规则; 不要使用样式特点命名,而是突出用途
.fw-1000 {
font-weight: 1000;
}
.red {
color:red;
}
// =====>
.font-heavy {
font-weight: 1000;
}
.danger {
color:red;
}
一般情况下ID不应该被用于样式,并且ID的权重很高,所以不使用ID解决样式的问题,而是使用class
0后面不带单位
//padding-bottom: 0px;
//=>
padding-bottom: 0;
尽量使用缩写属性对于代码效率和可读性是很有用的,比如font,margin,padding等
.padding-item{
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;
}
<!-- 修改为 -->
.padding-item{
padding: 0 1em 2em;
}
尽量注意的,最好在编写完成后按照结构属性和表现型属性前后进行整理;
子选择符
label1 > lalel2
//label1
必须是label2
的父元素,不能是其他的上级元素。
一般同胞选择符
label1 ~ label2
//label2
必须跟在同胞标签label1
的后面,不一定紧跟。
紧邻同胞选择符
label1 + label2
//label2
必须紧跟label1
的后面。
属性值选择符:标签名[属性名=属性值]。与属性名选择符基本类似,只是为属性名添加了特定的值限制
<img src="" title="border" alt="flower"/>
//css
img[title="border"] {
display:none; //title="border"的img元素会使用该样式
}
:hover
/:first-child
和:last-child
,:nth-child(n)
,:checked
等,这里不做展开,只是提及<ul class="list">
<li>tom</li> //选中,蓝色
<li>jack</li>
<li>tony</li>
</ul>
//css
.list li:first-child {color:blue;}
html
部分需要注意就是结构样式分离,结构合理清晰;
有必要可以使用语义化标签会更好;
img
的alt
属性尽量加上
参考文档:
redux
是 JavaScript
状态容器,提供可预测化的状态管理 ; 实际上是提供了一个在状态读写很复杂的情况下完成在同一个地方进行状态的查询、改变以及传递;
npm install --save react-redux //react中进行使用
先放图 :
- state 是只读的 , 只读是不允许对state 进行直接的赋值和修改,在redux 中是通过 action 和 reducer 返回一个新的 state 进行改变;
- 单一数据源 : 仅存在一个Store作数据的管理;
Action
本质上是 一个规定了type
和data
的对象; 其中的data
是store
数据的唯一来源;
//actionType.js
export const ADD_VALUE = 'add_value';
//createAction.js
import { ADD_VALUE } from './actionType.js'
export const crateAction = data => {
type: ADD_VALUE, //用于标识不同的action,通常定义成字符串常量
data // 来源--用及其他组件状态传递
}
action
描述了发生了什么, 通常情况下我们会在store
中对action
进行生成然后通过dispatch(action)
的方式将action
传递到Reducer
中进行处理;
指定了应用状态的变化即 : 如何响应 actions
的行为并将数据进行更改后发送到 store
;
值得注意的是: reducer 是纯函数 ,只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
(prevState, action) => newState; //接收原来的state以及action,返回新的state
import { ADD_VALUE } from './actionType.js';
//处理传递的Action,是一个纯函数
export default (state = "", action) => {
const newState = JSON.parse(JSON.stringify(state));
switch (action.type) {
case ADD_VALUE:
newState.date = action.data;
return newState;
default:
return state;
}
};
在redux
中store
是唯一的;
创建:
从 redux 包中导入 createStore 这个方法,并使用 reducer 纯函数作为一个参数;
import { createStore } from "redux";
import reducer from "./reducer";
const store = createStore(reducer);// 创建 store
export default store;
使用:
Store
依托于components
来聊的话,就是作为action
的传递者, 在组件中常用api
完成action
的传递
// component
import React, { Component } from "react";
import { crateAction } from "./crateAction/js";
import store from "./store";
class Test extends Component {
render() {
this.handleStoreChange = this.handleStoreChange.bind(this);
this.emitMethods = this.emitMethods.bind(this);
this.state = store.getState(); //初始化state
store.subscribe(this.handleStoreChange); //监听store数据变化
}
handleStoreChange() {
this.setState(store.getState()); //更新组件的store,view进行重新渲染;
}
emitMethods() {
const action = crateAction(data);
store.dispatch(action); //传递action到Reducers
}
}
导航守卫及 keep-alive 作用下的生命周期
在进行 Vue 项目开发的时候,很多组件是没必要多次渲染以及请求数据;为了避免多次刷新请求,Vue
提供了一个内置的抽象组件 keep-alive(不渲染 DOM
&& 不在父组件链中)来缓存组件内部状态;
包裹动态组件时,缓存不活动的组件实例
<keep-alive> <component v-if="showComponent" /> </keep-alive>
缓存部分组件或者组件: 大部分的使用场景,与
router-view
结合使用
router-view
: 配置路由对象的 meta
字段进行判断缓存部分页面或者组件<template>
<keep-alive>
<router-view v-if="$router.meta.keep_alive"></router-view>
</keep-alive>
<router-view v-if="!$router.meta.keep_alive"></router-view>
</template>
// router
import Index from './component/index'
const router = [{
path:'/',
redirect:'/index',
component: Index
meta:{
keep_alive: true // 该组件需要被缓存
}
}]
include
(需要被缓存)/ exclude
(不需要被缓存) 进行缓存的设置, 同时我们需要提前设置组件的 name
属性,同时可以使用 bind
动态设置(支持正则)<!-- 在使用过程中:exclude的优先级大于include -->
<template>
<keep-alive include="a,b"></keep-alive>
<keep-alive v-bind:include="includedComponents"></keep-alive>
</template>
通过该组件包裹的动态组件/路由,会附加两个生命周期函数 activated
与 deactivated
, 用于精确的控制行为;在 2.2.0 及其更高版本中,activated 和 deactivated 将会在树内的所有嵌套组件中触发;
我们一般将数据请求放置在 created
钩子函数中,因为在 beforeCreated
后就开始监测数据变化以及初始化内部事件了; 当然由于没有挂载数据,所以涉及到 dom
放置在 mounted
中进行, 当模板编译完成后进行数据的挂载 mounted
以及更新 updated
,之后离开组件销毁组件;
keep-alive
后我们的周期如何呢?created -> mounted -> activated
进行组件的数据请求以及数据的挂载, 将触发请求写在 created
生命周期中,就可以实现数据缓存;beforeCreate
/created
/beforeMount
/mounted
, 当我们需要获取到新的数据的时候,也就是在 activated
阶段进行数据的更新;在很多时候我们需要通过路由进行验证操作(例如:登录的权限验证)、路由跳转前后组件状态控制(例如:当该组件状态未保存的情况下取消跳转等)以及缓存组件的 数据加载(也可以在 activated
中完成)等;
所以我们需要在路由的跳转过程中更多的控制点,于是 Vue Router
提供了导航守卫(全局的,路由独享以及组件内守卫)植入到整个过程中进而完成更多的控制需求。
实际上是 router 实例对象的方法
router.beforeEach(callback)
全局前置守卫 进入路由之前router.beforeResolve(callback)
全局解析守卫 (特别的: 在进入的组件 beforeRouterUpdate
钩子后调用;router.afterEach(callback)
全局后置钩子 进入路由之后import router from "./router";
/**
* 通过全局守卫简单验证:
* @param {object} to - 将要进入的路由对象 (组件中的this.$route)
* @param {object} from - 将要离开的路由对象
* @param {function} next - 跳转新路由,参数可以是path,路由对象,false等,跳转结果与参数相关
*/
router.beforeEach((to, from, next) => {
const role = localStorage.getItem("user_name");
if (!role && to.path !== "./login") {
next("./login");
} else if (to.meta.permission) {
//meta: { title: '管理员权限页面', permission: true }
role === "admin" ? next() : next("/403");
} else {
next();
}
});
beforeRouteEnter(callback)
进入路由前(无法访问到this)beforeRouteUpdate(callback)
路由复用同一个组件时(例如通过参数查询进行的路由跳转, /user/tom 跳转到 /user/admin)beforeRouteLeave(callback)
离开组件对应路由时调用(禁止用户离开,setInterval 销毁等)//参数的意义和全局守卫相同
beforeRouteEnter((to, from, next) => {
next(callback); //此处的callback会在进入的组件mounted周期之后进行调用,所以可以进行数据的请求用于更新dom
});
一般情况下在action
(此时为一个包含type
和数据
的对象)被创建完成后,被store.dispath(aciton)
触发后会直接将action
传递给reducer
进行处理,这就是一个同步操作完成数据传递的流程 ;
当我们进行异步操作,想要完成一次数据的重新构造生成就必须处理数据之前需要获得保证原始数据~即获得: reducer(prevValue,action)
中的action.data
部分;
所以我们需要使用中间件(Middleware
)来改写store.dispath()
方法完成数据的传递;
函数类型的action可以被middleware
捕获,异步逻辑在action
创建,在component
的生命周期函数中进行调用进而达到简化逻辑以及异步数据传递;
下面是使用redux-thunk
的流程:
创建 store
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
// 使用 redux-devtools以及中间件redux-thunk
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const enhancer = composeEnhancers(applyMiddleware(thunk));
const store = createStore(reducer, enhancer);// 创建 store
export default store;
创建 action
const initListAction = data => {
type: "",
data
};
export const getTodoList = () => { // 返回一个函数
return (dispath) => {
axios.get("./api/v1").then(res => {
const action = initListAction(res.data);// 创建action
dispath(action);// 触发action
});
};
};
使用 Component
// 异步请求
componentDidMount() {
const action = getTodoList();
store.dispatch(action);
}
相比较redux-thunk
,redux-saga
同样可以完成对store.dispath()
的改造,并且将异步请求的代码封装到新的文件中进行管理
安装和初始化
在生成store
中生成并使用saga
中间件,并且引入独立的sagas.js
文件;
//安装redux-saga
npm install --save redux-saga
//生成store.js
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import mySaga from './sagas'
const sagaMiddleware = createSagaMiddleware() //生成中间件
const store = createStore(
reducer, applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(mySaga) //运行saga
编写sagas中的内容及触发
创建action
不必指定对应的state
,在view
中的component
中使用直接生成action
并触发后, 中间件捕捉到对应打action
后在saga.js
中处理
//createAction.js
export const initAction = () =>({
type: "GET_LIST"
});
//component
componentDidMount(){
const action = initAction();
store.dispatch(action)
}
//saga.js
import { put, takeEvery } from 'redux-saga/effects'
import initAction from './createAction';
import axios from 'axios'
// 将在 GET_LIST action 被 dispatch 时调用
function* getList(action) {
try{
const res = yield axios.get('./api/v1');
const action = initAction(res.data);
yield put(action);
}
catch(e){}
}
function* mySaga() {
yield takeEvery("GET_LIST", getList);
}
export default mySaga;
对于使用而言的
redux-saga
虽然将action
异步的部分进一步拆分,并且使用Generator
函数完成异步更新reducer中传递过去的action的值,但是显得十分繁琐;
redux-thunk
实际上将 action
由普通的对象修改 为函数,并在函数完成执行后进行传递到reducer
中;
这是Redux
官方提供的 React
绑定库 , 通过 react-redux
的干预下获得Redux
的Store
中的值 , 并通过connect
函数完成数据的get
和dispatch(action)
;
在项目中进行安装
npm install --save react-redux
相比较未使用的redux
写法, 发生改变的实际上在于
Store
中数据;Store
进行变化的监控;reducer
;API
说明组件如何获取到
Store
中数据
<Provider store>
使组件层级中的 connect()
方法都能够获得 Redux store
。
//正常情况下,根组件应该嵌套在<Provider>中才能使用connect()方法。
ReactDOM.render(
<Provider store ={store}>
<MyRootComponent />
</Provider>,
document.getElementById('root')
)
组件如何完成
Store
的映射以及传递action
到reducer
import { connect } from 'react-redux'
//通过react-redux中的connect方法完成映射以及事件触发
export default connect(mapStateToProps, mapDispatchToProps)(showComponent);
这里的 mapStateToProps
和 mapDispatchToPrps
是两个函数, 分别用于将 store
中的数据映射到组件和完成行为action
传递到 reducer
的作用 ;
react
绑定使用在入口文件中包裹根节点
import react, { createStore } from 'react';
import { Provider } from 'react-redux';
import App from './component/app';
import reducer from './reducer'
const store = createStore(reducer);
render(
<Provider store: { store }>
<App />
</Provider>,
document.getElementById('root');
)
reducer
和action
生成和原来没有区别,简单的罗列一下;
//action
export const actionName = id => ({
type: 'ACTION_NAME',
id
})
// reducer
const todos = (state = [], action) => {
switch (action.type) {
case 'CASE_NAME':
//do something
return newState
default:
return state
}
}
通过
connect
将展示组件转换为容器组件, 完成数据的传递;
import react from 'react';
import { actionNameOne } from './action';
import { connect } from 'react-redux';
const showComponent = (props) => {
const { inputValue, list, handleBtnClick, handleInputChange } = props;
return (
<div>
<div>
<input value={inputValue} onChange={handleInputChange}/>
</div>
<button onClick={handleBtnClick}></button>
</div>
)
}
//state就是传递出来的Store.getState()中的数据
const mapStateToProps = (state) => {
return {
//关联组件中this.props.value
inputValue: state.inputValue,
list: state.list
}
}
//dispacth完成Store.dispatch()方法
const mapDispatchToProps = (dispatch) => {
return {
//关联组件中的绑定的方法
handleInputChange: (e) => {
const action = actionNameOne(e);//创建action
dispatch(action);//触发action
}
}
}
//返回一个容器组件,组件可以不用手动订阅和触发监听完成数据的更新;
export default connect(mapStateToProps , mapDispatchToProps)(showComponent);
实际上在使用 Vue Router
构建 router
实例的时候,参数不仅仅是一个 routes
配置参数,主要是在全局设定路由模式、自定义激活样式以及定义滚动行为,这里翻阅了官网对应部分的 Api
并展示如下:
const router = new VueRouter({
routes,
base: "/app/", // 默认为'/' ; base的路径加在所有的路径前面 ex:/user:id => /app/user:id
// 关于路由模式
mode: "hash", // 浏览器端:hash(defalut)/history ; Node.js: abstract
fallback: true, // 当mode:history且浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式
// 可以用于进行对<router-link>组件进行设置自定义样式;
// 可以设置激活的class类名,以及包含在父级下的精确激活类名: 下面展示的是默认值
linkActiveClass: "router-link-active",
linkExactActiveClass: "router-link-exact-active",
// 当路由进行跳转时候如果保存过滚动信息的话,可以在完成路由跳转时候完成页面的滚动
scrollBehavior: (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition;
} else {
return { x: 0, y: 0 };
}
}
// parseQuery / stringifyQuery -- 提供自定义查询字符串的解析/反解析函数
});
router = [
{
path: "/user/:id",
component: User,
props: true,
name: "user", // name 可以使得在使用router-link 、 router.push 传入对象 {name: user} 直接访问
meta: {
// 元信息,在访问路由对象的时候可以进行访问,可以配合路由导航守卫
title: "User component",
description: "this is user component"
}
}
];
通过 params
的方式传递参数,这样我们拿到 this.$route
也就是当前的路由对象,所以很容易完成参数的传递; 但是我们这样获取到的动态参数其对应路由形成高度耦合,这样当前的组件便不能进行复用;
所以我们会设置 props
的方式解耦动态参数的获取,不用从路由对象获取;
此时 route.params
或者 会作为组件的属性直接被访问,这样也完成了组件和 Url
的解耦; 对于设置 props
的值的不同,在组件中的使用也略有不同:
// path: "/user/:id", props: true, ---- 布尔值
const User = {
props: ["id"],
template: "<div>User {{ id }}</div>"
};
// { path:'/login', component:Login, props: { userName: 'Tom'} } --- 对象
const Login = {
props: ["userName"],
created() {
console.log(this.userName);
}
};
// 当返回函数的时候可以接收一个参数,就是包含路由信息的route对象,同样的我们在组件中进行访问属性。
const propFun = route => ({
userName: "Tom",
age: route.parame.age
});
router = {
path: "/login",
component: Login,
props: propFun
};
// /login/?age=20 => {userName: "Tom", age:20}
在使用
Vue
做构建单页面应用的时候,Vue Router
充当的角色是路径管理器,通过组合组件,并将组件映射到路由,确定组件的渲染位置,进而完成路径切换,完成组件的切换或更新;
下载 -- 导入 -- 配置
下载依赖 后在 main.js
中引入使用 ,创建路由对象并配置路由规则,然后将路由对象传递给Vue
的实例加入在options
中加入 router
,并在模板中为router
留出渲染位置;
完成上面的操作后,我们就成功的在一个vue
项目中引入并使用了含有路由信息的Vue Router
,进而在 url
发生变化的时候完成渲染区域的;
编程式导航
提及在上面的一些内容里面,我们使用的是在 HTML
中进行使用 router-link
的 to
属性完成跳转,这样的标签会被渲染成 a
标签,实际上我们将Vue Router
加入到 Vue
后我们可以访问到路由实例,并向实例进项操作,完成编程式的路由跳转,; 下面类比router
和 route
// this.$router && this.$route( // 一个路由实例可以包含多个路由对象,为父子包含关系)
export default {
computed: {
userName() {
return this.$route.params.userName; //当前路由实例跳转的路由对象
}
},
methods: {
goBack() {
//整个的路由实例,利用push方法传入一个路由对象
window.history.length > 1 ? this.$router.go(-1) : this.$router.push("/");
}
}
};
实际上在项目中,一个组件并不是只能对应固定数据的渲染,在不同的参数下请求不同的数据并进行数据请求,完成数据的展示;这个时候我们会考虑使用动态路由,完成数据匹配
当组件内部需要进行一些内容切换展示的时候,我们考虑在将路由配置中添加child进行嵌套,这样用来完成相对复杂的结构 。下面是嵌套路由结合动态匹配的代码片段:
routes
配置中给某个路由设置名称,可以在路由层级嵌套较深的时候进行方便的链接;
这里提及到利用
vue
组件的异步加载和webpack
机制完成一个路由的懒加载,从而降低首屏加载压力;
当结构继续复杂化,考虑在组件中加入新的渲染区域 router-view
, 然后使用视图的命名指定组件渲染区域,默认为defalut
,加上配置的别名和重定向如下:
最后:
在默认的情况下,
Vue Router
使用hash(#)
的方式指导浏览器在hash
后发生改变的时候进行根据映射关系渲染指定位置 (router-view) 下的不同数据,而不会重新加载网页;当通过参数匹配是并且不会触发组件的生命周期,这就涉及到对hash
发生变化后的处理;
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.