Minimal and Clean blog theme for Hugo

自定义事件


两种的自定义事件的用法的介绍,addEventListener监听事件,event和customevent创建事件,dispatchEvent发送事件。…
Read more ⟶

浏览器缓存


看了这篇文章后记录下自己的理解。

缓存有2步:强制缓存和协商缓存。就是通过一些手段,设置强制缓存,让文件保存在本地硬盘,当强制缓存失效后,再通过与服务器协商看是否更新缓存文件。

强制缓存

强制缓存会把文件缓存在本地,当缓存未过期时访问网站,浏览器会直接使用缓存的资源。

控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control优先级比Expires高。

Expires是过时的方式,不推荐使用。推荐使用Cache-Control来控制。

Cache-Control用的多的值是如下几个:

  • public:(强制缓存)所有内容都将被缓存(客户端和代理服务器都可缓存)
  • private:
    (强制缓存) 所有内容只有客户端可以缓存,Cache-Control的默认取值
  • no-cache:(协商缓存)客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store:(决定不使用缓存)所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
  • max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效

协商缓存

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。

缓存标识是响应报文的HTTP头中和请求结果一起返回给浏览器的。

缓存标识有2组:

  • 【Last-Modified / If-Modified-Since 】
  • 【Etag / If-None-Match】

【Etag / If-None-Match】优先级较高。

Last-Modified / If-Modified-Since

Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间。

If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值。服务器通过比较这个值判断缓存是否失效。

协商缓存生效,返回304,浏览器使用缓存。

协商缓存失效,返回200和请求结果 。

Etag / If-None-Match

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)。

If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值。服务器通过比较这个值判断缓存是否失效。

协商缓存生效,返回304,浏览器使用缓存。

协商缓存失效,返回200和请求结果 。

Read more ⟶

加载script标签


将script标签写在2个地方——head中和body中——进行观察:

head中,他们大概是同时开始加载,运行后,body中的才开始加载运行。也及时body中的,会等待head中的加载完成运行完毕后,才开始加载。也就是页面会阻塞。

<figure class="wp-block-image">
  <img src="/uploads/2019/03/image-4-1024x409.png" alt="" class="wp-image-296"   /> 
</figure> 

还有可能在head中编写一段js代码,它会在head中再添加几个script标签。就会出现下面的情况。

<figure class="wp-block-image">
  <img src="/uploads/2019/03/image-5-1024x437.png" alt="" class="wp-image-297"   />
</figure> 

第一部分是head中写死的,第二部分是JS添加到head中的,第三部分是写在body中的。

第二部分会开始加载,然后body中的不会等待第二部分加载完成,也就是JS添加的script不会阻塞页面。

当body中的script依赖了JS添加的script,就有可能出现找不到依赖报错的情况。

defer async

async 异步加载资源,且加载完JS资源立即执行,并不会按顺序,谁快谁先上, 网页不会失去响应。

defer 异步加载资源,在DOM渲染后之后再按顺序执行JS。

Read more ⟶

字体bug


一个bug——页面中的字体无缘无故的而变成了宋体,而且有区别,用electron打包的页面是宋体,用chrome浏览器打开的是雅黑或者果方

由于span被设置了一个fontFamily只为雅黑,而苹果系统没有雅黑字体,所以它没有找父级元素继承字体,而是选择了浏览器的环境变量(设置)。

在mac的chrome的设置中,看字体。有标准、serif、sans-serif、宽度固定四种设置。在修改了 sans-serif 以后,原本只在electron中出现的宋体,也出现在了chrome中,由此推断,是这个设置的锅了。

electron也是一个浏览器环境,有一些配置项,发现 webPreferences 有如下配置:

此处设置为微软雅黑后,mac下表现就和chrome一致了。

Read more ⟶

Cheerio


1.解析表格table

<tr>
<td></td>
<td colspan="2">Summa tillgodoräknade poäng:</td>
<td class="credits">10,5</td>
<td></td>
</tr>

如果只是一段tr,cheerio不会识别,需要是一个完整的table才可以,而且table外面好像还有包一层div之类的东西,cheerio才能解析。

2.遍历

使用each方法可以return false跳出遍历:

https://cheerio.js.org/

2.1. 使用this

使用this的话,要使用function函数,不能使用箭头函数。function函数的this指向tr。

2.2. 使用箭头函数就别用this

因为this不会指向tr,所以要用回调函数的第二个参数。

Read more ⟶

React 与 Typescript


input的事件类型

需要注意两点。

  1. 事件类型可能有两种
  • ChangeEvent 对应 event.target
  • FormEvent 对应 event.currentTarget
  1. 泛型对应元素,可能是HTMLInputElementHTMLSelectElement 等。
