├── README.md
├── images
└── chrome-timing.png
├── mvvm
├── observer.html
└── observer.js
└── src
├── cross-domain.md
├── css-1px.css
├── debounce_throttle.js
├── flatten.js
├── index.html
├── jsonp.js
├── multi-page-webpack.md
├── net-optimize.md
├── promise.js
├── react-update-log.md
├── reset.css
├── skeleton.md
├── umd.js
├── vue-life-cycle.jpg
├── vue-mixins.md
└── vue-optimize.md
/README.md:
--------------------------------------------------------------------------------
1 |
7 | ### 将一些记录在印象笔记上的零散知识点代码自我实现下汇总过来
8 |
9 | + [移动端真机调试之spy-debugger](https://github.com/lusteng/daily-notes/issues/1)
10 |
11 | + [jsonp 封装](https://github.com/lusteng/daily-notes/blob/master/src/jsonp.md)
12 |
13 | + [取自淘宝的reset css](https://github.com/lusteng/daily-notes/blob/master/src/reset.css)
14 |
15 | + [1px解决方案](https://github.com/lusteng/daily-notes/blob/master/src/css-1px.css)
16 |
17 | + [封装amd、commonJs、window导出](https://github.com/lusteng/daily-notes/blob/master/src/umd.js)
18 |
19 | + [react 组件监听router变化方法](https://github.com/lusteng/daily-notes/issues/2)
20 |
21 | + [节流、防抖函数实现](https://github.com/lusteng/daily-notes/blob/master/src/debounce_throttle.js)
22 |
23 | + [promise简版实现](https://github.com/lusteng/daily-notes/blob/master/src/promise.js)
24 |
25 | + [扁平化数组处理](https://github.com/lusteng/daily-notes/blob/master/src/flatten.js)
26 |
27 | + [分析Chrome调试面板timing来优化用页面性能](https://github.com/lusteng/daily-notes/blob/master/src/net-optimize.md)
28 |
29 | + [vue 生命周期讲解图示](https://github.com/lusteng/daily-notes/blob/master/src/vue-life-cycle.jpg)
30 |
31 | + [vue mixins 提取不同组件的公共处理事务](https://github.com/lusteng/daily-notes/blob/master/src/vue-mixins.md)
32 |
33 | + [骨架屏](https://github.com/lusteng/daily-notes/blob/master/src/skeleton.md)
34 |
35 | + [多页面打包](https://github.com/lusteng/daily-notes/blob/master/src/multi-page-webpack.md)
36 |
37 | + [优化vue打包文件过大](https://github.com/lusteng/daily-notes/blob/master/src/vue-optimize.md)
38 |
39 | + [react项目版本升级踩坑日志](https://github.com/lusteng/daily-notes/blob/master/src/react-update-log.md)
40 |
41 | + [跨域方式实现总结](https://github.com/lusteng/daily-notes/blob/master/src/cross-domain.md)
42 |
43 | + [模拟实现Vue数据双向绑定响应](./mvvm/observer.js)
--------------------------------------------------------------------------------
/images/chrome-timing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lusteng/daily-notes/0fc652b7e510ea7c3b63865ca6c4e04dcfd07e66/images/chrome-timing.png
--------------------------------------------------------------------------------
/mvvm/observer.html:
--------------------------------------------------------------------------------
1 |
6 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Document
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
50 |
51 |
--------------------------------------------------------------------------------
/mvvm/observer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Demo mvvm/observer.js
3 | * @Description: 模拟vue mvvm 实现流程
4 | */
5 |
6 | /**
7 | * @description: 创建个vue实例
8 | */
9 | class Vue{
10 | constructor(options){
11 | if(options.data && typeof options.data === 'function'){
12 | this._data = options.data.call(this)
13 | new Observer(this._data)
14 | }else{
15 | throw new Error('data must is function')
16 | }
17 | }
18 | // 绑定数据
19 | mounted(){
20 | let vm = this
21 | new Watcher(vm, this.render)
22 | }
23 | // vue render 触发数据get
24 | render(){
25 | let vm = this
26 | // 假装访问了该data中属性
27 | for(let key in vm._data){
28 | vm._data[key]
29 | }
30 | }
31 | }
32 |
33 | /**
34 | * @description: 观察者 将传入的数据改造为可侦测数据
35 | * @param datas vue 数据对象
36 | * @return:
37 | */
38 | class Observer{
39 | constructor(datas){
40 | // 使用Object.defineProperty 转化可监测
41 | this.defReactive(datas)
42 | }
43 | defReactive(datas){
44 | for(let key in datas){
45 | let val = datas[key]
46 | // 每个订阅者数据下面建立一个专有dep,负责收集依赖和通知依赖变化
47 | let dep = new Dep()
48 | Object.defineProperty(datas, key, {
49 | enumerable: true,
50 | configurable: true,
51 | get(){
52 | // 加入当前依赖到dep
53 | dep.depend()
54 | return val
55 | },
56 | set(newVal){
57 | if(newVal === val) return
58 | // 当前dep通知变更
59 | dep.notify(key, newVal)
60 | }
61 | })
62 | }
63 | }
64 | }
65 |
66 | /**
67 | * @description: 依赖收集器,分发变更给订阅者
68 | * @param {type}
69 | * @return:
70 | */
71 | const dep_id = 0
72 | class Dep{
73 | constructor(){
74 | // 订阅者列表
75 | this.subs = []
76 | // 假装个id查看下当前对象内存
77 | this.id = dep_id
78 | dep_id ++
79 | }
80 | depend(){
81 | // 将当前watcher加入订阅者列表
82 | if(Dep.target) Dep.target.addSub(this)
83 | }
84 | notify(key, val){
85 | // 触发当前dep中的订阅者更新
86 | this.subs.forEach(sub => {
87 | sub.update(key, val)
88 | })
89 | }
90 | }
91 |
92 | /**
93 | * @description: 订阅者 更新界面
94 | * @param vm vue 对象
95 | * @param fn render 函数
96 | */
97 | class Watcher{
98 | constructor(vm, fn){
99 | this.vm = vm
100 | this.fn = fn
101 | Dep.target = this
102 | fn.call(vm)
103 | Dep.target = null
104 | }
105 | addSub(dep){
106 | dep.subs.push(this)
107 | }
108 | update(key, val){
109 | console.log(`data的${key}更新成${val}了~~~`)
110 | // 触发render函数,在render的过程再次触发了对应key的get
111 | this.fn.call(this.vm)
112 | }
113 |
114 | }
115 |
116 |
117 | /**
118 | * @flow:
119 | * 1. Vue 对象初始化阶段(beforeMount之前),将data数据通过(Observer 观察者对象)Object.defineProperty转化成可检测数据模型
120 | * 2. Vue 对象挂载数据阶段(beforeMount之后,mounted之前)初始化Watcher对象(界面使用数据的依赖)
121 | * tips: Watcher初始化在Observer之后,防止数据还没挂载到界面就触发了watcher update,使用Dep.target存储watcher对象
122 | * 3. get 数据,触发Dep对象(负责收集依赖,管理订阅通知)depend将watcher对象存入subs列表(订阅者列表)
123 | * 4. 界面操作数据,触发set ,Dep发送notify,触发watcher的update,更新Vue的render函数,同时又一次触发get,再次将
124 | * 此次的watcher存入Dep的subs列表,以此类推
125 | */
--------------------------------------------------------------------------------
/src/cross-domain.md:
--------------------------------------------------------------------------------
1 | ### 序言
2 | ---
3 | 某天,和某大大交流技术的时候,被问到能列出多少种解决跨域方式时,心里巴拉巴拉了一顿,除了日常工作中用到的JSONP(早期工作)、CORS、代理,突然发现能说上来的很少呀!于是乎感觉来总结一番。。。
4 |
5 | ### 何为跨域?
6 | 跨域源自早期浏览器的[同源策略](https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy),即协议 + 域名 + 端口三者相同,不同源之间的的脚本或文档、http请求不能相互访问。只有以下三个标签允许跨域加载资源:
7 | + \
8 | + \
9 | + \
60 |
61 | // b 页面 4000
62 | // 监听message
63 | window.onmessage = function(e) {
64 | console.log(e.data) // are you ok?
65 | e.source.postMessage('ok', e.origin)
66 | }
67 | ```
68 |
69 | 5. websocket
70 | > WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了
71 |
72 | ```html
73 | // socket.html
74 |
83 | ```
84 |
85 | ```js
86 | // node server.js
87 | const Koa = require('koa'),
88 | route = require('koa-route'),
89 | websockify = require('koa-websocket');
90 |
91 | const app = websockify(new Koa());
92 | app.ws.use(function (ctx, next) {
93 | return next(ctx);
94 | });
95 |
96 | // Using routes
97 | app.ws.use(route.all('/test', function (ctx) {
98 | ctx.websocket.on('message', function (message) {
99 | console.log(message);
100 | ctx.websocket.send('yes');
101 | });
102 | }));
103 |
104 | app.listen(3000);
105 |
106 | ```
107 | 6. window.name + iframe
108 | > 巧妙利用window.name(只能window.name属性)这个属性可以通过iframe切换时保留下来,然后可以通过本域页面访问到window.name 这个变量来绕过跨域机制
109 |
110 | ```html
111 |
112 |
113 |
114 |
115 |
116 |
130 |
131 |
132 |
133 | ```
134 |
135 | ```html
136 |
137 |
138 |
144 | ```
145 |
146 | 7. document.domain + iframe
147 | > 通过js将两个子域设置相同的主域,可以访问其他二级域名的变量,适合同一主域下的二级域名的情况,比如a.test.com 和 b.test.com
148 |
149 | ```html
150 |
151 |
152 |
153 |
154 |
162 | ```
163 | ```html
164 |
165 |
166 |
171 | ```
172 |
173 |
--------------------------------------------------------------------------------
/src/css-1px.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @Description: css 移动端1px解决方案
3 | * 1、缩放方案 伪类 + scale
4 | * 2、渐变背景色方案
5 | * 3、阴影方案 (部分设备容易border消失)
6 | * 4、背景图片方案(可维护性差)
7 | * 5、svg方案(代码复杂度高)
8 | * 6、设置viewport的scacle值,dpr2下设置为0.5,dpr2下设置为0.333(侵入性过高,不推荐)
9 | **/
10 |
11 | /* 参考 https://github.com/dengwb1991/owl-ui/blob/master/src/styles/common/border.less */
12 | /* 线上实例 http://www.liubaitong.com/1px/index.html */
13 | div {
14 | width: 80vw;
15 | height: 80px;
16 | line-height: 80px;
17 | margin: 30px auto;
18 | background-color: #f0f0f0;
19 | box-sizing: border-box;
20 | text-indent: 2em;
21 | }
22 |
23 | .dpr::after{
24 | content: "1"
25 | }
26 |
27 | /*border-top:1px*/
28 | .border_normal,
29 | /* 缩放方案 */
30 | .border_scale,
31 | /* 渐变背景色方案 */
32 | .border_gradient,
33 | /* 阴影方案 */
34 | .border_boxshadow,
35 | /* 背景图片方案 */
36 | .border_base64,
37 | /* svg方案 */
38 | .border_svg{
39 | border-top: 1px solid #999;
40 | }
41 |
42 | /* dpr2 */
43 | @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
44 | .dpr::after{
45 | content: "2"
46 | }
47 | .border_gradient {
48 | background-image: linear-gradient(to top, transparent 50%, #999 50%);
49 | background-size: 100% 1px;
50 | background-repeat: no-repeat;
51 | background-position: top center;
52 | border-top: 0 none;
53 | padding-top: 1px;
54 | }
55 |
56 | .border_scale {
57 | position: relative;
58 | padding-top: 1px;
59 | border-top: 0 none;
60 | }
61 | .border_scale::after {
62 | content: "";
63 | position: absolute;
64 | top: 0;
65 | left: 0;
66 | width: 200%;
67 | height: 200%;
68 | border: 1px solid #999;
69 | border-radius: 10px;
70 | transform: scale(0.5);
71 | transform-origin: 0 0;
72 | box-sizing: border-box;
73 | }
74 | .border_boxshadow{
75 | border-top: 0 none;
76 | box-shadow: 0 -1px 1px -1px red,
77 | 1px 0 1px -1px red,
78 | 0 1px 1px -1px red,
79 | -1px 0 1px -1px red;
80 | }
81 | .border_base64 {
82 | padding-top: 1px;
83 | border-top: 0 none;
84 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAQSURBVBhXY5g5c+Z/BhAAABRcAsvqBShzAAAAAElFTkSuQmCC);
85 | background-position: 0 0;
86 | background-repeat: repeat-x;
87 | background-size: 1px 1px;
88 | }
89 |
90 | .border_svg {
91 | border-top: 0 none;
92 | background-image: url("data:image/svg+xml;utf8,");
93 | background-position:0 0;
94 | background-repeat:no-repeat;
95 | }
96 | }
97 |
98 | /* dpr3 */
99 | @media only screen and (-webkit-min-device-pixel-ratio: 3), only screen and (min-device-pixel-ratio: 3) {
100 | .dpr::after{
101 | content: "3"
102 | }
103 | .border_gradient {
104 | background-image: linear-gradient(to top, transparent 66.66%, #999 66.66%);
105 | background-size: 100% 1px;
106 | background-repeat: no-repeat;
107 | background-position: top center;
108 | border-top: 0 none;
109 | padding-top: 1px;
110 | }
111 |
112 | .border_scale {
113 | position: relative;
114 | padding-top: 1px;
115 | border-top: 0 none;
116 | }
117 | .border_scale::after {
118 | content: "";
119 | position: absolute;
120 | top: 0;
121 | left: 0;
122 | width: 300%;
123 | height: 300%;
124 | border: 1px solid #999;
125 | border-radius: 15px;
126 | transform: scale(0.333);
127 | transform-origin: 0 0;
128 | box-sizing: border-box;
129 | }
130 | .border_boxshadow{
131 | border-top: 0 none;
132 | box-shadow: 0 -1px 1px -1px red,
133 | 1px 0 1px -1px red,
134 | 0 1px 1px -1px red,
135 | -1px 0 1px -1px red;
136 | }
137 | .border_base64 {
138 | padding-top: 1px;
139 | border-top: 0 none;
140 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAQSURBVBhXY5g5c+Z/BhAAABRcAsvqBShzAAAAAElFTkSuQmCC);
141 | background-position: 0 0;
142 | background-repeat: repeat-x;
143 | background-size: 1px 1px;
144 | }
145 |
146 | .border_svg {
147 | border-top: 0 none;
148 | background-image: url("data:image/svg+xml;utf8,");
149 | background-position:0 0;
150 | background-repeat:no-repeat;
151 | }
152 | }
--------------------------------------------------------------------------------
/src/debounce_throttle.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @desc 防抖函数(某段时间内只能执行一次)与节流函数(两次执行的间隔时间,频率)
4 | * @diff 防抖函数:初次调用了setTimeout后,若还继续触发则一直不执行fn函数(每次连续触发段内只能执行一次)
5 | * @diff 节流函数:初次调用了setTimeout后,若还继续触发则再次调用setTimeout(频率)
6 | * /
7 |
8 | /**
9 | * @desc 防抖
10 | * @param fn 调用函数
11 | * @param wait 延迟执行时间 ms
12 | * @param immediate 是否立即执行 bool true 是 false 否
13 | */
14 |
15 | /**
16 | * @desc 防抖
17 | * @param fn 调用函数
18 | * @param wait 延迟执行时间 ms
19 | * @param immediate 是否立即执行 bool true 是 false 否
20 | */
21 |
22 | function debounce(fn, wait = 1000, immediate = false){
23 | let timeout
24 |
25 | return function(){
26 | let _context = this,
27 | arg = arguments
28 | /**********
29 | * !!!防抖和节流最大区别 !!!
30 | * 防抖只允许连续动作内执行一次,若动作一直触发则该次后续触发的函数不执行,故此处清除timeout
31 | * 节流在连续动作内降低执行频率,一直触发则按wait参数频率触发setTimeout执行,此处节流和防抖最大区别处也是执行代码最大区别处
32 | ************/
33 | timeout && clearTimeout(timeout)
34 |
35 | if(immediate){ //立即执行
36 | let canRun = !timeout
37 | //wait时间后timeout为空,fn可再次执行
38 | timeout = setTimeout(() => {
39 | timeout = null
40 | }, wait)
41 |
42 | canRun && fn.apply(_context, arg)
43 | }else{
44 | timeout = setTimeout(() => {
45 | fn.apply(_context, arg)
46 | }, wait)
47 | }
48 | }
49 | }
50 |
51 | // 测试用例
52 | let fun = function(){
53 | console.log(333333333)
54 | }
55 | let test = debounce(fun, 2000, true)
56 | setInterval(() => {
57 | test()
58 | }, 100)
59 |
60 | /**
61 | * @desc 节流
62 | * @param fn 调用函数
63 | * @param interval 执行间隔时间 ms
64 | * @param immediate 时间段起始执行还是末位执行 bool true 起始 false 末位
65 | */
66 |
67 | function throttle(fn, interval = 300, immediate = false){
68 | let
69 | timeout,
70 | st = 0
71 |
72 | return function () {
73 | let _context = this,
74 | args = arguments
75 |
76 | if(immediate){ //时间段开头执行
77 | let nt = + new Date();
78 | if(nt - st > interval){
79 | fn.apply(_context, args)
80 | st = nt
81 | }
82 | }else{ //时间段末位执行
83 | if(!timeout){
84 | timeout = setTimeout(() => {
85 | fn.apply(_context, args)
86 | timeout = null
87 | }, interval)
88 | }
89 |
90 | }
91 | }
92 | }
93 |
94 |
95 |
96 | //工具包
97 | // lodash https://github.com/lodash/lodash
98 | // underscore https://github.com/jashkenas/underscore
99 |
--------------------------------------------------------------------------------
/src/flatten.js:
--------------------------------------------------------------------------------
1 | // 扁平化数组,处理数据时常用
2 | // const arr = [1, [[2], 3, 4], [22,12,[22]],5];
3 | const arr = [1, [[2, [3232, [444]]], 3, 4], [{"aaa": "fff"},12,['ggg']],5];
4 |
5 | function flatten(arr){
6 | let res = []
7 | arr.forEach(item => {
8 | if(Array.isArray(item)){
9 | res = res.concat(flatten(item))
10 | }else{
11 | res.push(item)
12 | }
13 | })
14 | return res
15 | }
16 |
17 | // 仅针对数组项为number类型
18 | function flatten(arr){
19 | return arr.toString().split(',').map(item => {
20 | return Number(item)
21 | })
22 | }
23 |
24 | /**
25 | * 巧妙应用apply
26 | * Array.prototype.concat每次调用apply,将一维数组当参数执行concat连接
27 | * 使用some检测存在是否存在嵌套数组(concat每次只能连接一维数组)
28 | */
29 | function flatten(arr){
30 | while(arr.some(item => Array.isArray(item))){
31 | arr = Array.prototype.concat.apply([], arr)
32 | }
33 | return arr
34 | }
35 |
36 | /**
37 | * 使用Array reduce 方法, reduce 传入两个参数
38 | * @param1 fn 自定义的函数,函数提供四个参数
39 | accumulator 上次循环返回的值,初始为数组第一项或者reduce 的参数2;
40 | currentValue 数组正在处理的元素
41 | currentIndex 数组中正在处理的当前元素的索引
42 | array 调用reduce()的数组
43 | * @param2 initialValue 为第一个函数参数 accumulator的初始值,不提供则以数组第一项为初始值
44 | */
45 | function flatten(arr){
46 | return arr.reduce((prev, cur) => prev.concat(Array.isArray(cur) ? flatten(cur) : cur), [])
47 | }
48 |
49 | let res = flatten(arr)
50 |
51 | console.log(res);
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 测试
6 |
7 |
8 |
9 | fsdfkdsjlfjdsfklj
10 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/jsonp.js:
--------------------------------------------------------------------------------
1 | /***
2 | *
3 | */
4 |
5 | // jsonp 原理 (利用请求script资源没有跨域限制)
6 | // 1.客户端(前端)注册一个callback函数
7 | function callback(data){
8 | //data 操作获取到的数据
9 | }
10 | // 2.服务端将需要返回的json数据作为入参到callback函数,返回给客户端
11 | // 请求跨域资源返回的内容
12 | callback(data)
13 |
14 |
15 | /**
16 | * @param {
17 | * url, // jsonp资源url
18 | * data, // jsonp url search参数
19 | * success, // 成功回调
20 | * error // 失败回调
21 | * } opts
22 | */
23 |
24 | function jsonp(opts = {}){
25 | let {
26 | url = "",
27 | data = {},
28 | success = () => {},
29 | error = () => {}
30 | } = opts
31 | if(!url) throw new Error("please pass in url params")
32 | // 随机名
33 | let radomName = `id_${+ new Date()}_${Math.random().toString().substr(2)}`
34 | // script 标签
35 | let urlParamsMerge = Object.assign({}, data, {callback: radomName})
36 | let scriptEle = document.createElement('script')
37 |
38 | for(let u in urlParamsMerge){
39 | url += url.indexOf('?') > -1 ? `&${u}=${ (urlParamsMerge[u])}` : `?${u}=${encodeURIComponent(urlParamsMerge[u])}`
40 | }
41 |
42 | scriptEle.id = radomName
43 | scriptEle.src = url
44 |
45 | // 执行callback回调
46 | window[radomName] = (d) => {
47 | // 清空执行
48 | window[radomName] = undefined
49 |
50 | // 清空script元素
51 | removeNode(document.getElementById(radomName))
52 |
53 | // 回传数据
54 | success(d)
55 | }
56 | // 错误回调
57 | scriptEle.onerror = error
58 |
59 | // 插入到页面head元素
60 | document.head.appendChild(scriptEle)
61 |
62 | // 移除节点
63 | function removeNode(node){
64 | let parent = node.parentNode
65 | if(parent && parent.type !== 11){
66 | parent.removeChild(node)
67 | }
68 | }
69 | }
70 |
71 |
72 | //测试
73 | jsonp({
74 | url: "https://floor.jd.com/user-v20/hotwords/get",
75 | data: {
76 | source: "pc-home",
77 | pin: "",
78 | uuid: 154512121,
79 | _: 1573462343824
80 | },
81 | success: function(data){
82 | console.log(data);
83 | },
84 | error: function(error){
85 | console.log(error);
86 | }
87 | })
88 |
89 | //测试
90 | jsonp({
91 | url: "https://floor.jd.com/user-v20/hotwords/g",
92 | data: {
93 | source: "pc-home",
94 | pin: "",
95 | uuid: 15451,
96 | _: 1573462343824
97 | },
98 | success: function(data){
99 | console.log(data);
100 | },
101 | error: function(error){
102 | console.log(error);
103 | }
104 | })
--------------------------------------------------------------------------------
/src/multi-page-webpack.md:
--------------------------------------------------------------------------------
1 | ### 本方案仅用于预研,无实际应用到项目中
2 | > 多页打包主要的两点
3 | 1. entry改为多入口
4 | 2. html-webpack-plugin 插件针对不同入口输出不同页面
5 |
6 | PS:当然还有那些css,js文件的拆分输入
7 |
8 | ### 配置固定多页应用
9 | > 本例中分为front和backend前后两个界面
10 |
11 | 1. 配置入口
12 | ```js
13 | // 改为多入口
14 | entry: {
15 | front: './src/project/front/main.js',
16 | backend: './src/project/backend/main.js',
17 | }
18 | ```
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/net-optimize.md:
--------------------------------------------------------------------------------
1 | ### 前言
2 | > http加载优化是前端性能优化中的重要一环,有效的优化可以缩短资源加载时间,提升用户体验,那么此处从chrome调试面板的timing切入了解
3 |
4 | ### network timing (资源加载时序)
5 |
6 | 如下图列出一个资源加载的时间线
7 |
8 |
9 |
10 | ### timing 解析
11 | + Queuing(排队)
12 |
13 | 如果一个请求排队,则表明请求被渲染引擎推迟,因为它被认为比关键资源(如脚本/样式)的优先级低。这经常发生在 images(图像) 上
14 |
15 | 请求排队的原因:
16 |
17 | 1. 这个请求被搁置,在等待一个即将被释放的不可用的TCP socket。
18 | 2. 这个请求被搁置,因为浏览器限制。在HTTP 1协议中,每个源上只能有6个TCP连接
19 | 3. 正在生成磁盘缓存条目(通常非常快)。
20 |
21 | + Stalled/Blocking (停止/阻塞)
22 |
23 | 发送请求之前等待的时间。它可能因为进入队列的任意原因而被阻塞。这个时间包括代理协商的时间。
24 |
25 | + Proxy Negotiation (代理协商)
26 |
27 | 与代理服务器连接协商花费的时间
28 |
29 | + DNS Lookup (DNS查找)
30 |
31 | 执行DNS查找所用的时间。 页面上的每个新域都需要完整的往返(roundtrip)才能进行DNS查找。
32 |
33 | + Initial Connection / Connecting (初始连接/连接)
34 |
35 | 建立连接所需的时间, 包括TCP握手/重试和协商SSL。
36 |
37 | + SSL
38 |
39 | 完成SSL握手所用的时间。
40 |
41 | + Request Sent / Sending (请求已发送/正在发送)
42 |
43 | 发出网络请求所花费的时间。 通常是几分之一毫秒。
44 |
45 | + Waiting (TTFB) (等待)
46 |
47 | 等待初始响应所花费的时间,也称为`Time To First Byte`(接收到第一个字节所花费的时间)。这个时间除了等待服务器传递响应所花费的时间之外,还捕获到服务器发送数据的延迟时间。
48 |
49 | + Content Download / Downloading (内容下载/下载)
50 |
51 | 接收响应数据所花费的时间,即从第一个字节开始,到下载完最后一个字节结束所花费的时间。
52 |
53 |
54 | ### 针对timing 产生问题优化
55 |
56 | + Queuing 等待时间过长
57 |
58 | 同域名下的请求数是否过多?发送的http请求数是否过多?
59 |
60 | + Stalled/Blocking 阻塞时间过长
61 |
62 | js性能太差,阻塞页面? 某个请求慢阻塞页面的加载?
63 |
64 |
65 |
66 | + Initial Connection / Connecting 花费时间长
67 |
68 |
69 | + Content Download 内容下载时间过长
70 |
71 | 考虑一些图片、样式资源文件体积过大,考虑优化体积大小
72 |
73 | [分析页面加载慢原因](https://www.jianshu.com/p/24b93b13e5a9)
74 |
--------------------------------------------------------------------------------
/src/promise.js:
--------------------------------------------------------------------------------
1 | /**
2 | * promise 核心处理then
3 | * 1.初始触发resolve或者reject函数,改变state,存储函数传参,调用延时队列
4 | * 2.外部调用then方法,存储执行函数队列,返回下一个Promise对象
5 | * 3.异步延时函数调用,处理当前状态下调用的处理函数 触发then的下一步Promise对象的resolve和reject函数调用
6 | */
7 |
8 |
9 | const PENDING = 'PENDING'
10 | const FULFILLED = 'FULFILLED'
11 | const REJECTED = 'REJECTED'
12 |
13 | class Promise {
14 | constructor(handle){
15 | if(!Promise._isFunc(handle)){
16 | throw new Error(`the Promise params must is function, but your params is ${Object.prototype.toString(handle)}`)
17 | return
18 | }
19 |
20 | // 记录状态机
21 | this.status = PENDING
22 | // 记录resolve/reject 传入值
23 | this.data = null
24 | // 状态为pending是置入的回调函数队列
25 | this.cbQueen = []
26 |
27 | // 首先调用传入promise函数
28 | try {
29 | handle(this.resolve.bind(this), this.reject.bind(this))
30 | } catch (err) {
31 | // 执行异常,直接切换失败状态
32 | this.reject.call(this, err)
33 | }
34 | }
35 |
36 | static _isFunc(fn){
37 | return typeof fn === 'function'
38 | }
39 |
40 | resolve(val){
41 | let _this = this
42 | if(val && (typeof val === 'object' || typeof val === 'function')){
43 | //返回的是个promise对象
44 | _this.then.call(val, _this.resolve.bind(this), _this.reject.bind(this))
45 | return
46 | }
47 | _this.data = val
48 | _this.status = FULFILLED
49 | _this._deplayRun()
50 | }
51 |
52 | reject(val){
53 | let _this = this
54 | _this.data = val
55 | _this.status = REJECTED
56 | _this._deplayRun()
57 | }
58 |
59 | then(resolve, reject){
60 | let _this = this
61 | return new Promise((nextResolve, nextReject) => {
62 | let cb = {
63 | resolve: resolve ? resolve : (val) => { return val},
64 | reject: reject ? reject : (val) => { return val },
65 | nextResolve,
66 | nextReject
67 | }
68 | _this.cbQueen.push(cb)
69 | })
70 | }
71 |
72 | // 延时:避免promise内部同步代码
73 | _deplayRun(){
74 | let _this = this
75 | setTimeout(() => {
76 | _this.cbQueen.forEach(cb => {
77 | _this._handleCbQueen(cb)
78 | })
79 | })
80 | }
81 |
82 | _handleCbQueen(cb){
83 | let
84 | _this = this,
85 | data = _this.data,
86 | status = _this.status;
87 | if(status === PENDING){
88 | return;
89 | }
90 | let curFn = status === FULFILLED ? cb.resolve : cb.reject
91 | let nextFn = status === FULFILLED ? cb.nextResolve : cb.nextReject
92 | if(!curFn){
93 | //then函数未传递任何东西,直接执行then下一步
94 | nextFn()
95 | return
96 | }
97 | try {
98 | let rt = curFn(data)
99 | cb.nextResolve(rt)
100 | } catch (error) {
101 | cb.nextReject(error)
102 | }
103 | }
104 |
105 | catch(reject){
106 | return this.then(null, reject)
107 | }
108 | }
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/src/react-update-log.md:
--------------------------------------------------------------------------------
1 | ### 为什么要升级?
2 | 16版React不仅体积缩小了30%,还新增了碎片(fragments)、错误边界、portals、支持自定义 DOM 属性、Hooks等一些功能强大的新特性啦啦啦
3 | 然鹅公司主业务线由于开展比较早,每当开发新功能却用不上新的强大特性,让人很不服气,这不为了开上新的React的车,组内讨论,决定利用一段时间把主业务线React和架构同时来次大的升级调整
4 |
5 | ### 想法是好的,路是坎坷的,踩坑之路不断~~~
6 | 升级react、react-dom版本连带产生的问题,落后版本库升级,打包工具升级,解决升级后的打包错误
7 |
8 | + #### webpack2 升级到 webpack4
9 | 1. 增加webpack4 mode development/production 配置项
10 |
11 | 2. loaders 改 rules
12 |
13 | 3. webpack.dll.js
14 | ```js
15 | - new webpack.optimize.OccurenceOrderPlugin()
16 | + new webpack.optimize.OccurrenceOrderPlugin()
17 |
18 | - new webpack.optimize.UglifyJsPlugin()
19 |
20 | + optimization: {
21 | minimizer: [
22 | new UglifyJsPlugin({
23 | uglifyOptions: {
24 | compress: false
25 | }
26 | })
27 | ]
28 | }
29 |
30 | ```
31 | 4. CSS文件提取插件
32 | mini-css-extract-plugin 替代 extract-text-webpack-plugin
33 |
34 | 5. autoprefixer-loader废弃
35 | 改为 postcss-loader + autoprefxer
36 | ```js
37 | // 根目录新增 postcss.config.js
38 | module.exports= {
39 | plugins: [
40 | require('autoprefixer')({
41 | 'browsers': [
42 | 'defaults',
43 | 'defaults',
44 | 'last 2 versions',
45 | '> 1%',
46 | 'iOS 7',
47 | 'last 3 iOS versions'
48 | ]
49 | })
50 | ]
51 | }
52 |
53 | // css loader 增加postcss-loader
54 | ```
55 |
56 | 6. 配置更新
57 | ```js
58 | - resolve: {
59 | root: [
60 | path.resolve('node_modules')
61 | ]
62 | }
63 |
64 | +
65 | resolve: {
66 | modules: [
67 | path.resolve('node_modules')
68 | ]
69 | }
70 | ```
71 |
72 | + ### babel工具 升级
73 |
74 | babel-loader v6 》 v8
75 |
76 | 1. 安装[@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env)插件
77 | > 去掉旧有babel-preset-es2015的babel-preset-esxxxx需要对es6、es7、es8等需要不停安装插件方案
78 |
79 | 2. babel-loader版本搭配错误引发的打包错误
80 | babel-loader@^8 搭配 @babel/x babel7版本工具库
81 | babel-loader@^7及以下 搭配 babel-x babel7版本之前工具库
82 |
83 | 3. 安装 @babel/plugin babel 7以上版本插件
84 | 安装[@babel/plugin-transform-destructuring](https://babeljs.io/docs/en/babel-plugin-transform-destructuring)插件
85 | > 解决 {...this.props} es6解构赋值语法转换
86 |
87 | 最后改后的.babelrc 配置文件
88 | ```js
89 | {
90 | "presets": [
91 | "@babel/preset-env",
92 | "@babel/preset-react"
93 | ],
94 | "plugins": [
95 | "react-hot-loader/babel",
96 | "@babel/plugin-proposal-class-properties",
97 | "@babel/plugin-transform-destructuring",
98 | "@babel/plugin-transform-modules-commonjs"
99 | ]
100 | }
101 |
102 | ```
103 |
104 | 4. babel打包插件报错
105 | ```js
106 | Uncaught TypeError: Cannot assign to read only property 'exports' of object '#