不觉

不觉 查看完整档案

上海编辑  |  填写毕业院校  |  填写所在公司/组织 bigdreamer.cc 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

不觉 发布了文章 · 7月22日

你是怎么分割银行卡号的,通用方法?

你是怎么分割银行卡号的,通用方法?

说明,一下代码均通过ts编写,请再运行时先搭建好ts运行时环境。

方法一

// 正则分割任意字符串

interface IStrType {
    length?: number
}

// 约束泛型
export interface IFunc<T extends IStrType = string, U = number, Z = string>{
    ( value:T | number, limits: U, suffix: Z ): string
}

const regExpFormatStr: IFunc
    = (value, limits, suffix) => {
   const _regExp: RegExp = new RegExp(`.(?=(.{${limits}})+$)`, 'g');
   return  (typeof value !== "number" && value?.length ? value : value.toString()).replace(_regExp, $n1 => {
        return `${$n1}${suffix}`
    });
}

方法二(有点cuo)

/*
 * @desc 处理数字,并且添加分割
 */

interface IToolFunction<T, U> {
    (target: T, limits?: number): U
}

type ITF<T, U> = IToolFunction<T, U>;

const numHandler: ITF<any, string> = (target: any, limits: number = 4) => {
    // 初始化数组
    let _$: string[] = target.toString().split('');
    // 记录原始数组长
    let _len: number = _$.length;
    // 新格式临时数组
    let _format: string[] = [];

    // loop
    for(let i: number = 0; i < _len; i++) {
        // 轮询数组长
        let _loopLen: number = _$.length;
        if(_loopLen % limits === 0) { _format.push(_$.splice(0, limits).join('')); }
        _format.push(_$.splice(0, _loopLen % limits).join(''));
    }

    return _format.join(' ');
}

小结

第一种方法提供了分割字符串的串、分割单位、分割符。第二种方法默认空格,如果你有好的方法,欢迎提供,如果需要转载,请注明出处。

查看原文

赞 0 收藏 0 评论 0

不觉 赞了文章 · 7月4日

程序员练级攻略(2018):前端性能优化和框架

图片描述

这个是我订阅 陈皓老师在极客上的专栏《左耳听风》,我整理出来是为了自己方便学习,同时也分享给你们一起学习,当然如果有兴趣,可以去订阅,为了避免广告嫌疑,我这就不多说了!以下第一人称是指陈皓老师。

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!

前端性能优化

首先是推荐几本前端性能优化方面的图书。

  • Web Performance in Action ,这本书目前国内没有卖的。你可以看电子版本,我觉得是一本很不错的书,其中有 CSS、图片、字体、JavaScript 性能调优等。
  • Designing for Performance ,这本在线的电子书很不错,其中讲了很多网页优化的技术和相关的工具,可以让你对整体网页性能优化有所了解。
  • High Performance JavaScript ,这本书在国内可以买到,能让你了解如何提升各方面的性能,包括代码的加载、运行、DOM 交互、页面生存周期等。雅虎的前端工程师尼古拉斯·扎卡斯(Nicholas C. Zakas)和其他五位 JavaScript 专家介绍了页面代码加载的最佳方法和编程技巧,来帮助你编写更为高效和快速的代码。你还会了解到构建和部署文件到生产环境的最佳实践,以及有助于定位线上问题的工具。
  • High Performance Web Sites: Essential Knowledge for Front-End Engineers ,这本书国内也有卖,翻译版为《高性能网站建设指南:前端工程师技能精髓》。作者给出了 14 条具体的优化原则,每一条原则都配以范例佐证,并提供了在线支持。

全书内容丰富,主要包括减少 HTTP 请求、Edge Computing 技术、Expires Header 技术、gzip 组件、CSS 和 JavaScript 最佳实践、主页内联、Domain 最小化、JavaScript 优化、避免重定向的技巧、删除重复 JavaScript 的技巧、关闭 ETags 的技巧、Ajax 缓存技术和最小化技术等。

接下来,重点推荐一个性能优化的案例学习网站 WPO Stats 。WPO 是 Web Performance Optimization 的缩写,这个网站上有很多很不错的性能优化的案例分享,一定可以帮助你很多。

然后是一些文章和案例。

接下来是一些性能工具。在线性能测试分析工具太多,这里只推荐比较权威的。

  • PageSpeed ,谷歌有一组 PageSpeed 工具来帮助你分析和优化网站的性能。Google 出品的,质量相当有保证。
  • YSlow ,雅虎的一个网页分析工具。
  • GTmetrix ,是一个将 PageSpeed 和 YSlow 合并起来的一个网页分析工具,并且加上一些 Page load 或是其它的一些分析。也是一个很不错的分析工具。
  • Awesome WPO ,在 GitHub 上的这个 Awesome 中,你可以找到更多的性能优化工具和资源。

另外,中国的网络有各种问题(你懂的),所以,你不能使用 Google 共享的 JavaScript 链接来提速,你得用中国自己的。你可以到这里看看中国的共享库资源,Forget Google and Use These Hosted JavaScript Libraries in China

前端框架

接下来,要学习的是 Web 前端的几大框架。目前而言,前端社区有三大框架 Angular.js、React.js 和 Vue.js。我认为,React 和 Vue 更为强劲一些,所以,我这里只写和 React 和 Vue 相关的攻略。关于两者的比较,网上有好多文章。我这里推荐几篇我觉得还不错的,供你参考。

其实,比较这些框架的优缺点还有利弊并不是要比出个输赢,而是让你了解一下不同框架的优缺点。我觉得,这些框架都是可以学习的。而在我们生活工作中具体要用哪个框架,最好还是要有一些出发点,比如,你是为了找份好的工作,为了快速地搭一个网站,为了改造一个大规模的前端系统,还是纯粹地为了学习……

不同的目的会导致不同的决定。我并不希望上述的这些比较会让你进入"二选一"或是"三选一"的境地。我只是想通过这些文章让你知道这些框架的设计思路和实现原理,这些才是让你受益一辈子的事。

React.js 框架

下面先来学习一下 React.js 框架。

入门

React 学起来并不复杂,就看 React 官方教程 和其文档就好了( React 的中文教程 )。

然后,下面的文章会带你了解一下 React.js 的基本原理。

提高

学习一个技术最重要的是要学到其中的思想和方法。下面是一些我觉得学习 React 中最重要的东西。

资源列表

最后就是 React 的资源列表。

  • Awesome React ,这是一些 React 相关资源的列表,很大很全。
  • React/Redux Links ,这也是 React 相关的资源列表,与上面不一样的是,这个列表主要收集了大量的文章,其中讲述了很多 React 知识和技术,比上面的列表好很多。
  • React Rocks ,这个网站主要收集各种 React 的组件示例,可以让你大开眼界。

Vue.js 框架

Vue 可能是一个更符合前端工程师习惯的框架。不像 React.js 那样使用函数式编程方式,是后端程序员的思路。

最令人高兴的是,Vue 的作者是我的好朋友尤雨溪(Evan You),最近一次对他的采访 “Vue on 2018 - Interview with Evan You” 当中有很多故事以及对 Vue 的展望。(注意:Vue 是完全由其支持者和用户资助的,这意味着它更接近社区而不受大公司的控制。)

要学习 Vue 并不难,我认为上官网看文档( Vue 官方文档中文版)),照着搞一搞就可以很快上手了。Vue.js screencasts 是一个很不错的英文视频教程。

另外,推荐 新手向:Vue 2.0 的建议学习顺序 ,这是 Vue 作者写的,所以有特殊意义。

Vue 的确比较简单,有 Web 开发经验的人上手也比较快,所以这里也不会像 React 那样给出很多的资料。下面是一些我觉得还不错的内容,推荐给你。

当然,最后一定还有 Awesome Vue ,Vue.js 里最为巨大最为优秀的资源列表。

小结

总结一下今天的内容。我先介绍的是前端性能优化方面的内容,推荐了图书、最佳实践性的文档、案例,以及一些在线性能测试分析工具。随后重点讲述了 React 和 Vue 两大前端框架,给出了大量的文章、教程和相关资源列表。我认为,React.js 使用函数式编程方式,更加符合后端程序员的思路,而 Vue 是更符合前端工程师习惯的框架。因此,两者比较起来,Vue 会更容易上手一些。

以上是陈皓老师分享的,结合上一篇其实内容是很多的,这个不是一时就能看完的,如果你不想当一辈子的码农,不只只是搬砖的,那我们目标是更具有创造的工程师,架构师,这些内容是值得我们花10年、20年,甚至一身要去学习的,希望大家有好东西也分享出来一起学习哈!

ps: 如果你想成为一名高级的程序员(工程师),英文能力是不可缺少的,平时也需要加强英文的学习!

你的点赞是我持续分享好东西的动力,欢迎点赞!

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 117 收藏 89 评论 8

不觉 发布了文章 · 6月23日

React阿里OSS上传组件

Ali-OSS-Img-Upload




Github 地址

传送门

Install

$ npm i ali-img-upload --save

Description

本组件接入了阿里云OSS SDK,配合antd Upload上传组件,进行分片上传图片至阿里云OSS对象存储。

组件使用了持久化授权,官网提供了如何去设置临时授权模式,你可以在本组件基础进行二次开发。

How To Use?


import React from 'react';
import { AliUpload } from 'ali-img-upload';
import { Form } from 'antd';

export default function Demo() {

    // antd form 实例
     const [form] = Form.useForm();

    return (
        <AliUpload
            ossConfig={{
                 region: '',
                 accessKeyId: '',
                 accessKeySecret: '',
                 bucket: ''
            }}
            baseURL={""}
            form={form}
        />
    )
}

项目接入方案(以create-react-app创建的项目为例)

采坑记:create-react-app内部配置的babel是不支持的编译node_modules的jsx语法与less的,所以借助cra配置。

  • 第一步:安装customize-crareact-app-rewired
  • 修改package.json
{
 "scripts": {
     "start": "react-app-rewired start",
     "build": "react-app-rewired build",
     "test": "react-app-rewired test"
  }
}
  • 创建config-overrides.js
const { override, addWebpackAlias, addWebpackModuleRule, babelInclude, addLessLoader } = require('customize-cra');
const path = require('path');
const resolve = dir => path.resolve(__dirname, dir);

module.exports = {
    webpack: override(

        addLessLoader({
            javascriptEnabled: true,
            // modifyVars: { '@primary-color': '#1DA57A' },
        }),
        addWebpackAlias({
            "@": resolve("src")
        }),
        // addBabelPreset({
        //     test: ["./node_modules/ali-img-upload"],
        //     presets: ["@babel/preset-react"]
        // })
        addWebpackModuleRule({
            test: /\.less$/,
            include: resolve('node_modules'),
            use: ['style-loader', 'css-loader', 'less-loader'],
        }),
        babelInclude([
            resolve('src'),
            resolve('node_modules')
        ])
    )
}

Note

  • 注意在重写babel配置的时候,对于less处理的loader顺序。loader编译的顺序始终是从下往上的,也即从右往左。
  • include部分应始终包含node_modules和src两部分。

Demo

alt

LICENSE MIT

Copyright By Eric Wang

查看原文

赞 0 收藏 0 评论 0

不觉 发布了文章 · 6月23日

webstorm配置TS运行时环境与自动给编译

webstorm配置TS运行时环境与自动编译

Step 1 安装TypeScipt

$ npm i typescript -g

Step 2 设置webstorm language

step2

Step 3 设置File Watch

  • 添加file watch类型

step3

  • file watch详细配置

step4

output path说明:$FileDir$$FileNameWithoutExtension$.js:$FileNameWithoutExtension$.js.map

Step 4 tsconfig.json

{
  "compilerOptions": {
    "module": "esnext",
    "target": "es5",
    "sourceMap": true,
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "noEmit": true,
    "jsx": "react",
    "isolatedModules": true
  },
  "exclude": [
    "node_modules"
  ],
  "include": [
    "src"
  ]
}

End

到这里,整个踩坑就结束了,TS配置应该可以自动编译了。


如何直接编译.ts文件呢?

下载ts-node


$ npm i ts-node -g

命令行运行

$ ts-node xxx.ts

webstorm如何配置?

因为这样配置会导致,中文编码乱码,个人用过很多方法,但是都基本无效。所以此处不做总结。

查看原文

赞 0 收藏 0 评论 0

不觉 收藏了文章 · 5月31日

React + TS

如果有任何错误,请务必指出。
参考链接:https://github.com/typescript...

(一)构建基础的组件文件

  1. 首先我们需要导入 reactimport * as React from 'react'
  2. 为每个文件创建一个类组件。在一个文件内请尽量只写一个类组件,其他的请使用函数组件

    // ReactTs.tsx
    class TsExample extends React.Component<any, any> {}

    使用 extends 来继承 React.component 接口

  3. 我们注意到 React.Component 后面带了 <any, any> 这个奇怪的字符串。这是 React.Component 接口的类型参数。第一个是 props 的类型,第二个是 state 的类型。
  4. 为了给 propsstate 定义类型,我们可以使用 interface 关键字。我们可以将类型定义写在当前的组件文件,但是由于在当前文件中具有了 import 关键字,当前文件会成为一个模块,即无法被全局引用。所以建议新建一个文件 index.d.ts 来储存 interface\type

    // index.d.ts
    interface ITsExampleProps {
      name: string
    }

    注:exportimport 会将一个文件变成模块,即里面的变量不会被暴露到全局

  5. 接下来我们为这个组件初始化 state

    // ReactTs.tsx
    class TsExample extends React.Component<ITsExampleProps, ITsExampleState> {
      public state = {
        count: 0
      }
    }

    此处建议不要 public state = {}constructor 两种方法混用

  6. 为组件添加 UI:每个类组件都应该有一个 render 方法来进行 UI 渲染。render 方法中必须返回一个 ReactNodeReactNode 可以是一个HTMLElement 或者一个字符串,数字

    // ReactTs.tsx
    class TsExample extends React.Component<ITsExampleProps, ITsExampleState> {
      public state = {
        count: 0
      }
    
      public render() {
        let { count } = this.state
        return <div styleName="wrap">这里是 ui</div>
      }
    }
  7. 为组件添加并调用方法,使用 state

    // ReactTs.tsx
    class TsExample extends React.Component<IExampleProps, IExampleState> {
      public state = {
        count: 0,
        currentCount: 0
      }
    
      public plusCount = () => {
        this.setState({
          count: ++this.state.count
        })
      }
    
      public render() {
        let { count } = this.state
        return (
          <div styleName="">
            {/* 调用state */}
            <div>{count}</div>
            {/* 调用方法  */}
            <Button onClick={this.plusCount}>增加</Button>
            <div>
              <Button
                onClick={() => {
                  this.showCurrentCount(this.state.count)
                }}
              >
                当前数量
              </Button>
            </div>
          </div>
        )
      }
    }

8) 接下来我们将该组件作为一个子组件放到另一个组件中

// example.tsx
class Example extends React.Component<IExampleProps, IExampleState> {
  public render() {
    return (
      <div styleName="example">
        <ReactTs />
      </div>
    )
  }
}
  1. 为子组件传入一个参数

    // example.tsx
    class Example extends React.Component<IExampleProps, IExampleState> {
        public render() {
            return (
                <div styleName='example'>
                    <ReactTs name="React-Ts" />
                </div>
            )
        }
    }

10) 在子组件中使用参数:this.props.name

// ReactTs.tsx
public render() {
        let {count} = this.state
        return (
            <div styleName='React-Ts'>
                <div>名字:{this.props.name}</div>
                <div>增加数量:{count}</div>
                <div>当前数量:{this.state.currentCount}</div>
                <div><Button onClick={this.plusCount}>增加</Button></div>
                <Button
                                        onClick={() => { this.showCurrentCount(this.state.count) }}>
                      当前数量
                                </Button>
            </div>
        )
    }

(二)使用函数式组件

  1. 一般,在一个文件内,我们应该只写一个类组件,其他的子组件应该是函数式组件。
  2. 函数式组件类型可以使用React.FC,使用这个类型有个好处就是,提醒你必须返回一个ReactNode

    const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => {
      return <div></div>
    }

3) 在函数式组件中,我们无法像在类组件中一样使用state和生命钩子函数,但React提供了HOOK

4) 使用 useState 来存储函数组件的状态。在下面的例子中,我们使用了React.useState,传入color的初始值,返回了一个对象,我们使用解构,获得了color, setColor。其中 color 相当于 this.state.colorsetColor('green')相当于 this.setState({color: 'green'})

