vuex简单使用

安装

npm i vuex -S

配置

在src下创建一个store文件夹文件夹内创建index.js文件

配置(index.js)文件

  1. 引入组件vuex

    1
    2
    3
    4
    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)

  2. 定义数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // 数据存储中心 对应于vue中的data
    const state = {
    count: 0
    }

    // 数据操作中心 操作state内的数据的变化
    // 只提供同步操作
    const mutations ={
    addCount() {
    state.count += 1
    },
    deCount() {
    state.count -= 1
    }
    }

    // 类似于mutation,他提交的是一个mutation而不是直接改变state内数据的状态
    // 可以包含任意异步操作
    const actions = {
    changeCount(context){
    context.commit('addCount') // addCount是mutations的一个方法名
    }
    }

    // 对state进行计算操作,类似于vue中的计算属性
    // 适用于多个组件复用的计算属性
    const getters = {
    computedCount: (state)=> {
    return state.count*2
    }
    }
  3. 实例化Vuex并暴露出去

    1
    2
    3
    4
    5
    6
    7
    const store = new Vuex.Store({
    state,
    mutations,
    getters,
    actions
    })
    export default store

    在main.js中引入使用store

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 引入store
    import store from './store'

    new Vue({
    // 在vue实例化中使用store
    store,
    render: h => h(App),
    }).$mount('#app')

    基本使用

  • state 通过 this.$store.state.xxx 来使用
  • mutation 通过 this.$store.commit(‘xxx’) 来使用
  • getter 通过 this.$store.getters.xxx 来使用
  • action 通过 this.$store.dispatch(‘xxx’) 来使用

配合辅助函数的使用

MapState:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import {mapState} from 'vuex'

computed: {
...mapState([
/**
* 当映射的计算属性的名称与state的子节点名称相同
* 我们可以直接给mapState传一个字符串数组
*/
'name', //映射 this.name 为 store.state.name
'age'
])

...mapState({
vName: state=>state.name, //映射 this.vName 为 store.state.name
vAge: state =>state.age
})

},

mapGetters:

1
2
3
4
5
6
7
8
computed: {
...mapGetters({
name: 'NAME' //异名映射
}),
...mapGetters([
'NAME' //同名映射
])
},

mapMutations:

