Minimal and Clean blog theme for Hugo
自定义事件
浏览器缓存
看了这篇文章后记录下自己的理解。
缓存有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和请求结果 。
…加载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。
…字体bug
一个bug——页面中的字体无缘无故的而变成了宋体,而且有区别,用electron打包的页面是宋体,用chrome浏览器打开的是雅黑或者果方。
由于span被设置了一个fontFamily只为雅黑,而苹果系统没有雅黑字体,所以它没有找父级元素继承字体,而是选择了浏览器的环境变量(设置)。
在mac的chrome的设置中,看字体。有标准、serif、sans-serif、宽度固定四种设置。在修改了 sans-serif 以后,原本只在electron中出现的宋体,也出现在了chrome中,由此推断,是这个设置的锅了。
electron也是一个浏览器环境,有一些配置项,发现 webPreferences 有如下配置:
此处设置为微软雅黑后,mac下表现就和chrome一致了。
…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跳出遍历:
2.1. 使用this
使用this的话,要使用function函数,不能使用箭头函数。function函数的this指向tr。
2.2. 使用箭头函数就别用this
因为this不会指向tr,所以要用回调函数的第二个参数。
…React 与 Typescript
input的事件类型
需要注意两点。
- 事件类型可能有两种
ChangeEvent
对应event.target
FormEvent
对应event.currentTarget
- 泛型对应元素,可能是
HTMLInputElement
或HTMLSelectElement
等。
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自动修复问题:
…命令行发送email
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的情况下使用。
…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 。
…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方法:
…