today

today 查看完整档案

西安编辑西安科技大学  |  GE 编辑某外包公司  |  前端开发 编辑 www.cnblogs.com/dzlixu/ 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

today 回答了问题 · 2月23日

vite+react+ts+eslint

eslint 有配置文件叫eslintrc.js ,在用webpack 的项目中一般也有这个文件吧

关注 2 回答 1

today 关注了标签 · 2月21日

deno

A secure TypeScript runtime on V8.

关注 40

today 回答了问题 · 2月3日

Rust开发移动端app?

为什么要难为自己呢, swift,java, js-react-native , dart-flutter都有成熟的工具链,开发app不仅仅是功能代码,开发工具也是一个很重要的因素。

关注 2 回答 1

today 赞了回答 · 1月12日

vue 精度丢失如何处理啊

首先这跟vue没有关系,具体原因可以看这个js精度丢失
实际业务中设计到精确计算的,建议使用bignumber.js

关注 2 回答 2

today 回答了问题 · 1月12日

Angular 11+自定义指令中调用scroll无效

换个生命周期函数呢,操作dom的操作,不知道在ngAfterViewInit中是否有效

关注 2 回答 1

today 关注了问题 · 1月12日

解决ng-alain内的core.module.ts里的providers内的参数怎么取值

core.module.ts内定义 host

import { NgModule, Optional, SkipSelf } from '@angular/core';
import { throwIfAlreadyLoaded } from './module-import-guard';


@NgModule({
  providers: [
    {
      provide: 'host',
      useValue: 'http://localhost/'
    }
  ]
})
export class CoreModule {
  constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
    throwIfAlreadyLoaded(parentModule, 'CoreModule');
  }
}

在routes/passport/login/login.component.ts内如何使用?

关注 2 回答 1

today 回答了问题 · 1月12日

解决ng-alain内的core.module.ts里的providers内的参数怎么取值

login组件所在的module引入CoreMoulelogin, 然后在login组件中注入 host

  constructor(@Inject('host') private host){
  }

示例

关注 2 回答 1

today 收藏了文章 · 1月11日

面试官:webpack原理都不会?

image

引言

前一段时间我把webpack源码大概读了一遍,webpack4.x版本后,其源码已经比较庞大,对各种开发场景进行了高度抽象,阅读成本也愈发昂贵。

过度分析源码对于大家并没有太大的帮助。本文主要是想通过分析webpack的构建流程以及实现一个简单的webpack来让大家对webpack的内部原理有一个大概的了解。(保证能看懂,不懂你打我 🙈)
image

webpack 构建流程分析

首先,无须多言,上图~
image
webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:首先会从配置文件和 Shell 语句中读取与合并参数,并初始化需要使用的插件和配置插件等执行环境所需要的参数;初始化完成后会调用Compilerrun来真正启动webpack编译构建过程,webpack的构建流程包括compilemakebuildsealemit阶段,执行完这些阶段就完成了构建过程。

初始化

entry-options 启动

从配置文件和 Shell 语句中读取与合并参数,得出最终的参数。

run 实例化

compiler:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译

编译构建

entry 确定入口

根据配置中的 entry 找出所有的入口文件

make 编译模块

从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理

build module 完成模块编译

经过上面一步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系

seal 输出资源

根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会

emit 输出完成

在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

分析完构建流程,下面让我们自己动手实现一个简易的webpack吧~
image

实现一个简易的 webpack

准备工作

目录结构

我们先来初始化一个项目,结构如下:

|-- forestpack
    |-- dist
    |   |-- bundle.js
    |   |-- index.html
    |-- lib
    |   |-- compiler.js
    |   |-- index.js
    |   |-- parser.js
    |   |-- test.js
    |-- src
    |   |-- greeting.js
    |   |-- index.js
    |-- forstpack.config.js
    |-- package.json