const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (event.target.value) {
        setKeywords(event.target.value);
        props.onUpdateKeyword(event.target.value);
    }
}

class组件的类型

class组件需要定义props和state的类型:

interface IProps {
  addTodo: (input: string) => void;
}
interface IState {
  input: string;
}
class AddTodo extends React.Component<IProps, IState>{}

创建项目

creact-react-app可以直接创建 typescript 项目。

create-react-app my-app --scripts-version=react-scripts-ts

修改配置

把 tslint.json 修改为这样,能舒服一点:

{
  "extends": [
    "tslint:recommended", 
    "tslint-react", 
    "tslint-config-prettier"
  ],
  "defaultSeverity": "warning",
  "linterOptions": {
    "exclude": [
      "config/**/*.js",
      "node_modules/**/*.ts",
      "coverage/lcov-report/*.js"
    ]
  },
  "rules": {
    "object-literal-sort-keys": false,
    "no-console": false
  }
}

在package.json的script中加入这条,可以让tslint自动修复问题:

Read more ⟶

命令行发送email


python发送邮件…
Read more ⟶

dva取消异步任务


先声明一个axios的网络请求函数login🤪:

//service.js
import axios from 'axios';

export default {
  login: async (params, cancelToken) => {
    try {
      const response = await axios.post(`${ApiServer}/auth/user`, params, cancelToken);
      return response;
    } catch (error) {
      if (axios.isCancel(error)) {
        console.log('Request canceled', error.message);
      } else {
        message.error(error.message);
      }
    }
  },

这段代码可以处理3中情况的异常:

  • axios.interceptors.response中throw Error
  • http status code !== 200时的promise reject,不使用async时是在catch中处理
  • axios请求被代码cancel掉

有了网络请求,可以写saga函数了:

如果是在dva中,saga函数会被写在model中的effects对象中:

const CancelToken = axios.CancelToken;
...  
effects: {
    *startLogin({ payload }, { put, call, race }) {
      const source = CancelToken.source();
      const { data, timeout } = yield race({
        data: call(login, {username: payload.username,}, {cancelToken: source.token,}),
        timeout: call(delay, 1000),
      });
      if (data) {
        yield put({ type: 'log', payload: 'success' });
      } else {
        yield source.cancel('登录超时');
        yield put({ type: 'log', payload: 'timeout' });
      }
     },
   }

当然startLogin函数也可以写成generator函数作为普通saga,在不用dva的情况下使用。

Read more ⟶

Redux Saga


redux-saga中使用fetch

const response = yield call(fetch, '/consult/v1/headCarousel');
const data = yield response.json();
yield put({ type: 'save', payload: data });

为了对它有一个感性的认识,首先看一下 中文文档 把dome写出来,跑一下。另外还需要了解 ES6的Generator 。

跑完了文档中的dome之后,在来看一个例子。

// 类 thunk 的 worker “进程”
function* load() {
  yield put({ type: BEGIN_LOAD_DATA });
  try {
    const result = yield call(fetch, UrlMap.loadData);
    yield put({ type: LOAD_DATA_SUCCESS ,payload: result });
  } catch (e) {
    yield put({type: LOAD_DATA_ERROR });
  }
}

function* saga() {
  // 创建一个监听“进程”
  yield fork(watch(CLICK_LOAD_BUTTON, load))
}

这里有几个 redux-saga 的概念需要再了解一下。

1、put

作用和 redux 中的 dispatch 相同。

yield put({ type: 'CLICK_BTN' });

2、select

作用和 redux thunk 中的 getState 相同。

const id = yield select(state => state.id);

3、take

等待 redux dispatch 匹配某个 pattern 的 action 。

Read more ⟶

Redux 概要


基本概念

store:保存数据的容器,整个应用通常只有一个store。

state:store在某时点的一个快照,一个state对应一个view。

action:定义一个用户对view的操作。

可以暂时这样理解:

store生成一个快照state,state对应一个view,view发出action来通知store变化,使store变化而生成一个新的state,新的state又对应新的view。

ActionCreator:通常action只是一个对象,且只有type属性使必须的。我们知道action代表着view的操作,所以action可能有很多个又被经常使用,所以用一个函数来生成各种action是比较合理的操作,这个函数就叫做ActionCreator。

Redux的API

生成store、获得state

view通过store.dispatch() 发出action:

store在接收到action时,会根据action生成一个新的state,那么如何生成新的state,这个计算过程怎么定义呢?

Reducer

Reducer是一个函数,它定义store在接收到action时state如何发生变化。action只描述发生了什么,而reducer描述state如何变化。这个函数是这样的:

实际应用中,Reducer 函数不用像上面这样手动调用,store.dispatch方法会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法:

Read more ⟶