const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => {
  const [color, setColor] = React.useState('blue')

  const changeColor = () => {
    setColor('green')
  }

  return (
    <div>
      <div style={{ color }}>函数式组件</div>
      <Button type="primary" className="example-button" onClick={changeColor}>
        点击换色{' '}
      </Button>
    </div>
  )
}
  1. 假如你想像在类组件中使用 componentWillUnmountcomponentDidMountcomponentDidUpdate,你可以使用useEffectuseEffect接受一个回调函数,这个回调函数内代码会在 componentDidMountcomponentDidUpdate时执行。回调函数的返回值应该是一个函数,这个函数会在 componentWillUnmount被执行。

    const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => {
        const [color, setColor] = React.useState('blue')
      const [fontSize, setFontsize] = React.useState('16px')
    
      const changeColor = () => {
        setColor('green')
      }
    
      React.useEffect(() => {
        let timer = setInterval(() => {
          setFontsize('100px')
        }, 10000)
    
        return () => {
          clearInterval(timer)
        }
    
      return (
          <div>
          <div style={{color,fontSize}}>函数式组件</div>
          <Button type='primary' className='example-button' onClick={changeColor}>点击换色        </Button>
        </div>
      )
    }
  2. 假如我们需要操作 DOM,我们可以通过 useRef 来获取。text.current 就是被我们绑定的元素。注意:不允许直接操作 DOM,除非迫不得已

    const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => {
        const [color, setColor] = React.useState('blue')
      const [fontSize, setFontsize] = React.useState('16px')
    
      const text = React.useRef<HTMLDivElement>(null)
    
      const changeColor = () => {
        setColor('green')
      }
    
      const changeBgC = () => {
        (text.current as HTMLDivElement).style.backgroundColor = '#e9e9e9'
      }
    
      React.useEffect(() => {
        let timer = setInterval(() => {
          setFontsize('100px')
        }, 10000)
    
        return () => {
          clearInterval(timer)
        }
    
      return (
          <div>
          <div style={{color,fontSize}}>函数式组件</div>
          <Button type='primary' className='example-button' onClick={changeColor}>点击换色        </Button>
          <Button type='primary' className='example-button' onClick={changeBgC}>点击换背景色</Button>
        </div>
      )
    }

(三) 其他

  1. 有时候我们需要使用到默认参数 defaultProps ,这个默认参数是从多个组件的 props 中提取出来的。这时可以使用 交叉类型 & ,注意,请不要在交叉类型中使用相同的属性,就算使用了,也请不要为两个同名属性定义不同的基础类型,这样将会造成这个属性需要同时满足两种基础类型。
type propsType = typeof defaultProps & {
  count: number
}

const defaultProps = {
  name: 'world'
}

const DefaultPrppsExample = (props: propsType) => {
    return (
      <div>
        {props.name}
        {props.count}
      </div>
    )
  }

  // 在另一个组件中使用
;<DefaultPrppsExample count={1} name={'默认参数示例'} />
  1. createRefforwardRef

    在函数式组件中,我们可以使用 useRef 来获取到 React 元素。在类组件中,我们可以使用 createRef 来获取 React 元素。当我们需要获取某个组件中的元素时,使用 forwardRef 来获取这个组件内部的元素。还是不建议直接操纵 DOM 元素

    const RefExample = React.forwardRef((props: React.CSSProperties, ref: React.Ref<HTMLInputElement>) => {
        return <input style={{ ...props}} ref={ref} />
    })
    
    class TsExample extends React.Component<ITsExampleProps, ITsExampleState> {
      ...
      public state = {
        inputRef: React.createRef()
      }
    
        public getFocus = () => {
            if (this.state.inputRef) {
                // 注意:这里断言的类型应该是使用 RefObject,因为其里面的具有 current: T | null,
                // 如果使用的是 ReactRef ,这个类型是一个联合类型,里面有可能是 null
                ((this.state.inputRef as React.RefObject<HTMLInputElement>).current as HTMLInputElement).focus()
            }
    
        }
    
         ...
       <div styleName="example-item">
              <h3>使用 createRef 和 forwardRef</h3>
                    <RefExample color='red' ref={this.state.inputRef} />
                    <Button onClick={this.getFocus}>获取焦点</Button>
       </div>
      ...
    }

3) React 默认会把我们的子组件挂载到父组件上。当我们想自己指定挂载的元素时,就需要用到 createPortal 了。

这里写了一个简陋的蒙层组件(样式未完全实现),即使我们是TsExample中使用了这个组件,但这个组件还是会被挂载到一个插入 body 的元素上

注意:这里我们在父组件上绑定了一个点击事件,PortalExample 被点击时依然会冒泡进而触发父组件的点击事件

const PortalExample = (props: IPortarExampleProps) => {
    const [modelDOM, setModelDOM] = React.useState(document.createElement('div'))
    modelDOM.classList.add('modal')
    if (props.visible) {
        document.body.append(modelDOM)
        return ReactDOM.createPortal(
            <div styleName='modal-inner'>
                蒙层
            </div>,
            modelDOM)
    }else {
        if (modelDOM.parentNode) {
            modelDOM.parentNode.removeChild(modelDOM)
        }
        return null
    }

}

class TsExample extends React.Component<ITsExampleProps, ITsExampleState> {
  ...
  <div styleName="example-item" onClick={this.handleModalClick}>
             <h1>使用 createPortal 将子组件挂载到父组件以外的元素内</h1>
             <PortalExample visible={this.state.modalVisible} />
             <Button onClick={this.toggleModal}>切换modal</Button>
    </div>
  ...
}

(四) TS 类型基本使用

我们在使用类型注解的时候往往会使用以下类型

  1. 基本类型包括了js的七种基本类型 string, number, boolean, undefined, null, symbol,还有 void, never
  2. 可以使用 type, interface 来声明变量的形状

1. 联合类型

使用 | 来创造一个联合类型。例如:type pet = 'cat' | 'dog'

当我们需要规定一个函数参数的类型时,又恰巧有一个已经有一个接口已经满足了,我们可以使用 keyof 来快速生成一个联合类型

2. 类型守护

当我们在使用联合类型时,假如两个子类型 A 和 B 都是接口或类等时,我们预想的是要么是 A,要么是 B。但实际上,A&B 也是符合 A|B 的。

这个时候我们需要先用 in 关键字来判断是符合哪个类型再进行操作

interface IMan {
  handsNum: number
}

interface IDog {
  tailNum: number
}

public typeGuarding = (obj: IMan | IDog) => {
    if ('handsNum' in obj) {
      ...
    } else if ('tailNum' in obj) {
      ...
    }
  }

3. 交叉类型

使用 & 来创造一个联合类型,之前已经有示例了

4. 可选类型

可选类型其实不是一个类型,但在接口和函数参数中都有可选这个概念。

interface IPet {
    name: string
  age?: number
}

interface IMan {
  ...
}

const feedPet (pet: IPet, man?: IMan) => {
  ...
}

注意:函数的可选参数必须写在最后面

5. 默认参数

在函数声明中,直接为参数赋予一个值,可以起到默认参数的作用

interface IPetDefault {
    name: string
  age = 1
}

const feedPet (pet: IPet, man={name: '我'}) => {
  ...
}

6.enum

当我们需要使用枚举类型时,就需要用到enum 关键字。默认,第一个的值为 0,然后递增,可以手动指定每一个的值,之后的值也会递增。我们可以通过值查key,也可以通过key 查值

enum animal {
    乌龟,
    鳄鱼,
    麻雀,
    河马
}

animal['乌龟'] // 0
animal[0] // 乌龟

7. 类型断言

当我们给一个函数传入参数时,我们知道这个参数时符合被规定的参数类型的,但编译器是不知道的。为了让编译器能知道类型符合,我们就需要使用类型断言,关键字:as

type animalName = '乌龟' | '鳄鱼' | '麻雀' | '河马'

animal[this.state.animalName as animalName] // 0

类型断言里还有另一个类型:在值的后面使用 ! ,表示这个值不为 null

type animalNameWithNull = '乌龟' | '鳄鱼' | '麻雀' | '河马' | null

animal[(this.state.animalName as animalName)!] // 0

8. 使用类型扩展(type branding)

注:类型扩展是直译的结果,如果你知道这种用法的名字,请修正它

TS 在检查一个类型是否符合是,并不是查找类型的名字,而是比较类型的结构。也就是说,当你为两种类型userIdorderId 都定义了一样的结构:{name: string}。这时候,TS 会判定它们是同一种类型。请看下面的例子:

interface IOrderId {
  name: string
}

interface IUserId {
  name: string
}

let userId: IUserId = {
  name: 'userId'
}
let orderId: IOrderId = userId // ok

这里你是否会疑惑呢?两种不同的类型却可以相互赋值。这就是 结构类型检查 了。假如名字不同,类型就不同的就是 名称类型检查

那我们如何来避免呢?第一种方法是多加一个字段

interface MyUserId {
  name: string
  type: 'user'
}

interface MyOrderId {
  name: string
  type: 'order'
}

let myUserId: MyUserId = {
  name: 'user',
  type: 'user'
}

let myOrderId: MyOrderId = myUserId // error

还有另一种方法,那就是利用 unique symbol 和 交叉类型

type UserIdString = string & { readonly brand: unique symbol }
type OrderIdString = string & { readonly brand: unique symbol }

const getUniqueUserId = (id: string) => {
  return id as UserIdString
}

const getUniqueOrderId = (id: string) => {
  return id as OrderIdString
}

let uniqueUserId: UserIdString = getUniqueUserId('1')
let uniqueOrderId: OrderIdString = uniqueUserId // error

unique symbolsymbol 的子类型。我们在上面用交叉类型将 {readonly brand: unique symbol} 也加入到类型中去,这样,两种类型就算结构一样,它们也是不相同的

9. 函数重载

函数重载用于我们的函数拥有不同的输入或输出时

第一个函数声明应该是最精确的,因为编译器是从前往后开始匹配的。函数实现应该是兼容各个函数声明的。

这里函数重载的意义在于,如果们直接使用第三种声明,那么传入 3 个参数也是可以接受的,但是据我们所知, padding 是不接受 3 个参数的

// 重载
function padding(all: number)
function padding(topAndBottom: number, leftAndRight: number)
function padding(top: number, right: number, bottom: number, left: number)
// Actual implementation that is a true representation of all the cases the function body needs to handle
function padding(a: number, b?: number, c?: number, d?: number) {
  if (b === undefined && c === undefined && d === undefined) {
    b = c = d = a
  } else if (c === undefined && d === undefined) {
    c = a
    d = b
  }
  return {
    top: a,
    right: b,
    bottom: c,
    left: d
  }
}

10. 使用 typeof 关键字快速定义一个类型

有时候,我们在前面定义了一个变量,并使用类型推导(也就是说,这时候我们并没有明确地给它声明类型)。然后我们想要使用这个变量的类型了,怎么办?回去定义一下类型吗?使用 typeof 变量名 就可以获取到这个变量的类型了

11. 工具泛型的使用

TS 中内置了一些语法糖给我们使用,例如 Partial、Omit、Exclude

这里举例 partial 的用法:Patial 关键字可以让将一个接口内部的属性变成可选

interface dog {
  name: string
  bark: boolean
}

type littleDog = Partial<dog>

let dog1: littleDog = {
  name: 'dog1'
}

let dog2: littleDog = {
  bark: false
}

12. 模块声明

当我们需要引入某些模块时,这些模块又没有被声明过,这个时候就会报错了。我们只需要使用

decleare module 模块名 就可解决

decleare module "*.png"
import * as logo from 'logo.png'
查看原文

不觉 赞了文章 · 5月31日

React + TS

如果有任何错误,请务必指出。
参考链接:https://github.com/typescript...

(一)构建基础的组件文件

  1. 首先我们需要导入 reactimport * as React from 'react'
  2. 为每个文件创建一个类组件。在一个文件内请尽量只写一个类组件,其他的请使用函数组件

    // ReactTs.tsx
    class TsExample extends React.Component<any, any> {}

    使用 extends 来继承 React.component 接口

  3. 我们注意到 React.Component 后面带了 <any, any> 这个奇怪的字符串。这是 React.Component 接口的类型参数。第一个是 props 的类型,第二个是 state 的类型。
  4. 为了给 propsstate 定义类型,我们可以使用 interface 关键字。我们可以将类型定义写在当前的组件文件,但是由于在当前文件中具有了 import 关键字,当前文件会成为一个模块,即无法被全局引用。所以建议新建一个文件 index.d.ts 来储存 interface\type

    // index.d.ts
    interface ITsExampleProps {
      name: string
    }

    注:exportimport 会将一个文件变成模块,即里面的变量不会被暴露到全局

  5. 接下来我们为这个组件初始化 state

    // ReactTs.tsx
    class TsExample extends React.Component<ITsExampleProps, ITsExampleState> {
      public state = {
        count: 0
      }
    }

    此处建议不要 public state = {}constructor 两种方法混用

  6. 为组件添加 UI:每个类组件都应该有一个 render 方法来进行 UI 渲染。render 方法中必须返回一个 ReactNodeReactNode 可以是一个HTMLElement 或者一个字符串,数字

    // ReactTs.tsx
    class TsExample extends React.Component<ITsExampleProps, ITsExampleState> {
      public state = {
        count: 0
      }
    
      public render() {
        let { count } = this.state
        return <div styleName="wrap">这里是 ui</div>
      }
    }
  7. 为组件添加并调用方法,使用 state

    // ReactTs.tsx
    class TsExample extends React.Component<IExampleProps, IExampleState> {
      public state = {
        count: 0,
        currentCount: 0
      }
    
      public plusCount = () => {
        this.setState({
          count: ++this.state.count
        })
      }
    
      public render() {
        let { count } = this.state
        return (
          <div styleName="">
            {/* 调用state */}
            <div>{count}</div>
            {/* 调用方法  */}
            <Button onClick={this.plusCount}>增加</Button>
            <div>
              <Button
                onClick={() => {
                  this.showCurrentCount(this.state.count)
                }}
              >
                当前数量
              </Button>
            </div>
          </div>
        )
      }
    }

8) 接下来我们将该组件作为一个子组件放到另一个组件中

// example.tsx
class Example extends React.Component<IExampleProps, IExampleState> {
  public render() {
    return (
      <div styleName="example">
        <ReactTs />
      </div>
    )
  }
}
  1. 为子组件传入一个参数

    // example.tsx
    class Example extends React.Component<IExampleProps, IExampleState> {
        public render() {
            return (
                <div styleName='example'>
                    <ReactTs name="React-Ts" />
                </div>
            )
        }
    }

10) 在子组件中使用参数:this.props.name

// ReactTs.tsx
public render() {
        let {count} = this.state
        return (
            <div styleName='React-Ts'>
                <div>名字:{this.props.name}</div>
                <div>增加数量:{count}</div>
                <div>当前数量:{this.state.currentCount}</div>
                <div><Button onClick={this.plusCount}>增加</Button></div>
                <Button
                                        onClick={() => { this.showCurrentCount(this.state.count) }}>
                      当前数量
                                </Button>
            </div>
        )
    }

(二)使用函数式组件

  1. 一般,在一个文件内,我们应该只写一个类组件,其他的子组件应该是函数式组件。
  2. 函数式组件类型可以使用React.FC,使用这个类型有个好处就是,提醒你必须返回一个ReactNode

    const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => {
      return <div></div>
    }

3) 在函数式组件中,我们无法像在类组件中一样使用state和生命钩子函数,但React提供了HOOK

4) 使用 useState 来存储函数组件的状态。在下面的例子中,我们使用了React.useState,传入color的初始值,返回了一个对象,我们使用解构,获得了color, setColor。其中 color 相当于 this.state.colorsetColor('green')相当于 this.setState({color: 'green'})