1
2
3
4
5
6
7
8
methods: {
...mapMutations([
"increment", // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
...mapMutations({
add: "increment" // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}

mapAction:

1
2
3
4
5
6
7
8
9
10
11
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}

vue-router的简单使用

安装

npm i vue-router -S

配置

在src下创建一个router文件夹文件夹内创建index.js文件

配置路由(index.js)文件

  1. 创建组件、引入组件

    1
    2
    3
    4
    import Vue from 'vue'
    import Router from 'vue-router'

    import Nav from '../components/Nav.vue'
  2. 配置路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    const routes = [
    { path:'/nav',component:Nav },
    { path: '/user/:id', component: User } // 动态路由
    // ....
    { path:'*',redirect:'/nav' }, // 默认跳转路径

    // 嵌套路由
    { path: '/user/:id', component: User,
    children: [
    {
    // 当 /user/:id/profile 匹配成功,
    // UserProfile 会被渲染在 User 的 <router-view> 中
    path: 'profile',
    component: UserProfile
    },
    ]
    }
    ]
    ]
  3. 实例化VueRouter并暴露出去

    1
    2
    3
    4
    const router = new VueRouter({
    routes
    })
    export default router

    在main.js中引入使用router

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 引入router
    import router from './router'

    new Vue({
    // 在vue实例化中使用router
    router,
    render: h => h(App),
    }).$mount('#app')

    使用

  • 声明式方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 路由配置的区域
    <router-link to="/nav">nav</router-link>
    <router-link to="/user/123" >user</router-link>

    // 路由需要配置参数 可以通过以下两种方法
    <router-link
    :to="{
    name:'tab', // index配置的时候就得添加name
    query:{
    name:'doreen'
    }, // 不会刷新 /tab?name=doreen
    params:{
    id:'123' // 会刷新 /tab/123
    }
    }"
    >tab</router-link>

    <router-link :to="{ path:'/tab/'+id }" >user</router-link>

    // 组件内容的显示区域
    <router-view />
  • 编程式导航

    path和params一起写的时候params不生效!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 字符串
    router.push('home')

    // 对象
    router.push({ path: 'home' })

    // 命名的路由
    router.push({ name: 'user', params: { userId: '123' }})

    // 带查询参数,变成 /register?plan=private
    router.push({ path: 'register', query: { plan: 'private' }})

    // 在浏览器记录中前进一步,等同于 history.forward()
    router.go(1)

    // 后退一步记录,等同于 history.back()
    router.go(-1)

    // 前进 3 步记录
    router.go(3)

    // 如果 history 记录不够用,那就默默地失败呗
    router.go(-100)
    router.go(100)

vue-property-decorator的简单使用

vue-property-decorator

提供给vue内写typescript语法的时候引入的组件(基于vue-class-component)

安装

npm i -s vue-property-decorator

基本语法

@Props

props是子组件用来接收父组件传递过来的参数

在常规的vue语法环境下的书写方式:

1
2
3
4
5
6
7
8
9
10
11
export default {
props: {
propA: {
type: Number,
// type: [ String,Boolean ]
// default: 'value'
// default: function (){ return 'hello' }
// required: true
}
}
}


在vue-property-decorator组件中使用:

@Prop( options: (PropOptions | Constructor[] | Constructor )={} )

@Prop装饰器接收一个参数,这个参数的三种写法:

  • constructor String、Number、Boolean等,指定prop的类型
  • constructor[] 指定prop的可选类型
  • propOptions 对象参数可以是 type,default,required…
1
2
3
4
5
6
7
8
9
10
11
12
<script lang='ts'>
import { Vue,Component,Prop } from 'vue-property-decorator'

@Component({}) // 不管有没有组件都必须写
export default class 组件名 extends Vue{

@Props(Number) propA!: number;
@Props([String,Boolean]) propB: string | boolean;
@Props({default:'value'}) propC!: string;

}
</script>

! 表示一定有值(非null和非undefined), ? 表示可选

@Component

用来接收一个对象作为参数,在对象中可以声明components,filters,directives等,也可以声明computed,watch

在常规的vue语法环境下的书写方式:

1
2
3
4
5
6
7
import DetailTable from './components/detail-table.vue';

export default {
components: {
DetailTable
},
}


在vue-property-decorator组件中使用:

Component( options:ComponentOptions = {} )

1
2
3
4
5
6
7
8
9
10
11
12
13
import DetailTable from './components/detail-table.vue';
// ...
@Component({
components:{
DetailTable
},
filters: {
toFixed: ()=>{ ... }
}
})
export default class 组件名 extends Vue {
// ...
}

@Watch

侦听数据的变化

在常规的vue语法环境下的书写方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
watch: {
msg: [
{
handler: 'onMsgChange',
immediate: false,
deep: false
}
]
},
methods: {
onMsgChange(newValue,oldValue) { }
}

在vue-property-decorator组件中使用:

@Watch( path: string, options: WatchOptions={} )

@Watch装饰器接收两个参数:

  • path:string 被侦听的属性名
  • options?: WatchOptions={} options 可以包含两个属性:
    • immediate?: boolean 侦听开始之后是否立即调用该回调函数;
    • deep?: boolean 被侦听的对象的属性被改变时,是否调用该回调函数;
      1
      2
      3
      4
      5
      6
      export default class 组件名 extends Vue {
      @Watch('msg')
      public onMsgChange (newValue: string, oldValue: string) {
      // ...
      }
      }

@Emit

广播方法获取变量

在常规的vue语法环境下的书写方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
data (){
count: 0
},
methods: {
addToCount(n) {
this.count += n
this.$emit('add-to-count',n)
},
resetCount() {
this.count = 0
this.$emit('reset')
}
}
}

@Emit(event?:string)

@Emit装饰器接收一个可选参数,作为事件名
@Emit将回调函数的返回值作为第二个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
export default class 组件名 extends Vue{
count = 0

@Emit()
public addToCount(n:number){
this.count += n
}

@Emit('reset')
public resetCount() {
this.count = 0
}
}

vue-class-component的简单使用

vue-class-component

vue对typescript支持的库

安装

npm install –save vue vue-class-component

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<script lang='ts'>
import { Component,Provide,Vue } from 'vue-property-decorator'
import { BaseAxios } from '../api/axios'
import input from 'components/input.vue'

// 组件的引入
@Component({
components: { input }
})

export default class Hello extends Vue {
// data
obj: Object = {}
msg: string = 'hello world'
num: number = 0

// methods
addTimes (a: number) :number {
this.num += a
return this.num
}

// computed
get computedMsg() {
return 'computed' + msg
}

// 生命周期
mounted() {
BaseAxios.post('...').then( res=>{
console.log(res)
})
}

}

</script>

createDecorator

自定义装饰器,接收一个回调函数作为第一个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// decorator.js
import { createDecorator } from 'vue-class-component'

export const NoCache = createDecorator((options,key)=>{
options.computed[key].cache = false
})

// MyComp.js
import { NoCache } from './decorators'
@Component
class MyComp extends Vue {
// computed属性不会被缓存
@NoCache
get random() {
return Math.random()
}
}

Component.registerHooks

自定义钩子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// class-component-hooks.js
import Component from 'vue-class-component'

// 注册钩子name
Component.registerHooks([
'beforeRouterEnter',
'beforeRouterLeave',
'beforeRouterUpdate',
])

// MyComp.js
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
class MyComp extends Vue {
// 在组件内部为注册的钩子函数实现原型方法
beforeRouterEnter(to,from,next) {
console.log('before-router-enter')
next()
}
beforeRouterLeave(to,from,next) {
console.log('before-router-leave')
next()
}
}

typescript基础语法

1.1 简介

  • ts 是由微软开发的开源的编程语言

  • typescriptjavascript的超集

  • ts 是开发大型应用的基石

  • ts 提供了更丰富的语法提示

  • ts 在编写阶段能够检查错误

2.1 ts 是静态类型 , js 是动态类型

动态类型运行时,解析器基于变量的类型决定变量的类型 (可以不需要提前声明变量类型)

3.1 数据类型

  • 原始数据类型: boolean string number null undefined symbol

  • 引用数据类型: object

  • 基础类型:boolean string number null undefined symbol any never void

    • any 没办法有代码提示;
    • any 在代码测试的时候用;
    • 对象 interface
    • 数组 number[] string[] boolean[] 泛型的写法: Array<number>

    函数的注解:

    1
    2
    3
    let test = function (a: number, b: number): number {
    return a + b
    }

    新的语法特性:
    as 类型 ;或者 <类型 >值 // 断言
    class (OOP 面向对象的三大特性) : 封装 , 继承, 多态;

4.1 原始数据的注解

  • 布尔值的注解:

    1
    let isDone: boolean = false;
  • 数字的注解:

    1
    let decLiteral: number = 6;
  • 字符串的注解:

    1
    let name: string = "bob";

5.1 原始数据的注解

any 数据类型: 任意数据类型;

  1. 如果是不同变量的话,可以是任意的数据类型:
  2. 如果是对象的话, any 不能够提示原有的属性和方法;
  3. 未给初始值的变量类型 为 any;

6.1 原始数据的注解

  • void 当一个函数没有返回值时

  • null && undefined

    • nullundefined是所有类型的子类型
    • –strictNullChecks : let num: number = undefined;
  • never 类型表示的是那些永不存在的值的类型;

  • object 表示非原始类型

7.1 类型注解 && 类型推断

7.2 联合类型;

注意:

  1. 联合类型的共有属性是不会报错;

  2. 在赋值的时候确认类型;

    1
    let a: string | number;

8.1 接口 interface

  1. 对象的形状进行描述;

  2. 对类的一部分行为的抽象;

    interface 不可多,不可少;
    可选属性:age?: number;
    任意属性:[propName: string]: any
    只读属性:readonly

    1
    2
    3
    4
    5
    interface Person {
    name: string;
    age?: number; // 可选属性;
    [propName: string]: any
    }

9.1数组的注解

写法方式

  1. 类型[] // number[] (纯数字的数组, 没有长度限制) , push 非类型的数据,是进不去的;

  2. Array<类型> Array

  3. interface 方式;

    1
    2
    3
    interface List {
    [index: number]: number | string
    }
  4. 类数组

    1
    2
    3
    4
    5
    interface Args {
    [index: number]: any;
    length: number;
    callee: Function;
    }

    10.1 函数的注解方式

    1. 函数声明的注解方式;

      1
      2
      3
      4
      5
      function test(a: number, b: number): number {
      return a + b
      // console.log(1)
      // throw new Error()
      }
    2. 函数表达式的注解方式;

    1
    2
    3
    let test1: (a: number, b: number):{} = function (a, b) {
    return { a: 1 }
    }

    10.2 函数的重载

  • 相同的函数因为传入的参数类型不同 返回的值的类型也不同

  • 表意更清楚;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function reverse(x: string): string;
    function reverse(x: number): number;
    function reverse(x: string | number) {
    if (typeof x === 'string') {
    return x.split('').reverse().join('')
    }

    if (typeof x === 'number') {
    return Number(x.toString().split('').reverse().join(''))
    }
    }

11.1 类

"strictPropertyInitialization": false,

类的注解方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 父类 / 基类
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}

class Cat extends Animal {
constructor(name:string){super(name)} // 1
move(position:number){
super.move(position) // 2
}
}

  • 1->继承中的super指的是构造函数
  • 2->构造函数以外的super指的是父类 ; 可以调用父类的属性和方法

11.2 类的修饰符

  • public 公共的成员属性 (默认):

    1. 自身调用;
    2. 子类可以调用;
    3. 实例调用;
  • private 私有属性:

    1. 自身调用;
  • protected 受保护的成员属性:

    1. 自身调用;
    2. 子类可以调用;

11.3 类的修饰符 readonly;

  1. 顺序 -> public readonly

  2. 是否可写: readonly 只读, 不可写

  3. 不能修饰成员方法;

    参数属性: 简写的写法;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    class Animal1 {
    constructor(private name: string) { }
    move(distanceInMeters: number) {
    console.log(`${this.name} moved ${distanceInMeters}m.`)
    }
    }
    let jake = new Animal1('jake');
    console.log(jake);
    class Animal2 {
    private name: string;
    constructor(name: string) { this.name = name }
    move(distanceInMeters: number) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
    }

11.4 类的存取器;

  • getter 取值函数 obj.a

  • setter 存值函数 obj.a = ‘123’

    //同时出现, 就是一个属性;

    改变赋值和读取的行为;

