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 过程。
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())const unsubscribe = store.subscribe(() => console .log(store.getState())) 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)) unsubscribe()
严格的单向数据流
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注入的帮助程序、避免代码的多次注入,节省代码体积
主要作用:
自动转换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 { }"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 preset
是 babel
提供的插件组合(一组插件)
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-js
和 regenerator-runtime
模块
babel 几乎可以编译所有最新的 Javascript 语法,但对于 API 来说确并非如此
里面包含 core-js
core-js
是 JavaScript
标准库的 polyfill
core-js和 babel 高度集成,可以对 core-js的引入进行最大程度的优化
参考文档 && 链接 core-js@3 博客园
CSS 伪选择器(CSS_Pseudo-Selectors)
选择器权重: 0级只没有优先级,1级是标签选择器,10级是累选择器、属性选择器,100级是 ID 选择器
伪类
伪类优先级和类选择器一致,都是10
逻辑伪类 | 作用 :not() | 取反(反选伪类) :is() | 判断 :where() | 条件
但是对于逻辑伪类来说,优先级为0,例如: :not(), :is(), :where() 等
逻辑伪类的优先级由括号里面的选择器决定,支持多个选择器通过逗号隔开,不支持及联选择器
:not (.disabled ) {} :not (a ) {} :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
gitError
git push 时报错 在push到远程的时候收到报错:
git push origin developerror: 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
gitLab CD 持续交付 持续发布则指将构建好的程序发布到各种环境,如预发布环境,正式环境.
gitLabCI
gitLab CI 持续集成 GitLab CI 最大的作用是管理各个项目的构建状态,运行构建任务这种浪费资源的事情就交给 GitLab Runner 来做 因为 GitLab Runner 可以安装到不同的机器上,所以在构建任务运行期间并不会影响到 GitLab 的性能~
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
其中 staged
是 Git
里面的概念,指待提交区(暂存区), 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 分支进行如下的版本发布操作:
git pull origin master
根据 pacakage.json 中的 version 更新版本号,更新 changelog
git add -A, 然后 git commit
git tag 打版本操作
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 钩子