const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => {
  const [color, setColor] = React.useState('blue')

  const changeColor = () => {
    setColor('green')
  }

  return (
    <div>
      <div style={{ color }}>函数式组件</div>
      <Button type="primary" className="example-button" onClick={changeColor}>
        点击换色{' '}
      </Button>
    </div>
  )
}
  1. 假如你想像在类组件中使用 componentWillUnmountcomponentDidMountcomponentDidUpdate,你可以使用useEffectuseEffect接受一个回调函数,这个回调函数内代码会在 componentDidMountcomponentDidUpdate时执行。回调函数的返回值应该是一个函数,这个函数会在 componentWillUnmount被执行。

    const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => {
        const [color, setColor] = React.useState('blue')
      const [fontSize, setFontsize] = React.useState('16px')
    
      const changeColor = () => {
        setColor('green')
      }
    
      React.useEffect(() => {
        let timer = setInterval(() => {
          setFontsize('100px')
        }, 10000)
    
        return () => {
          clearInterval(timer)
        }
    
      return (
          <div>
          <div style={{color,fontSize}}>函数式组件</div>
          <Button type='primary' className='example-button' onClick={changeColor}>点击换色        </Button>
        </div>
      )
    }
  2. 假如我们需要操作 DOM,我们可以通过 useRef 来获取。text.current 就是被我们绑定的元素。注意:不允许直接操作 DOM,除非迫不得已

    const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => {
        const [color, setColor] = React.useState('blue')
      const [fontSize, setFontsize] = React.useState('16px')
    
      const text = React.useRef<HTMLDivElement>(null)
    
      const changeColor = () => {
        setColor('green')
      }
    
      const changeBgC = () => {
        (text.current as HTMLDivElement).style.backgroundColor = '#e9e9e9'
      }
    
      React.useEffect(() => {
        let timer = setInterval(() => {
          setFontsize('100px')
        }, 10000)
    
        return () => {
          clearInterval(timer)
        }
    
      return (
          <div>
          <div style={{color,fontSize}}>函数式组件</div>
          <Button type='primary' className='example-button' onClick={changeColor}>点击换色        </Button>
          <Button type='primary' className='example-button' onClick={changeBgC}>点击换背景色</Button>
        </div>
      )
    }

(三) 其他

  1. 有时候我们需要使用到默认参数 defaultProps ,这个默认参数是从多个组件的 props 中提取出来的。这时可以使用 交叉类型 & ,注意,请不要在交叉类型中使用相同的属性,就算使用了,也请不要为两个同名属性定义不同的基础类型,这样将会造成这个属性需要同时满足两种基础类型。
type propsType = typeof defaultProps & {
  count: number
}

const defaultProps = {
  name: 'world'
}

const DefaultPrppsExample = (props: propsType) => {
    return (
      <div>
        {props.name}
        {props.count}
      </div>
    )
  }

  // 在另一个组件中使用
;<DefaultPrppsExample count={1} name={'默认参数示例'} />
  1. createRefforwardRef

    在函数式组件中,我们可以使用 useRef 来获取到 React 元素。在类组件中,我们可以使用 createRef 来获取 React 元素。当我们需要获取某个组件中的元素时,使用 forwardRef 来获取这个组件内部的元素。还是不建议直接操纵 DOM 元素

    const RefExample = React.forwardRef((props: React.CSSProperties, ref: React.Ref<HTMLInputElement>) => {
        return <input style={{ ...props}} ref={ref} />
    })
    
    class TsExample extends React.Component<ITsExampleProps, ITsExampleState> {
      ...
      public state = {
        inputRef: React.createRef()
      }
    
        public getFocus = () => {
            if (this.state.inputRef) {
                // 注意:这里断言的类型应该是使用 RefObject,因为其里面的具有 current: T | null,
                // 如果使用的是 ReactRef ,这个类型是一个联合类型,里面有可能是 null
                ((this.state.inputRef as React.RefObject<HTMLInputElement>).current as HTMLInputElement).focus()
            }
    
        }
    
         ...
       <div styleName="example-item">
              <h3>使用 createRef 和 forwardRef</h3>
                    <RefExample color='red' ref={this.state.inputRef} />
                    <Button onClick={this.getFocus}>获取焦点</Button>
       </div>
      ...
    }

3) React 默认会把我们的子组件挂载到父组件上。当我们想自己指定挂载的元素时,就需要用到 createPortal 了。

这里写了一个简陋的蒙层组件(样式未完全实现),即使我们是TsExample中使用了这个组件,但这个组件还是会被挂载到一个插入 body 的元素上

注意:这里我们在父组件上绑定了一个点击事件,PortalExample 被点击时依然会冒泡进而触发父组件的点击事件

const PortalExample = (props: IPortarExampleProps) => {
    const [modelDOM, setModelDOM] = React.useState(document.createElement('div'))
    modelDOM.classList.add('modal')
    if (props.visible) {
        document.body.append(modelDOM)
        return ReactDOM.createPortal(
            <div styleName='modal-inner'>
                蒙层
            </div>,
            modelDOM)
    }else {
        if (modelDOM.parentNode) {
            modelDOM.parentNode.removeChild(modelDOM)
        }
        return null
    }

}

class TsExample extends React.Component<ITsExampleProps, ITsExampleState> {
  ...
  <div styleName="example-item" onClick={this.handleModalClick}>
             <h1>使用 createPortal 将子组件挂载到父组件以外的元素内</h1>
             <PortalExample visible={this.state.modalVisible} />
             <Button onClick={this.toggleModal}>切换modal</Button>
    </div>
  ...
}

(四) TS 类型基本使用

我们在使用类型注解的时候往往会使用以下类型

  1. 基本类型包括了js的七种基本类型 string, number, boolean, undefined, null, symbol,还有 void, never
  2. 可以使用 type, interface 来声明变量的形状

1. 联合类型

使用 | 来创造一个联合类型。例如:type pet = 'cat' | 'dog'

当我们需要规定一个函数参数的类型时,又恰巧有一个已经有一个接口已经满足了,我们可以使用 keyof 来快速生成一个联合类型

2. 类型守护

当我们在使用联合类型时,假如两个子类型 A 和 B 都是接口或类等时,我们预想的是要么是 A,要么是 B。但实际上,A&B 也是符合 A|B 的。

这个时候我们需要先用 in 关键字来判断是符合哪个类型再进行操作

interface IMan {
  handsNum: number
}

interface IDog {
  tailNum: number
}

public typeGuarding = (obj: IMan | IDog) => {
    if ('handsNum' in obj) {
      ...
    } else if ('tailNum' in obj) {
      ...
    }
  }

3. 交叉类型

使用 & 来创造一个联合类型,之前已经有示例了

4. 可选类型

可选类型其实不是一个类型,但在接口和函数参数中都有可选这个概念。

interface IPet {
    name: string
  age?: number
}

interface IMan {
  ...
}

const feedPet (pet: IPet, man?: IMan) => {
  ...
}

注意:函数的可选参数必须写在最后面

5. 默认参数

在函数声明中,直接为参数赋予一个值,可以起到默认参数的作用

interface IPetDefault {
    name: string
  age = 1
}

const feedPet (pet: IPet, man={name: '我'}) => {
  ...
}

6.enum

当我们需要使用枚举类型时,就需要用到enum 关键字。默认,第一个的值为 0,然后递增,可以手动指定每一个的值,之后的值也会递增。我们可以通过值查key,也可以通过key 查值

enum animal {
    乌龟,
    鳄鱼,
    麻雀,
    河马
}

animal['乌龟'] // 0
animal[0] // 乌龟

7. 类型断言

当我们给一个函数传入参数时,我们知道这个参数时符合被规定的参数类型的,但编译器是不知道的。为了让编译器能知道类型符合,我们就需要使用类型断言,关键字:as

type animalName = '乌龟' | '鳄鱼' | '麻雀' | '河马'

animal[this.state.animalName as animalName] // 0

类型断言里还有另一个类型:在值的后面使用 ! ,表示这个值不为 null

type animalNameWithNull = '乌龟' | '鳄鱼' | '麻雀' | '河马' | null

animal[(this.state.animalName as animalName)!] // 0

8. 使用类型扩展(type branding)

注:类型扩展是直译的结果,如果你知道这种用法的名字,请修正它

TS 在检查一个类型是否符合是,并不是查找类型的名字,而是比较类型的结构。也就是说,当你为两种类型userIdorderId 都定义了一样的结构:{name: string}。这时候,TS 会判定它们是同一种类型。请看下面的例子:

interface IOrderId {
  name: string
}

interface IUserId {
  name: string
}

let userId: IUserId = {
  name: 'userId'
}
let orderId: IOrderId = userId // ok

这里你是否会疑惑呢?两种不同的类型却可以相互赋值。这就是 结构类型检查 了。假如名字不同,类型就不同的就是 名称类型检查

那我们如何来避免呢?第一种方法是多加一个字段

interface MyUserId {
  name: string
  type: 'user'
}

interface MyOrderId {
  name: string
  type: 'order'
}

let myUserId: MyUserId = {
  name: 'user',
  type: 'user'
}

let myOrderId: MyOrderId = myUserId // error

还有另一种方法,那就是利用 unique symbol 和 交叉类型

type UserIdString = string & { readonly brand: unique symbol }
type OrderIdString = string & { readonly brand: unique symbol }

const getUniqueUserId = (id: string) => {
  return id as UserIdString
}

const getUniqueOrderId = (id: string) => {
  return id as OrderIdString
}

let uniqueUserId: UserIdString = getUniqueUserId('1')
let uniqueOrderId: OrderIdString = uniqueUserId // error

unique symbolsymbol 的子类型。我们在上面用交叉类型将 {readonly brand: unique symbol} 也加入到类型中去,这样,两种类型就算结构一样,它们也是不相同的

9. 函数重载

函数重载用于我们的函数拥有不同的输入或输出时

第一个函数声明应该是最精确的,因为编译器是从前往后开始匹配的。函数实现应该是兼容各个函数声明的。

这里函数重载的意义在于,如果们直接使用第三种声明,那么传入 3 个参数也是可以接受的,但是据我们所知, padding 是不接受 3 个参数的

// 重载
function padding(all: number)
function padding(topAndBottom: number, leftAndRight: number)
function padding(top: number, right: number, bottom: number, left: number)
// Actual implementation that is a true representation of all the cases the function body needs to handle
function padding(a: number, b?: number, c?: number, d?: number) {
  if (b === undefined && c === undefined && d === undefined) {
    b = c = d = a
  } else if (c === undefined && d === undefined) {
    c = a
    d = b
  }
  return {
    top: a,
    right: b,
    bottom: c,
    left: d
  }
}

10. 使用 typeof 关键字快速定义一个类型

有时候,我们在前面定义了一个变量,并使用类型推导(也就是说,这时候我们并没有明确地给它声明类型)。然后我们想要使用这个变量的类型了,怎么办?回去定义一下类型吗?使用 typeof 变量名 就可以获取到这个变量的类型了

11. 工具泛型的使用

TS 中内置了一些语法糖给我们使用,例如 Partial、Omit、Exclude

这里举例 partial 的用法:Patial 关键字可以让将一个接口内部的属性变成可选

interface dog {
  name: string
  bark: boolean
}

type littleDog = Partial<dog>

let dog1: littleDog = {
  name: 'dog1'
}

let dog2: littleDog = {
  bark: false
}

12. 模块声明

当我们需要引入某些模块时,这些模块又没有被声明过,这个时候就会报错了。我们只需要使用

decleare module 模块名 就可解决

decleare module "*.png"
import * as logo from 'logo.png'
查看原文

赞 41 收藏 22 评论 4

不觉 发布了文章 · 2019-12-29

初级前端工程师面试所具备的知识梳理

初级前端工程师面试所具备的知识梳理


目录

一面/二面


一面/二面


网页布局

相对定位布局实现自适应
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .layout .box1{
            width: 33.3%;
            height: 300px;
            background-color: rosybrown;
            float: left;
            position: relative;
        }
        .layout .box2{
            width: 33.3%;
            float: left;
            height: 300px;
            background-color: brown;
            position: relative;
        }
        .layout .box3{
            width: 33.3%;
            float: left;
            height: 300px;
            background-color: aquamarine;
            position: relative;
        }
    </style>
</head>
<body>

<section class="relative layout">
    <div class="box1"></div>
    <div class="box2">相对定位自适应</div>
    <div class="box3"></div>
</section>

</body>
</html>
绝对定位布局自适应
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .layout {
            width: 100%;
        }
        .layout .box1{
            width: 33.3%;
            height: 300px;
            background-color: rosybrown;
            position: absolute;
            left: 0;
        }
        .layout .box2{
            width: 33.3%;
            height: 300px;
            background-color: brown;
            position: absolute;
            right: 0;
            left: 0;
            top: 0;
            bottom: 0;
            margin: 0 auto;
        }
        .layout .box3{
            width: 33.3%;
            height: 300px;
            background-color: aquamarine;
            position: absolute;
            right: 0;
        }
    </style>
</head>
<body>

<section class="relative layout">
    <div class="box1"></div>
    <div class="box2">相对定位自适应</div>
    <div class="box3"></div>
</section>

</body>
</html>

BFC

概念

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其他元素的交互限定区域。

下列方式会创建块格式化上下文:
  • 根元素或包含根元素的元素
  • 浮动元素(元素的 float 不是 none)
  • 绝对定位元素(元素的 position 为 absolute 或 fixed)
  • 行内块元素(元素的 display 为 inline-block)
  • 表格单元格(元素的 display为 table-cell,HTML表格单元格默认为该值)
  • 表格标题(元素的 display 为 table-caption,HTML表格标题默认为该值)
  • 匿名表格单元格元素(元素的 display为 table、table-row、 table-row-group、table-header-group、table-footer-group(分别是HTML table、row、tbody、thead、tfoot的默认属性)或 inline-table)
  • overflow 值不为 visible 的块元素
  • display 值为 flow-root 的元素
  • contain 值为 layout、content或 strict 的元素
  • 弹性元素(display为 flex 或 inline-flex元素的直接子元素)
  • 网格元素(display为 grid 或 inline-grid 元素的直接子元素)
  • 多列容器(元素的 column-count 或 column-width 不为 auto,包括 column-count 为 1)
  • column-span 为 all 的元素始终会创建一个新的BFC,即使该元素没有包裹在一个多列容器中(标准变更,Chrome bug)。

块格式化上下文包含创建它的元素内部的所有内容.

BFC子元素即使是float也会参与父元素的高度计算

DOM事件

DOM事件的级别
  • DOM0 element.onclick = function(){}
  • DOM2 element.addEventListener('click',function(){},false)
  • DOM3 element.addEventListener('keyup',function(){},false) 新增诸多鼠标键盘事件
DOM事件模型
  • 自上而下捕获,自下而上冒泡
DOM事件流
  • 第一阶段 捕获
  • 第二阶段 目标阶段 (到达目标元素)
  • 第三阶段 冒泡阶段 (从目标元素冒泡到window对象)
描述DOM事件捕获的具体流程
  • window --> document --> html --> body --> 父级元素 --> 目标元素
EVENT对象的常见应用
  • event.preventDefault() 阻止默认事件
  • event.stopPropagation() 阻止冒泡事件
  • event.stopImmediatePropagation() 在A中阻止B事件
  • event.currentTarget 当前绑定事件的对象
  • event.target 应用于事件委托
自定义事件
var eve = new Event('custome');

ev.addEventListener('custome',function(){
console.log('custome');
})

ev.dispatchEvent(eve)
  • DEMO
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <script>
        const eve = new Event('warning');
        console.log(eve);

    </script>
</head>
<body>

<button class="test">测试自定义事件</button>

<script>
    document.querySelector('.test').addEventListener('warning',function () {
         alert('你好,我是自定义事件warning!');
    });
    document.querySelector('.test').dispatchEvent(eve);
</script>

</body>
</html>

HTTP协议

HTTP协议的主要特点
  • 简单快速(URI固定)
  • 灵活()
  • 无连接(连接一次就会断掉不会保持连接)
  • 无状态(客户端与服务端是两种身份)
HTTP报文的组成部分
  • 请求报文

    • 请求行(HTTP方法,链接地址,http协议以及版本)
    • 请求头(Key and value)
    • 空行(告诉服务端解析请求体)
    • 请求体
  • 响应报文

    • 状态行(HTTP版本,状态码)
    • 响应头
    • 空行
    • 响应体
HTTP方法
  • get ----->获取资源
  • post ----->传输资源
  • put ---->更新资源
  • delete ---->删除资源
  • head ---->获得报文收首部
Post与Get的区别
  • GET在浏览器回退是无害的,而POST会再次提交请求。
  • GET产生的URL地址可以被收藏,而POST不可以。
  • GET请求会被浏览器主动缓存,而POST不会,除非手动设置。
  • GET请求只能进行URL编码,而POST支持多种编码格式。
  • GET请求参数会被完整保留在浏览器历史记录中,而POST中的参数不会被保留。
  • GET请求在URL中传送的参数是有长度限制的,而POST没有限制。
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • GET比POST更不安全,因为GET参数会暴露在URL中,所以不能用来传递敏感信息。
  • GET参数通过URL传递,POST放在Request body中。
HTTP状态码
  • 1xx:指示信息-表示请求已经接收,继续处理
  • 2xx:成功-表示请求已经被接收
  • 3xx:重定向-要完成请求必须进步性更近异一步的操作
  • 4xx:客户端错误-请求有语法错误或者请求无法实现
  • 5xx:服务器错误-服务器未能实现合法的请求或者服务器内部代码有问题

进一步举例
  • 200 OK:客户端请求成功
  • 206 Partial Content:客户发送了一个带有Range头的GET请求,服务器完成了它
  • 301 Moved Permanently:所请求的页面已经转移至新的URL
  • 302 Found:所请求的页面已经临时转移至新的url
  • 304 Not Modified:客户端有缓冲的文档并发出了一个条件请求,服务器告诉客户端,原来缓冲的文档还可以继续使用
什么是持久连接
  • HTTP协议采用“请求-应答”模式,当使用普通模式,即非Keep-Alive模式时,每个请求/应答客户和服务器都要创建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议)
  • 当使用Keep-Alive模式(又称持久连接,连接重用)时,Keep-Alive功能使得客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接
什么是管线化
  • 在使用持久连接的情况下,某个连接上的消息的传递类似于
请求1->响应1->请求2->响应2->请求3->响应3
  • 管线化指的是在持久连接的基础上把所有的请求一次性打包过去,类似于
请求1->请求2->请求3->响应1->响应2->响应3
特点
  • 管线化机制通过持久连接完成,仅HTTP/1.1支持此技术
  • 只有GET和HEAD请求可以进行管线化,而POST则有所限制
  • 初次创建连接的时候不应启动管线机制,因为对方(服务器)不一定支持HTTP/1.1版本的协议
  • 管线化不会影响响应的顺序,如上面的例子所示,响应返回的顺序并未改变
  • HTTP/1.1要求服务器端支持管线化,但是并不要求服务器端也对响应进行管线化处理,只是要求对于管线化的请求不失败即可
  • 由于上面提到的服务器端问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对于管线化的支持并不好,因此现代浏览器如chrome和firefox默认并未开启管线化支持

原型链

创建对象有几种方法
const obj = {name:'张三'};

const obj1 = new Object({name:'张三'});
const obj = function (name){
this.name = name
}

const obj1 = new obj('张三');
const obj = {name:'张三'}

const obj1 = Object.create(obj);

//
原型、构造函数、实例、原型链

alt

  • 其实,理解原型链只需要看懂下面代码就可以了。

  Function.prototype = {
        constructor : Function,
        __proto__ : parent prototype,
        some prototype properties: ...
    };

/**
* @time  2019/2/20 11:11
* @author  Bill Wang <vuejs@vip.qq.com>
* @desc   函数的原型对象constructor默认指向函数本身,
*         原型对象除了有原型属性外,为了实现继承,还
*         有一个原型链指针__proto__,该指针指向上一
*         层的原型对象,而上一层的原型对象的结构依然
*         类似,这样利用__proto__一直指向Object的原
*         型对象上,而Object的原型对象
*         用Object.prototype.__proto__ = null表示原
*         型链的最顶端,如此变形成了javascript的原型链
*         继承,同时也解释了为什么所有的javascript对象
*         都具有Object的基本方法。
*/
instanceof的原理

alt

  • instanceOf的原理就是判断实例对象的__proto__属性和构造函数的prototype是不是同一个引用。
Demo
const obj = function (name){
this.name = name
}

const obj1 = new obj('张三');

console.log(obj1 instanceof obj)
console.log(obj1 instanceof Object)
new运算符
  • 步骤一:一个新对象被创建,它继承自foo.prototype
  • 步骤二:构造函数foo被执行,执行的时候,相应的参数会被传入,同时执行上下文(this)会被指定为这个新实例。new foo等同于new foo(),

只能用在不传递任何参数的情况下。

  • 步骤三:如果构造函数返回了一个“对象”,那么这个对象又会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤一创建的对象。

面向对象

类与实例
  • 类的声明
/**
 * 传统类的声明
 */

 function Animal(name) {
  this.name = name;
 }
 /**
  * ES6中类的声明
  */

  class Animal {

    constructor(name){
       this.name = name;
    }

    // other methods
  }
  • 生成实例

 /**
  * 实例化一个类
  */

  new Animal('鸽子');
类与继承
  • 如何实现继承

    • 继承的本质就是原型链
  • 继承的几种方式

    • 构造函数实现继承
      function Parent(name){
        this.name = name;
      }
    
      function Child(){
        //父类挂载在子类
        Parent.call(this);
        // Parent.apply(this);
      }
    
      //缺点
      //Parent原型链上的内容并没有被Child继承
    • 原型链实现继承
        function Parent(name){
           this.name = name;
         }
    
         function Child(){
    
         }
    
         Child.prototype = new Parent('鸽子');
    
         //缺点
         //不同的实例对象共享属性
    • 组合方式
           function Parent(name){
             this.name = name;
           }
    
           function Child(){
            Parent.call(this);
           }
    
          Child.prototype = new Parent();
    
          //缺点
          //父级的构造函数执行了两次(完全没必要)
    
    
    //-------------------------------------------------
    
          // 优化组合继承1
          function Parent(name){
             this.name = name;
           }
    
           function Child(){
            Parent.call(this);
           }
    
          Child.prototype = Parent.prototype;
    
          //缺点
          //无法区分一个对象是谁实例化的
    
      //------------------------------------------------
    
           // 优化组合继承2
                  function Parent(name){
                     this.name = name;
                   }
    
                   function Child(){
                    Parent.call(this);
                   }
    
                  Child.prototype = Object.create(Parent.prototype);
    
                  Child.prototype.constructor = Child;
    

通信类

什么是同源策略及限制
  • 同源策略是从一个源加载的文档或脚本如何来自另一个源的资源进行交互。
  • 这是一个用于隔离潜在恶意文件的关键的安全机制。
什么是源?

包含三部分:协议、域名、端口。三者有一不一样就算跨域。

什么是限制?

不是一个源的文档,你没有权利去操作另一个源。

  • Cookie、LocalStorage和IndexDB无法读取
  • DOM无法获取
  • AJAX请求不能发送
前后端如何通信
  • Ajax(一般只适合同源通信)
  • WebSocket(不受同源策略限制)
  • CORS (支持跨域通信也支持同源通信)
如何创建ajax
  • XMLHttpRequest对象的工作流程
  • 兼容性处理
  • 事件的触发条件
  • 事件的触发顺序
const XHR = XMLHttpRequest ? new XMLHttpRequest() : new Window.ActiveXObject('Microsoft');

XHR.open("get", "yourFile.txt", true);
XHR.send();
跨域通信的几种方式
  • JSONP

    • 原理

      • 主要原理就是利用script标签异步加载的原理工作的。
    • 实现
    function handleCallback(result) {
        console.log(result.message);
    }
    
    const jsonp = document.createElement('script');
    const element = document.getElementById('demo');
    jsonp.type = 'text/javascript';
    jsonp.src = 'http://localhost:8080?callback=handleCallback';
    element.appendChild(jsonp);
    element.removeChild(jsonp);
    • JSONP只支持get请求
    • JSONP有一个弊端就是需要服务器的配合,也就是服务器如果传输一段恶意代码,浏览器也会毅然决然的执行。
  • Hash(Hash改变页面不会刷新)

    • 场景:利用hash,场景是当前页面A通过iframe嵌入了跨域的页面B
    • 实现

      const B = document.getElementByTagName('iframe');
      B.src = B.src + '#' + 'data';
      
      window.onhashchange = function () {
         var data = window.location.hash;
      };
      
  • postMessage(H5新增)
  • WebSocket
  • CORS(可以理解为支持跨域通信的Ajax)

    • 实现
    fetch('http://example.com/movies.json')
      .then(function(response) {
        return response.json();
      })
      .then(function(myJson) {
        console.log(myJson);
      });
    • 原理 :浏览器会拦截ajax请求,如果ajax是跨域的。它会加一个origin。

安全类

CSRF
  • 基本概念和缩写

    • CSRF通常称为跨站请求伪造,英文名Cross-site request forgery缩写CSRF。
  • 攻击原理

    ALT

    • 网站某一个接口存在漏洞
    • 用户确实登录过
  • 防御措施

    • Token验证(访问API附带Token)
    • Referer验证(页面来源)
    • 隐藏令牌(隐藏在Http Header头中)
XSS
  • 基本概念以及缩写
  • 原理

    • 向页面内部注入JS

算法类

  • 排序

    • 快速排序
    • 选择排序
    • 希尔排序
    • 冒泡排序
  • 堆栈、队列、链表
  • 递归
  • 波兰式和逆波兰式

二面/三面


面试技巧

  • 知识面要广
  • 理解要深刻
  • 内心要诚实
  • 态度要谦虚
  • 回答要灵活
  • 要学会赞美

渲染机制

什么是DOCTYPE及作用
DTD(document type definition ,文档类型定义) 是一系列的语法规则,用来定义XML或者HTML的文件类型。浏览器会使用它来判断文档类型,决定使用何种协议来解析以及切换浏览器。

DOCTYPE是用来声明文档类型和DTD规范的,一个主要的用途便是文件的合法性验证。如果文件代码不合法,那么浏览器解析时便会出一些差错。

  • 常见的DOCTYPE

    • <!DOCTYPE html> HTML5
    • HTML 4.01 Strict
    • HTML 4.01 Transitional
浏览器渲染过程

alt

重排Reflow
  • 定义:DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置,这个过程称为reflow。
  • 触发Reflow

    • 当增加、删除、修改DOM节点时,会导致Reflow或者Repaint
    • 当移动DOM的位置的时候,或者有动画的时候
    • 当修改CSS样式的时候
    • 当Resize窗口的时候(移动端设备不存在此类问题),或者滚动的时候
    • 当修改网页的默认字体的时候
重绘Repaint
  • 定义:当各种盒子的位置、大小以及其它属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制一遍,于是页面的内容出现了,这个过程称为repaint。
  • 触发Repaint

    • DOM改动
    • CSS改动
布局Layout

JS运行机制

Demo引出
console.log(1);

setTimeout(() =>{
   console.log(2);
},0);

console.log(3);

//print code

1

3

2
  • 同步任务持续执行
  • 异步任务会挂起
  • JS运行机制,单线程优先执行同步任务
 console.log('A');

 while(true) {

 }
 console.log('B');
 
 //print code

 A
  • 此处是进入同步无限循环,永远也进入不到B

for(var i = 0;i<4;i++) {
 setTimeout(()=>{
   console.log(i);
 },1000)
}

//print code

4
4
4
4
  • 异步任务的放入时间和执行时间
  • 当时间到了之后,会把setTimeout扔进异步队列中
异步任务
  • setTimeout和setInterval
  • DOM事件
  • ES6中的Promise
总结
  • 理解JS的单线程的概念
  • 理解任务队列
  • 理解Event Loop
  • 理解哪些语句会放入异步任务队列
  • 理解语句放入异步任务队列的时机

页面性能

提升页面性能的方法有哪些?
  • 资源压缩合并,减少Http请求
  • 非核心代码异步加载——>异步加载的方式——>异步加载的区别

    • 异步加载的方式
     #(1)动态脚本的加载(也就是利用JS动态创建script标签加载)
     #(2)defer
     #(3)async
    • 异步加载的区别
    #(1)defer是在HTML解析完成之后才会执行,如果是多个,按照加载顺序依次执行
    #(2)async是在加载完之后立即执行,如果是多个执行顺序和加载顺序无关
  • 利用浏览器缓存——>缓存的分类——>缓存的原理

    • 缓存的分类

(1)强缓存

  Expires  Expires:Thu,21 Jan 2017 23:39:02 GMT(服务器的绝对时间)
  Cache-Control Cache-Control:max-age = 3600(浏览器的相对时间)

(2)协商缓存(与服务器协商一番)

  Last-Modified If-Modified-Since  Last-Modified:Wed,26 Jan 2017 00:35:11 GMT
  Etag If-None-Match
  • 使用CDN
  • 预解析DNS
<meta http-equiv = "x-dns-prefetch-control" content = "on">(强制打开a标签的DNS预解析)
<link rel = "dns-prefetch" href = "//host_name_to_prefetch.com">

错误监控

前端错误的分类
  • 即时运行错误:代码错误
  • 资源加载错误
错误的捕获方式
  • 即时运行错误
#(1)try catch
#(2)window.onerror
  • 资源加载错误(不会冒泡)
#(1)object.onerror
#(2)performance.getEntries()  [获取资源加载时长,返回一个数组]
#(3)Error事件捕获

window.addEventListener('error',function(e){
console.log('捕获'+e)
},true)

  • 延伸:跨域的js运行错误可以捕获吗,错误提示是什么,应该怎么处理?
Resource interpreted as script but transferred
错误信息:Script error
出错行号:0
出错列号:0
错误详情:null
  • 在script标签中增加crossorigin属性
  • 设置js资源响应头Access-Control-Allow-Origin:*
上报错误的基本原理
  • 采用Ajax通信的方式上报
  • 利用Image对象上报

(new Image()).src = 'http://www.baidu.com'
查看原文

赞 1 收藏 0 评论 0

不觉 关注了用户 · 2019-12-29

二月 @hyy1115

我不入地狱谁入地狱

关注 496

不觉 赞了文章 · 2019-12-29

react与redux通信之hook

react和redux建立通信的方式

有2种方案:

  • 老方案connect
  • 新方案hook

老方案connect

曾经,我们会使用connect建立react和redux的通信,例如,在一个class写法的组件中:

import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import globalAction from 'actions/global'
@connect(
    // 取得reducer中的state
    state => ({global: state.global}), 
    // 取得action
    dispatch => bindActionCreators({ globalAction }, dispatch)
)
class Component extends React.Component {
    componentDidMount() {
        // 从props中读取reducer和action
        const {global, globalAction} = this.props
        globalAction()
        console.log(global)
    }
    render() {
        return <div />
    }
}

对于用习惯了class组件的开发者来说,这种写法烂熟于心了。但是,不管你多喜欢这种模式,还是得学习react hook。

新方案hook

随着react16.8的发布,hook功能正式投入使用。
将react的class组件转换成函数式组件,想必你已经看过官网的demo了,如果没看,回头看一下也不晚。那么,如果我们使用了hook,又该如何跟redux通信呢?
针对于这个问题,业界有人提供了一个取代react-redux的新插件redux-react-hook。
redux-react-hook使用了react提供的Context(上下文)功能,给顶层组件Provide传入了store对象,绑定到上下文。
使用了redux-react-hook之后,上面的demo就变成了下面这种写法:

import React, { useEffect } from 'react'
import { useDispatch, useMappedState, StoreContext } from 'redux-react-hook'
import globalAction from 'actions/global'
function Component {
    // 获取上下文的store对象
    const store = useContext(StoreContext)
    // 从store中读取reducer
    const {global} = store
    // 从store中读取dispatch
    const dispatch = useDispatch()
        
    useEffect(() => {
        dispatch(globalAction())
        console.log(global)
    }, [global, dispatch, globalAction])
    
    render() {
        return <div />
    }
}

修改后的demo使用到了redux-react-hook提供的其中2个API,StoreContext和useDispatch,其次,还可以使用useMappedState来获取reducer中的状态。

const mapState = useCallback(
    state => ({
        global: state.global
    }),
    [],
);
const { global } = useMappedState(mapState);

redux-react-hook

简单介绍写3个API,StoreContext,useDispatch,useMappedState。

StoreContext

React提供的createContext创建上下文,返回该对象。

import { createContext } from 'react';
// 创建context
const StoreContext = createContext<TStore | null>(null)
return StoreContext

useDispatch

读取StoreContext,返回dispatch。

function useDispatch(): Dispatch<TAction> {
    // 从上下文读取store
    const store = useContext(StoreContext);
    if (!store) {
      // store不存在,抛出异常
      throw new MissingProviderError();
    }
    return store.dispatch;
  }
return useDispatch

useMappedState

useMappedState跟其他2个API不太一样,它是一个自定义的hook,用来订阅reducer里的状态。

总结

hook式的写法究竟是好是坏,暂且无法分辨,就像有人觉得函数式编程很好,但有人觉得函数式编程使得代码难于维护。
可以预见的是,当你使用了hook,会在项目中逐渐把class消灭,最后跟class语法糖告别,回归函数的世界。

查看原文

赞 18 收藏 10 评论 1

不觉 发布了文章 · 2019-12-26

# Node.js葵花宝典

Node.js葵花宝典

前言

​ 欲练此功,必先自宫;

​ 不必自宫,亦可练成;

​ 兄台还是好好修炼此功吧!保持一个清醒的头脑,你将驾驭这匹野马!!!

—— 致读者

​ 知识就像海洋一样,永远也学不完,但是不断精益求精是一种态度,是对新事物的一种持续保持瞻望的态度,但愿你在学习的乐园里不断完善己身,不断修炼,等待破茧成蝶。

文档风格

  • 书名采用 #
  • 大标题采用##
  • 单元小结标题采用####

​ —— 致开发者

​ 好的书写风格能够让读者的思路清晰,同样能让人有继续阅读的兴趣,但愿你能按照此风格继续完善本书籍。

第一篇 为何偏爱Node.js

1.1 引子

  • 前端职责范围变大,为了统一流程开发体验
  • 在处理高并发,I/O密集型场景性能优势足够明显

1.2 CPU密集 ==VS== IO密集

  • CPU密集:压缩、解压、加密、解密
  • I/O密集:文件操作、网络操作、数据库

1.3 Web常见的场景

  • 静态资源获取
  • ......

1.4 高并发对应之道

  • 增加物理机的个数
  • 增加每台机器的CPU数------多核

1.5 关于进程线程那些事儿

  • 进程:用一句比较接地气的一句话叙述就是,执行中的程序就叫做进程。
  • 多进程:启动多个进程处理一个任务。
  • 线程:进程内一个相对独立、可以调度的执行单元,与同属一个进程的线程共享进程资源。

1.6 再来谈谈Node.js的单线程

  • 单线程只是针对主进程,I/O操作系统底层进行多线程调度。也就是它仅仅起一个监听作用。

    ###  举个栗子叭
         场景:饭店
         情节:人流量高并发
         BOSS的策略:雇佣多名厨师,只雇佣一个服务员,当多名顾客点餐的时候,服务员告诉厨师,做菜,上菜。
  • 单线程并不是只开一个进程。

    ###  Node.js中的cluster(集群)模块
         官方介绍:单个 Node.js 实例运行在单个线程中。 为了充分利用多核系统,有时需要启用一组 Node.js 进程去处理负载任务。
         Demo:
    const cluster = require('cluster');
    const http = require('http');
    const numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
      console.log(`主进程 ${process.pid} 正在运行`);
    
      // 衍生工作进程。
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
    
      cluster.on('exit', (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 已退出`);
      });
    } else {
      // 工作进程可以共享任何 TCP 连接。
      // 在本例子中,共享的是 HTTP 服务器。
      http.createServer((req, res) => {
        res.writeHead(200);
        res.end('你好世界\n');
      }).listen(8000);
    
      console.log(`工作进程 ${process.pid} 已启动`);
    }
  • 友情链接Cluster

1.7 Node.js的常用场景

  • Web server
  • 本地代码构建

第二篇 环境&调试

2.1 环境安装

2.2 环境必须

  • CommonJS

    • 每个文件都是一个模块,有自己的作用域。(它会自动包裹函数)
    • 在模块内部module变量代表模块本身。
    • module.exports属性代表模块对外接口。

      ModuleDemo.js

      console.log('this is a module');
      
      const testVar = 100;
      
      function test () {
          console.log(testVar);
      }
      
      module.exports.testVar = testVar;
      module.exports.testFn = test;
    • 使用模块之require规则

​ - /表示绝对路径,./表示相对于当前文件的路径。

​ - 支持jsjsonnode拓展名

​ - 不写路径则认为是build-in模块或者各级node-modules内的第三方模块

CommonJS Use Module

const modu = require('ModuleDemo.js');

console.log(modu.testVar);

console.log(modu.test);
    • require特性

      • module被加载的时候执行,加载后缓存。(后边这一句的意义就是,只加载一次,加载完缓存)【注:可以做一个小test,在一个test文件中,同时加载两次模块,你就会发现其中的奥秘了。】
      • 一旦出现某个模块被循环加载,就只会输出已经执行的部分,还未执行的部分不会输出。
    • Global
    • Process

    2.3 Node.js引用系统模块与第三方模块

    • 引用系统模块

      const fs = require('fs');
      
      const result = fs.readFile('fs.js',( err, data) => {
          if (err) {
              return err;
          }
          
          console.log(data);
      });
      
      console.log(result);
      
    • 引用第三方模块

      npm i chalk -S
      const chalk = require('chalk');

    2.4 exports与module.exports

    {
        function(exports,require,module,__filename,__dirname) {
            // code
        }
    }
    • 简而言之,exports就是module.exports的一个简写,也就是一个引用,别名。

      exports = {
          a: 1,
          b: 2,
          test: 123
      }
      //这样是错误的
      exports.test = 100;
      //只能添加属性,但是不能修改其指向
      
      //当然
      module.exports = {
          a:1,
          b:2,
          test:123,
      }
      //这样是没问题的

    2.5 Global对象

    • CommonJS
    • Buffer、process、console
    • timer
    global.test = 200;

    2.6 process模块

    /**
     *  argv
     *  argv0 
     *  execArgv
     *  execPath
     */
    
    const {argv, argv0, execArgv, execPath} = require('process');
    
    argv.forEach( item => {
        console.log(item);
    })
    
    //打印当前工作进程的路径
    
    console.log(process.cwd());
    
    //setImmediate(fn),不需要任何时间参数,执行最慢
    //process.nextTick(fn)
    //二者的区别就是后者的执行会先于前者
    
    
    
    • 简单说明一下,就是process.nextTick()会把任务放在当前事件循环队列的队尾,而setImmediate()会把任务放在下一个队列的队首,而setTimeout()会把任务放在它俩中间。

    2.7 Debug

    第三篇 Node.js-API

    3.1 path

    和路径有关的内置模块

    • path.basename()取得一个路径的最后一部分文件名
    • path.normalize()帮助修正路径
    • path.join()用于路径拼接(参数为多个路径参数)
    • path.resolve()将一个相对路径解析为绝对路径
    • {basename, dirname, extname}

      • basename 完整名
      • dirname 上级路径名
      • extname 后缀名
    • {parse, format}

      • parse用于解析当前路径为一个json格式的数据
      • format相当于格式化json数据为一个字符串

    说明:__dirname__filename总是返回文件的绝对路径

    process.cwd()总是返回node命令所在文件夹

    3.2 Buffer

    三个要点:

    • Buffer用于处理二进制数据流
    • 实例类似整数数组,大小固定
    • C++代码在V8堆外分配物理内存

    Buffer常用的方法

    • byteLength统计buffer所占字节数
    • isBuffer用来判断目标数据是不是一个Buffer
    • concat合并连接Buffer
    • from将目标数据转换为Buffer
    • toString用来转换Buffer为字符串

    3.3 events

    • eventEmitter.on('eventName',callback())用于注册监听器
    • eventEmitter.emit('eventName')用于触发事件
    const EventEmitter = require('events');
    
    class CustomEvent extends EventEmitter {
        
    }
    
    const ce = new CustomEvent();
    
    ce.on('eventName',callback);
    
    ce.emit('eventName','your msg to eventEmit',....);
    
    //有一个通用参数就叫error
    
    ce.on('error',fn);
    
    //Example
    ce.on('error',(err, item) => {
        console.log(err);
        console.log(item);
    });
    
    ce.emit('error', new Error('出错了'), Date().now);

    针对事件只需要响应一次:

    ce.once('test', ()=> {
        console.log(test);
    });
    
    

    针对事件需要移除的话:

    ce.removeListener('eventName',fn);
    
    //or
    
    ce.removeAllListeners('test');

    3.4 fs

    ​ 首先需要注意的就是Node.js的设计模型就是错误优先的模式。

    fs.readFile('fileUrl', 'utf8', (err, data) => {
        if(err) throw err;
        
        console.log(data);
    })
    • stat()查看文件的详细信息

      const fs = require('fs');
      
      fs.stat('fileUrl', (err, data) => {
          if (err) {
              throw err;//这里可以判断文件是否存在
          }
          
          console.log(data);
      });
    • rename()更改文件名

      fs.rename('./text.txt','hahah.ttx');
    • unlink删除文件
    fs.unlink('fileName', err => err);
    • readdir()读取文件夹
    • mkdir()创建文件夹
    • rmdir()删除文件夹
    • watch()监视文件或目录的变化
    fs.watch('fileUrl', {
        recursive:true //是否监视子文件夹
    }, (eventType, fileName) => {
        console.log(eventType, fileName);
    })
    • readStream()读取流
    const rs = fs.createReadStream('urlPath');
    
    rs.pipe(process.stdout);//导出文件到控制台
    • writeStream()写入流
    • pipe()管道,导通流文件

      const ws  = fscreateWriteStream('urlPath');
      
      ws.write('some content');
      
      ws.end();
      
      ws.on('finish',()=>{
         console.log('done!!!'); 
      });

    第四篇 项目Init

    4.1 .gitignore

    • 匹配模式前/代表项目根目录
    • 匹配模式最后加\代表是目录
    • 匹配模式前加!代表取反
    • *代表任意一个字符
    • ?匹配任意字符
    • **匹配任意目录

    4.2 ESLint

    完整配置.eslintrc.js

    module.exports = {
        env: {
            es6: true,
            mocha: true,
            node: true
        },
        extends: ['eslint:recommended', 'plugin:sort-class-members/recommended'],
        parser: 'babel-eslint',
        plugins: ['babel', 'sort-class-members'],
        root: true,
        rules: {
            'accessor-pairs': 'error', // 强制 getter 和 setter 在对象中成对出现
            'array-bracket-spacing': 'off', // 强制数组方括号中使用一致的空格
            'arrow-parens': 'off', // 要求箭头函数的参数使用圆括号
            'arrow-spacing': 'error', // 强制箭头函数的箭头前后使用一致的空格
            'babel/arrow-parens': ['error', 'as-needed'],
            'babel/generator-star-spacing': ['error', 'before'],
            'block-scoped-var': 'error', // 强制把变量的使用限制在其定义的作用域范围内
            'block-spacing': 'off', // 强制在单行代码块中使用一致的空格
            'brace-style': 'off', // 强制在代码块中使用一致的大括号风格
            camelcase: 'off', // 强制使用骆驼拼写法命名约定
            'comma-dangle': 'off', // 要求或禁止末尾逗号
            'comma-spacing': 'off', // 强制在逗号前后使用一致的空格
            'comma-style': 'off', // 强制使用一致的逗号风格
            complexity: 'off', // 指定程序中允许的最大环路复杂度
            'computed-property-spacing': 'error', // 强制在计算的属性的方括号中使用一致的空格
            'consistent-return': 'off', // 要求 return 语句要么总是指定返回的值,要么不指定
            'consistent-this': 'error', // 当获取当前执行环境的上下文时,强制使用一致的命名
            'constructor-super': 'error', // 要求在构造函数中有 super() 的调用
            curly: 'off', // 强制所有控制语句使用一致的括号风格
            'default-case': 'error', // 要求 switch 语句中有 default 分支
            'dot-location': ['error', 'property'], // 强制在点号之前和之后一致的换行
            'dot-notation': 'off', // 强制在任何允许的时候使用点号
            'eol-last': 'off', // 强制文件末尾至少保留一行空行
            eqeqeq: ['error', 'smart'], // 要求使用 === 和 !==
            'func-names': 'off', // 强制使用命名的 function 表达式
            'func-style': ['error', 'declaration', { // 强制一致地使用函数声明或函数表达式
                allowArrowFunctions: true
            }],
            'generator-star-spacing': 'off', // 强制 generator 函数中 * 号周围使用一致的空格
            'id-length': ['error', { // 强制标识符的最新和最大长度
                exceptions: ['_', 'e', 'i', '$']
            }],
            indent: ['error', 4, { // 强制使用一致的缩进
                SwitchCase: 1
            }],
            'key-spacing': 'off', // 强制在对象字面量的属性中键和值之间使用一致的间距
            'keyword-spacing': ['off', { // 强制在关键字前后使用一致的空格
                overrides: {
                    case: {
                        after: true
                    },
                    return: {
                        after: true
                    },
                    throw: {
                        after: true
                    }
                }
            }],
            'linebreak-style': 'off',
            'lines-around-comment': 'off',
            'max-depth': 'error', // 强制可嵌套的块的最大深度
            'max-nested-callbacks': 'off',
            'max-params': ['error', 4],
            'new-cap': 'off',
            'new-parens': 'error', // 要求调用无参构造函数时有圆括号
            'newline-after-var': 'off',
            'no-alert': 'error', // 禁用 alert、confirm 和 prompt
            'no-array-constructor': 'error', // 禁止使用 Array 构造函数
            'no-bitwise': 'error', // 禁用按位运算符
            'no-caller': 'error', // 禁用 arguments.caller 或 arguments.callee
            'no-catch-shadow': 'off',
            'no-class-assign': 'error', // 禁止修改类声明的变量
            'no-cond-assign': ['error', 'always'], // 禁止条件表达式中出现赋值操作符
            'no-confusing-arrow': 'error', // 不允许箭头功能,在那里他们可以混淆的比较
            "no-console": 0,
            'no-const-assign': 'error', // 禁止修改 const 声明的变量
            'no-constant-condition': 'error', // 禁止在条件中使用常量表达式
            'no-div-regex': 'error', // 禁止除法操作符显式的出现在正则表达式开始的位置
            'no-dupe-class-members': 'error', // 禁止类成员中出现重复的名称
            'no-duplicate-imports': 'off', // disallow duplicate module imports
            'no-else-return': 'error', // 禁止 if 语句中有 return 之后有 else
            'no-empty-label': 'off',
            'no-empty': 'off',
            'no-eq-null': 'error', // 禁止在没有类型检查操作符的情况下与 null 进行比较
            'no-eval': 'error', // 禁用 eval()
            'no-extend-native': 'error', // 禁止扩展原生类型
            'no-extra-bind': 'error', // 禁止不必要的 .bind() 调用
            'no-extra-parens': 'error', // 禁止不必要的括号
            'no-floating-decimal': 'error', // 禁止数字字面量中使用前导和末尾小数点
            'no-implied-eval': 'error', // 禁止使用类似 eval() 的方法
            'no-inline-comments': 'error', // 禁止在代码行后使用内联注释
            'no-iterator': 'error', // 禁用 __iterator__ 属性
            'no-label-var': 'off',
            'no-labels': 'off',
            'no-lone-blocks': 'error', // 禁用不必要的嵌套块
            'no-lonely-if': 'off',
            'no-loop-func': 'error', // 禁止在循环中出现 function 声明和表达式
            'no-mixed-requires': 'error', // 禁止混合常规 var 声明和 require 调用
            'no-mixed-spaces-and-tabs': 'off',
            'no-multi-spaces': 'off',
            'no-multi-str': 'off',
            'no-native-reassign': 'error', // 禁止对原生对象赋值
            'no-nested-ternary': 'error', // 不允许使用嵌套的三元表达式
            'no-new-func': 'error', // 禁止对 Function 对象使用 new 操作符
            'no-new-object': 'error', // 禁止使用 Object 的构造函数
            'no-new-require': 'error', // 禁止调用 require 时使用 new 操作符
            'no-new-wrappers': 'error', // 禁止对 String,Number 和 Boolean 使用 new 操作符
            'no-new': 'error', // 禁止在非赋值或条件语句中使用 new 操作符
            'no-octal-escape': 'error', // 禁止在字符串中使用八进制转义序列
            'no-path-concat': 'error', // 禁止对 __dirname 和 __filename进行字符串连接
            'no-process-env': 'error', // 禁用 process.env
            'no-process-exit': 'error', // 禁用 process.exit()
            'no-proto': 'error', // 禁用 __proto__ 属性
            'no-restricted-modules': 'error', // 禁用指定的通过 require 加载的模块
            'no-return-assign': 'error', // 禁止在 return 语句中使用赋值语句
            'no-script-url': 'error', // 禁止使用 javascript: url
            'no-self-compare': 'error', // 禁止自身比较
            'no-sequences': 'error', // 禁用逗号操作符
            'no-shadow-restricted-names': 'error', // 禁止覆盖受限制的标识符
            'no-shadow': 'off',
            'no-spaced-func': 'off',
            'no-sync': 'off', // 禁用同步方法
            'no-this-before-super': 'error', // 禁止在构造函数中,在调用 super() 之前使用 this 或 super
            'no-throw-literal': 'error', // 禁止抛出非异常字面量
            'no-trailing-spaces': 'error', // 禁用行尾空格
            'no-undef-init': 'error', // 禁止将变量初始化为 undefined
            'no-undefined': 'off',
            'no-underscore-dangle': 'off',
            'no-unexpected-multiline': 'error', // 禁止出现令人困惑的多行表达式
            'no-unneeded-ternary': 'error', // 禁止可以在有更简单的可替代的表达式时使用三元操作符
            'no-unused-expressions': 'error', // 禁止出现未使用过的表达式
            "no-unused-vars": [1, { // 禁止出现未使用过的变量
                "vars": "all",
                "args": "after-used"
            }],
            'no-use-before-define': 'error', // 不允许在变量定义之前使用它们
            'no-useless-call': 'error', // 禁止不必要的 .call() 和 .apply()
            'no-useless-concat': 'error', // 禁止不必要的字符串字面量或模板字面量的连接
            'no-var': 'off',
            'no-void': 'error', // 禁用 void 操作符
            'no-warning-comments': 'off',
            'no-with': 'off',
            'object-curly-spacing': 'off',
            'object-shorthand': 'error', // 要求或禁止对象字面量中方法和属性使用简写语法
            'one-var': 'off',
            'operator-assignment': 'error', // 要求或禁止在可能的情况下要求使用简化的赋值操作符
            'operator-linebreak': 'off',
            'padded-blocks': 'off',
            'prefer-arrow-callback': 'off',
            'prefer-const': 'error', // 要求使用 const 声明那些声明后不再被修改的变量
            'prefer-spread': 'error', // 要求使用扩展运算符而非 .apply()
            'prefer-template': 'off', // 要求使用模板字面量而非字符串连接
            quotes: 'off',
            'quote-props': 'off',
            radix: 'error', // 强制在parseInt()使用基数参数
            'require-yield': 'error', // 要求generator 函数内有 yield
            "semi": ["error", "always"], // 要求或禁止使用分号
            'semi-spacing': 'off',
            'sort-vars': 'error', // 要求同一个声明块中的变量按顺序排列
            'space-before-blocks': 'off',
            'space-before-function-paren': 'off',
            'space-in-parens': 'off',
            'space-infix-ops': 'off',
            'space-unary-ops': 'off',
            'spaced-comment': 'off',
            strict: 'off',
            'valid-jsdoc': 'error', // 强制使用有效的 JSDoc 注释
            'vars-on-top': 'off',
            yoda: 'off',
            'wrap-iife': 'off',
            'wrap-regex': 'error' // 要求正则表达式被括号括起来
        }
    }

    第五篇 静态资源服务器

    5.1 http模块

    const http = require('http');
    const chalk = require('chalk');
    const hostname = '127.0.0.1';
    
    const port = '3000';
    const server = http.createServe((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World');
    });
    
    server.listen(port, hostname, () => {
        const addr = `Server running at http://${hostname}:${port}/`;
        console.log(`${chalk.green(addr)}`);
    })
    • supervisor监视文件模块
    • hotnode热启动模块

    完整代码:请参照

    第六篇 测试

    6.1 断言测试

    • assert- 断言调试
    console.assert(a===b,msg);

    常用断言库:chai.js

    6.2 mocha.js

    //you should install mocha
    
    // $ npm install --save-dev mocha
    
    // you should know the '#math' only is it's description
    describe('#math', ()=> {
        describe('#add', ()=> {
            it('should return 5 when 2 + 3', () => {
                ecpect('your test','targetResult');
            });
        });
    });

    Note :如果你想只执行一个模块的,你可以在package.json中配置

    "scripts": {
      "test": "mocha /url/xxx.js
    }

    6.3 istanbul

    你可以参照)

    6.4 benchmark.js

    • 性能测试框架,具体参见
    //常见测试方法
    console.time('markPoint');
    
    console.timeEnd('markPoint');

    6.5 jsbench.js

    • 在FCC社区听老外告诉的,自己做了测试,确实是可以测试两段代码的性能差异。

    第七篇 UI测试

    7.1 传统测试

    ## 传统测试自然就是人工点击
    
    ## 弊端就是发现错误不健全

    7.2 webdriver

    参照

    第八篇 爬虫

    8.1 爬虫与反爬虫

    ==爬虫==:按照一定的规则自动抓取网络信息的程序

    ==反爬虫==:

    • User-Agent,Referer,验证码
    • 单位时间的访问次数,访问量
    • 关键信息用图片混淆
    • 异步加载

    8.2 爬虫思路

    思路一:使用cheerio与http/https

    • cheerio:一个可以像jQuery那样操作DOM元素
    • http/https:我就不用说了吧
    //简单示例
    const cheerio = require('cheerio');
    const https = require('https');
    let html = '';
    const $ = '';
    https.get('url',res => {
        res.on('data',(data) => {
            html += data;
        });
        res.on('finish',()=> {
            $ = cheerio.load(html);
            //some code
        })
    })

    思路二:puppeteer

    • 下边贴一段爬虫代码吧!

    index.js

    const puppeteer = require('puppeteer');
    const https = require('https');
    const fs = require('fs');
    (async () => {
        //跳过安装  npm i --save puppeteer --ignore-scripts
        const browser = await puppeteer.launch({
            executablePath: 'G:/chrome-win/chrome-win/chrome.exe'
        });
        const page = await browser.newPage();
        //指定浏览器去某个页面
        await page.goto('https://image.baidu.com');
        // await page.cookies('https://image.baidu.com')
        //     .then(data => {
        //         console.info(data)
        //     });
        //调大视口,方便截图,方便容纳更多地图
        await page.setViewport({
            width:2000,
            height:1000,
        });
        //模拟用哪个户输入
        await page.keyboard.sendCharacter('车模');
        //模拟用户点击搜索
        await page.click('.s_search');
        console.info('开始点击查询......');
        //await page.screenshot({path: 'example.png'});
         page.on('load', async()=> {
             console.info('页面已加载完毕,开始爬取');
            await page.screenshot({path: 'example.png'});
              let srcs = await page.evaluate(() => {
                 let img =  document.querySelectorAll('img.main_img');
                 return Array.from(img).map(item => item.src);
             });
             await srcs.forEach((item,index) => {
                      if(/^https:/g.test(item))
                      https.get(item,res =>{
                          res.pipe(fs.createWriteStream(__dirname + `/img/${index}.png`))
                              .on('finish',()=>{
                                  console.info(`已成功爬取${index + 1}条记录!`);
                              });
                      })
                  })
             await browser.close();
        });
         
    })();

    package.json

    {
      "name": "reptiles",
      "version": "1.0.0",
      "description": "This is a replite",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "node index.js"
      },
      "author": "vuejs@vip.qq.com",
      "license": "ISC",
      "dependencies": {
        "puppeteer": "^1.14.0"
      }
    }
    
    • How To Use?
    ## 初始化你的项目
    
    $ npm init
    
    ## 建立文件
    
    $ cd your project
    $ mkdir index.js
    ## 拷贝我的package.json
    ## 注意,这是重点,因为被墙,所以必须跳过安装
    $ npm i --save puppeteer --ignore-scripts
    
    ## 启动项目
    
    $ npm start
    
    • Note SomeDetails

      • 1.安装puppeteer,必须跳过Chromium 的连接
      • 2.安装好你的Chromium ,在lanuch的时候必须用绝对路径,切记不可把chrom.exe单纯拷贝到项目目录
      • 3.不可把浏览器视口调节的太大,不然会触碰反爬虫协议。
      • 4.如果你调大了也没关系,我们可以page.waitFor(200),让任务沉睡一会儿

    结语

      这篇大长文就算彻底结束了,我相信文章里面还有很多不足或者小错误,诚恳的希望大家不要喷我。

    ​    "你能不能,在于你想不想!"

    ​    加油,每一天都是新生。

    ​    对于这篇文章,我也会不断的完善它。

    查看原文

    赞 2 收藏 0 评论 1

    认证与成就

    • 获得 4 次点赞
    • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

    擅长技能
    编辑

    (゚∀゚ )
    暂时没有

    开源项目 & 著作
    编辑

    (゚∀゚ )
    暂时没有

    注册于 2018-11-26
    个人主页被 108 人浏览