小别致真东西
文章77
标签31
分类26
redux 笔记

redux 笔记

Action

定义

Action 本质是 JS 对象,是把数据从应用传到 store 的唯一来源, 一般通过 store.dispatch(action) 将 action 传到 store。action 只是描述有事情发生

const ADD_TODO = 'ADD_TODO';

{
type: ADD_TODO,
text: 'Build my first Redux app'
}
  • action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作(通常为常量)
  • 当 action 过多的时候,使用单独的文件进行管理 actionTypes

action 创建函数

Action 创建函数 就是生成 action 的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,使用时最好注意区分。

const ADD_TODO = 'ADD_TODO';

function addTodo(text) {
return {
type: ADD_TODO,
text
}
}

这样做将使 action 创建函数更容易被移植和测试

Redux 中只需把 action 创建函数的结果传给 dispatch() 方法即可发起一次 dispatch 过程。

dispatch(addTodo(text));

store 里能直接通过 store.dispatch() 调用 dispatch() 方法,但是多数情况下你会使用 react-redux 提供的 connect() 帮助器来调用。
使用 connect() 前,需要先定义 mapStateToProps 这个函数来 指定如何把当前 Redux store state 映射到展示组件的 props 中

class DemoComponent extends React.Component {
// ...
}

const mapStateToProps = state => ({
text: state.text,
});

export default connect(mapStateToProps)(DemoComponent);

除了 state,还可以分发 dispatch

import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'

const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}

const mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}

const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}

const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)

export default VisibleTodoList

异步 Action

reducer

Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,描述如何更新 state。

Reducer 是纯函数,它不应做有副作用的操作,如 API 调用或路由跳转。这些应该在 dispatch action 前发生

import {
ADD_TODO,
TOGGLE_TODO,
SET_VISIBILITY_FILTER,
VisibilityFilters
} from './actionTypes'

function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
default:
return state
}
}
Object.assign(state, {visibilityFilter: action.filter}) // ❌
Object.assign({}, state, {visibilityFilter: action.filter})
  • 不要修改 state, object()将第一个参数设为空对象,第一种会修改 state 的值
  • object.assign() 是 ES6 新特性,存在兼容性问题,要么使用 @babel/polyfill babel 插件,要么使用其他库 _assign()
  • 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state

reducer 拆分: 可以将数据和单纯 UI 控制相关进行拆分

通过 combineReducers() 将拆分的 reducers 进行 合并

import { combineReducers } from 'redux'

const todoApp = combineReducers({
visibilityFilter,
todos
})

export default todoApp
export default function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}

两种方法等价

store

store 是将 action 和 reducer 二者联系起来的对象
再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store

通过 redux 提供的 createStore() 方法根据已有的 reducer 来创建 store, 可以通过第二个参数提供 initState 初始化应用

const store = createStore(reducers, [initState])

import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

store的职责:
-维持应用的 state;

  • 提供 getState() 方法获取 state
  • 提供 dispatch(action) 方法更新 state
  • 通过 subscribe(listener) 注册监听器
  • 通过 subscribe(listener) 返回的函数注销监听器
import {
addTodo,
toggleTodo,
setVisibilityFilter,
VisibilityFilters
} from './actions'

// 打印初始状态
console.log(store.getState())

// 每次 state 更新时,打印日志
// 注意 subscribe() 返回一个函数用来注销监听器
const unsubscribe = store.subscribe(() => console.log(store.getState()))

// 发起一系列 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))

// 停止监听 state 更新
unsubscribe()

严格的单向数据流

babel7 配置

babel7 配置

babel, JS 编译器

Babel 是一个工具链,主要用于将 ECMAScript 2015+ (又可称为ES6,ES7,ES8等)版本的代码转换为向后兼容的 JavaScript 语法,以便旧版本浏览器或其他环境中运行代码

从 babel7 开始,所有的官方插件和主要模块,都放在了 @babel 的命名空间下。从而可以避免在 npm 仓库中 babel 相关名称被抢注的问题。
如:
babel-cli babel 6
@babel/cli babel 7

插件

想要通过 babel 编译出兼容的代码、需要配合插件对源码进行语法和 API 转换处理

插件在 presets 前执行,多个插件从前往后顺序执行,插件配置:

{
"plugins": [
[
"@babel/plugin-transform-runtime"
]
]
}

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime 可以重复使用babel注入的帮助程序、避免代码的多次注入,节省代码体积

主要作用:

  • 自动转换generators/async
  • 使用 core-js 按需给内置类型打上 polyfill, 和下面 presets配置重的 useBuiltIns: ‘usage’ 作用一样
  • 通过 helpers 选项自动移除嵌入的babel helper,并且用module引用来代替。否则每个文件中都会加入这些inline babel helper,造成代码冗余。默认为ture。

@babel/plugin-transform-runtime 需要依赖 @babel/runtime,二者配合使用

@babel/runtime 作为运行时需要作为生产依赖进行安装,而@babel/plugin-transform-runtime作为开发依赖使用

对如下代码

class Person {

}

// 不使用用 @babel/plugin-transform-runtime 经过`babel` 编译后的文件内容为
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Person = function Person() {
_classCallCheck(this, Person);
};

可以看到在编译后的文件内 生成了 _classCallCheck() 模块、在项目中经常会在多个地方使用到 class 去创建类,那么在每一个文件内顶部都会生成 _classCallCheck() 模块,会极大的增加代码的体积。通过 @babel/plugin-transform-runtime 编译生成的代码文件如下:

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var Person = function Person() {
(0, _classCallCheck2.default)(this, Person);
};

在文件顶部没有直接注入之前的整个 function 模块、而是在 helpers 目录下生成模块,在编译转换需要的地方通过 require 引入。

presets

presetbabel 提供的插件组合(一组插件)

presets 配置 使用数组,第一个表示 preset 名字,第二个表示配置参数, 如下:

{
"presets": [
[
"@babel/preset-env",
{
"targets": { // 配置目标环境,优化编译生成代码体积
"browsers": [
"> 1%",
"last 2 versions"
]
},
"useBuiltIns": "usage", // 按需加载
"corejs": 3
}
],
"@babel/preset-react"
]
}

注意:presets 的执行顺序是由后往前的 如上先执行 @babel/preset-react 再执行 @babel/preset-env

preset-env

将高级语法转换成低版本的写法

@babel/preset-env 会根据你配置的目标环境,生成插件列表来编译。对于基于浏览器或 Electron 的项目,官方推荐使用 .browserslistrc 文件来指定目标环境
一般通过 package.json 中 browserlist 字段配置如:

"browserslist": [
"> 0.5%", // 市场份额
"last 2 versions" // 最近两个版本
],

那这个配置与babelrc中的target配置有啥联系吗??
尽量指定目标环境,保持编译代码体积最小

主要模块

@babel/core babel 核心

包含 babel 的核心功能

@babel/polyfill

V7.4.0 版本开始,@babel/polyfill废弃,不建议使用 polyfill 了,需单独安装 core-jsregenerator-runtime 模块

babel 几乎可以编译所有最新的 Javascript 语法,但对于 API 来说确并非如此

里面包含 core-js

core-js

  • core-jsJavaScript 标准库的 polyfill
  • core-js和 babel 高度集成,可以对 core-js的引入进行最大程度的优化

参考文档 && 链接

关于 browsersList 配置

core-js@3 博客园

CSS 伪选择器(CSS_Pseudo-Selectors)

CSS 伪选择器(CSS_Pseudo-Selectors)

选择器权重: 0级只没有优先级,1级是标签选择器,10级是累选择器、属性选择器,100级是 ID 选择器

伪类

伪类优先级和类选择器一致,都是10

逻辑伪类 | 作用
:not() | 取反(反选伪类)
:is() | 判断
:where() | 条件

但是对于逻辑伪类来说,优先级为0,例如: :not(), :is(), :where() 等

逻辑伪类的优先级由括号里面的选择器决定,支持多个选择器通过逗号隔开,不支持及联选择器

:not(.disabled) {}    /* 优先级等同于.disabled选择器 */
:not(a) {} /* 优先级等同于a选择器 */
/* 既不是 div 也不是 span 的元素 */
:not(div, span) {}
:not(a.disabled) {} /* 无效,不支持 */
/* 需要拆开写 */
:not(a):not(.disabled) {}
状态伪类 作用
:link 未访问过的超链接
:visited 访问过的超链接
:hover 鼠标悬停的元素
:actived 选取点中的元素
:focus 获得焦点的元素
筛选伪类 作用
:first-child 当前选择器下第一个元素
:last-child 当前选择器下最后一个元素
:nth-child(n) 选取第n个,:nth-child(2n + 1) 奇数位
:checked 只对 radio 和 checkbox 有效,选取当前选中的元素
:disabled 选取禁用的表单元素
:only-child 选取唯一子元素。如果一个元素的父元素只有它一个子元素,这个伪类就会生效。如果一个元素还有兄弟元素,这个伪类就不会对它生效。

伪元素

