Commit 5a3ff1f4 authored by wangxuxiao's avatar wangxuxiao
parents 7b3ad1ae 5a2b9567
Pipeline #15600 failed with stage
---
title: TypeScript基础类型
date: 2022/3/31 18:00:00
tags:
- 前端
- typescript
author: 杨靖
---
# TypeScript基础类型
TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型等。TypeScript是ECMAScript 2015的语法超集,是JavaScript的语法糖。JavaScript程序可以直接移植到TypeScript,TypeScript需要编译(语法转换)生成JavaScript才能被浏览器执行。
<!-- more -->
## 1. 布尔值
```typescript
let a: boolean = true;
let b: boolean = false;
```
## 2. 数字
ts所有数字都是浮点数,浮点数的类型是number,支持二进制,八进制,十进制,十六进制字面量
```typescript
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
```
## 3. 字符串
string表示文本数据类型,可用单引号(‘’)或者双引(“”)号表示字符串
```typescript
let name: string = "yj";
```
使用模版字符串,它可以定义多行文本和内嵌表达式。 这种字符串是被反引号包围( `),并且以${ expr }这种形式嵌入表达式
```typescript
let name: string = "yj";
let words: string = `my name is ${name}`
```
## 4. 数组
数组定义第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组:
```typescript
let list: number[] = [1, 2, 3];
```
第二种方式是使用数组泛型,Array<元素类型>:
```typescript
let list: Array<number> = [1, 2, 3];
```
## 5. 元祖Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
```typescript
let x: [string, number];
x = ['hello', 10]; // OK
x = [10, 'hello']; // Error
```
当访问一个已知索引的元素,会得到正确的类型:
```typescript
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
```
当访问一个越界的元素,会使用联合类型替代:
```typescript
x[2] = 'world'; // OK, 字符串可以赋值给(string | number)类型
console.log(x[3].toString()); // OK, 'string' 和 'number' 都有 toString
x[4] = true; // Error, 布尔不是(string | number)类型
```
## 6. 枚举
enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
```typescript
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
```
默认情况下,从0开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1开始编号:
```typescript
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
```
或者,全部都采用手动赋值:
```typescript
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
```
枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为2,但是不确定它映射到Color里的哪个名字,我们可以查找相应的名字:
```typescript
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];
console.log(colorName); // 显示'Green'因为上面代码里它的值是2
```
## 7. Any
any类型即声明的变量为任意类型。指在定义变量的时候不知道变量的指定类型,但是我们不希望动态赋值的时候,类型检查器不通过编译阶段的检查。这时候可以用any类型来标记这些变量:
```typescript
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
```
any类型它允许你在编译时可选择地包含或移除类型检查。 你可能认为 Object有相似的作用,就像它在其它语言中那样。 但是 Object类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法:
```typescript
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
```
当你只知道一部分数据的类型时,any类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:
```typescript
let list: any[] = [1, true, "free"];
list[1] = 100;
```
## 8. Void
某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:
```typescript
function warnUser(): void {
console.log("This is my warning message");
}
```
声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null:
```typescript
let a: void = undefined;
let b: void = null;
```
## 9. Null和Underfined
TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null。 和 void相似,它们的本身的类型用处不是很大:
```typescript
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
```
默认情况下null和undefined是所有类型的子类型。 就是说你可以把 null和undefined赋值给number类型的变量。
然而,当你指定了--strictNullChecks(***strictNullChecks标记在项目配置里面配置***)标记,null和undefined只能赋值给void和它们各自。 这能避免 很多常见的问题。 也许在某处你想传入一个 string或null或undefined,你可以使用联合类型string | null | undefined。 (***联合类型表示一个值可以是几种类型之一。 我们用竖线( |)分隔每个类型***)
## 10. Never
never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。
never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never。
下面是一些返回never类型的函数:
```typescript
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
```
## 11. Object
object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型。
使用object类型,就可以更好的表示像Object.create这样的API。例如:
```typescript
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
```
## 类型断言
类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
类型断言有两种形式。 其一是“尖括号”语法:
```typescript
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
```
另一个为as语法:
```typescript
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
```
两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有 as语法断言是被允许的。
注:本文摘选于TypeScript中文文档。
---
title: Vue如何优化页面的SEO
date: 2022/4/15 10:44:20
tags:
- SEO
- 服务端渲染
- 预渲染编译
author: 蔡海飞
---
因项目要求,对于之前用Vue写的官网项目需要进行SEO优化。就此,对于该项目进行分析并选取合适的解决方案。首先,我们需要简单了解下SEO到底是什么?为什么要优化SEO,SEO能为我们带来什么?作为一个技术人员如何去优化SEO?
![seo](http://www.enjoytoday.cn/wp-content/uploads/2022/04/9636dd69232af81.png)
## SEO是什么?
SEO 中文名称为搜索引擎优化(Search Enine Optimization)。直白点是迎合搜索服务商的爬虫,让他多爬爬我们的网站,都索引我们的网站内容。我在网上找到了一个很形象的图述过程,它可以比较准确的去表达整个爬虫搜索、索引的过程。
<!-- more -->
![搜索引擎搜索过程](http://www.enjoytoday.cn/wp-content/uploads/2022/04/79a857486134955.png)
## 为什么要优化SEO,SEO能为我们带来什么?
为什么要做SEO昵,由于大多数用户都是通过搜索引擎去搜索想要访问的内容和服务。为了让我们的网站内容被更多的人群找到,或者是说需要的人找到,以便我们为他提供我们的服务来达到某种商业目的。这时,我们就得遵循搜索引擎制定的索引规则,以爬虫能读懂的方式来表述自己网站的内容、服务,以及想要对外提供的服务等信息。
简而言之,SEO是一种商业行为,是为了使我们达到某种目的的一种交换商业行为。
## 作为一个技术人员如何去优化SEO?
![](http://www.enjoytoday.cn/wp-content/uploads/2022/04/2c221e6947c409f.png)
SEO的优化其实需要很多方面的支撑。常见的SEO优化内容涉及:
- 网站meta要素和title
- 内容结构
- 代码优化
- 代码优化
如上,可以看得出来,SEO优化我们技术能做的有很多。那么,问题来了:Vue单页面为什么更不易被爬虫爬到昵?在这个问题前我们需要了解几个小的知识点:
- 搜索引擎更喜欢爬html内容,极少会执行js文件(国内的爬虫没有运行js的),可以等同理解为只爬html内容
- Vue、React之类的单页面框架html代码极少,都是通过Js动态操作dom的,其dom结构只存在于内存
从上面两个问题就不难理解了,Vue没法让搜索引擎满意。那么常规的Vue之类的应用如何优化搜索引擎昵?
- 静态网页
- 前后端不分离
- 前后端伪分离【SSR】
那回到今天我说的问题上,我司的是一个营销类网站需要进行优化,所以,我只需要通过一个插件对vue项目进行预渲染编译就可以了。这里用到两个插件:prerender-spa-plugin和vue-meta-info 操作流程如下:
#### 安装
```shell
$ npm i prerender-spa-plugin vue-meta-info -s // 或
$ yarn add prerender-spa-plugin vue-meta-info -D
```
#### 配置vue.config.js
```js
configureWebpack: () => {
if (process.env.NODE_ENV !== "production") return;
return {
name: name,
resolve: {
alias: {
"@": resolve("src"),
},
},
performance: {
hints: false,
},
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: ['/'], // 这里制定需要预渲染编译的页面
renderer: new Renderer({
inject: {
foo: 'bar'
},
headless: false,
renderAfterDocumentEvent: 'render-event'
})
})
]
};
```
#### mainjs添加渲染事件
```js
import MetaInfo from 'vue-meta-info'
Vue.use(MetaInfo) // 配置页面的title、description、keywords
Vue.config.productionTip = false
Vue.use(animated)
new Vue({
router,
render: h => h(App),
mounted() {
document.dispatchEvent(new Event('render-event')) // 这里是添加渲染后的事件
}
}).$mount('#app')
```
#### 对应页面添加配置
```js
export default {
name: "home",
metaInfo: {
title: "人事考试服务平台-人事考试报名管理系统",
meta: [
{
name: "keywords",
content: "人事考试服务平台、人事考试报名管理系统、标准化招聘、人事考试、考试报名管理系统、考试报名系统、报名考试系统、考务管理系统、考务管理",
},
{
name: "description",
content: "人事考试服务平台,人事考试报名管理系统,提供岗位发布、网上报名、资格审核、线上缴费、准考证打印等功能,减轻管理人员的工作负担,实现人事招聘流程的规范化管理。",
},
],
},
}
```
#### 编译
```shell
$ npm run build // 或者
$ yarn run build
```
如此部署就可,给大家瞅瞅结构,该有的都有。
![结果](http://www.enjoytoday.cn/wp-content/uploads/2022/04/a0f58af424cdfd5-1536x529.png)
\ No newline at end of file
---
title: jQuery从$开始
date: 2022/03/30 11:25:23
updated2022/03/30 11:25:23
updated: 2022/03/30 11:25:23
tags:
- 前端
- JQuery
......@@ -12,6 +12,7 @@ author: 王许晓
> jQuery requires a window with a document.[jQuery版本3.6.0](https://code.jquery.com/jquery-3.6.0.js)
使用jQuery的第一行代码总是从`$(document).ready(function(){});`或者`$(function(){});`开始,我从jQuery执行过程去了解$是怎么被识别并被使用的。
<!-- more -->
# jQuery 入口函数
jQuery根据不同的js运行时环境将jQuery实例挂载到不同属性上。如果是浏览器引用,则挂载到window的$上,如果是require方式在例如node.js环境引用,则需要判断当前环境的文档流在哪一层(写此博客时还没有搭建node环境,这里没有做验证,只是想法猜测),jQuery需要运行在有文档流的环境中。
......
......@@ -11,7 +11,7 @@ author: 刘磊
### 概念
本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
<!-- more -->
### 模块化
大家在接触到webpack的时候,可能并不知道它有什么用处,只知道它必不可缺而且听着很复杂。那到底为什么要使用它呢?首先需要了解模块化。
......
---
title: 探究React中setState的执行机制
date: 2022/3/31 18:00:00
updated: 2022/3/28 09:42:24
tags:
- 前端
- JavaScript
author: 方玉新
---
### 相关的一些问题
1. `setState` 是同步还是异步的,为什么有时可以里见到更新的结果而有时又不行?
1.1 钩子函数和React合成事件中的 `setState`
假如有两个组件
```javascript
componentDidMount(){
console.log('这是父组件componentDidMount');
}
render(){
return (
<div>
<SetState1></SetState1>
<SetState></SetState>
</div>
)
}
```
组件内部放入同样的代码,并在`SetState1`中的`componentDidMount`中添加一段同步延时代码,打印延时时间
```javascript
componentWillUpdate(){
console.log('componentWillUpdate');
}
componentDidUpdate(){
console.log('componentDidUpdate');
}
componentDidMount(){
console.log('SetState调用setState');
this.setState({index:this.state.index +1});
console.log('state',this.state.index);
console.log('SetState调用setState');
this.setState({index:this.state.index+1});
console.log('state', this.state.index);
}
```
说明:
* 1. 调用 `setState` 不会立即更新
* 2. 所有组件使用的是同一套更新机制,当所有组件`didmount`后,父组件`didmount`,然后执行更新
* 3. 更新时会把每个组件的更新合并,每个组件只会触发一次更新的生命周期。
<!-- more -->
1.2 异步函数和原生事件中的 `setstate`
`setTimeout` 中调用 `setState`(例子和在浏览器原生事件以及接口回调中执行效果相同)
```javascript
componentDidMount(){
setTimeout(()=>{console.log('调用setState');
this.setState({index:this.state.index+1})
console.log('state',this.state.index);
console.log('调用setState');
this.setState({index:this.state.index +1})
console.log('state',this.state.index);},0);
}
```
根据执行结果可以得出:
* 1. 在父组件 `didmount` 后执行
* 2. 调用 `setState` 同步更新
2. 为什么有时连续两次 `setState` 只有一次生效
分别执行以下代码:
```javascript
componentDidMount(){
this.setState({index:this.state.index +1},()=>{console.log(this.state.index);})
this.setState({index:this.state.index +1},()=>{console.log(this.state.index);})
}
componentDidMount(){
this.setState((preState)=>({index:preState.index+1}),()=>{
console.log(this.state.index);
})
this.setState(preState => ({index:preState.index+1}),()=>{
console.log(this.state.index);
})
}
```
执行结果:
```javascript
1
1
2
2
```
说明:
* 1. 直接传递对象的 `setstate` 会被合并成一次
* 2. 使用函数传递 `state` 不会被合并
### setState执行过程
##### 1. 流程图
* partialState: setState传入的第一个参数,对象或函数
* _pendingStateQueue:当前组件等待执行更新的 state队列
* isBatchingUpdates:react用于标识当前是否处于批量更新状态,所有组件公用
* dirtyComponent:当前所有处于待更新状态的组件队列
* transcation:react的事务机制,在被事务调用的方法外包装n个 waper对象,并一次执行: waper.init、被调用方法、 waper.close
* FLUSH_BATCHED_UPDATES:用于执行更新的 waper,只有一个 close方法
##### 2. 执行过程
按照上面的流程图可以得到:
* 1.将setState传入的 partialState参数存储在当前组件实例的state暂存队列中。
* 2.判断当前React是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。
* 3.如果未处于批量更新状态,将批量更新状态标识设置为true,用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中。
* 4.调用事务的 waper方法,遍历待更新组件队列依次执行更新。
* 5.执行生命周期 componentWillReceiveProps。
* 6.将组件的state暂存队列中的 state进行合并,获得最终要更新的state对象,并将队列置为空。
* 7.执行生命周期 componentShouldUpdate,根据返回值判断是否要继续更新。
* 8.执行生命周期 componentWillUpdate。
* 9.执行真正的更新, render。
* 10.执行生命周期 componentDidUpdate。
### 总结
##### 1. 钩子函数和合成事件中:
在 react的生命周期和合成事件中, react仍然处于他的更新机制中,这时 isBranchUpdate为true。
按照上述过程,这时无论调用多少次 setState,都会不会执行更新,而是将要更新的 state存入 _pendingStateQueue,将要更新的组件存入 dirtyComponent。
当上一次更新机制执行完毕,以生命周期为例,所有组件,即最顶层组件 didmount后会将 isBranchUpdate设置为false。这时将执行之前累积的 setState。
##### 2. 异步函数和原生事件中
由执行机制看, setState本身并不是异步的,而是如果在调用 setState时,如果 react正处于更新过程,当前更新会被暂存,等上一次更新执行后在执行,这个过程给人一种异步的假象。
在生命周期,根据JS的异步机制,会将异步函数先暂存,等所有同步代码执行完毕后在执行,这时上一次更新过程已经执行完毕, isBranchUpdate被设置为false,根据上面的流程,这时再调用 setState即可立即执行更新,拿到更新结果。
##### 3. partialState合并机制
我们看下流程中 _processPendingState的代码,这个函数是用来合并 state暂存队列的,最后返回一个合并后的 state。
```javascript
_processPendingState : function(props,context)
{
var inst = this._instance;
var queue = this._pendingStateQueue;
var replace = this._pendingReplaceState;
this._pendingReplaceState = false;
this._pendingStateQueue = null;
if(!queue)
{
return inst.state;
}
if(replace && queue.length === 1)
{
return queue[0];
}
var nextState = _assign({}, replace ? queue[0] : inst.state);
for(var i = replace ? 1 : 0 ; i < queue.length;i++)
{
var partial = queue[i];
_assign(nextState, typeof partial === 'function' ? partial.call(inst,nextState,props,context) : partial);
}
return nextState;
}
```
我们只需要关注下面这段代码:
```javascript
_assign(nextState,typeof partial === 'function' ? partial.call(inst,nextState,props,context) : partial);
```
如果传入的是对象,很明显会被合并成一次:
```javascript
Object.assign(
nextState,
{index: state.index+1},
{index: state.index+1},
)
```
如果传入的是函数,函数的参数`preState`是前一次合并后的结果,所以计算结果是准确的。
##### 4. componentDidMount调用 setstate
> 在componentDidMount()中,你 可以立即调用setState()。它将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了在此情况下即使render()将会调用两次,用户也不会看到中间状态。谨慎使用这一模式,因为它常导致性能问题。在大多数情况下,你可以 在constructor()中使用赋值初始状态来代替。然而,有些情况下必须这样,比如像模态框和工具提示框。这时,你需要先测量这些DOM节点,才能渲染依赖尺寸或者位置的某些东西。
以上是官方文档的说明,不推荐直接在 `componentDidMount` 直接调用 `setState`,由上面的分析: `componentDidMount` 本身处于一次更新中,我们又调用了一次 `setState`,就会在未来再进行一次 `render`,造成不必要的性能浪费,大多数情况可以设置初始值来搞定。
当然在 `componentDidMount` 我们可以调用接口,再回调中去修改 `state`,这是正确的做法。
当state初始值依赖dom属性时,在 `componentDidMount``setState`是无法避免的。
##### 5. componentWillUpdate componentDidUpdate
这两个生命周期中不能调用 `setState`
由上面的流程图很容易发现,在它们里面调用 `setState`会造成死循环,导致程序崩溃。
##### 6. 推荐使用方式
在调用 `setState`时使用函数传递 `state`值,在回调函数中获取最新更新后的 `state`
......@@ -12,6 +12,8 @@ author: 张芳利
众做周知Vue2的双向绑定原理是利用了ES5的一个API`Object.defineProperty()`对数据进行劫持,结合发布订阅模式的方式来实现的。
而Vue3 中则是使用了ES6的Proxy 对数据代理。
<!-- more -->
## Vue2 的Object.defineProperty()
1. 定义:
Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment