NestJS

NestJS

Nest.js

初始化 Nest.js pnpm dlx @nestjs/cli new <project name>

生成resource资源 nest generate resource

生成module文件 nest generate module <module name>

生成service文件 nest generate service <module name>

生成filter文件 nest generate filter prisma-client-exception

Prisma

初始化 Prisma prisma init

生成 migration 与 初始化数据库 prisma migrate dev --name <migrate name>

执行种子方法 prisma db seed

react

react

React

React 概述

React 概念

React 用于构建用户界面的 JavaScript 库

MVC 中的 V {视图层}

React 特点

声明式
基于组件
应用范围广

react 基本使用

react 安装

npm i react react-dom

react 使用

1
2
<script type="text/javascript" src="./node_modules/react/umd/react.development.js"></script>
<script type="text/javascript" src="./node_modules/react-dom/umd/react-dom.development.js"></script>
1
2
const title = React.createElement('h1', null, 'Hello React');
ReactDOM.render(title, document.getElementById('root'));

React.createElement
返回 React 元素
第一个参数为元素名
第二个参数为元素属性
第三个及之后的参数为元素的子节点

ReactDOM.render
第一个参数为要渲染的 react 元素
第二个参数为将挂载的 DOM 对象

React 脚手架使用

npx create-react-app <my-app>

npx 在不安装脚手架包情况下使用脚手架包提供的命令

npm init react-app <my-app>
yarn create react-app <my-app>

导入 React

创建 React 元素

渲染 React 元素

JSX 概述

JSX 基本使用

比 createElement 简洁

在 JS 中书写 HTML 格式代码

1
2
3
const title = <h1>Hello JSX</h1>;
const root = document.getElementById(root);
ReactDOM.render(title, root);

JSX 注意点

React 元素属性名使用驼峰命名法

class 属性需替换为 className 属性
for 属性需替换为 htmlFor 属性

无子节点的元素可以以单标签形式写

建议使用小括号包裹 JSX

JSX 嵌入 JS 表达式

单大括号

1
2
3
const title = <h1>Hello JSX</h1>;
const root = document.getElementById(root);
ReactDOM.render(title, root);

允许使用任意的 JS 表达式
但使用对象可能会出现 bug
不能使用任意的 JS 语句

条件渲染

函数 - ifelse
函数 - 三元表达式
函数 - 逻辑与运算符

列表渲染

使用数组 map 方法实现

1
2
3
4
5
6
const array = [1, 2, 3, 4, 5]
const list = (
<ul>
{array.map(v => <li key={v}>{v}</li>)}
</ul>
)

样式处理

行内样式

<h1 style={{ color: red; }}></h1>

类名 className

React 组件介绍

使用 React 组件介绍
从而实现完整的页面功能

函数组件

使用函数创建组件

  • 函数名称必须以大写字母开头
  • 函数组件必须有返回值,表示组件内容 | 返回值为 null,为无内容

渲染组件,直接使用函数名为组件标签名

类组件

使用 class 类创建组件

  • 类名称必须以大写字母开头
  • 类组件需继承 React.component 父类,以使用父类提供的方法
  • 类组件需提供 render 方法,且应有返回值,表示组件将渲染的内容

独立文件

单独组件放在单个JS文件内

事件绑定

示例如 onClick={ () => {} }
采用驼峰命名法

事件对象

合成事件(包含原生事件)
兼容性好

组件分类

函数组件 - 无状态组件
类组件 - 有状态组件

状态 - 数据

组件状态是私有的

受控组件 - value 绑定 state 变量,监听 change 事件实时改变 state 变量

非受控组件 - |

组件高级

组件通讯

组件 props | 父组件=>子组件

可以传递各种数据,包括对象、函数乃至JSX等
props 只读

  • 父->子 props属性传数据
  • 子->父 props属性传回调方法
  • 兄弟<->兄弟 状态共享至公共父组件,由公共父组件负责管理

跨多个组件的通讯 context
Provider 传送数据
Customer 接纳数据

props.children 组件标签的子节点

props 校验

使用 prop-types

设置propTypes
支持 array bool func number object string bigint symbol
element node elementType
instanceOf
oneOf oneOfType
arrayOf objectOf
required
shape
设置defaultProps

组件生命周期

仅类组件有生命周期

  • 创建时
    • constructor 初始化state 绑定this
    • render 渲染UI
    • componentDidMount 发送网络请求 DOM操作
  • 更新时
    • render
    • componentDidUpdate
  • 卸载时
    • componentWillUnmount