11.5 类的静态属性 && 抽象类;

  • 抽象类: 能够提供其他类的基类;

  • 抽象类:

    1. 无法创建实例;
    2. 抽象方法一定要有实现;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
          
    abstract class Animal {
    abstract makeSound(): void;
    move(): void {
    console.log('roaming the earch...');
    }
    }
    class Snack extends Animal {
    makeSound() {
    console.log('zzzzzzz');
    }
    move(): void {
    console.log('roaming the earch...');
    }
    }

    class Rhino extends Animal {
    makeSound() {
    console.log('mmmmmmm');
    }
    move(): void {
    console.log('roaming the earch...');
    }
    }

    new Snack();

11.6 高阶技巧;

  1. 定义类的时候 , 定义了一个类型;
  2. 定义类的时候, 定义了一个构造函数;
  3. 接口可以继承类;

12.1 接口和 函数类型接口;

函数类型接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

interface SearchFunc {
(source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function (source: string, subString: string): boolean {
let result = source.search(subString);
return result > -1;
}

type SearchFunc = (source: string, subString: string) => boolean

// function mySearch(source: string, subString: string): boolean {
// let result = source.search(subString);
// return result > -1;
// }

12.2 接口和 可索引的类型接口;

  1. 共有支持两种索引签名:字符串和数字。

    1
    2
    3
    interface NumberArray {
    [index: number]: number
    }

    是因为当使用number来索引时,
    JavaScript会将它转换成string然后再去索引对象;

  2. 索引签名 有点像老大的意思;

    1
    2
    3
    4
    5
    6
    7


    interface NumberDictionary {
    [index: string]: number | string;
    length: number; // 可以,length是number类型
    name: string // 错误,`name`的类型与索引类型返回值的类型不匹配
    }
  3. 索引签名可以设置为只读;

    1
    2
    3
    4
    5
    6

    interface ReadonlyStringArray {
    readonly [index: number]: string;
    }
    let myArray: ReadonlyStringArray = ["Alice", "Bob"];
    myArray[2] = "Mallory"; // error!

    12.3 类类型接口; – 类实现接口;

    对类的一部分行为的抽象;

    类实现所有接口中的属性和方法; && 对比 抽象类: 抽象方法需要实现;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
      
    interface ClockInterface {
    currentTime: Date;
    }

    class Clock implements ClockInterface {
    currentTime: Date;
    constructor(h: number, m: number) { }
    }

    interface Alarm {
    alert(): void;
    }

    interface Light {
    color: string;
    lightOn(): void;
    lightOff(): void;
    }

    class Door { }

    class SecurityDoor extends Door implements Alarm {
    alert() {
    console.log('hei');
    }
    }

    class Car implements Alarm, Light {
    color = 'red';
    lightOn() {

    }
    lightOff() {

    }
    alert() {
    console.log('hello');
    }
    }

12.4 类类型接口 –类静态部分与实例部分的区别

constructor存在于类的静态部分,所以不在检查的范围内

类静态部分与实例部分需要单独做;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 实例部分
interface ClockInterface {
currentTime: Date;
getTime(h: number, m: number): any;
}
// 构造函数 --静态类型部分
interface ClockConstructor {
new(h: number, m: number): any
}

class Clock implements ClockInterface {
currentTime = new Date()
constructor(h: number, m: number) { }
getTime() {}
}

function createClock(C: ClockConstructor, h: number, m: number) :ClockInterface{
return new C(h, m);
}

let clock = createClock(Clock, 12, 12);

12.5 接口继承接口 | 类实现接口;

1
2
3
4
5
6
7
8
9
10
11
 interface Shape {
color: string;
}

interface PenStroke {
penWidth: number;
}

interface Square extends Shape, PenStroke {
sideLength: number;
}

12.6 混合类型;

函数类型的interface, 添加属性的方式来实现 对象的interface;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 函数类型 + 对象的类型
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}

function getCounter(): Counter {
// 断言 => (<any>result).id => (result as any).id
// 初始化值的时候使用

let counter = <Counter>function(start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

12.7 混合类型的应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface ClockInterface {
currentTime: Date;
getTime(h: number, m: number): any;
}

interface ClockConstructor {
new(h: number, m: number): any;
getTime1(): void;
}
class Clock implements ClockInterface {
currentTime = new Date()
constructor(h: number, m: number) { }
getTime() {}

static getTime1() {}
}

function createClock(C: ClockConstructor, h: number, m: number) {
return new C(h, m);
}

let clock = createClock(Clock, 12, 12);

12.8 接口继承类:

  1. 类可以实现接口;
  2. 接口可以继承接口;
  3. 接口可以继承类;

13.1 泛型

*T 泛型变量 | 类型变量 *

1
2
3
4
5
6
7
8
9
10
11
12
13
function identity1<T>(arg: T): T {
return arg;
}

identity1('123')
identity1<number>(123)

interface MyArray<T> {
[n: number]: T;
}

let a: number[] = [12, 3, 4];
let b: Array<number> = [1, 2, 3, 4]

13.2 泛型类型

1
2
3
4
5
6
7
8
9
10
function identity<U>(arg: U): U {
return arg;
}

interface IdentityInterface<T> {
(arg: T): T
}
interface IdentityInterface {
<T>(arg: T): T
}
  1. 函数泛型的注解方式;

    1
    2
    let a: <T>(arg: T) => T = identity;

  2. 对象字面量的方式来定义泛型类型;

    1
    2
    let a: { <T>(arg: T): T } = identity;

  3. 泛型接口的定义方式:

    1
    2
    let a: IdentityInterface = identity;

    13.3 泛型类 泛型约束;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class MinClass<T>{
    public list: T[] = [];
    add(num: T) {
    this.list.push(num);
    }

    min(): T {
    let minNum = this.list[0];
    for (var i = 0; i < this.list.length; i++) {
    if (minNum > this.list[i]) {
    minNum = this.list[i];
    }
    }
    return minNum;
    }
    }
    • 泛型约束:
      extends 接口的方式(不一定是接口);
    1
    2
    3
    4
    5
    6
    7
    8
    interface LenthWise1 {
    length: number;
    }
    type LenthWise = string | LenthWise1
    function loggingIdentity<T extends LenthWise>(arg: T): T {
    console.log(arg.length); // Error: T doesn't have .length
    return arg;
    }

    13.4 多重泛型约束; || && | &

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    interface Sentence {
    content: string;
    title: string;
    }

    interface Music {
    url: string;
    }

    class Test<T extends Sentence & Music>{
    props: T
    constructor(public arg: T) {
    this.props = arg
    }

    info() {
    return {
    url: this.props.url,
    content: this.props.content,
    title: this.props.title
    }
    }
    }

13.5 在泛型里使用类类型, 约束或是更好的推论;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function create<T>(c: { new(): T; }): T {
return new c();
}

class BeeKeeper {
hasMask: boolean;
}

class ZooKeeper {
nametag: string;
}

class Animal {
numLegs: number;
}

class Bee extends Animal {
keeper: BeeKeeper;
}

class Lion extends Animal {
keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}

createInstance(Lion).keeper.nametag; // typechecks!
createInstance(Bee).keeper.hasMask;

14 元组;

  1. 确定成员 类型长度;

  2. push 越界的时 , 类型为联合类型;

  3. 可选的元素类型:

    1
    let list: [number, string, boolean?];
  4. rest 拓展运算符;

    declare function test(...args: [number, string, boolean]): void;
    declare function test(arg1: number, arg2: string, arg3: boolean): void;
    
    let list1: [number, ...string[]] = [1, '2', '3', '4'];
    let list2: [string, ...number[]] = ['1', 2, 3, 4, 5, 6, 7];

Next.js

next中文文档

next.js简介

Next.js 是一个轻量级的 React 服务端渲染应用框架。

  • 完善React项目架构,搭建轻松。比如:Webpack配置,服务器启动,路由配置,缓存能力,这些在它内部已经完善的为我们搭建完成了。
  • 自带数据同步策略,解决服务端渲染最大难点。把服务端渲染好的数据,拿到客户端重用,这个在没有框架的时候,是非常复杂和困难的。有了Next.js,它为我们提供了非常好的解决方法,让我们轻松的就可以实现这些步骤。
  • 丰富的插件帮开发人员增加各种功能。每个项目的需求都是不一样的,包罗万象。无所不有,它为我们提供了插件机制,让我们可以在使用的时候按需使用。你也可以自己写一个插件,让别人来使用。
  • 灵活的配置,让开发变的更简单。它提供很多灵活的配置项,可以根据项目要求的不同快速灵活的进行配置。

创建next.js应用程序

1
2
3
4
5
6
// 1.安装next的项目的脚手架工具 create-next-app
npm install -g create-next-app

// 2.创建next项目
npx create-next-app xxx

项目结构

1
2
3
4
5
6
7
8
xxx
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public // 公共静态文件存放地
├── components // 组件存放地
└── page // 页面存放地,自动生成路由渲染在服务器

page的使用

需要新添加路由则直接在page文件夹下创建新文件
eg:

1
2
3
export default function Index(){
return (<h1>hello world</h1>)
}

打开http://localhost:3000/Index即可访问到相关内容

component的使用

  1. 以创建一个item组件为例

    1
    2
    3
    4
    export default ({children})=>{
    <button>{children}</button>
    }
    // {children} => props.children 也可以在里面直接写props 获取的时候 props.children.xx
  2. 在page文件夹下的页面中引入

    1
    import Item from '../components/item'
  3. 使用组件

    1
    <Item>按钮组件 / {xx}</Item>

路由相关

页面跳转一般有两种形式,第一种是利用标签,第二种是用js编程的方式进行跳转,也就是利用Router组件。

标签式导航

在主页面和可跳转页面都引入link

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// home-page
import Link from 'next/link'
export default Home=()=>{
<div>
<h1>首页</h1>
<Link href='/pageA'><a>A页面</a></Link>
{/*
带参数传递
<Link href='/pageA?id=123'><a>A页面</a></Link>
<Link href={{pathname:'/pageA',query:{id:'123'}}}><a>A页面</a></Link>
*/}
<Link href='/pageB'><a>B页面</a></Link>
</div>
}
// ----------
// pageA
import Link from 'next/link'
export default ({router})=>{
<div>
<h1>page A</h1>
{/*
query接收参数
<span>{router.query.id}</span>
*/}
<Link href='/'><a>返回首页</a></Link>
</div>
}
// -----------
// pageB
import Link from 'next/link'
export default ()=>{
<div>
<h1>page B</h1>
<Link href='/'><a>返回首页</a></Link>
</div>
}

Router模块进行跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Router from 'next/router'
const Home = ()=>{

function gotoB(){
Router.push('/pageB')
/*
带参数传递

Router.push('/pageB?id=123')

Router.push({
pathname: '/pageB',
query: {
id: '123'
}
})
*/
}

return (
...
<div>
<button onClick={()=>{Router.push('pageA')}}>A页面</button>
<button onClick={gotoB}>B页面</button>
</div>
...
)
}

钩子函数

routeChangeStart路由发生变化时

routerChangeComplete路由结束变化时

beforeHistoryChange浏览器history触发前

routeChangeError路由跳转发生错误时

hashChangeStart hash开始跳转时

hashChangeComplete hash完成跳转时

getInitialProps数据获取

配合axios

  1. 安装引入axios
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    yarn add axios

    // 在所需的页面引入axios
    import axios from 'axios'

    const Home = ({router,list})=>{
    return (
    <>
    <div>{list}</div>
    </>
    )
    }

    /*
    通过getInitialProps获取数据
    然后页面通过props.list获取数据,并进行显示
    */
    Home.getInitialProps = async () => {

    const res = await axios('http://rap2.taobao.org:38080/app/mock/250724/get/data').then((res) => {
    return res.data
    })
    //返回值只能是resolve对象
    return res
    }
    export default withRouter(Home)

    CSS编写

  • 使用style jsx语法
    1
    2
    3
    4
    5
    6
    // 在return内部编写
    <style jsx>
    {`
    div{ color:red }
    `}
    </style>
  • 引入CSS文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 安装配置相应内容
    yarn add @zeit/next=css

    // 在根目录建立next.config.js配置
    const withCss = require('@zeit/next-css')

    if(typeof require !== 'undefined'){
    require.extensions['.css']=file=>{}
    }

    module.exports = withCss({})
    重启项目之后就可以进行使用
    1
    2
    // 在所需页面引入样式文件
    import '../public/test.css'

react-redux的简单使用

简介

redux为react提供的一个专用库
react-redux组成:

  1. UI组件(presentational component)

    • 只负责ui的呈现,不带有任何业务逻辑
    • 没有状态(不使用this.state这个变量)
    • 所有数据都由参数(this.props)提供
    • 不使用任何Redux的API
  2. 容器组件(container component)

    • 负责管理数据和业务逻辑,不负责UI的呈现
    • 带有内部状态
    • 使用Redux的API

安装

npm i react-redux –save

配合redux使用

npm i redux –save

使用

全局配置

利用<Provider>组件来实现容器组件获取state对象
在src下的index.js全局进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux'
import store from './store'
const app = (
<Provider store={store}> // 让App所有的子组件都默认获取到了state
<App/>
</Provider>
)
//ReactDOM.render(<App />,document.getElementById('root'));
ReactDOM.render(app,document.getElementById('root'));

组件配置

connect方法用于从UI组件生成容器组件
在组件内部引入connect方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { Component } from 'react';
import { connect } from 'react-redux'

// 将app转化为UI组件的形式进行展示
const App = (props)=>{
let {inputValue,add,list,changeInput} = props // 从属性中获取到方法和对象
return (
<div>
<input type="text" placeholder={inputValue} onChange={changeInput} />
<button onClick={add}>提交</button>
<ul>
{
list.map((item, index) => {
return <li key={index}>{item}</li>
})
}
</ul>
</div>
);
}

connect方法接收两个参数:

  1. mapStateToProps
  2. mapDispatchToProps
    它们定义了ui组件的业务逻辑
    前者负责输入逻辑 => state映射到ui组件的参数(props)
    后者负责输出逻辑 => 用户对ui组件的操作映射成Action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 映射关系
const stateToProps = (state) => {
return {
inputValue: state.inputValue,
list: state.list
}
}
const dispatchToProps = (dispatch)=>{
return {
changeInput(e){
let action = {
type: 'changeInput',
value: e.target.value
}
dispatch(action)
},
add(){
let action = {
type: 'add'
}
dispatch(action)
}
}
}
export default connect(stateToProps, dispatchToProps)(App);

redux配合react的简单使用

简介

javascript状态容器,提供可预测化的状态管理。(类似于vuex)

redux的工作流程

store 是数据的存放地,组件从store中获取数据
action 存放着组件(view)对数据(data)的操作
reducer 根据传来的action的类型来更新state

流程详解

  1. 用户操作视图数据发出action

    store.dispatch(action)

  2. store自动调用reducer,并传入两个参数state和action。reducer根据action返回新的state

    (state=defaultState,action)=>{ if(action.type===’…’){ return newState}… }

  3. state发生变化,store调用监听函数

    store.subscribe(listener)

  4. listener可以通过store.getState()得到当前状态,重新渲染页面

    function(){
    let newState = store.getState()
    component.setState(newState)
    }

    三大原则

  5. 单一数据源
    整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

  6. state是只读的
    唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

  7. 使用纯函数来执行修改
    为了描述 action 如何改变 state tree ,需要编写 reducers。它接收先前的 state 和 action,并返回新的 state。

    安装

    npm i redux –save

    使用

    一般会创建一个单独的文件夹来存放这些数据

    1
    2
    3
    4
    5
    src
    └── store
    ├── index.js
    ├── reducer.js
    └── actions.js

    index (state)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /*
    createStore():
    创建一个store来存放所有的state
    应用中有且仅有一个store
    参数
    1:reducer(Function)
    2:[preloadeState](any): 初始的state
    3:enhancer(Function): 强化的store creator ??
    */
    import { createStore } from 'redux' // 引入方法
    import reducer from './reducer' // 引入reducer
    const store = createStore( // 创建存储仓库
    reducer,
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    )
    export default store // 把创建好的数据仓库暴露出去

在组件中获取数据

1
2
3
4
5
6
7
8
9
import store from './store';
...
class App extends Component {
constructor(props){
super(props)
this.state = store.getState() //获取state
}
}
export default App;

actions

1
2
3
4
5
6
7
8
// actions.js
export function addAction = ()=> ({
type: 'add'
})
export function changeInputAction = (value)=>({
type: 'changeInput',
value
})

在组件内使用action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import {changeInputAction,addAction} from './store/actions'

class App extends Component {
// 触发事件就会触发action
changeInputValue(e){
const action = changeInputAction(e.target.value)
store.dispatch(action) // 更新state
}
add(){
const action = addAction()
store.dispatch(action)
}
storeChange(){
this.setState(store.getState())
}
}

export default App;

reducer

它没有直接改变state的数据,而是返回新的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 创建初始的state数据
const defaultState = {
list:['hehe','enen'],
inputValue:'write somthing'
}
export default (state=defaultState,action)=>{
if(action.type==='changeInput'){
let newState = JSON.parse(JSON.stringify(state)) // 深拷贝
newState.inputValue = action.value // 更新数据
return newState // 返回新的state
}
if(action.type==='add'){
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
return state
}

// 官方文档给出的代码段 是用switch-case进行对action类型的判断,assign进行拷贝和更新:
function todoApp(state = defaultState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
default:
return state
}
}

参考

redux中文文档
技术胖博客

【react】开发环境搭建

首先安装 Node 和 npm

我的版本

1
2
node v10.15
npm v6.4.1

创建应用程序

  1. 针对于npm 5.2+

    npx create-react-app xxx

  2. 针对于npm 6+

    npm init react-app xxx

  3. 针对于yarn 0.25+

    yarn create react-app xxx

输出

进入到xxx目录中,应该生成的目录格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
xxx
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js

安装完成后,即可进入到项目目录:

cd xxx

运行

  1. 运行:

    npm start

  2. 打包:

    npm run build

遇到的问题

按照文档的方式安装运行脚手架工具报错,并且生成的文件缺失
报错信息如下:

A template was not provided. This is likely because you’re using an outdated version of create-react-app.
Please note that global installs of create-react-app are no longer supported.

(大概意思是 官方不支持 create-react-app 这个 cli 脚手架,版本已经过时,不支持全局安装)
因为我之前也有看过react的相关视频 可能安装的有之前的脚手架版本

解决方法

卸载全局安装的脚手架

npm uninstall -g create-react-app
然后在重新按照之前的步骤在运行一次

ES2020新特性

不是所有的都会记录 只记录一些我目前能理解的和我觉得比较有用的

类的私有变量和方法

通过 # 代表私有变量和方法,在class外部无法访问 (相当于private)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Num{
#privateNum = 0 //私有变量
static #privateStaticNum = 0 //静态私有变量
add(){
this.#privateNum++
Num.#privateStaticNum++
}
print(){
console.log(this.#privateNum,Num.#privateStaticNum)
}
}
var a = new Num()
a.add()
a.print() // 1 1
console.log(a.#privateNum) // SyntaxError
console.log(Num.#privateStaticNum) // SyntaxError

在MDN和其他的一些大佬的总结里面也写了可以定义私有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 
类似于以这种形式
不知道是什么原因会报错 unexpected token
*/
class Method(){
#privateStaticMethod(){
return 'hello world'
}
publicStaticMethod(){
return #privateStaticMethod()
}

/*
另外看到有人总结是用这种形式
可以正常运行 但是我感觉这种形式就是私有变量的定义赋给一个函数而已?
*/
#privateStaticMethod = ()=>{ ... }
}

空值合并运算符(Nullish coalescing Operator)

和 || 的作用类似,但是 || 会将空字符串、0、false等直接覆盖,可能会造成一些问题,
?? 运算符只针对 null、undefined 进行覆盖

1
2
const b = '' || 123     // 123
const a = '' ?? 123 // ''

可选链(Optional chaining)

简化繁琐重复的前置校验操作

1
2
3
4
5
6
7
8
9
10
11
const user = {
info: {
name: {
getName: function () {
return 'doreen'
}
}
}
}
let names = user && user.info && user.info.name && user.info.name.getName && user.info.name.getName()
let name = user?.info?.name?.getName?.() // => user?.info?.name?.getName()

dynamic-import

之前的按需引入是静态import、目前更新的是动态的import()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 用法区别:static import没有括号,dynamic import() 带有括号
// static import : 有声明提升、一般放在头部位置
import name from 'url'

// dynamic import : 放在任意位置
const name = import('url')

/*
test.js:

export {xxx} 引入的时候 import {xxx} from 'xxx' 可以有多个
export default function(){...} / export default { a(){...},b(){...} }
引入的时候 import xxx from 'xxx' 只能写一次
*/
const add = (n,m)=>{
return n+m
}
export { add }


// a.js:
// import()相当于一个promise的实例对象
const doAdd = async (n,m)=>{
const foo = await import('./test.js')
console.log(foo.add(n,m))
}
const doAdd = (n,m)=>{
import('./test.js').then(res=>{
console.log(res.add(n,m))
})
}
doAdd(1,2)

globalThis

提供了一个标准的方式去获取不同环境下的全局对象(gloabl、window)
在web下能正常打印,但是在node下还是不能正常输出?

参考:

https://blog.csdn.net/duyujian706709149/article/details/104014127
https://segmentfault.com/a/1190000022006869
https://www.jb51.net/article/178289.htm
MDN

  • Copyrights © 2019-2026 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信