这里我先解释下每个文件/文件夹对应的含义:

  • dist:打包目录
  • lib:核心文件,主要包括compilerparser

    • compiler.js:编译相关。Compiler为一个类, 并且有run方法去开启编译,还有构建modulebuildModule)和输出文件(emitFiles
    • parser.js:解析相关。包含解析ASTgetAST)、收集依赖(getDependencies)、转换(es6转es5
    • index.js:实例化Compiler类,并将配置参数(对应forstpack.config.js)传入
    • test.js:测试文件,用于测试方法函数打console使用
  • src:源代码。也就对应我们的业务代码
  • forstpack.config.js: 配置文件。类似webpack.config.js
  • package.json:这个就不用我多说了~~~(什么,你不知道??)

先完成“造轮子”前 30%的代码

项目搞起来了,但似乎还少点东西~~
image

对了!基础的文件我们需要先完善下:forstpack.config.jssrc

首先是forstpack.config.js

const path = require("path");

module.exports = {
  entry: path.join(__dirname, "./src/index.js"),
  output: {
    path: path.join(__dirname, "./dist"),
    filename: "bundle.js",
  },
};

内容很简单,定义一下入口、出口(你这也太简单了吧!!别急,慢慢来嘛)

其次是src,这里在src目录下定义了两个文件:

  • greeting.js
// greeting.js
export function greeting(name) {
  return "你好" + name;
}
  • index.js
import { greeting } from "./greeting.js";

document.write(greeting("森林"));

ok,到这里我们已经把需要准备的工作都完成了。(问:为什么这么基础?答:当然要基础了,我们的核心是“造轮子”!!)
image

梳理下逻辑

短暂的停留一下,我们梳理下逻辑:

Q: 我们要做什么?

A: 做一个比webpack更强的super webpack(不好意思,失态了,一不小心说出了我的心声)。还是低调点(防止一会被疯狂打脸)
image

Q: 怎么去做?

A: 看下文(23333)

Q: 整个的流程是什么?

A: 哎嘿,大概流程就是:

  • 读取入口文件
  • 分析入口文件,递归的去读取模块所依赖的文件内容,生成AST语法树。
  • 根据AST语法树,生成浏览器能够运行的代码

正式开工

compile.js 编写

const path = require("path");
const fs = require("fs");

module.exports = class Compiler {
  // 接收通过lib/index.js new Compiler(options).run()传入的参数,对应`forestpack.config.js`的配置
  constructor(options) {
    const { entry, output } = options;
    this.entry = entry;
    this.output = output;
    this.modules = [];
  }
  // 开启编译
  run() {}
  // 构建模块相关
  buildModule(filename, isEntry) {
    // filename: 文件名称
    // isEntry: 是否是入口文件
  }
  // 输出文件
  emitFiles() {}
};

compile.js主要做了几个事情:

  • 接收forestpack.config.js配置参数,并初始化entryoutput
  • 开启编译run方法。处理构建模块、收集依赖、输出文件等。
  • buildModule方法。主要用于构建模块(被run方法调用)
  • emitFiles方法。输出文件(同样被run方法调用)

到这里,compiler.js的大致结构已经出来了,但是得到模块的源码后, 需要去解析,替换源码和获取模块的依赖项, 也就对应我们下面需要完善的parser.js

parser.js 编写

const fs = require("fs");
// const babylon = require("babylon");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const { transformFromAst } = require("babel-core");
module.exports = {
  // 解析我们的代码生成AST抽象语法树
  getAST: (path) => {
    const source = fs.readFileSync(path, "utf-8");

    return parser.parse(source, {
      sourceType: "module", //表示我们要解析的是ES模块
    });
  },
  // 对AST节点进行递归遍历
  getDependencies: (ast) => {
    const dependencies = [];
    traverse(ast, {
      ImportDeclaration: ({ node }) => {
        dependencies.push(node.source.value);
      },
    });
    return dependencies;
  },
  // 将获得的ES6的AST转化成ES5
  transform: (ast) => {
    const { code } = transformFromAst(ast, null, {
      presets: ["env"],
    });
    return code;
  },
};

看完这代码是不是有点懵(说好的保证让看懂的 😤)

别着急,你听我辩解!!😷
image

这里要先着重说下用到的几个babel包:

  • @babel/parser:用于将源码生成AST
  • @babel/traverse:对AST节点进行递归遍历
  • babel-core/@babel/preset-env:将获得的ES6AST转化成ES5

parser.js中主要就三个方法:

  • getAST: 将获取到的模块内容 解析成AST语法树
  • getDependencies:遍历AST,将用到的依赖收集起来
  • transform:把获得的ES6AST转化成ES5

完善 compiler.js

在上面我们已经将compiler.js中会用到的函数占好位置,下面我们需要完善一下compiler.js,当然会用到parser.js中的一些方法(废话,不然我上面干嘛要先把parser.js写完~~)
image

直接上代码:

const { getAST, getDependencies, transform } = require("./parser");
const path = require("path");
const fs = require("fs");

module.exports = class Compiler {
  constructor(options) {
    const { entry, output } = options;
    this.entry = entry;
    this.output = output;
    this.modules = [];
  }
  // 开启编译
  run() {
    const entryModule = this.buildModule(this.entry, true);
    this.modules.push(entryModule);
    this.modules.map((_module) => {
      _module.dependencies.map((dependency) => {
        this.modules.push(this.buildModule(dependency));
      });
    });
    // console.log(this.modules);
    this.emitFiles();
  }
  // 构建模块相关
  buildModule(filename, isEntry) {
    let ast;
    if (isEntry) {
      ast = getAST(filename);
    } else {
      const absolutePath = path.join(process.cwd(), "./src", filename);
      ast = getAST(absolutePath);
    }

    return {
      filename, // 文件名称
      dependencies: getDependencies(ast), // 依赖列表
      transformCode: transform(ast), // 转化后的代码
    };
  }
  // 输出文件
  emitFiles() {
    const outputPath = path.join(this.output.path, this.output.filename);
    let modules = "";
    this.modules.map((_module) => {
      modules += `'${_module.filename}' : function(require, module, exports) {${_module.transformCode}},`;
    });

    const bundle = `
        (function(modules) {
          function require(fileName) {
            const fn = modules[fileName];
            const module = { exports:{}};
            fn(require, module, module.exports)
            return module.exports
          }
          require('${this.entry}')
        })({${modules}})
    `;

    fs.writeFileSync(outputPath, bundle, "utf-8");
  }
};

关于compiler.js的内部函数,上面我说过一遍,这里主要来看下emitFiles

emitFiles() {
    const outputPath = path.join(this.output.path, this.output.filename);
    let modules = "";
    this.modules.map((_module) => {
      modules += `'${_module.filename}' : function(require, module, exports) {${_module.transformCode}},`;
    });

    const bundle = `
        (function(modules) {
          function require(fileName) {
            const fn = modules[fileName];
            const module = { exports:{}};
            fn(require, module, module.exports)
            return module.exports
          }
          require('${this.entry}')
        })({${modules}})
    `;

    fs.writeFileSync(outputPath, bundle, "utf-8");
  }

这里的bundle一大坨,什么鬼?
image

我们先来了解下webpack的文件 📦 机制。下面一段代码是经过webpack打包精简过后的代码:

// dist/index.xxxx.js
(function(modules) {
  // 已经加载过的模块
  var installedModules = {};

  // 模块加载函数
  function __webpack_require__(moduleId) {
    if(installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    module.l = true;
    return module.exports;
  }
  __webpack_require__(0);
})([
/* 0 module */
(function(module, exports, __webpack_require__) {
  ...
}),
/* 1 module */
(function(module, exports, __webpack_require__) {
  ...
}),
/* n module */
(function(module, exports, __webpack_require__) {
  ...
})]);

简单分析下:

  • webpack 将所有模块(可以简单理解成文件)包裹于一个函数中,并传入默认参数,将所有模块放入一个数组中,取名为 modules,并通过数组的下标来作为 moduleId
  • modules 传入一个自执行函数中,自执行函数中包含一个 installedModules 已经加载过的模块和一个模块加载函数,最后加载入口模块并返回。
  • __webpack_require__ 模块加载,先判断 installedModules 是否已加载,加载过了就直接返回 exports 数据,没有加载过该模块就通过 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__) 执行模块并且将 module.exports 给返回。

(你上面说的这一坨又是什么鬼?我听不懂啊啊啊啊!!!)
image

那我换个说法吧:

  • 经过webpack打包出来的是一个匿名闭包函数(IIFE
  • modules是一个数组,每一项是一个模块初始化函数
  • __webpack_require__用来加载模块,返回module.exports
  • 通过WEBPACK_REQUIRE_METHOD(0)启动程序

(小声 bb:怎么样,这样听懂了吧)
image

lib/index.js 入口文件编写

到这里,就剩最后一步了(似乎见到了胜利的曙光)。在lib目录创建index.js

const Compiler = require("./compiler");
const options = require("../forestpack.config");

new Compiler(options).run();

这里逻辑就比较简单了:实例化Compiler类,并将配置参数(对应forstpack.config.js)传入。

运行node lib/index.js就会在dist目录下生成bundle.js文件。

(function (modules) {
  function require(fileName) {
    const fn = modules[fileName];
    const module = { exports: {} };
    fn(require, module, module.exports);
    return module.exports;
  }
  require("/Users/fengshuan/Desktop/workspace/forestpack/src/index.js");
})({
  "/Users/fengshuan/Desktop/workspace/forestpack/src/index.js": function (
    require,
    module,
    exports
  ) {
    "use strict";

    var _greeting = require("./greeting.js");

    document.write((0, _greeting.greeting)("森林"));
  },
  "./greeting.js": function (require, module, exports) {
    "use strict";

    Object.defineProperty(exports, "__esModule", {
      value: true,
    });
    exports.greeting = greeting;

    function greeting(name) {
      return "你好" + name;
    }
  },
});

和上面用webpack打包生成的js文件作下对比,是不是很相似呢?
image

来吧!展示

我们在dist目录下创建index.html文件,引入打包生成的bundle.js文件:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script data-original="./bundle.js"></script>
  </body>
</html>

此时打开浏览器:
image

如你所愿,得到了我们预期的结果~
image

总结

通过对webpack构建流程的分析以及实现了一个简易的forestpack,相信你对webpack的构建原理已经有了一个清晰的认知!(当然,这里的forestpackwebpack相比还很弱很弱,,,,)
image

查看原文

today 关注了问题 · 1月10日

关于rxjs使用的问题

rxjs虽然对着文档学着使用了一下,但感觉还是很难融会贯通。
我设计了这样一个简单的场景,一个异步的函数get,希望他同步得按顺序依次打印一个数组,并记录这个函数调用的次数。
不使用rxjs我是这样实现的:

const arr = [1, 2, 3, 4, 5]
let count = 0;

f(0)

function get(callback) {
    setTimeout(callback, 1000)
}

function f(idx) {
    if (arr.length === idx) {
        console.log(`共执行${count}次`)
        return
    }
    get(() => {
        console.log(arr[idx])
        ++count
        f(++idx)
    })
}

请问各位,在rxjs下,怎样优雅得实现这个功能?

关注 2 回答 0

today 赞了文章 · 1月9日

2020年终总结——前端入坑四年,今年实惨

写在前面

2020最后一天,我在KFC写总结。前几天就想写了,嗯,我是拖延症患者。😔 2020年,换工作、被裁员,体检一堆问题... 回顾我31年的人生,没有重点小学、没有重点初高中、专科学历、没有获奖经历、没有特殊技能、学习能力差、英语不好、拖延.... 加上现在算大龄程序员了吧,似乎我的人生拿了一把烂牌,可是我还是想赢啊。

我怎么走上了前端这条路?

11年毕业后,在工厂里打工,做技术员,跟IT无关。最好的4年光阴,感觉错付了,但是不后悔,每个人都有不同的经历。然后14年的时候,觉得这么下去我能看见我退休甚至死亡的时刻。所以我觉得我应该学点什么?好让我的人生稍微有点不同。 于是我买了大学接触过的单片机,想做硬件开发工程师哦。

(就是这个玩意)

所以要学汇编、学C(事实上我没学好,也能做个计时器啥的...) ,然后放弃了,这个不太好找工作。 然后学Java去了(因为上网找C语言资料的时候),学了一段时间后,大概到SSH框架吧,15年那时候来过一次杭州,面了一个做Java开发的,给了3500的工资(没去,那时候正在跟女朋友谈婚阶段了)。 后面16年初的时候接触了web前端(因为JSP要写HTML,查资料的时候web前端培训的广告挺火的),慎重考虑后,16年6月辞了工作(已经结婚了,老婆也支持),来杭州报了一个线下的培训班学习4个月,挺贵的,我还是觉得自己没学好。然后10月底的时候拿了个7K的offer,感觉月薪过万有希望了。 我就正式入坑了...^_^

四年的工作经历

第一份工作

16年10月份找工作,面了4家公司拿了2个offer,选择了这家做医疗检验的。工作了差不多三年。一开始去公司,改.net项目样式。后面前后端分离用JQ写项目。然后用Vue全家桶来做项目。这个阶段技术成长了很多。 也组建了一个8人的前端团队(特别感谢老板的信任,当初我买房还找他借了20多万周转,现在还欠着这个人情呢)。后面有想法做了一些工程化的工作,对于前端基建方面没有任何认知,团队管理方面也没有管理知识的支撑。

第二份工作

出于提升自己的目的来到这家公司的,结果跟我想的不太一样(虽然公司大了些,开发跟第一家公司也差不多),这一年成长太少了。好在学了算法。参加了早早聊,打开了视野。也在团队中也做了几次分享。

第三份工作

本打算好好工作的,奈何天不遂人愿,被裁了(为了融资扩招,然后裁员,找工作真的要擦亮眼啊)。三个多月的时间,简历被搞花了....

第四份工作(还在面试中......)

可能会选择去大一点的团队吧。

2020,我到底干了啥?

工作

7月份之前,主要还是写业务代码,不过从年初到7月,大概面试了有小百人吧,这个对我来说收获还是蛮大的。接触了从应届到工作10年工作经验、大小厂的各种前端大小朋友。最大的感觉就是:

  • 工作年限不等于工作能力
  • 平台牛不代表你牛
  • 基础任何阶段都不能拉下

7月份换工作的时候也不是我本意,本来是申请回到总部办公的。由于其他原因变成了离职。7月底的时候来了新公司(当时Scott建议我去丁香园的,考虑距离问题选择了这家),然后11月被裁了(为了融资扩招,可怜我试用期还没过呢)。内心委屈,毕竟是想好好上班的...
11月到现在,面试了9家大型互联网公司,暂时没有拿到offer,但是很有信心,我一定可以。 这个月面试的感触:

  • 基础知识要夯实
  • 要有前端广阔的视野,后端要有一定的认知
  • 要有某一方向的技术深度
  • 性能优化必问
  • 算法一定要刷

学习

这段时间刷了很多大厂面试题,做了一个整理,也是为了方便自己复习,有空就可以拿出来刷一刷。

HTML 和 CSS
  • 你如何理解 HTML 结构的语义化?
  • 谈谈以前端角度出发做好 SEO 需要考虑什么?
  • 有哪项方式可以对一个 DOM 设置它的 CSS 样式?
  • CSS 都有哪些选择器?
  • CSS 中可以通过哪些属性定义,使得一个 DOM 元素不显示在浏览器可视范围内?
  • 超链接访问过后 hover 样式就不出现的问题是什么?如何解决?
  • 什么是 Css Hack?ie6,7,8 的 hack 分别是什么?
  • 请用 Css 写一个简单的幻灯片效果页面
  • 行内元素和块级元素的具体区别是什么?行内元素的padding和margin可设置吗?
  • 什么是外边距重叠?重叠的结果是什么

......

JS基础
  • call 和 apply 的区别
  • b 继承 a 的方法
  • JavaScript this 指针、闭包、作用域
  • 事件委托是什么
  • 闭包是什么,有什么特性,对页面有什么影响
  • 如何阻止事件冒泡和默认事件
  • 添加 删除 替换 插入到某个接点的方法
  • javascript 的本地对象,内置对象和宿主对象
  • document load 和 document ready 的区别
  • “==”和“===”的不同
  • javascript 的同源策略
  • 编写一个数组去重的方法

Ajax
  • Ajax 是什么? 如何创建一个 Ajax?
  • 同步和异步的区别?
  • 如何解决跨域问题?
  • 页面编码和被请求的资源编码如果不一致如何处理?
  • 简述 ajax 的过程。
  • 阐述一下异步加载。
  • 请解释一下 JavaScript 的同源策略。
  • GET 和 POST 的区别,何时使用 POST?
  • Ajax 的最大的特点是什么。
  • ajax 请求的时候 get 和 post 方式的区别
  • 解释 jsonp 的原理,以及为什么不是真正的 ajax
  • http 常见的状态码有那些?分别代表是什么意思?
  • 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?

......

JS高级
  • JQuery 一个对象可以同时绑定多个事件,这是如何实现的?
  • 知道什么是 webkit 么? 知道怎么用浏览器的各种工具来调试和 debug 代码么?
  • 如何测试前端代码么? 知道 BDD, TDD, Unit Test 么? 知道怎么测试你的前端工程么(mocha, sinon, jasmin, qUnit..)
  • 前端 templating(Mustache, underscore, handlebars)是干嘛的, 怎么用?
  • 简述一下 Handlebars 的基本用法?
  • 简述一下 Handlerbars 的对模板的基本处理流程, 如何编译的?如何缓存的?
  • 用 js 实现千位分隔符?
  • 检测浏览器版本版本有哪些方式?
  • 我们给一个 dom 同时绑定两个点击事件,一个用捕获,一个用冒泡,你来说下会执
  • 行几次事件,然后会先执行冒泡还是捕获

......

Vue
  • vuex 有哪几种属性?
  • vuex 的 State 特性是?
  • vuex 的 Getter 特性是?
  • vuex 的 Mutation 特性是?
  • Vue.js 中 ajax 请求代码应该写在组件的 methods 中还是 vuex 的 actions 中?
  • 什么是 MVVM?
  • mvvm 和 mvc 区别?它和其它框架(jquery)的区别是什么?哪些场景适合?
  • vue 的优点是什么?
  • 组件之间的传值?
  • vue.cli 中怎样使用自定义的组件?有遇到过哪些问题吗?
  • vue 如何实现按需加载配合 webpack 设置
  • Vue 中引入组件的步骤?
  • 指令 v-el 的作用是什么?
  • 在 Vue 中使用插件的步骤
  • vue 生命周期的作用是什么
  • vue 生命周期总共有几个阶段
  • 第一次页面加载会触发哪几个钩子
  • DOM 渲染在 哪个周期中就已经完成
  • 简单描述每个周期具体适合哪些场

浏览器
  • 跨标签页通讯
  • 浏览器架构
  • 浏览器下事件循环(Event Loop)
  • 从输入 url 到展示的过程
  • 重绘与回流
  • 存储
  • Web Worker
  • V8 垃圾回收机制
  • 内存泄露
  • reflow(回流)和 repaint(重绘)优化
  • 如何减少重绘和回流?
  • 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
  • localStorage 与 sessionStorage 与 cookie 的区别总结
  • 浏览器如何阻止事件传播,阻止默认行为

生活

平平淡淡,两个姑娘越来越漂亮了 这个月没上班,接送大姑娘上下学、去培训班。现在的小孩子要学的可真多,我3岁多的时候应该在捏泥巴吧。 遗憾的是,因为疫情的原因没能带老婆孩子出去玩玩,明年补上吧。

2021,一定会更好的

许多事情要提上日程了,健身、英语、算法、理财、旅游、装修........想做的事情很多。

  • [ ] 多写一些总结,多复盘【每月输出2篇文档】
  • [ ] 算法【每日打卡,要弄懂】
  • [ ] 一次旅游 【但愿疫情彻底过去吧】
  • [ ] 读书【尽量每月1本吧】
  • [ ] 装修【房子交付要装修了】
  • [ ] 健身 【动起来,体检好几项都不行,得关注一下身体健康了】
  • [ ] 英语【背背单词】
  • [ ] 理财 【暂定吧】
  • [ ] ....

2021年,走的慢一点没关系,千万不能停!未来一定会更好的。

最后

前端工作四年多点了,好在没有放弃。虽然离优秀还很远,但贵在一直坚持。技术慢慢在提升、视野越来越开阔、薪水也比刚做前端时多了3倍多,我没法跟那些优秀的人比,我只能跟自己比,今天的我比昨天的我进步了一点就很开心了。 2021年,一定要读完的3本书《刻意练习》、《复盘》、《戒了吧,拖延症》。 愿自己越来越好。

查看原文

赞 21 收藏 7 评论 9

认证与成就

  • 获得 28 次点赞
  • 获得 16 枚徽章 获得 0 枚金徽章, 获得 3 枚银徽章, 获得 13 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-03-10
个人主页被 1.1k 人浏览