组件复用

  • render props 模式

    将单独的state及state方法封装为单独的组件
    利用 prop 传递参数
    传递函数以函数返回值作为将渲染的 UI 内容(或使用 children)

  • 高阶组件 HOC

    函数
    接受需包装的组件,返回增强的组件
    (名称约定以 with 开头)
    组件配置 displayName 作为组件名称

路由

SPA 单页面应用,单HTML页面的web应用程序

路由 - 配对路径何组件

  1. 安装 react-router-dom
  2. 导入组件 import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
  3. Router 组件包裹整个应用
  4. Link 作为路由入口
  5. Route 配置路由规则和需要展示的组件|路由出口

Router 组件包裹整个应用,仅使用一次
包括 BrowserRouter 和 HashRouter

Link 组件作为导航链接
Route 组件指定路由展示组件相关信息

编程式导航
this.props.history.push
this.props.history.go

默认路由
path为 / 的路由

默认 route 遵从模糊匹配
设置 extra 遵从精确匹配

vue

vue

vue

一套构建用户界面的前端框架

  • 数据驱动视图
  • 双向数据绑定

MVVM:Model-View-ViewModel

Model 数据源

View DOM 结构

ViewModal Vue 实例

创建 Vue 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const app = new Vue({
// Vue实例挂载对象,接收一个选择器
// 若匹配多个DOM对象,取匹配到的第一个DOM对象
el: "#app",
// 普通属性
data: {
msg: "",
},
// 计算属性:带返回值的方法
computed: {},
// 监听器
watch: {},
// 普通方法
methods: {},
// 过滤器
filters: {},
});

指令结构

指令:参数:修饰符

v-on:submit.prevent=""

文本

“Mustache”语法 {{ }} v-text

<span>{{msg}}</span> 只能用于内容节点

<span v-text="msg"></span> 会覆盖内部文本

v-once 单次渲染

原始 HTML

  • v-html

<span v-html="msg"></span> 会覆盖内部内容

避免使用,可能会发生 XSS 攻击存在安全风险

绑定 attr

  • v-bind

<a v-bind:href='url'></a>

简写 <a :href='url'></a>

计算属性

computed 对象

属性需为一个函数,并返回相应的值

使用时同正常的 data 属性即可

1
2
3
4
5
computed: {
val1: function () {
return `the number is ${this.val0} in the input box`;
}
}

其是依赖于响应式依赖进行缓存的,仅在其相应式依赖改变时才重新计算,因而可以提高性能

可以添加 setter 属性,默认只针对 getter 属性,这样可以同时更新 data 中数据

1
2
3
4
5
6
7
8
9
10
computed: {
val1: {
get: function () {
return `the number is ${this.val0} in the input box`;
},
set: function (val_1) {
this.val0 = val_1;
}
}
}

侦听属性

watch 对象

1
2
3
4
5
6
7
8
9
10
11
watch: {
// 方法形式
val0: function (newData, oldData) {
},
// 对象形式
val1: {
handler (newVal, oldVal) {},
immediate: true,
},
deep: true,
},

实质是一个函数,函数名是监听的变量名

  • immediate 选项

立即以表达式当前值触发回调

  • deep 选项

用于监听对象内部值的变化

若侦听特殊格式变量,可包裹单引号 如’info.id’ () {}

计算属性与侦听属性区别,计算属性根据其响应式属性的改变而改变,侦听属性根据其监视的属性的改变而修改相应的属性

绑定事件

  • v-on

<a v-on:click='handle_click'></a>

简写 <a @click='handle_click'></a>

  • 动态事件

<a @[event]='handle_click'></a>

  • 传参

<a @click='handle_click(1)'></a>

  • 传原生 DOM 事件
    $event

<a @click='handle_click($event)'></a>

实际方法无参时可以不用该方法,但有参数时必须使用该方法

  • 事件修饰符

  • prevent 阻止事件默认行为

  • stop 阻止事件传播

  • once 使事件仅触发一次

  • self 仅在触发事件的元素为绑定的元素本身时起效

  • capture 捕获事件模式:比内部元素绑定事件处理方法优先处理

  • passive 预先完成事件的默认行为而不是等待事件处理方法完成

修饰符可以串联使用

鼠标修饰符

  • left
  • right
  • middle

分别仅在点击鼠标相应侧按键时起效

按键修饰符

  • 可以指定为相应的键码才起效

.enter .tab .delete .esc .space .up .down .left .right

系统修饰符

  • exact 控制仅为给定的系统修饰符触发的事件
  • ctrl alt shift meta 控制需按下对应的按键才可触发事件

绑定表单

v-model

表单 <input><textarea><select> 元素有效

会忽略所有表单元素的 valuecheckedselected attribute 的初始值,故需在 data 中声明数据的初始值

  • text 和 textarea 元素使用 value property 和 input 事件
  • checkbox 和 radio 使用 checked property 和 change 事件
  • select 字段将 value 作为 prop 并将 change 作为事件

修饰符

  • number 自动转为数值类型
  • trim 自动过滤字符串两端空格符
  • lazy 改为在 change 事件后同步而不是默认的 input 事件后同步,即输入时失去或获得焦点才会触发事件

条件绑定

  • v-if truthy 值显示,falsy 值不显示

实质惰性渲染

  • v-show truthy 值显示,falsy 值不显示

实质仍渲染,通过 display 属性控制

若经常切换显示与否用 v-show,否则用 v-if;可以大幅提高性能

1
2
3
<div v-if="val === 0">0</div>
<div v-else-if="val === 1">1</div>
<div v-else>other</div>
  • :key 可以用于强制区别元素对象

循环绑定

  • v-for

  • :key 建议绑定 key 属性,并使用独一无二的值作为 key 的值 一般需为字符串或数字类型

1
<div v-for="(item, index) in array">{{index + ' : ' + item}}</div>

过滤器

filters

常用于实现文本格式化

可以用于插值表达式或 v-bind 属性

  • 语法

{{ message | capitalize }}

v-bind:id="rawId | formatId"

前一个是定义的变量,后一个是定义的过滤器方法,接收唯一一个参数为变量

  • 定义过滤器方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 局部过滤器
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}

// 全局过滤器
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
  • 其他玩法

过滤器串联 {{ message | filterA | filterB }}

过滤器接收参数 {{ message | filterA('arg1', arg2) }}

默认 message 是第一个参数,’arg1’是第二个参数,arg2 是第三个参数 da

SPA 单页面应用程序 & vue-cli

vue-cli 用于简化使用 webpack 创建 vue 项目的过程

  • 创建 vue 项目
    vue create PROJECT_NAME

  • vue 项目目录

  1. src:
    1. main.js:项目入口脚本文件
    2. App.vue:项目根组件
    3. assets:项目静态资源 如 css 样式表、图片资源
    4. components:组件
  2. public:
    1. favicon.ico:项目网页图标
    2. index.html:项目页面文件
  • vue 项目运行流程

通过 main.js 把 App.vue 渲染到 index.html 的指定位置

导入 vue
import Vue from 'vue';

导入 app 模板
import App from './App.vue';

1
2
3
4
5
// 添加 Vue 实例
new Vue({
// 渲染 render 函数指定的组件
render: (h) => h(App),
});

使用 $mount() 代替 el 属性绑定 DOM 对象

组件

使用 vue 后缀名文件定义独立组件

  1. 注册组件

    全局:建议注册于入口文件 main.js

1
2
3
Vue.component("my-component-name", {
// ... options ...
});

局部:注册于预期使用的区域

1
2
3
4
5
6
7
new Vue({
components: {
"com-1": {
/* options */
},
},
});
  1. 独立组件文件

    • template 组件模板结构
    • script 组件 js 行为
    • style 组件样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div></div>
</template>
<script>
export default = {
name: 'my-component-name',
data: function () {
return {};
},
watch: {},
computed: {},
filters: {},
// props: ['my-prop-name'],
props: {
'my-prop-name' : {
default: 'default-value',
},
},
};
</script>
<style></style>
  1. 注意点

    • 组件应拥有一个根元素
    • 组件内部 data 必须是返回包含数据的对象的函数
    • 指定 style 的类型 <style lang="less">
    • 组件封装后是相互独立的,使用时具有父子关系
  2. 导入 vue 文件方法

1
2
3
4
5
6
7
8
import Test from "./components/test.vue";
export default {
name: "App",
// 声明使用Test组件
components: {
Test,
},
};
  1. 组件属性 props

    大多数属性同 Vue 实例对象

    • name: String 组件名称
    • props: Array | Object 组件自定义属性
      可以直接在方法中使用,只读,可以绑定 v-bind 以动态修改自定义属性的值
      • default 规定属性的默认值
      • type 规定属性的类型
        String Number Boolean Array Object Date Function Symbol 一个自定义的构造函数
      • required 规定属性是否为必须的
  2. 样式冲突问题

    组件内的样式全局生效,可能会影响到整个 index 页面
    利用 scoped 设定 <style scoped>
    原理是通过自定义 HTML 属性结合 CSS 属性选择器实现
    /deep/ div 仅修改子组件的样式

组件生命周期

  • 生命周期:组件的运行阶段
  • 生命周期函数:Vue 的内置函数,按组件生命周期自动按次序执行
  1. 组件创建状态

    • new Vue()
    • init events & lifecycle:初始化事件与生命周期函数
    • beforeCreate:尚未初始化数据方法
    • init injections & reactivity:初始化 props、data、methods
    • created:尚未形成实例模板结构【常用】
    • 基于数据和模板,在内存中编译生成 HTML 结构
    • beforeMount:即将编译好的 HTML 结构渲染至浏览器中
    • 用内存中编译完成的 HTML 结构替换掉 el 属性指定的 DOM 元素
    • mounted:已将编译好的 HTML 结构渲染至浏览器中,DOM 树已存在当前组件【常用】
  2. 组件运行状态

    • beforeUpdate:即将根据变化后最新的数据重新渲染组件的模板结构
    • 根据变化后最新的数据重新渲染组件的模板结构
    • updated:已将根据变化后最新的数据重新渲染组件的模板结构【常用】
  3. 组件销毁阶段

    • beforeDestroy:即将销毁组件
    • destroyed:已将组件销毁

生命周期

组件数据共享

  • 父->子 自定义属性 props

  • 子->父 自定义事件 $emit()

  • 兄弟<->兄弟 EventBus

利用 vue 实例中转,使用 bus.$emit() 触发自定义事件,使用bus.$on() 注册自定义事件

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
// son
export default {
data() {
return {
i = 0;
};
},
methods: {
a() {
this.i++;
this.$emit('e', this.i);
},
},
};
// fa
// <Son @e='f'></Son>
export default {
data() {
return {
j = 0;
};
},
methods: {
f(v) {
this.j = v;
},
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// send data
import bus from './eventBus.js';
export default {
methods: {
send_msg() {
bus.$emit('share', /* data */);
},
},
}
// receive data
import bus from './eventBus.js';
export default {
created() {
bus.$on('share', (/* data */) => {
/* do something with data */
});
},
}
// eventBus.js
import Vue from 'vue';
export default new Vue();

操作 DOM 元素

ref 引用
默认 vue 组件实例包含 $refs 属性,包含组件内所有的设置 ref 属性的 DOM 元素引用,其为一对象,键名是 ref 的值,键值即为相应的 DOM 对象

1
2
3
4
/* <component ref="name"></component> */
fun() {
// using this.$refs.name
}

this.$nextTick() 将回调函数推迟至下一个 DOM 更新周期后执行

动态组件

利用<component></component>实现动态组件渲染

1
<component is="Left"></component> <component :is="comType"></component>
  • is 属性指定预期渲染的组件的名称,可以绑定变量实现动态切换使用的组件
  • 可以用<keep-alive>包裹组件,使之能在失活时缓存,从而可以高效重复使用组件
    • mount:
      • activated() 钩子 组件被激活
      • deactivated() 钩子 组件被缓存
    • props:
      • include 仅名称匹配的组件被缓存
      • exclude 任意名称匹配的组件不被缓存
      • 注意 include 与 exclude 不应同时使用

插槽

普通插槽

将组件内部在使用组件时插入内容时提供占位符

1
2
3
4
5
6
7
8
<!-- 组件 -->
<template>
<div>
<slot></slot>
</div>
</template>
<!-- 文件 -->
<Com> g </Com>

使用 <slot></slot> 声明一个插槽区域

内容使用 v-slot: 指令,且应用在 template 标签上,内部放置填充的内容
v-slot:default 可简写为 #default

编译时,插槽内使用的数据,父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的

后备内容

可以在组件内的插槽的 slot 标签内放入内容,指定若未给出应插入内容时的渲染内容

1
2
3
4
5
6
7
8
<!-- 组件 -->
<template>
<div>
<slot>default value</slot>
</div>
</template>
<!-- 文件 -->
<Com> given value </Com>

具名插槽

具有多个插槽的组件,需要指定插槽的名称,才可以正确地插入部件

通过指定 name 属性实现对插槽进行区别, name 属性默认值为 default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 组件 -->
<template>
<div>
<p>t</p>
<slot name="s1"></slot>
<slot name="s2"></slot>
</div>
</template>
<!-- 文件 -->
<Com>
<template v-slot:s1>
<p>s1</p>
</template>
<template v-slot:s2>
<p>s2</p>
</template>
</Com>

可以在组件内指定插槽的默认内容

作用域插槽

通过插槽自子元素组件向父元素传参

1
2
3
4
5
6
7
8
9
10
<!-- 子组件 -->
<span>
<slot v-bind:user="user"> {{ user.lastName }} </slot>
</span>
<!-- 父组件 -->
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>

自定义指令

  • 私有自定义指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 使用
<div v-color="color">color</div>;
export default {
// 私有自定义指令声明在该节点之下np
directives: {
color: {
// 首次绑定时调用
bind(el, binding) {
el.style.color = binding.value;
},
// DOM 更新时调用
update(el, binding) {
el.style.color = binding.value;
},
},
/**
* 若binding与update相同时可简写为
* color(el, binding) {},
*/
},
};
  • 全局自定义指令
1
2
3
4
5
Vue.directive("colo", {
bind() {},
update() {},
});
Vue.directive("colo", function () {});
  • 钩子函数

    • bind 指令绑定到元素时调用
    • inserted 被绑定元素被插入父节点时调用
    • update 组件的 VNode 更新时调用
    • componentUpdated 组件的 VNode 及其子 VNode 全部更新时调用
    • unbind 指令自元素解绑时调用
  • 钩子函数参数

    • el 指令绑定的元素
    • binding 指令的一些属性

路由

hash 地址与组件之间的对应关系

初始化配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建实例对象
// @/src/router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
const router = new VueRouter();
export default router;
// 导入 App.vue
// @/App.vue
import router from "@/router"; // 相当于 import router from '@/router/index.js';
new Vue({
router,
/* ... */
}).$mount("#app");

模块化导入,给定文件夹,默认导入文件夹下的 index.js 文件

基本路由

规定占位符与切换组件

  • <router-view></router-view> 占位符,显示组件
  • <router-link to="/about">关于</router-link>路由的切换

定义路由关系

1
2
3
4
5
6
7
8
9
10
11
12
// 导入组件
import Home from "@/components/Home.vue";
// 声明路由关系
const router = new VueRouter({
routes: [
{
path: "/home",
component: Home,
},
// ...
],
});

路由重定向

1
2
3
4
5
6
7
8
9
const router = new VueRouter({
routes: [
{
path: "/",
redirect: "/home",
},
// ...
],
});

动态路由

使用动态参数实现相同组件不同路由

<router-link to="/about/${id}">关于</router-link>

1
2
3
4
5
6
7
8
9
10
const router = new VueRouter({
routes: [
{
path: "/movie/:id",
component: Movie,
props: true, // 配置利用 props 获取参
},
// ...
],
});

组件内部获取参数 {} = this.$route.params

props 方式:直接在 Vue 实例中的 props 中获取参数

  • 路径参数 this.$route.params
  • 查询参数 this.$route.query

path:仅路径部分 & fullPath:完全参数

嵌套路由

路由内部的路由

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
const router = new VueRouter({
routes: [
{
path: "/about",
component: Home,
/**
* ! 重定向
* redirect: '/about/tab1',
*/
children: [
{
path: "tab1",
component: Tab1,
},
{
/**
* ! 默认子路由
*/
path: "",
component: Tab1,
},
],
// ...
},
],
});

声明式导航 & 编程式导航

  • 声明式:点击链接
    • <a> <route-link>
  • 编程式:调用 API 跳转
    • location
    • this.$route.push(${url}) 跳转至指定的地址,并增加一条历史记录
    • this.$route.replace(${url}) 跳转至指定的地址,并替换当前的历史记录
    • this.$route.go(${number}) 在浏览器记录中进行前进 or 后退
    • this.$route.push()
    • this.$route.back()

导航守卫

控制路由的访问权限

全局前置守卫

在所有路由触发时起效

1
router.beforeEach((to, from, next) => {});

接收一个回调函数参数
回调函数第一个参数代表将访问的对象,第二个参数代表将离开的对象,第三个代表调用允许的路由导航

调用 next()方法

  • next() 允许跳转
  • next(url) 强制跳转至指定的页面
  • next(false) 不允许跳转

eslint

检查代码的风格的一款插件

.eslintrc.js eslint的配置文件

  • quotes
  • key-spacing
  • comma-dangle
  • no-multiple-empty-lines
  • no-trailing-spaces
  • eol-last
  • spaced-comment
  • indent
  • space-before-function-paren

Vue3


:D 一言句子获取中...