集成谷歌分析
网站跟踪
登录 Google Analytics 后,根据项目情况创建媒体资源并集成到项目中,通常包括以下步骤:
- 初始化跟踪实例
- 上报页面访问事件,集成 Google Analytics 后会自动上报页面访问事件,但现在的应用通常是单页应用,需要特殊处理一下,在路由发生变化时上报页面信息
- 标识用户
- Google 后台,管理 -> 媒体资源/.js 跟踪信息 -> User-ID,启用 User-ID 功能,可查看示例代码
- 代码中再获取到用户信息时进行上报,如
gtag('set', {'user_id': 'USER_ID'});
- 清除 cookie 后,将产生一个新用户,即使设置了相同的 user_id
- 在报告 -> 受众群体 -> 行为 -> User ID 覆盖率中,可查看被标识用户的比例
- 跟踪流量来源
- 在页面 url 中带有的
utm_
系列参数会自动上报,utm_source 是必须的,否则无法收集其它数据 - 也可通过代码手动上报
- utm_source,广告系列来源,用于确定具体的搜索引擎、简报或其他来源,如 google 或 weixin
- utm_medium,广告系列媒介,用于确定电子邮件或采用每次点击费用的广告等媒介,如 cpc 或 appmessage 或 timeline
- utm_campaign,广告系列名称,用于关键字分析,以标识具体的产品推广活动或战略广告系列,如 spring_sale 或 sharePlan
- utm_term,广告系列字词,用于付费搜索,为广告提供关键字,如 running + shoes
- utm_content,广告系列内容,用于 A/B 测试和内容定位广告,以区分指向相同网址的不同广告或链接,如 logolink 或 textlink
- 在页面 url 中带有的
上报事件,可上报自定义指标和维度
- 事件类别,如购买
- 事件操作,如购买成功、购买失败
- 事件标签,如单品1、单品2
12345678910111213gtag('config', config.gaTrackingId, {custom_map: {dimension1: 'viewerId',dimension2: 'activityName',},});gtag('event', '事件操作', {event_category: '事件类别',event_label: '事件标签',viewerId: '维度1',activityName: '维度2',});
《用户体验要素》笔记
介绍
“如何提出正确的问题”。
用户体验并不是指一见产品本身是如何工作的,用户体验是指“产品如何与外界发生联系并发挥作用”,也就是人们如何“接触”和“使用”它。
拥有更多的“产品特性”,被证明只能保持短时间的竞争优势。随着功能的不断膨胀,网站变得越来越复杂、越来越笨重、越来越难以使用,最后就失去了对初次访问者应有的吸引力。
转化率是一种常用的方式,来衡量用户体验的效果。
任何在用户体验上所做的努力,目的都是为了提高效率。“帮助人们工作得更快”和“减少他们犯错的几率”。
创建吸引人的、高效的用户体验的方法称为“以用户为中心的设计(user-centered design)”。以用户为中心的设计思想非常简单:在开发产品的每一个步骤中,都要把用户列入考虑范围。
用户体验的整个开发流程,都是为了确保用户在你的产品上的所有体验不会发生在你“明确的、有意识的意图”之外。这就是说,要考虑到用户有可能采取的每一个行动的每一种可能性,并且去理解在这个过程的每一个步骤中用户的期望值。
每一个层面都是根据它下面的那个层面来决定的。当我们做出的决定没有和上下层面保持一致的时候,项目常常会偏离正常轨道,完成日期延迟,而在开发团队试图把各个不匹配的要素勉强拼凑在一起的同时,费用也开始飞速上涨。
战略层
成功的用户体验,其基础是一个被明确表达的“战略”。经营者想从网站得到什么,是产品目标;用户想从网站得到什么,是用户需求,结合内外两者就组成了战略层。
对于任何一个网站,它需要明确描述的基础目标之一就是品牌识别(brand identity)。
理解你的目标,有一个重要的部分,就是理解你要怎样才能知道“什么时候到达了终点”,一些可追踪的指标,在产品上线以后用来显示它是否满足了我们自己的目标和用户的需求,这就是成功标准(success metrics)。
对驱动用户体验决策而言有意义的成功标准,一定是可以明确地与用户行为绑定的标准,而这些用户行为也一定是可以通过设计来影响的行为。
理想化用户就是“某些与我们完全一样的人”。
要对用户需求寻根究底,必须要定义谁是我们的用户。可以通过用户细分(user segmentation)把大量的用户需求划分成几个可管理的部分。
可根据人口统计学(demographic)、消费心态档案(psychographic profile)、对技术和网页本身的想法、对网站相关内容的知识多少等标准来划分用户。
创建细分用户群只是一种用于“揭示用户最终需求的手段”,你真正只需要得到的是和你发现的“用户需求数目”一样多的细分用户群。
创建细分用户群还有其他重要的原因,不仅仅是因为不同的用户群有不同的需求,还因为有时候这些需求是彼此矛盾的。我们无法提供一种方案可以同时满足这两种用户的需求,要么选择针对单一用户群设计而排除其他用户群,要么为执行相同任务的不同用户群提供不同的方式。
用户研究(User Research)的领域致力于收集必要的信息来达成共识,问卷调查、用户访谈、焦点小组最适合用于收集用户的普遍观点与感知,用户测试、现场调查则更适用于理解具体的用户行为以及用户在和产品交互时的表现。
范围层
定义项目范围则同时在做两件事:这是一个有价值的过程,同时能产生有价值的产品。过程(process)的价值在于,当整个事情还处在假设阶段的时候,它能迫使你去考虑潜在的冲突和产品中一些粗糙的点。我们能确定现在能解决哪些事情,而哪些必须要再迟一点才能解决。产品(product)的价值在于,被定义的这个产品给了整个团队一个参考点,明确了这个项目中要完成的全部工作,它也提供了一门用于讨论这件事情的共同的语言。
用文档定义产品需求,很麻烦但必须要做。原因是:这样你才知道你正在建设什么,这样你才知道你不需要建什么。
功能型产品考虑的是功能规格,哪些方面应该被当成软件产品的功能以及相应的组合。在信息型产品方面,考虑的是内容,这属于编辑和营销推广的传统领域。我使用一个词“特性(feature)”来同时表示软件的功能和所提供的内容。真正的内容常常是通过一个内容管理系统(Content Management System,CMS)来进行管理。
撰写功能规格说明的几条原则:乐观、具体、避免主观语气,功能规格必须可验证。
结构层
确定网站各种特性和功能最合适的组合方式。用来设计用户如何到达某个页面,并且在他们做完事情之后能去什么地方。
在传统的软件开发行业,涉及“为用户涉及结构化体验”的方法被称为交互设计(interaction design)。在内容建设方面,主要是通过信息架构(information architecture)来构建用户体验。交互设计和信息架构都强调一个重点:确定各个将要呈献给用户的元素的“模式”和“顺序”。交互设计关注于将影响用户执行和完成任务的元素。信息架构则关注如何将信息表达给用户的元素。
交互设计关注于描述“可能的用户行为”,同时定义“系统如何配合与相应”这些用户行为。
用户对于“交互组件将怎样工作”的观点称为概念模型(conceptual model)。规划好概念模型能帮助你做出一致的设计决定。
第一个同时也是最好的防止错误的方法,是将系统设计成不可能犯错的那种。第二个避免错误的方法就是使错误难以发生。一些最令人反感的行为,往往出现在软件试图善意地修正用户错误的时候。系统应该为用户提供从错误中恢复的方式。
信息架构研究的是人们如何认知信息的过程,对于产品而言,信息架构关注的就是呈现给用户的信息是否合理并具有意义。在以内容为主的网站上,信息架构主要的工作是设计组织分类和导航的结构,让用户可以高效率、有效地浏览网站的内容。
从上到下的信息架构方法将从战略层所考虑的内容,即根据产品目标与用户需求直接进行结构设计。从下到上的信息架构方法也包括了主要分类与次级分类,但它是根据对“内容和功能需求的分析”而来的。
架构图最重要的是记录概念关系:哪些类别需要放在一起,而哪些需要保持独立?在交互过程中那些步骤要怎样相互配合?
框架层
框架是结构的具体表现方式,用于确定用什么样的功能和形式来实现,关注点几乎全部在独立的组件以及它们之间的相互关系上。通过界面设计来确定框架,通过导航设计呈现信息的一种界面形式,信息设计呈现有效的信息沟通。如果涉及提供给用户做某些事的能力,则属于界面设计;如果提供给用户去某个地方的能力,则属于导航设计;如果是传达想法给用户,则是信息设计。
界面设计要做的全部事情就是选择正确的界面元素,还要通过适当的方式让它们容易被理解和使用。成功的界面设计是那些能让用户一眼就看到“最重要的东西”的界面设计。
任何一个网站的导航设计都必须同时完成三个目标:必须提供给用户一种在网站间跳转的方法,必须传达出这些元素和它们所包含内容之间的关系,必须传达出它的内容和用户当前浏览页面之间的关系。
信息设计常常充当一种把各种设计元素聚合到一起的粘合剂的角色,最后,信息设计变成决定如何呈现这些信息,使人们能很容易使用或理解它们。
表现层
解决并弥补“产品框架层的逻辑排布”的感知呈现问题。如果你对设计是成功的,用户眼睛的移动轨迹的模式应该遵循的是一条流畅的路径,在不需要用户太多细节来吓倒用户的前提下,它为用户提供有效选择的、某种可能的“引导”。
在视觉设计中,我们用于吸引用户注意的一个主要工具就是对比。一致性能使你的设计有效地传达信息,而不会导致用户迷惑或焦略。
内部一致性的问题,是说在产品的两个不同的地方反应了不同的设计方法。解决内部一致性问题比较好的方法,是在进行设计之前,从这些不同的环境中反复出现的设计元素独立出来,然后将这个设计方案应用到整个产品中去,在需要的时候进行调整。
色彩可能是向外界传递品牌识别的一个最有效的方法。
Next.js 学习
介绍
Next.js 是一个轻量级的 React 服务端渲染应用框架,是 Create-React-App 的升级版。当使用 React 开发时,常常需要配置很多繁琐的参数,如 Webpack 配置、Router 配置和服务器配置等。如果需要做 SEO,要考虑的事情就更多了,怎么让服务端渲染和客户端渲染保持一致是一件很麻烦的事情,需要引入很多第三方库。针对这些问题,Next.js 提供了一个很好的解决方案,使开发人员可以将精力放在业务上,从繁琐的配置中解放出来。
优点
- 服务端渲染(SSR)
- 基于文件系统的页面路由
- 自动按页面拆分代码
- 静态页面导出
- 使用 styled-jsx
- 零配置,基于 Webpack 的开发环境,支持热模块替换
- 支持 Babel 和 Webpack 的配置项定制,可定制服务端、路由 和 next 插件
缺点
- 注意 window、document 等只能在浏览器端使用的变量
- 区分服务端还是客户端
- 图片等静态文件只能放在 static 目录下,不能通过 require 来引入,也就是没办法通过 webpack 来进行模块化管理,如果各个组件有自身依赖的图片,也只能一股脑放 static 里,也很难实现版本管理控制浏览器缓存
简单地说,很适合快速搭建简单站点,但自由度不高,且带样式或图片的 React 组件无法直接使用,个人看法是一个用自由度和通用性来换取易用性的框架
Next.js vs create-react-app
Next.js:优化首屏加载速度,有SEO,如着陆页项目
create-react-app:适用于 SPA,不需SEO、SSR的项目,如后台项目
使用
Command
next dev 开发模式,编译完后只是搭建基础模块,访问到哪些页面再进行编译,提升开发效率
next build 构建打包生产环境代码
next start 发布模式编译后本地运行 server
next export 导出静态页面,使用 next.config.js
Pages
Next.js 项目有两个特殊目录,pages 和 public。public 目录存放静态文件和 robots.txt 文件。pages 目录下的文件可以是页面路由或 api 路由,可通过文件名直接访问,pages/api 目录下的文件都是 api 路由,可通过 /api/* 访问,其它为页面路由。pages 目录下文件名由 [] 包裹的会产生动态路由,文件的名称会作为query参数传入页面,Link 中需要使用 as 属性。
Router and Link
useRouter() 可以获取 router 对象,withRouter() 可以将 router 对象传入组件。页面跳转不要使用 <a>
,它不会执行客户端导航,而是浏览器发起请求到服务器页面,会刷新整个页面,使用 <Link>
是客户端导航,并且使用了 location.history
。Link
只是一个接受 href 和类似属性的包装组件,如果想给链接标签添加属性,需要添加到它的子组件上,href 属性是到 pages 下的路径,as 属性是浏览器地址栏显示的地址。
Style
使用 <style jsx>
内联样式,只作用在本组件内,可以通过 global 属性定义全局样式,也可以使用引用 css 文件的方式。可以通过 [name].module.css
命名方式使用 css module。可以继承 styled-components 来支持服务端渲染。
Data Fetching
Next.js 有两种预渲染方式,Static Generation 和 Server-side Rendering。有3个方法可以用于预渲染获取数据:
- getStaticProps (Static Generation): 在打包构建生产环境时获取数据,因此无法获取 query 参数或 headers 等再请求发生时才有的信息,只在服务端执行,然后把获取到的数据用 props 传入页面
- getStaticPaths (Static Generation): 根据数据指定动态路由进行预渲染,如果一个动态路由页面使用了 getStaticProps 方法,那它也必须使用 getStaticPaths 方法
- getServerSideProps (Server-side Rendering): 每次请求时获取数据,只在服务端执行,
除此之外,还可使用 getInitialProps API 获取数据,getInitialProps 适合通用数据抓取和服务器端渲染,有利于 SEO。这些方法只能在 page 页面导出。
有用的包
- 使用 next-optimized-images 引入图片文件,并在部署时自动优化图片
- 使用 next-css 加载 css 文件
- 使用 next-compose-plugin 维护 next.config.js 文件整洁性
- 使用 @next/bundle-analyzer 分析打包效果
create-react-app 学习
Monorepo
先说一下 Multirepos 的管理方式,一个项目会有一个 repo 或者说一个 module 一个 repo,缺点:
- 项目或者 module 因为功能或者属性或者历史的原因不得不拆分到不同的 organization 中,这会导致后期人员交接或者自己项目管理时不知道哪里去找 repo 的境地
- issue 不知道往哪里提,导致项目管理混乱
- 版本管理带来的日常开销,如 webpack 需要变更时,需要人工同步所有的项目
- changelog 梳理又是一场灾难,需要人工同步所有变动的仓库最终列出一个 changelog
Monorepo 是一种相反的思路,可以将以上问题都解决。 Monorepo 是一种管理代码的方式,在这种方式下会摒弃原先一个 module 一个 repo 的方式,取而代之的是把所有的 modules 都放在一个 repo 内来管理,用一个项目维护所有的代码,每个子项目单独管理发布和运行,内部互相引用,但 Monorepo 也有缺陷,就是单个 repo 体积较大、如何进行权限管理。从代码结构看,一般使用 Monorepo 管理方式的项目,都会有一个 packages 目录,里面是一个个独立管理和发布的子项目。
目前诸如 Babel、React、Angular、Ember、Jest、Create-React-App 等等都采用了 Monorepo 这种方式来进行源码管理。
个人思考:
1、如果由一个 repo 管理所有项目,这个 repo 体积会越来越大,这会需要强大的构建工具,版本控制、权限管理也会变难。Monorepo 适用于什么场景呢?是否是由多个子项目共同完成一件事或者跟一件事相关,而且至少存在内部互相引用的情况,适用的边界在哪里?
2、我们的3个前端项目适用 monorepo 的管理方式吗?
Lerna
Lerna 是基于 Monorepo 理念在工具端的实现,优化使用 git 和 npm 管理多包代码库的工作流程,可以在主项目下管理多个子项目,从而解决多个包互相依赖,且发布时需要手动维护多个包的问题,根据 git 提交记录自动生成 CHANGELOG。
|
|
此时目录结构为:
|
|
此时目录结构为:
在 module-a 的 package.json 的 dependencies 中添加 module-b,执行 lerna bootstrap,引导packages安装各自的依赖。如果想把 package 发布到 npm 上,可以执行 lerna publish 命令。
如何学习
CONTRIBUTION.md 看项目结构,怎么更改和维护。React-boilerplate 样板 ,最佳实践,弄成项目样板,在此基础上写业务代码。
- 读代码
- 看 commit 记录
packages
把配置和开发调试依赖的东西分成独立项目了,使用者只关注业务代码。
babel-preset-react-app:react-scripts 使用的 babel 预设,将 ES6 以上的语法转义。
eslint-config-react-app:eslint 语法检查配置。
react-dev-utils:开发调试模式用到的一些脚本和方法,如编译时的错误提示?
create-react-app:全局 CLI 命令行工具,不应该经常变换,需适配多版本的 node,委托所有的配置给了 react-scripts。
react-scripts:项目核心,项目基础配置,包括开发服务的脚本和设置,如 webpack、babel、eslint 配置,构建生产模式等。
create-react-app
create-react-app 由于是全局安装的,不会经常升级,因此要保持简单,少变更。它只负责做好一件事,初始化仓库,把其他版本的包下载到本地配置好,应适配多版本的 node。
create-react-app 好的思路:
- index.js 中 : 生成一个新项目到本地,加载依赖项。检查 node 版本,过低给出警告。这样很安全。
- 使用 commander 项目,解决控制台如何使用指令。
- react-script 中执行 init,把 package.json 填充好,把 template 目录中的模板文件拷贝到项目中。
- 配置相关的安装包都以特定的方式预配置到项目中,如果需要更高程度的自定义,使用 eject 命令。
npx
需要全局安装的包,用了一次或两次就不用了。全局安装包 + 执行命令 + 删除全局包。npx create-react-app my-app
===npm install -g create-react-app && create-react-app my-app && npm uninstall -g create-react-app
通过 create-react-app + 自定义 react-scripts 的方式,fork create-react-app 项目,做了:
- 将使用 js 改为 ts
- 添加了 fork-ts-checker-webpack-plugin 负责语法检查,添加了使用 ts-lint 的配置
- 添加了 cache
- 增加了 ts-jest 的配置
- 修改了 template 下的模板文件
- 增加了对 graphql 文件类型的处理 graphql-tag/loader
- 加载文件预处理的耗时多的操作,都用了 thread-loader
- 原来使用 webpack 的 uglyjs 的 plugin ,但是效率低,改为使用社区开源的 uglyjsplugin
- 使用 module.css 思路,以 .module.css 使用 css module 的处理,增加了对 scss sass 的处理
- webpack 由3升级到4
使用我们自定义的安装包npx create-react-app@next --scripts-version=@canway/react-scripts my-app
性能优化:Typescript,会运行 tsc,src/complier/tsc.ts,使用 server-client 的设计思路模式。对于 typescript 其实是一个编译器,把 ts 文件转成 js 文件,把一个文件怎么编译和转换当做一个基础的服务放到 server,client 在个个地方怎么调用这些服务,tsc 是去调用提供的服务,所以 tsc 不在 server 下。ts 的服务一共两大部分:语法检查和转义。好处是,vscode 等 ide 中只要语法检查部分。直接使用 tsc 进行语法检查和转义,效率低,webpack 打包的时候只进行转义,独立的使用 fork-ts-checker-webpack-plugin 做语法检查,ts-loader 添加选项 happyPackMode: true 只进行转义,配合了 thread-loader 利用电脑多核使用多线程,使用 cache-loader 缓存转义(babel-loader本身是有缓存机制的,ts-loader 没有)。用独立的线程进行语法检查 fork-ts-checker-webpack-plugin。
我的问题:
- node_modules 没有包括所有 package.json 中的 dependencies 下的包
- 执行 yarn install 后没有 yarn.lock 文件
- createReactApp.js 中执行过安装 react、react-dom了,在 react-scripts/init.js 中再次执行
- tasks 目录
- yarn e2e:docker
项目中如何修改
- yarn add @private/react-scripts –exact
删除编译相关的包、scripts
12345678910111213141516171819202122232425262728graphql-tagisomorphic-loaderserialize-javascriptwhatwg-fetch@babel/*jest 相关webpack 相关postcss 相关ts 相关ali-ossasync-retryautoprefixerchalkchokidarfront-matterfs-extraglobidentify-obj-proxymarkdown-itmkdirpobject-assignprettier-errorpromisereact-deep-force-updatereact-dev-utilsreact-error-overlayrimrafsource-map-support更新 scripts 只留 precommit、start、build、test、eject
更新 browerslist- 删除 config 、scripts、test 目录,babel、docker、jest、nginx 配置文件
- 拷贝 tsconfig、tslint
- 修改入口文件为 src/index.tsx
- 添加 serviceWorker.ts 文件
- 添加 node-sass 安装包
- 可能存在 react、react-dom 类型文件版本冲突,redux、apollo 也可能有问题,可能需要手动修改 yarn.lock 文件
- webpack 4 “export does not found” 问题,修改 webpack 配置中
strictExportPresence: false
- 更新环境变量为 REACTAPP 开头
PostCss 改为 SCSS
- 删除 css.d.ts 声明文件
- css 文件改为 module.scss,修改文件引入
- 变量的定义及使用、多媒体查询、@next 替换为 @at-root
- 图标样式文件 Icon.module.scss 中变量需定义为字符串
- antd 样式没有生效,原因是 webpack 配置中使用了 thread-loader
- 修正 stylelint 配置文件,支持 scss 中的 @include 等使用 123456789101112module.exports = {plugins: [...'stylelint-scss', // 需安装],...rules: {...'at-rule-no-unknow': null,'scss/at-rule-no-unknow': true,}};
Mac-使用
- 新建:command + n
- 返回行首/尾:command + 左箭头 / 右箭头
- 返回页顶/底:command + 上箭头 / 下箭头
- 重命名文件夹:点击文件夹回车
- 显示桌面:command + f3 / fn + f11
- 打开Finder:option + command + 空格
- 最小化当前窗口:command + m
- 最小化当前应用程序所有窗口:command + option + m
- 隐藏当前应用程序:command + h
- 关闭当前应用程序:command + q
- 将模拟器屏幕截图保存到桌面:command + s
- command + tab 选中程序,松开Tab,按住Command不放的同时按住Opt,松开Command 将最小化的窗口恢复回来
- Xcode中clean项目:command + shift + k
- 调整模拟器窗口大小:Simulator -> window -> Scale
- 模拟器返回主界面:shift + command + h
- 全屏:ctrl + command + f
- 显示隐藏文件夹 12defaults write com.apple.finder AppleShowAllFiles -bool true;KillAll Finder
终端命令
- 新建文件夹:mkdir
- 文件重命名:mv old.ar new.ar
- 列出当前目录所有文件:ls
- 列出当前目录某格式文件:ls *.ar
- 删除文件:rm 只能删除文件或者空的文件夹
- -r 删除文件夹内的子文件夹及内容
- -f 强制删除参数
- 查看文件内容:vi aa.txt,i 进行编辑,shift + : + w + q 保存
- 进入目录:cd
- 回到根目录:cd ~
- 输入补全:tab键
- 终端新建文件:touch .gitignore
- 查看ip:ifconfig
- 删除整行命令:ctrl + u
- 清除屏幕并将当前行移到页面顶部:Ctrl + L
- 删除一个单词:alt + delete
- 列出文件和目录的详细信息:ls -l
- sudo chmod +x xx.sh
- 查找命令执行文件的路径:where node
- 输出:echo $PATH
- 打印系统环境变量:printenv
- 显示当前的目录路径:pwd
- 显示或连接文件内容:cat /etc/profile
- 通过简单正则表达式搜索文件:grep
- 处理文本文件的程序:awk
- 追加式写入:ifconfig >> .log
- 覆盖式写入:ifconfig > .log 覆盖
- 查看当前运行的 Shell:echo $SHELL
- 判断命令的来源:type echo
好用工具
- Magnet 分屏工具
- Xnip 截图工具
- Bear Markdown 笔记
项目基础配置
在 github 中配置
- 默认分支
- 保护分支,注意里面的配置项
VSCode 中添加 Code Spell Checker 进行拼写检查
VSCode 中添加 EditorConfig for VS Code 进行风格统一
- 参考 EditorConfig 官网
- 项目根目录添加 .editorconfig 文件
- editorConfig 不是什么软件,而是一个名称为 .editorconfig 的自定义文件,该文件用来定义项目的编码规范,编辑器的行为会与.editorconfig 文件中定义的一致,并且其优先级比编辑器自身的设置要高
格式检查
- 参考 prettier 官网 进行配置,它可以很好的集成的到项目中,利用 git 的 hooks 的机制,在提交 commit 时自动调用 prettier,使用 husky 和 lint-staged 配合使用
- husky :可以方便的通过 npm scripts 来调用各种 git hooks
- lint-staged :利用 git 的 staged 特性,可以提取出本次提交的变动文件,让 prettier 只处理这些文件
- husky 配合 lint-stage 的过程可以通过 pretty-quick 来取代,但如果项目中也使用了其它工具,比如ESLint,请使用lint-stage
- VSCode 中添加 Prettier - Code formatter 插件
- 执行
./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx,json,css}"
来检查整个项目
- 参考 prettier 官网 进行配置,它可以很好的集成的到项目中,利用 git 的 hooks 的机制,在提交 commit 时自动调用 prettier,使用 husky 和 lint-staged 配合使用
样式检查
- 参考 stylelint 进行配置
- 安装 stylelint-config-standard、stylelint-order
- VSCode 中添加 stylelint 插件
语法检查
- 参考 TSLint 官网 tslint-react 进行配置
- tslint-config-prettier 防止 tslint 和 prettier 发生冲突,prettier 负责格式,tslint 负责其它
- VSCode 中添加 TSLint 插件
自动化测试
- 参考 Jest,需安装 @types/jest
- 参考 ts-jest,作用是将 ts 写的测试文件转为 js 的,再对这些文件执行 jest
VSCode 中添加 jest 插件
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374import * as React from 'react';import * as renderer from 'react-test-renderer';import configureStore from 'redux-mock-store';import thunk from 'redux-thunk';import App from '../App';import Badge, { BadgeVariant } from './Badge';const middlewares = [thunk];const mockStore = configureStore(middlewares);const initialState = {};test('正确渲染', () => {const store = mockStore(initialState);let tree = renderer.create(<Appcontext={{fetch: () => {return;},store,client: {},}}><Badge className="badge">{10}</Badge></App>,).toJSON();expect(tree).toMatchSnapshot();tree = renderer.create(<Appcontext={{fetch: () => {return;},store,client: {},}}><Badge>New</Badge></App>,).toJSON();expect(tree).toMatchSnapshot();});test('variant属性值应为 primary, info, success, warning, error 中的一个', () => {const store = mockStore(initialState);for (const variant in BadgeVariant) {if (BadgeVariant[variant]) {const tree = renderer.create(<Appcontext={{fetch: () => {return;},store,client: {},}}><Badge variant={BadgeVariant[variant]}>职问</Badge></App>,).toJSON();expect(tree).toMatchSnapshot();}}});
配置文件
.editorconfig 文件
123456789101112root = true[*]indent_style = spaceindent_size = 2end_of_line = lfcharset = utf-8trim_trailing_whitespace = trueinsert_final_newline = true# editorconfig-tools is unable to ignore long strings or urlsmax_line_length = null.prettierrc 文件
1234{"singleQuote": true,"trailingComma": "all"}.stylelintrc 文件
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196{extends: 'stylelint-config-standard',plugins: ['stylelint-order',],rules: {'property-no-unknown': [true,{ignoreProperties: ['composes',],},],'selector-pseudo-class-no-unknown': [true,{ignorePseudoClasses: ['global',],},],'string-quotes': 'single','order/order': ['custom-properties','dollar-variables','declarations','at-rules','rules',],'order/properties-order': ['composes','position','top','right','bottom','left','z-index','display','align-content','align-items','align-self','flex','flex-basis','flex-direction','flex-flow','flex-grow','flex-shrink','flex-wrap','justify-content','order','float','width','height','max-width','max-height','min-width','min-height','padding','padding-top','padding-right','padding-bottom','padding-left','margin','margin-top','margin-right','margin-bottom','margin-left','margin-collapse','margin-top-collapse','margin-right-collapse','margin-bottom-collapse','margin-left-collapse','overflow','overflow-x','overflow-y','clip','clear','font','font-family','font-size','font-smoothing','osx-font-smoothing','font-style','font-weight','hyphens','src','line-height','letter-spacing','word-spacing','color','text-align','text-decoration','text-indent','text-overflow','text-rendering','text-size-adjust','text-shadow','text-transform','word-break','word-wrap','white-space','vertical-align','list-style','list-style-type','list-style-position','list-style-image','pointer-events','cursor','background','background-attachment','background-color','background-image','background-position','background-repeat','background-size','border','border-collapse','border-top','border-right','border-bottom','border-left','border-color','border-image','border-top-color','border-right-color','border-bottom-color','border-left-color','border-spacing','border-style','border-top-style','border-right-style','border-bottom-style','border-left-style','border-width','border-top-width','border-right-width','border-bottom-width','border-left-width','border-radius','border-top-right-radius','border-bottom-right-radius','border-bottom-left-radius','border-top-left-radius','border-radius-topright','border-radius-bottomright','border-radius-bottomleft','border-radius-topleft','content','quotes','outline','outline-offset','outline-width','outline-style','outline-color','opacity','filter','visibility','size','zoom','transform','box-align','box-flex','box-orient','box-pack','box-shadow','box-sizing','table-layout','animation','animation-delay','animation-duration','animation-iteration-count','animation-name','animation-play-state','animation-timing-function','animation-fill-mode','transition','transition-delay','transition-duration','transition-property','transition-timing-function','background-clip','backface-visibility','resize','appearance','user-select','interpolation-mode','direction','marks','page','set-link-source','unicode-bidi','speak',],},}tslint.json 文件
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152{{"extends": ["tslint:latest", "tslint-config-prettier", "tslint-react"],"rules": {"interface-name": [true, "never-prefix"],"no-submodule-imports": false,"jsx-boolean-value": false,"jsx-no-multiline-js": false,"jsx-wrap-multiline": false,"class-name": true,"comment-format": [true, "check-space"],"curly": true,"indent": [true, "spaces"],"one-line": [true, "check-open-brace", "check-whitespace"],"no-var-keyword": true,"quotemark": [true, "single", "avoid-escape", "jsx-double"],"semicolon": [true, "always", "ignore-bound-class-methods"],"whitespace": [true,"check-branch","check-decl","check-operator","check-module","check-separator","check-type"],"typedef-whitespace": [true,{"call-signature": "nospace","index-signature": "nospace","parameter": "nospace","property-declaration": "nospace","variable-declaration": "nospace"},{"call-signature": "onespace","index-signature": "onespace","parameter": "onespace","property-declaration": "onespace","variable-declaration": "onespace"}],"no-internal-module": true,"no-trailing-whitespace": true,"no-null-keyword": true,"prefer-const": true,"jsdoc-format": true,"object-literal-sort-keys": false}}}tsConfig.json 文件
1234567891011121314151617181920212223242526272829{"compilerOptions": {"outDir": "build/dist","module": "esnext","target": "es5","lib": ["es7", "dom"],"sourceMap": true,"allowJs": true,"jsx": "react","moduleResolution": "node","rootDirs": ["src", "config"],"forceConsistentCasingInFileNames": true,"noImplicitReturns": true,"noImplicitThis": true,"noImplicitAny": true,"strictNullChecks": true,"suppressImplicitAnyIndexErrors": true,"noUnusedLocals": true},"exclude": ["node_modules","build","scripts","acceptance-tests","webpack","jest","src/setupTests.ts"]}package.json 文件
1234567891011121314151617181920212223242526272829{..."lint-staged": {"*.{json,md,graphql}": ["prettier --write","git add"],"*.{ts,tsx}": ["prettier --write","tslint --fix","git add"],"*.{css,less,scss,sass,sss}": ["prettier --write","stylelint --fix","git add"]},"scripts": {"precommit": "lint-staged","lint": "yarn run lint-ts && yarn run lint-css","fix": "yarn run fix-ts && yarn run fix-css","lint-ts": "tslint 'src/**/*.{ts,tsx}'","fix-ts": "tslint --fix 'src/**/*.{ts,tsx}'","lint-css": "stylelint 'src/**/*.{css,less,scss,sass,sss}'","fix-css": "stylelint --fix 'src/**/*.{css,less,scss,sass,sss}'",...}}jest.config.js 文件
12345678910111213141516171819202122232425module.exports = {automock: false,browser: false,bail: false,collectCoverageFrom: ['src/**/*.{ts,tsx}','!**/node_modules/**','!**/vendor/**',],coverageDirectory: '<rootDir>/coverage',globals: {__DEV__: true,},moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],moduleNameMapper: {'\\.(css|less|scss|sss)$': 'identity-obj-proxy','\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':'GlobalImageStub',},transform: {'^.+\\.tsx?$': 'ts-jest',},testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',verbose: true,};
持续集成 CI
- 参考 CircleCi
- 登录 CircleCi,进入 Projects,Add Project,找到项目,Follow Project,Builds 中运行
- 登录 GitHub,github setting branches,require status check,ci/circleci
- 项目中配置 .circleci/config.yml 文件如下
CI 中需配置环境变量
123456789101112131415161718192021222324252627282930version: 2jobs:build:working_directory: ~/repodocker:- image: circleci/node:lateststeps:- checkout- restore_cache:keys:- v1-dependencies-{{ checksum "package.json" }}- v1-dependencies-- run: yarn install- save_cache:paths:- node_moduleskey: v1-dependencies-{{ checksum "package.json" }}- run: yarn run lint- run: yarn run test- run:name: yarn buildcommand: |if [ "$CIRCLE_BRANCH" != "develop" ] && [ "$CIRCLE_BRANCH" != "master" ]; thenyarn build;fi- store_artifacts:path: builddestination: build- store_test_results:path: coverage在运行测试时
yarn test
命令有时会带参数yarn test --maxWorkers 2
,Jest 官方文档描述如下:设定测试会使用的最大 worker 数目。 默认会使用你的计算机上可用的内核的数量。 在类似 CI 等有资源限制的环境下需要进行相关调整时很有用。但多数场景都应该使用默认值。
注意:TypeScript 2.7 支持 import React from 'react'
的方式,需要在 ts.config 中配置 "module": "commonjs"
"esModuleInterop": true
。
Git-基础2
git diff
工作区:就是你在电脑里能看到的目录
暂存区:git add 是把文件到暂存区
本地仓库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库
git diff 进行对比时不会对比新创建的文件。
git diff branchName
比较当前分支与 branchName 分支git diff branchName1 branchName2
比较 branchName1 分支与 branchName2 分支git diff develop origin/develop
比较本地 develop 分支和远程 develop 分支的差别git diff --staged
比较暂存区与 HEADgit diff HEAD
比较工作区与 HEADgit diff
比较工作区与暂存区
git commit
git commit
提交所有添加到索引库中、或从索引库删除的文件
输入 i 进入编辑状态,第一行添加的内容作为 summary 描述, 隔一个空行,再添加的内容作为 description 描述,按 esc 退出编辑状态,输入 shift + :,输入 wq,保存并提交。git commit -m "summary 描述"
提交所有添加到索引库中、或从索引库删除的文件
简化版提交命令,-m 添加 summary 描述,不能添加 description 描述。git commit -a
提交所有已在索引库中发生了修改的文件,新添加的文件不会被提交
输入 i 进入编辑状态,第一行添加的内容作为 summary 描述, 隔一个空行,再添加的内容作为 description 描述,按 esc 退出编辑状态,输入 shift + :,输入 wq,保存并提交。git commit --amend
产生一个新的提交用来替换掉当前指向的这个提交
输入 i 进入编辑状态,第一行添加的内容作为 summary 描述, 隔一个空行,再添加的内容作为 description 描述,按 esc 退出编辑状态,输入 shift + :,输入 wq,保存并提交。git commit --fixup [commitId]
提交所有添加到索引库中、或从索引库删除的文件
提交一个新的 commit,summary 为指定 commitId 的 summary 添加了 “fixup!” 前缀。在执行rebase --autosquash
命令时针对该次 commit 的操作默认为 fixup,本次提交信息会被清除,不会进入编辑提交信息的状态。git commit --squash [commitId]
提交所有添加到索引库中、或从索引库删除的文件
提交一个新的 commit,summary 为指定 commitId 的 summary 添加了 squash!” 前缀。在执行rebase --autosquash
命令时针对该次 commit 的操作默认为 squash,会进入编辑提交信息的状态。
git rebase
类似于 git merge,将指定分支合并到当前分支。区别是 git merge 会创建一次新的提交记录,git rebase 不会,它会把当前分支里的每个提交取消掉,并且把它们临时保存为补丁(这些补丁放到”.git/rebase”目录中),然后把当前分支更新为最新的指定分支,最后再把保存的这些补丁应用到当前分支上,形成一个线性的历史记录。
“-i” 是指交互模式,就是说你可以干预 rebase 这个事务的过程,包括设置 commit message,删除 commit 等等。
“–autosquash” 是自动合并 commit 的参数。
git rebase -i --autosquash [branchName]
当前在特性分支上执行,合并 branchName 分支到当前分支
git rebase -i --autosquash [branchName] [feature]
无需在特性分支上执行
git rebase --abort
终止 rebase 的行动,回到 rebase 前的状态
特性分支合并了最新的主分支后,原有的 commit 是逐个应用到当前分支的,当正常执行的 commit 与已有代码发生冲突时,需执行下面三部,直到 rebase 成功。
① 解决冲突
② git add .
③ git rebase --continue
变基操作会改变提交历史,如果分支已经推送到远程分支,需要使用 git push --force
强制推送到远程。如果有其它人跟你一起在该分支上协同开发,需使用破坏性没那么强的 --force-with-lease
参数,如果远程有其它人推送了新的提交,那么推送将被拒绝。在使用 git push --force-with-lease
命令被拒绝时,需要 fetch 仓库,然后确认其它人是否对此分支有新的修改,如果没有你才可以继续强制推送。
git hooks
pre-commit
在键入提交信息前运行,被用来检查即将提交的快照,当从该挂钩返回非零值时,Git 放弃此次提交,但可以用 git commit –no-verify 来忽略prepare-commit-msg
在提交信息编辑器显示之前,默认信息被创建之后运行,该挂钩对通常的提交来说不是很有用,只在自动产生默认提交信息的情况下有作用,如提交信息模板、合并、压缩和修订提交等commit-msg
接收一个参数,此参数是包含最近提交信息的临时文件的路径,如果该挂钩脚本以非零退出,Git 放弃提交,可以用来在提交通过前验证项目状态或提交信息post-commit
挂钩在整个提交过程完成后运行,作为通知之类使用的pre-rebase
在衍合前运行,脚本以非零退出可以中止衍合的过程,可以使用这个挂钩来禁止衍合已经推送的提交对象post-rewrite
被那些会替换提交记录的命令调用,如 git commit –amend 和 git rebasepost-checkout
git checkout 成功运行后,该挂钩会被调用,可以用来为你的项目环境设置合适的工作目录post-merge
在merge命令成功执行后,该挂钩会被调用,可以用来在 Git 无法跟踪的工作树中恢复数据pre-push
会在 git push 运行期间,更新了远程引用但尚未传送对象时被调用,可以在推送开始之前,用它验证对引用的更新操作
cd .git/hooks
可查看 git 所有 hooks
git fork 机制
使用场景:如果想要修改他人 github 的项目,直接 clone 代码到本地是不能 pull 或 push 的,所以要使用 fork,先把他人代码 fork 到自己的 github 仓库,然后 clone 到本地修改,然后再提交到自己 github 仓库,这时候想要把修改的代码提交给他人的话,就可以在自己的 github 上提交 pull request,等其他人看到后就可以把代码做一个合并。当他人的代码有更新的时候,也可以将更新同步到自己的 github 仓库。
github 的 fork
在 github 上,fork 一个项目,如 lodash,如果远程有更新,红框内会有提示,点击 Pull request,base fork 选则本地仓库,head fork 选则 lodash 仓库,可将 lodash 的更新通过一个 pull request 合并到自己的仓库中。也可以通过交换 base fork 和 head fork 提交一个 pull request 到 lodash 仓库。
git 命令
在 github 创建一个仓库命名为 local-lodash 后,执行下面操作:git clone https://github.com/lodash/lodash.git local-lodash
克隆远程 lodash 仓库到本地git remote remove origin
删除远程仓库地址git remote add origin https://github.com/zhulichao/local-lodash.git
添加自己的远程仓库地址git remote add upstream https://github.com/lodash/lodash.git
添加上游远程仓库地址git push -u origin master
初次提交到自己的远程仓库git pull upstream master
同步上游仓库 lodash 的更新git push origin
提交到自己的远程仓库git remote -v
查看远程仓库地址,pull 和 push 可以是两个不同的地址
这种方式与在 github 上执行 fork 还是有区别的,如图所示,没有上面图片中红框中的内容,在 github 上是无法通过 pull request 同步远程 lodash 更新的,当然也不能提交 pull request 到远程 lodash,只能通过上面的命令 git pull upstream master
同步远程 lodash 的更新。如果你恰好也是远程 lodash 的贡献者,也可以通过 git push upstream master
直接提交 commit 到 远程 lodash。
git 分支
删除所有已经不在远程仓库维护的分支git fetch -p && for branch in
git branch -vv | grep ‘: gone]’ | awk ‘{print $1}’; do git branch -D $branch; done
《精益创业》笔记(未完成)
统计基础
基础概念
数据集是由数据组成的集合,数据质量越好价值也越高。
统计能够帮助我们:量化不确定性(quantify uncertainty)、辨别偏见(discern biases)。
需要考虑数据是哪里来的、是如何计算的,需要辨别哪些数据好、哪些数据不好,这些数据是做出决定的正确数据吗。
一手数据是自己从自然环境或实验中调查收集到的;二手数据就是现成的数据,从别人手里拿到的数据。
整理后的数据能够提供服务和方便、帮助做决定、有说服力、节省时间和金钱。
数据的中心(The Center of the Data)
平均值(Mean/Average)
数据的总和除以数据的个数。
中位数(Median)
有相同数量的高于该点的数据和低于该点的数据,如果数据的个数是偶数个,Median 是中间两个数的平均值。
加权平均数(Weighted Mean)
将各数值乘以相应的权重,然后求和得到总体值,再除以总的权重和。
场景:课程总分由多部分构成,考试成绩、出勤情况、完成练习程度
众数(Mode)
一组数据中出现最多的那个数,表示最有可能出现的数。
数据的变化性(Data Variability)
极差/范围误差/全距(Range)
数据集中最大值与最小值的差。
标准差/均方差(Standard Deviation)
标准差是离均值平方的算术平均数的平方根。标准差能反映一个数据集的离散程度,一个较大的标准差,代表大部分数值和其平均值之间差异较大;一个较小的标准差,代表这些数值较接近平均值。方差是标准差的平方。
离群值(Outlier)
离群点是指一个数据集中,远离序列的一般水平的极端大值和极端小值。可通过表格和图标、标准差、z分数可以帮助我们识别离群点。
离群点没有明确的定义,离群点可以被认为是机会,它们是新趋势的开端。关于离群点,需要考虑这是一个真的离群点吗,它是怎么产生的,其中能学到什么,需要改进什么。
分布和相对位置(Distribution and Relative Position)
标准分数/z分数(Z-Score)
z分数是以标准差为单位度量原始分数离开平均数多少个标准差,正数表示大于平均数,负数表示小于平均数。
经验规则(Empirical Rule/Three Sigma Rule)
这里的 Sigma 代表标准差,作用于对称分布,对称分布通常用一个以均值为中心的钟形曲线。
经验规则是统计规律,指出了在正态分布,几乎所有数据都将落在均值的三倍标准差内。经验规则表明,68%的数据将分布在距离均值的一个标准差之内,95%的数据将分布在距离均值的两个标准差之内,99.7%的数据将分布在均值的三个标准偏差之内。
经验法则最常在统计中用于预测最后结果。在得到数据的标准差,并在可以收集确切的数据之前,该规则可作为一个对即将到来的数据的结果的粗略估计。该概率特别适用与一些需要消耗大量时间去收集的数据,或者甚至是不可能获得的数据。
百分位数(Percentile Rank)
将一组数据从小到大排序,并计算相应的累计百分位,则某一百分位所对应数据的值就称为这一百分位的百分位数,可表示为:一组n个观测值按数值大小排列,如处于p%位置的值称第p百分位数,中位数是第50百分位数。第100百分位数是不可能的,因为那就是说你是班级中的前0%,既可以说你是班级的前1%,也可以说你是第99百分位数。
概率(Probability)
概率是事件发生的可能性,样本空间所有可能结果的概率之和一定是100%,概率分为客观概率和主观概率。客观概率是基于计算的,又分为:古典概率/事前概率,所有可能是已知的,并且出现概率相同,如投币;经验概率,特定的事件发生的次数占总体实验样本的比率,如估算任意一年的二月份中最低的日最高温度低于零度的概率,罚球命中率。主观概率没办法计算,无法重复,建立在过去的经验与判断的基础上,根据对未来事态发展的预测和历史统计资料的研究确定的概率,如解雇率。
多事件概率(Multiple Event Probability)
事件1/事件2出现的概率 = 事件1出现的概率 + 事件2出现的概率 - 事件1和事件2共同出现的概率。
条件概率,事件1已经发生的情况下,事件2出现的概率。两个事件 A 和 B 是独立的当且仅当 Pr(A∩B) = Pr(A)Pr(B)。
贝叶斯定理(Bayes theorem)
贝叶斯经典问题
Monty Hall problem: 出自美国的电视游戏节 Let’s Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。参赛者会看见三扇关闭的门,其中一扇的后面有辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门参赛者可以赢得汽车的概率是多少?
答案:简单的想选择当前门赢汽车的概率是1/3,选择另外两扇门赢汽车的概率是2/3,其中一扇已经打开了,换成另一个门赢汽车的概率就是2/3了。
毒品检测问题。已知一家公司 0.5% 的员工吸毒。现在引入一个毒品检测的方式,对吸毒者检测为阳性的概率是 99%,非吸毒者检测出阳性的概率是 1%。已知一个公司的员工被检测出阳性,那么他吸毒的概率是多少?
答案:使用贝叶斯公式
事物是如何排列的(How Objects Are Arranged)
排列(Permutation)
总数为n,有序的排列,有多少可能 = n!
总数为n,选出有序的x个,有多少可能 = n! / (n - x)!
组合(Combination)
总数为n,选出无序的x个,有多少可能 = n! / [(n - x)! * x!]
离散与连续(Discrete VS. Continuous)
实验最终出现的结果是未知的、随机的,因此实验的结果叫做随机变量。随机变量表示随机试验各种结果的实值单值函数,随机事件不论与数量是否直接有关,都可以数量化,即都能用数量化的方式表达。离散指的是有限多种可能性,自然数或整数计算。连续指的是有无限多种可能性,是某个区间内的任一实数。因为连续随机变量的取值有无限多种可能,因此需要使用一种替代的方式来计算这种类型的概率。
离散概率分布(Discrete Probability Distributions)
连续概率分布(Continuous Probability Distributions)
概率密度(Probability densities)
结果的可能是无限时,用曲线来描述结果的分布,这个曲线被叫做概率密度,曲线下的区域表示每种可能的结果。
钟形曲线(Bell-shaped curve)
正态分布。
模糊中心极限定理(Fuzzy central limit theorem)
一些现象受到许多相互独立的随机因素的影响,如果每个因素所产生的影响都很微小时,总的影响可以看作是服从正态分布的。