伪元素的权重和标签选择器权重相同,为 1
常用的伪元素:
::after,::before :,
其中 content 默认是行内元素

伪元素 作用
::before 在某个元素之前插入一些内容
::after 在某个元素之后插入一些内容
::first-line 为某个元素的第一行文字使用样式
::first-letter 为某个元素中的文字的首字母或第一个字使用样式
::selection 对光标选中的元素添加样式。

:after + data-descr 实现tooltip效果

  • 不能通过 js 去操作伪元素生成的内容,因为伪元素构造的元素是虚拟的。
  • first-line 和 first-letter 不适用于内联元素,在内联元素中这两个选择器都会失效。
  • 除了 selection,其余四个伪元素选择器已经在 CSS2 中存在且和伪类用的是一样的单冒号表示的。为了向下兼容,现在的浏览器中伪元素选择器用单冒号和双冒号都可以。

伪类与伪元素的区别

伪类的效果可以通过添加一个实际的类来达到,而伪元素的效果则需要通过添加一个实际的元素才能达到,这也是为什么他们一个称为伪类,一个称为伪元素的原因。
伪元素和伪类之所以这么容易混淆,是因为他们的效果类似而且写法相仿,但实际上 css3 为了区分两者,已经明确规定了伪类用一个冒号来表示,而伪元素则用两个冒号来表示。

sdfas

包管理工具笔记

包管理工具笔记

# yarn 的全局操作
$ yarn global add packageName
$ yarn global romove packageName
$ yarn global List
#npm 全局
$ npm install -g packageName
$ npm uninstall -g packageName
$ npm list -g --depth 0
Taro小程序 Jest 使用总结

Taro小程序 Jest 使用总结

gitError

gitError

git push 时报错

在push到远程的时候收到报错:

git push origin develop

error: src refspec develop does not match any.
error: failed to push some refs to 'https://gitlab-ci-token:***'

ERROR: Job failed: command terminated with exit code 1

主要原因是当时的暂存区中为空 没有文件

背景:
在处理 gitlab 的时候 有一个流程是:
生成版本 -> 生成CHANGELOG -> push到repo

因为我在本地在做一个功能测试的时候,生成了CHANGELOG,在gtilab中再次生成log的时候生成的是一样的文件也就不存在任何修改。此时push暂存区是没有任何内容的,因此报错。

gitlabCD

gitlabCD

gitLab CD 持续交付

持续发布则指将构建好的程序发布到各种环境,如预发布环境,正式环境.

gitLabCI

gitLabCI

gitLab CI 持续集成

GitLab CI 最大的作用是管理各个项目的构建状态,运行构建任务这种浪费资源的事情就交给 GitLab Runner 来做 因为 GitLab Runner 可以安装到不同的机器上,所以在构建任务运行期间并不会影响到 GitLab 的性能~

gitHooks

gitHooks

最开始只是想要实现基本的一个风格的检查以及项目的版本的控制

工作流: git add 将本地修改加入暂存区 -> commit -> 风格、规范检查 -> 生成版本 -> 日志生成 -> push

在提交前对提交部分进行风格检查

husky + lint-staged + eslint + prettier

husky

使用 husky 来管理提交的修改,可以在 git commit 之前再校验一次代码,达到使用 pre-commit + shell/ruby等脚本进行检查的效果, exit 不为0 则中断提交

"husky": {
"hooks": {
"pre-commit": "prettier -c"
}
},

如此配置会将提项目中的所有文件进行一次 prettier 检查,浪费了时间在无关文件上。解决方案,往下瞅

lint-staged

lint-staged github

其中 stagedGit 里面的概念,指待提交区(暂存区), git add 将代码修改移入暂存区。

只会校验提交里面指定需要校验的文件

  "husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
// 指定文件及校验规则
"lint-staged": {
"src/*.js": [
"eslint --ext .js",
"eslint --fix",
"prettier --check 'src/*.js'"
],
"*.css": [
"prettier --check"
]
},

版本控制

版本号: MAJOR.MINOR.PATCH

standard-version / release-it

standard-version 是一款遵循语义化版本(semver)commit message 标准规范 的版本和 changlog 自动化工具。
通常情况下,我们会在 master 分支进行如下的版本发布操作:

  1. git pull origin master
  2. 根据 pacakage.json 中的 version 更新版本号,更新 changelog
  3. git add -A, 然后 git commit
  4. git tag 打版本操作
  5. push 版本 tag 和 master 分支到仓库

其中2,3,4则是 standard-version 工具会自动完成的工作,配合本地的 shell 脚本,则可以自动完成一系列版本发布的工作了。

CHANGELOG 生成

