刘金星个人博客

个人博客


  • 首页

  • 归档

  • 标签

  • 关于

常用命令记录

发表于 2019-05-09 | 更新于 2019-08-22

git 命令

  1. 打包某次提交的文件

    1
    git archive --format=zip HEAD `git diff --name-only 8bbf69c253801228ff504ab080ce7cf44a924971 a27d045d8c60d6c62a4061b94763886577e1c0eb` > a.zip
  2. 常用命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 拉取文件
    git pull

    # 查看本地变动文件
    git status

    # 添加文件到git版本关键(.* 表示全部,建议后面跟需要的文件)
    git add .*

    # 提交到本地仓库
    git commit -m 'update'

    # 推送文件
    git push origin master
  3. 使用github托管静态资源

1
2
// --prefix=表示需要把那个文件夹提交到gh-pages
git subtree push --prefix=dist origin gh-pages

通过 你的github名称.github.io/你的项目名称 可以访问你的项目

例如:https://mylovegy.github.io/gallery/

openssl 证书格式互转

  1. p12 转 pem

    1
    openssl pkcs12 -in 文件名.p12 -out 输出文件.pem -nodes
  2. cer 转 pem

    1
    openssl x509 -inform der -in 文件名.cer -out 输出文件名.pem

Vue 学习笔记

发表于 2019-05-08 | 更新于 2019-08-22

一、vue 基础

vue 官方文档

1、使用和模板语法

1、变量输出

1
2
3
<div id="app">
<div>{{ message }}</div>
</div>
1
2
3
4
5
6
7
8
let app = new Vue({
el: "#app",
data() {
return {
message: "hello Vue!"
}
}
})

2、html输出

1
2
3
4
<div id="app">
<p> {{ message }} </p>
<p v-html="html"></P>
</div>
1
2
3
4
5
6
7
8
9
10
11
let app = new Vue({
el: "app",
data() {
return {
"message": "我是普通信息",
html: "<div style='color:red'>我是html信息</div>"
}
}
});

// app.message = app.html

区别在于: 这种如果message中包含html 内容,会被转义;要渲染 html 内容 需要使用 v-html 指令

2、常用指令

  1. v-if 根据条件渲染
1
2
3
<div id="app">
<p v-if="hide"> 我渲染出来了 </p>
</div>
1
2
3
4
5
6
7
8
9
10
let app = new Vue({
el: "#app",
data(){
return {
hide: false
}
}
})

// app.hide = true
  1. v-show 根据条件显示&隐藏
1
2
3
<div id="app">
<p v-show="show"> 我显示出来了 </p>
</div>
1
2
3
4
5
6
7
8
9
10
let app = new Vue({
el: "#app",
data(){
return {
show: false
}
}
})

// app.show = true
  • v-if 和 v-show 的区别, v-if 是条件成立,才会渲染元素、但 v-show 是元素渲染出来,只是改变css display 属性
  1. v-for 循环渲染
1
2
3
4
5
6
7
<div id="app">
<ul>
<li v-for="(value, index) in items" :key="index">
我是第 {{ index }} 个 li : {{ item }}
</li>
</ul>
</div>
1
2
3
4
5
6
7
8
9
10
let app = new Vue({
el: "#app",
data(){
return {
items: [1, 2, 3, 4, 5, 6]
}
}
})

// app.items.psuh(100)
  1. v-model 表单绑定
1
2
3
4
<div id="app">
<label> 用户名 <label> <input v-model="username" />
<p> 您输入的用户名为: {{ username }} </p>
</div>
1
2
3
4
5
6
7
8
let app = new Vue({
el: "#app",
data(){
return {
username: ""
}
}
})
  1. v-bind 绑定变量
1
2
3
<div id="app">
<p v-bind:href="url" v-bind:class="{'text-warning': warning}"> 连接地址 </a>
</div>
1
2
3
4
5
6
7
8
9
let app = new Vue({
el: "#app",
data(){
return {
href: "/",
warning: false
}
}
})
  1. v-on 监听事件
1
2
3
4
5
6
<div id="app">
<button v-on:click="btnClick" > 按钮 </button>
<button v-on:click="show = !show">
{{ show ? '隐藏' : '显示' }}
</button>
</div>
1
2
3
4
5
6
7
8
let app = new Vue({
el: "#app",
methods: {
btnClick() {
alert('按钮点击了')
}
}
})

注意事项

  • v-if 和 v-show 的区别 v-if 只有条件满足才会渲染,v-show 只是控制 css display 属性, html 始终是会渲染出来的; v-show 必须作用在真实的 html 上面, v-if 还可以使用在
    上面, v-for 也可以使用 ```
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    * v-for 作用在 html 标签上, 需要定义唯一属性key
    * v-on 可以简写为 @; v-bind 可以简写为 :
    * v-on 绑定事件,还可以使用修饰符; 常用的 stop 阻止冒泡,prevent 阻止默认行为 [其他修饰符](https://cn.vuejs.org/v2/guide/events.html#%E4%BA%8B%E4%BB%B6%E4%BF%AE%E9%A5%B0%E7%AC%A6)

    ```html
    <div id="app">
    <a @click.prevent.stop="show = !show" :href="url">
    {{ show ? '隐藏' : '显示' }}
    </button>
    </div>
1
2
3
4
5
6
7
8
let app = new Vue({
el: "#app",
data() {
return {
url: "/vue-demo/index.html"
}
}
})
  • v-model 也有修饰符
  1. number (转为数字,可以转才会转)
  2. lazy(change 事件触发变更数据)
  3. trim (去除两边空格)

3、组件

1、定义

1
2
3
4
<div id="app">
<my-demo></my-demo>
<test></test>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   // 定义一个组件(全局)
Vue.component("MyDemo", {
template: "<p>我的第一个组件</p>"
});

// 定义组件(局部),需要引入
let test = {
template: "<p>我定义的组件</p>"
}

// 使用组件
let app = new Vue({
el: "#app",
methods: {
btnClick() {
alert('按钮被点击了')
}
}
});

2、props 传值

1
2
3
   <div id="app">
<my-demo :info="info"></my-demo>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   // 定义组件(全局)
Vue.component("MyDemo", {
props: {
info: [String],
message: {
type: String,
default: "我的测试"
}
},
template: "<p>info: {{ info }} ; message: {{ message }} </p>"
});

// 使用组件
let app = new Vue({
el: "#app",
data(){
return {
info: "123"
}
}
});

3、向父组件通知事件

1
2
3
   <div id="app">
<my-demo :info="info" @p-click="demoClick"></my-demo>
</div>
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
   // 定义组件(全局)
Vue.component("MyDemo", {
props: {
info: [String],
message: {
type: String,
default: "我的测试"
}
},
template: "<p @click='click'>info: {{ info }} ; message: {{ message }} </p>",
methods: {
click() {
alert('我被点击了');

// 向父组件提交事件
this.$emit("p-click")
}
}
});

// 使用组件
let app = new Vue({
el: "#app",
data(){
return {
info: "123"
}
},
methods: {
demoClick() {
alert('接收到子组件的事件了');
this.info = "收到你的事件了!"
}
}
});

4、我们用到的其他插件

  1. Vue Router 路由处理
  2. Vuex 状态管理
  3. Axios Http 请求
  4. iView UI 组件库

二、目前前端项目

1、目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public                           前端资源
src 项目代码目录
assets/ 前端资源(css)
components/ 项目公共组件目录
business/ 目前业务公共组件
mock/ 接口模拟数据
model/ 数据model(本地化存储)
pages/ 具体页面
plugins/ 插件目录
router/ 路由配置
service/ 服务(定义接口api)
store/ 状态管理
modules/ 状态管理modules
util/ 自定义工具库
vue.config.js 配置代理(修改需要重新编辑)

2、项目规范

  1. 页面组件文件命名为大驼峰,可以通过功能(或者路径)建立目录
  2. 组件的样式必须有命名空间,防止组件样式相互污染
  3. store中异步 action 使用co包裹 , 不处理异常
  4. store中的 action 定义必须以 Action 后缀结束 , 避免在map到组件中产生冲突
  5. store中的 state 定义必须和业务相关 , 避免使用 (list / data / item)这样无意义的数据 , 以提高在组件内数据识别度/冲突
  6. store中的 mutations 定义为加前缀 set / save / change 和具体的 state
  7. vue组件中使用 vuex 提供的 map系列函数 , 避免直接使用 this.$store对象
  8. components 为通用组件库 , 所有目录下面必须有 index.js , 组件必须逐级导出的 @components 顶级
  9. components/buissiz 为业务组件库 , 可以根据需求随意更改 , 其他组件库更改需谨慎 ,一般情况下不要更改 ,可以写业务员组件来增强通用组件的功能。如更改 , 必须考虑 兼容性 / 易用性 / 扩展性 / 独立性 等等
  10. css使用less ,(!!!!)不要混用 , 由于要和ivew保持一致 , ivew 使用 less , 而且项目有less通用变量 (accesset/common.less),有条件的尽量使用 less 变量

补充:

动态路由和路由传递参数

定义

1
2
3
4
5
6
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User, name: "user" }
]
})

上面路由定义 /user/123 和 /user/456 都能匹配到

路由参数获取

  • 在组件里面可以通过 this.$route.params.id 获取的路由参数
  • 视图里面可以通过 $route.params.id 获取路由参数
1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div> {{ $route.params.id }} </div>
</template>

<script>
export default {
name: "demo",
created() {
this.$route.params.id
}
}
</script>

在组件中手动跳转到动态路由

  • 方式一: 自动拼接路由 this.$router.push(“/user/“ + user.id)
  • 方式二: 通过路由名称 this.$router.push({name: “user”, params: {id: user.id}})

通过 query 方法在路由中添加参数

组件中通过

1
2
3
4
5
this.$router.push({path: "/user/1", query: {id: 1, "name": 123}})
// 生成的路由为 /user/1?id=1&name=123

// 获取请求参数
this.$route.query.id ; // 视图中 $route.query.id

注意在组件中 this.$route 和 this.$router 是两个对象,this.$route 表示当前路径;this.$router 表示路由对象

5、页面组件开发

  1. 页面尽量保持简单 , 一个页面不要写过多的业务逻辑 , 就好比PHP中的controller , 负责调度就好

  2. 约定: 如果目录中包含 index.vue 那么这个文件就是 page 的入口 , 其他文件则为改页面的私有组件 , 否则改页面没有私有组件

1. vue 单文件组件

文件定义

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
页面标签,只允许定义单个父级标签,不能定义多个同级父标签
</template>

<script>
export default {
name: "组件名称"
}
</script>

<style scoped lang="less">
css 样式,没有可以不用定义style 标签 lang 表示 css 使用的 less ,不需要的使用 less 可以去掉 lang 属性
</style>

在组件中使用 store 中的数据和方法

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
<template>
<div @click="setMessage('12323')">
{{ message }} {{ getMessage }}
</div>
</template>

<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
export default {
name: "test",
methods: {
// mapMutations, mapActions 第一个参数都是 module 名称, 第二个参数就是需要引入的方法(可以为数组形式,也可以是对象形式)
...mapMutations("app", ["setMessage"]),
...mapActions("app", ["getMessageAction"])
},
computed:{
// mapState(), mapGetters() 第一个参数都是 module 名称, 第二个参数就是需要引入的数据(可以为数组形式,也可以是对象形式)
...mapState("app", ["message"]),
...mapGetters("app", ["getMessage"])
},
created() {
this.getMessageAction()
}
}
</script>

在组件中请求接口

  • 接口定义都在service 下面定义
1
2
3
4
5
6
// 主商户列表
import {createApi} from "../util/request";
// 主商户列表
export const brandListApi = data => createApi('/bank/brand/list', data);
// 首页营收报表
export const homeIndexApi = data => createApi('/bank/home/index', data);
  • 组件中调用接口
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
<template>
<div> {{ message }} </div>
</template>

<script>
// 需要引入我们公共的基础包的 Process 方法
import {Process} from "@verypay/baseui"
import {getMessageApi} from "./service"

export default {
name: "test",
data() {
return {
message: ""
}
},
created() {
let me = this;
Process(function* () {
// 获取到data 就是 接口返回数据中 data 字段中的数据
let data = yield getMessageApi();
// 做你想做的事
me.message = data;
});
}
}

</script>

2. vuex 状态管理(所有组件数据共享)

开始使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import vuex from 'vuex'
import Vue from "vue";
import app from './modules/app'

Vue.use(vuex);

export const store = new vuex.Store({
state: {},
mutations: {},
getters: {},
actions: {},
modules: {
app,
},
});

window['store'] = store;

挂载到APP组件上

1
2
3
4
5
6
7
8
9
10
11
12
import Vue from 'vue'
import App from './App.vue'
import {store} from "./store";
import {router} from "./router";

Vue.config.productionTip = false

new Vue({
store,
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
22
23
24
25
26
27
28
29
30
31
32
import co from "co"

export default {
namespaced: true,
// 数据
state: {
message: "my name is demo"
},

// 基于state 的计算属性
getters: {
messageInfo(state) {
return state.message + " 我添加了新数据 "
}
},
// 同步修改数据
mutations: {
setMessage(state, data) {
state.message = data;
}
},

// 异步获取数据
actions: {
getMessageAction({commit}) {
return co(function*() {
let data = yield createApi(["user_id": 1])
commit("setMessage", data)
})
}
},
}

在非组件js 文件中使用 store 状态管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 引入store 对象
import {store} from "../store"

// 获取 app 模块 state 数据
let message = store.state.app.message;

// 获取 app 模块中的计算属性
let messageInfo = store.getters["app/messageInfo"];

// 使用 app 模块的同步提交数据
store.commit("app/setMessage")

// 使用 app 模块的异步操作
store.dispatch("app/getMessageAction")

React 学习笔记

发表于 2019-05-08 | 更新于 2019-08-22

react 学习笔记

[TOC]

ES6 基础

一. 变量的解构赋值

1. 数组的解构赋值

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

// 简单
let [a, b, ...c] = [1, 2, 3, 4, 5]
// a = 1, b = 2, c = [3, 4, 5] (...操作符表示其余的元素都赋值给 c 组成一个数组)

// 同样适用于二维数组
let [a, [b, c]] = [1, [2, 3]]
// a = 1, b = 2, c = 3

// 忽略其中的值
let [, , a] = [1, 2, 3]
// a = 3

// 解构不成功值为 undefined
let [, , a] = [1, 2]
// a = undefined

// 解构的时候给默认值
let [a, b = true, c = true] = [1, false]
// a = 1 b = false c = true 只有右边的值为 === undefined 的时候,才会赋值默认值

数组的解构,前提是右边一定要是数组(可以是可遍历的结构),不然会报错

2. 对象的解构赋值

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

// 简单解构
let {a, b, ...c} = {a: 1, b: 2, c: 4, d: 5}
// a = 1 b = 2 c = {c: 4, d: 5} (...表示把右边剩余的字段全部赋值给 c 组成一个对象)

// 同样适用于多层对象
let {a, b: {c}} = {a: 123, b: {c: 456}}
// a = 123 c = 456 这里并没有解构出 b 左边的写法只是为了解构 a 和 c

// 解构的时候,重新命名变量名称 (左边的字段名称必须与右边字段对应,不然解构失败 值为undefined, 但可以在左边解构的时候重新命名变量)
let {a, b:name, c} = {a: 123, b: 456}
// a = 123 name = 456 c = undefined 解构的时候,将右边的b 字段重新命名为 name, c 字段在右边对象中不存在 为 undefined

// 深层对象解构,如果第一层字段就不存在,那么会报错
let {a: {c}} = {}
// 报错 因为右边对象没有 a 字段信息

// 同样可以赋值默认值(当右边 === undefined 时才有效)
let {a = 1, b = 456, c = 1} = {b: true, c: null}
// a = 1 b = true c = null
  • 解构对象的字段都是普通类型,是值拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
let object1 = {a: 1, b: 2, c: 3}

let {...object2} = object1
// object2 = {a: 1, b: 2. c: 3}
// 不会影响 object 1
object2.a = 4

// 需要区别直接赋值的情况,直接赋值还是对象引用
let ojbect3 = object1
// object3 = {a: 1, b: 2, c:3}

// 会影响 object1
object3.a = 2
  • 解构对象的字段还是一个对象的话,也是赋值的对象引用,不是值拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14

let object1 = {
a: 123,
b: {
username: '123',
age: 456
}
}

let {a, b} = object1

// 这个修改会影响 object1 b 字段
b.username = '456'
// b = {username: '456', age: 456} object1 = {a: 123, b: {username: '456', age: 456}}

3. 函数参数的解构

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

/**
* getUser 函数需要接收一个对象作为参数
* 函数只需要这个对象的 username, age 字段,并用这个两个字段重新组合为一个对象返回
*/
function getUser({username, age}) {
return {
username: username,
age: age,
}
}

const a = getUser({username: '123', age: 456, sex: 1})
// a = {username: '123', age: 456}

const b = getUser({})
// b = {username: undefined, age: undefind}

// 使用箭头函数简写getUser函数
const f = ({username, age}) => ({username, age})
  • 补充说明 ( … 操作符)

    1. …操作符一般用来解构对象和数组,解构的时候,只能放到最后一个参数使用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 正确解构赋值
      let {a, ...b} = {a: 123, b: 2, c: 3}
      // a = 123 b = {b: 2, c: 3}

      let object1 = {a: 123, b: 2, c: 3}
      let {...a} = object1
      // a = {a: 123, b: 2, c: 3} 这里 a 和 直接赋值 ( let a = object1 ) 还是有区别的
      // 解构的话是 object1 值的拷贝; 直接赋值 是 object1 对象引用

      // 错误
      let {...a, b} = {a: 123, b: 456}
      // 程序报错
2. ...操作符也能用于对象的合并和数组的合并

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

// 对象的合并
let object1 = {username: 123, age: 456}
let object2 = {name: '123', sex: 1}

// 后者的同名字段会覆盖前者的同名字段 相当于php数组的 array_merge
let object3 = {...object1, ...object2, username: 'jinxing'}
// object2 = {username: 'jinxing', age: 456, name: '123', sex: 1}

// 数组的合并
let a = [1, 2, 3, 4, 5]
let b = [1, 2, 3, 4, 5]

// 相当于将 a, b 数组展开追加到新数组中 c = [], c.push(...a), c.push(...b), c.push(7, 8)
let c = [...a, ...b, 7, 8]
// c = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 7, 8]
3. ...操作符用于函数,表示接收不确定参数个数 (和 go 有点相似)
1
2
3
4
5
6
7
8
9
// 求和 params 是传入参数组成的数组
function sum(...params) {
let i = 0
params.forEach(v => i += v)
return i
}

const a = sum(1, 2, 3)
// a = 6
4. ...操作符用于函数传参数,表示将数组各个元素传递给函数参数
1
[1, 2, 3].push(...[4, 5, 6])

二. 箭头函数的使用

1. 定义

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
43
44
45
46
47
48
49
50
51
// 不要参数
var x1 = () => {
console.info(123)
}

// 相当于
function x1() {
console.info(123)
}


// 单个参数
var x2 = a => {
console.info(a)
}

// 相当于
function x2(a) {
console.info(a)
}

// 多个参数
var x3 = (a, b, c) => {
console.info(a, b, c)
}

// 相当于
function x3(a, b, c) {
console.info(a, b, c)
}


// 简单返回值可以一行写完
var x4 = (a, b) => a + b

// 返回对象的话
var x5 = (a, b) => ({a, b})

// 返回函数
var x6 = (a, b) => (c) => a + b + c

// 相当于
function x6(a, b) {
return function(c) {
return a + b + c
}
}

// 调用
const a = x6(1, 2)(3)
// a = 6

2. this 指向

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this

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
// 不使用箭头函数
function Person() {
// Person() 构造函数定义 `this`作为它自己的实例.
this.age = 0;

setInterval(function growUp() {
// 在非严格模式, growUp()函数定义 `this` 作为全局对象,
// 与在 Person() 构造函数中定义的 `this` 并不相同.
this.age++;
console.info(this.age) // NaN, NaN ....
}, 1000);
}

var p = new Person();

// 解决 this 指向问题
function Person() {
var that = this;
that.age = 0;

setInterval(function growUp() {
// 回调引用的是`that`变量, 其值是预期的对象.
that.age++;
console.info(this.age) // 1, 2, 3 ...
}, 1000);
}

// 使用箭头函数

function Person(){
this.age = 0;

setInterval(() => {
this.age++; // this 正确地指向 p 实例
}, 1000);
}

三. 其他补充

1. 对象字段的简写

1
2
3
4
5
6
7
8

let field1 = 1
let field2 = 2
let username = 'react'

// 变量名称为对象字段名称,值为对象字段的值
let object1 = {username, field1, field2, name: '123', age: 456}
// object1 = {username: 'react', field1: 1, field2: 2, name: '123', age: 456}

2. 对象字段名为变量

1
2
3
4
5
6

let username = 'username'
let age = 'test'
let object1 = {[username]: 123, [age]: '123'}

// object1 = {username: 123, test: '123'}

3. export 和 export default 的区别

export 导出的函数或者类 名称是什么,外面引入的时候就要是什么, 但可以引入的时候修改名称; 而且import 引入的时候,需要加上 {}
1
2
3
4
5
6
7
8
9

// a.js 文件 使用 export 导出的,函数名称是什么,外面引入的时候就要用什么
export const username = () => 'username'

// b.js 引入 a.js
import {username} from './a.js'

// c.js 引入 a.js 并且修改名称
import {username as user} from './a.js'
export default 导出的函数或者类,在其他文件引入,需要为其定义名称;import 不要使用 {}
  • 一个 js 文件中 只能有一个 export default 导出
1
2
3
4
5
6

// a.js 文件,使用 export default 导出 并且一个文件只能有一个 export default 导出
export default function() { return 'username' }

// b.js 文件引入 a.js
import user from './a.js'

React 基础

创建一个 React 的单页应用

1
2
3
npx create-react-app my-app
cd my-app
npm start

一、JSX语法

JSX,是一个 JavaScript 的语法扩展

1、使用

1
const element = <h1>测试</h1>

2、使用表达式和变量

1
2
3
4
5
6
7
8
const username = "my-test"
const element = (
<h1 className={'test'} style={{textAlign: 'center'}} title="123">
{username}
{username === 'my-test' ? '1' : '2'}
<input disabled />
</h1>
)
  • 属性 使用{}包起来的表示变量或者表达式,如果使用""包起来表示简单的字符串
  • 属性定义了但没有给值 默认为 true

二、定义组件

1. 使用类定义

1
2
3
4
5
6
7
8
9
import React, {Component} from 'react'

export default class MyComponent extends Component {
render() {
return (
<div> my component </div>
)
}
}

2. 使用函数定义

1
2
3
4
5
const MyComponent = props => {
return (
<div> my component {props.children} </div>
)
}

3. 注意事项

  • 组件命名必须组照大驼峰命名法
  • 渲染class类名,需要使用className
  • 渲染style时候,必须使用对象,并且字段需要使用小驼峰法 例如:font-size: “16px” 需要写为 fontSize: “16px”
1
2
3
4
5
6
7
8
9
const MyComponent = props => {
return (
<div className="my-component" style={{
fontSize: "16px",
textAlign: "center",
color: "red"
}}> my component {props.children} </div>
)
}
  • render 函数里面渲染的时候,最外层必须要一个父级标记,不允许同时出现两个同级外层标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 正确
const MyComponent = props => {
return (
<div> my component {props.children} </div>
)
}


// 错误(最外层出现两个同级标签)
const MyComponent = props => {
return (
<div> my component {props.children} </div>
<div> 123 </div>
)
}
react 允许外层不适用外层标签、即允许两个同级标签,不过需要使用Fragment 标签标记
1
2
3
4
5
6
7
8
9
10
11

import React, {Fragment} from 'react'

const MyComponent = props => {
return (
<Fragment>
<div> my component {props.children} </div>
<div> 123 </div>
</Fragment>
)
}
出于安全考虑的原因(XSS 攻击),在 React.js 当中所有的表达式插入的内容都会被自动转义,如果需要使用html 标记必须使用dangerouslySetInnerHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
import React, {Component} from 'react'

export default class MyComponent extends Component {
state = {
html: "<h1> my component </h1>"
}

render() {
return (
<div dangerouslySetInnerHTML={{__html: this.state.html}}/>
)
}
}

4、state和生命周期

state 相当于 vue 的 data

修改state 必须使用 this.setState() 函数

详细的生命周期

  • constructor() 初始化
  • render() 渲染
  • componentDidMount() 挂载之后

5、条件渲染&循环

  1. 条件渲染

    • 可以使用 && 操作符
    • 可以使用 三元运算符 ?:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      const MyComponent = props => {

      return <div>
      {this.props.title && <h1>{this.props.title}<h1>}

      {this.props.content ? <div>{this.props.conent}</div> : ''}

      {0 && <div>123</div>}
      </div>
      }
注意: 使用 `&&` 操作符的时候,前面表达式最好是一个布尔值 
  1. 列表渲染
    • 需要给同级元素一个唯一的key
    • 如果是数组变量可以直接渲染
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      const MyComponent = props => {
      const items = [1, 2, 3]
      return <div>
      {items}

      {items.map(v => v)}

      <ul>
      {item.map(v => <li key={v}>{v}</li>)}
      </ul>
      </div>
      }

三、组件传参

父组件传参给子组件直接像html标签属性一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, {Component} from 'react'
import Children from './Children'

export default class MyComponent extends Component {
state = {
message: "my message"
}

render () {
return (
<div>
<Children name="my-child" message={this.state.message}>
这里面的信息为子组件 props.children
</Children>
</div>
)
}
}

子组件接收父组件的参数,使用this.props 接收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, {Component} from 'react'

export default class Children extends Component {
render() {
return (
<div>
<h3> {this.props.name} </h3>
<p> {this.props.message} </p>
<div>
{this.props.children}
</div>
</div>
)
}
}

四、默认传参defaultProps

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'

export default class Children extends Component {
static defaultProps = {
name: "liujinxing",
message: "my-test"
}

render() {
return (
<div>
<h3> {this.props.name} </h3>
<p> {this.props.message} </p>
<div>
{this.props.children}
</div>
</div>
)
}
}

五、PropTypes 和组件参数验证

  1. 需要引入React 提供的第三方库 prop-types
1
npm install --save prop-types
  1. 使用说明
1
2
3
4
5
6
7
8
9
10
import React, {Component} from 'react'
import PropTypes from 'prop-types'

class Item extends Component {
static propTypes = {
// 必须要传递title为字符串信息,并且不能为空
title: PropTypes.string.isRequired,
items: PropTypes.object.isRequired
}
}

六、组件的事件处理

  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
  • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

1. 绑定事件时候 this 指向问题

文档
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

class Item extends Component {

name = '按钮'

handleClick() {
// 这里会存在 this 指向问题 this = undefined
alert(this.name)
}

render() {
return <a onClick={this.handleClick} > 点击效果 </a>
}
}

// 解决方法一 在初始化的时候,为函数绑定 this

class Item extends Component {

constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}

name = '按钮'

handleClick() {
alert(this.name)
}

render() {
return <a onClick={this.handleClick} > 点击效果 </a>
}
}

// 解决方法二 在指定属性的时候,为函数绑定 this

class Item extends Component {

name = '按钮'

handleClick() {
alert(this.name)
}

render() {
return <a onClick={this.handleClick.bind(this)} >点击效果</a>
}
}

// 解决方法三 使用箭头函数(推荐使用箭头函数)

class Item extends Component {

name = '按钮'

handleClick = () => {
alert(this.name)
}

render() {
return <a onClick={this.handleClick} >点击效果</a>
}
}
  • 推荐使用箭头函数

2. 绑定事件的时候,添加传递参数

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

class Item extends Component {

name = '按钮'

handleClick = (key) => {
alert(this.name, key)
}

render() {
return <a onClick={this.handleClick.bind(this, '123456')} >点击效果</a>
}
}
  • 使用函数的 bind 方法, 第一个参数,函数内部 this 指向谁, 后面参数为函数需要接收的参数
如果原有事件就有传递参数,但是处理的时候还想添加参数的话,同样使函数的 bind 方法,自己添加的参数在最前面; 原有的参数,会在自定义参数后面传递
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
// 子组件
export class Children extends Component {

name = '按钮'

handleClick = (key) => {
const {onClick} = this.props
alert(this.name, key)

// 向上调用事件
onClick && onClick(this.name, key)
}

render() {
return <a onClick={this.handleClick.bind(this, '123456')} >点击效果</a>
}
}


// 父组件
import {Children} from './Children.js'

class Parent extends Component {

/**
* 参数 name 是自己在Children 组件 onClick 绑定时候添加的参数
* 参数 childrenName, key 是 Children 组件自己的点击事件传递的参数
*/
handleClick = (name, childrenName, key) => {
alert(name, childrenName, key)
// name = 'parent' children = '按钮' key = '123456'
}

render() {
return <Children onClick={this.handleClick.bind(this, 'parent')} />
}
}
使用箭头函数解决传递参数问题
  • 通过箭头函数返回函数实现 注意箭头函数的写法 () => () => {}
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
// 子组件
export class Children extends Component {

name = '按钮'

// 注意这个地方的写法
handleClick = (key) => () => {
const {onClick} = this.props
alert(this.name, key)

// 向上调用事件
onClick && onClick(this.name, key)
}

render() {
// 绑定的时候直接传递参数就好了
return <a onClick={this.handleClick('123456')} >点击效果</a>
}
}


// 父组件
import {Children} from './Children.js'

class Parent extends Component {

/**
* 第一个函数的参数 name 是自己在Children 组件 onClick 绑定时候添加的参数
* 第二个函数的参数 childrenName, key 是 Children 组件自己的点击事件传递的参数
*/
handleClick = (name) => (childrenName, key) => {
alert(name, childrenName, key)
// name = 'parent' children = '按钮' key = '123456'
}

render() {
return <Children onClick={this.handleClick('parent')} />
}
}

七、… 操作符在 JSX 语法中常用方式

1. 接收剩余传递参数

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

class Children extends Component {
render() {
// 接收剩余参数,传递给 button 组件
const {title, name, ...otherProps} = this.props
return (
<div>
<h2>{title}</h2>
<Button {...otherProps}>{name}</Button>
</div>
)
}
}

// 使用组件
<Children title="测试" name="点击" onClick={() => console.info(123)} type="button" />

// 其中 onClick, type 传递的参数被传递给了 Button 组件

2. 属性展开

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

const defaultProps = {
type: 'button',
onClick: () => alert('被点击了')
className: 'btn btn-success',
style: {
backgroundColor: 'red',
width: '100px',
height: '100px'
}
}

// 将defaultProps 展开传递给组件
<button {...defaultProps}>按钮</button>

// 上面相当于
<button type={defaultProps.type} onClick={defaultProps.onClick} className={defaultProps.className} style={defaultProps.style}>按钮</button>

八、高阶组件

  1. 定义高阶组件
    就是像定义普通函数一样,不过第一个参数是一个组件,而且必须要返回一个组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const hocComponent = WrappedComponent => {

    class HocComponent extends Component {
    render() {
    return <WrappedComponent style={{padding: '5px', borderRadius: '3px'}} {...this.props}/>
    }
    }

    // 这个是约定: 需要定义一个displayName属性,方便调试
    HocComponent.displayName = `HocComponent(${getDisplayName(WrappedComponent)})`

    return HocComponent
    }

    // 使用
    const Button = props => {
    return <button {...props}>{props.children}</button>
    }

    const HocButton = hocComponent(Button)
  2. 高阶组件静态属性的导出(务必复制静态方法)

    • 默认不会导出静态属性,需要手动去导出
    • 使用hoist-non-react-statics
  3. 高阶组件ref问题

    • refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。
    • 默认 ref 指向高阶组件自己,如果需要转发到被高阶组件包裹的组件中,要用到refs转发

九、Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法

四个API:

- React.createContext
- Context.Provider
- Class.contextType
- Context.Consumer

我们项目中常用组件

基于 Ant Design Pro
Ant Design of React UI库

一、表格组件CTable

1
<CTable columns={[....]} api={queryApi} btnClick={this.handleClick} />

常用的属性列表

属性名 类型 说明
columns array 表格字段信息
api function 表格数据来源API
btnClick (key, rows) => {} 表格操作项点击事件处理
index string or function 表格每一行唯一的key
filters object 查询的默认条件

其他说明

columns中,元素配置 _render 表示生成操作项,配置格式 {key: 'update', title: '修改'}
或者 [{key: 'update', title: '修改'}], 点击事件交给表格的 btnClick 函数处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<CTable 
columns={[
{title: 'ID', dataIndex: 'id'},
{
title: '操作',
_render: [
{key: 'update', title: '修改'},
[
{key: 'delete', title: '删除'},
{key: 'offline', title: '启用'},
],
]
}
]}
api={queryApi}
btnClick={this.handleClick}
/>

需要后端服务器返回数据格式

1
2
3
4
5
6
7
8
9
10
11
12
{
code: 10000,
data: {
items: [...],
page: {
total: 10,
current: 1,
pageSize: 10,
}
},
message: "success",
}

如果不需要分页, page 返回 false

二、表单组件CForm

  1. CForm 表单组件
1
2
3
4
5
<CForm rules={{name: [{required: true, message: '不能为空'}]}} onSubmit={(values) => {
console.info(values)
}}>
<CInput name="name" label="名称"/>
</CForm>

常用的属性列表

属性名 类型 说明
rules object 表单字段验证信息
onSubmit (values) => {} 表单提交事件处理
initialValue object 表单字段初始值数据
  1. CInput Input 输入框
1
<CInput name="name" label="名称" />

常用的属性列表

属性名 类型 说明
name string 表单字段名称(html input 元素的name属性,可以是 :name="user[name]"
label string label 名称
initialValue mixed 表单字段的初始值
show (values) => boolean 显示处理函数,values表示所有表单的值,需要返回ture表示渲染

JS异步

一、异步示例

1
2
3
4
5
for (var i = 0; i < 10; i ++) {
setTimeout(function () {
console.info(i)
}, 1000)
}

二、使用Promise对象

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

Promise对象特点:

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。

1. 定义Promise对象

Promise 函数有两个参数 resolve 和 reject,这两个参数都是函数

- resolve 表示成功返回
- reject 表示错误返回
1
2
3
4
5
6
7
8
9
10
const handle = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
alert('加载完成了')
resolve('执行完成')
}, 2000)

// 如果失败 reject()
})
}

2. 使用Promise对象

  • then 表示 resolve 后的回调函数;参数是 resolve 返回
  • catch 表示 reject 后的回调函数;参数是 reject 返回
  • finally 表示不管是resolve 还是 reject 都会执行的回调
1
2
3
4
5
6
7
handle().then(value => {
console.info(value)
}).catch(error => {
console.info(error)
}).finally(() => {
console.info('我都呗执行了')
})

三、使用co包

为什么使用co

  • 解决异步回调的嵌套
  • 让异步回调,同步的形式执行

例如: 发送一个异步请求,但这个请求依赖于另一个异步请求

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

// jquery.ajax 写法
$.ajax({
url: 'a.php',
type: 'get',
dataType: 'json',
success: function (data) {
$.ajax({
url: 'b.php',
type: 'get',
dataType: 'json',
}).done(function (data) {
console.info(123)
})
}
})

// 使用 Promise 同样存在这样的问题
const handle = (a) => {
return new Promise(resolve => {
console.info(a)
setTimeout(() => {
resolve(a)
}, 1000)
})
}

handle(1).then(value => {
console.info(value)
return handle(2)
}).then(val => {
console.info(val)
})

// 使用 co 包
co(function *() {
const a = yield handle(1)
const b = yield handle(a)
console.info(a, b)
})

四、使用async函数

async 函数是 ES2017 标准引入的,是生成器函数的语法糖

使用 async 就是将 生成器函数的 * 改为 async, 将 yield 改为 await

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

const handle = (x) => {
return new Promise(resolve => {
setTimeout(() => {
console.info(x)
resolve(x + 1)
}, 1000)
})
}

(async () => {
const a = await handle(1)
const b = await handle(a)
console.info(a, b)
})()

async 返回的还是一个 Promise 所以可以继续调用 then,catch,finally

1
2
3
4
5
6
7
(async () => {
const a = await handle(1)
const b = await handle(a)
console.info(a, b)
})()
.then(v => console.info(v))
.catch(error => console.info(error))
12
jinxing.liu@qq.com

jinxing.liu@qq.com

刘金星个人博客
13 日志
8 标签
GitHub E-Mail
© 2019 jinxing.liu@qq.com
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Pisces v7.1.1
优秀博客: fifsky的技术笔记 | littlebug