CHANGELOG只有在发版时刻才生成,前提是规范化提交
在 CHANGELOG.md 的头部加上自从上次发布版本以来的变动。

  • commitizen
    提供的 git cz 命令替代 git commit 命令, 帮助生成符合规范的 commit message

  • cz-conventional-changelog
    适配器、不同的项目本身的构建方式的不同,commitizen 支持不同适配器的扩展,从而去满足不同的构建需求的。一个符合 Angular 团队规范的 preset

  • conventional-changelog-cli
    默认推荐的 commit 标准是来自 angular
    目前集成了包括 atom, codemirror, ember, eslint, express, jquery 等项目的标准

  • conventional-changelog-custom-config
    github 地址
    实现自定义的 CHANGELOG 需要该插件支持自定义

    script命令
    conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0 -n ./changelog-option.js
    参数如下:

    选项 详情 说明
    -p –preset 使用的模版、默认是 angular,-p custom-config 使用自定义的配置
    -i –infile Read the CHANGELOG from this file
    -s –same-file Outputting to the infile so you don’t need to specify the same file as outfile
    -r –release-count 从最新生成的版本数如果为0,将重新生成整个变更日志,并覆盖输出文件默认值:1
    -n –config 指定配置文件的路径

    更多请使用👉 conventional-changelog --help

  • standard-version
    包含版本管理和生成日志的功能

"scripts": {
"commit": "prettier --check 'src/*.js' && git cz",
"lint": "eslint --ext .js",
"clog": "conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0 -n ./changelog-option.js",
"prettier": "prettier --check --write 'src/*.js' 'static/*.css'",
"release:major": "standard-version -r major",
"release:minor": "standard-version -r minor",
"release:patch": "standard-version -r patch",
"test": "echo \"Error: no test specified\" && exit 1"
},
"commitizen": {
"path": "cz-conventional-changelog"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/*.js": [
"eslint --ext .js",
"eslint --fix",
"prettier --check 'src/*.js'"
],
"*.css": [
"prettier --check"
]
},
"devDependencies": {
"commitizen": "^4.0.3",
"conventional-changelog-cli": "^2.0.31",
"conventional-changelog-custom-config": "^0.2.0",
"cz-conventional-changelog": "^3.0.2",
"eslint": "^6.8.0",
"husky": "^3.1.0",
"lint-staged": "^9.5.0",
"prettier": "^1.19.1",
"standard-version": "^7.0.1"
}

commit 类型相关

feat:新功能
fix:修补 bug
docs:修改文档,比如 README, CHANGELOG, CONTRIBUTE 等等
style:不改变代码逻辑 (仅仅修改了空格、格式缩进、逗号等等) 并不是修改css style样式
refactor:重构(既不修复错误也不添加功能)
perf: 优化相关,比如提升性能、体验
test:增加测试,包括单元测试、集成测试等
build: 构建系统或外部依赖项的更改
ci:自动化流程配置或脚本修改
chore: 非 src 和 test 的修改(其他修改)
revert: 恢复先前的提交

Git Hooks相关

git hook 存在于项目中 .git 文件夹下的 hooks中,默认以点 sample 扩展名存在,不会被 git 管理到,使用hook进行控制git流程需要另外在项目中保存hooks

钩子 阶段 用途
pre-commit 键入提交信息前 对提交的快照进行检查 可以用 git commit --no-verify绕过该环节 客户端钩子
prepare-commit-msg 启动提交信息编辑器之前,默认信息被创建之后运行 客户端钩子
commit-msg 接收一个存有当前提交信息的临时文件的路径参数 用来在提交通过前验证项目状态或提交信息 客户端钩子
post-commit 整个提交过程完成后运行 一般用于通知之类 客户端钩子
post-merge git merge 成功运行之后 客户端钩子

TODO: 服务端钩子

钩子 阶段 用途
pre-recive 处理来自客户端的推送操作时,最先被调用的脚本是 pre-receive 可以用这个钩子阻止对引用进行非快进(non-fast-forward)的更新,或者对该推送所修改的所有引用和文件进行访问控制 对提交的快照进行检查 服务端钩子
update 为每一个准备更新的分支各运行一次 服务端钩子
post-recive 整个提交过程完成后运行 一般用于通知之类 更新其他系统服务或者通知用户 包括给某个邮件列表发信,通知持续集成(CI)的服务器 服务端钩子

参考地址:
1: git commit 、CHANGELOG 和版本发布的标准自动化
2: 用 husky 和 lint-staged 构建超溜的代码检查工作流
3: 掘金社区
4: git 钩子

SSH

SSH