Giter Site home page Giter Site logo

frontend's Introduction

Hi there 👋

  • 🔭 I’m currently working on Rocket Software.
  • 🌱 I’m currently learning angular/ts/jhipster
  • 🤔 I’m enjoy current work.

llccing's GitHub stats

Top Langs

frontend's People

Contributors

dependabot[bot] avatar llccing avatar npmcdn-to-unpkg-bot avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

frontend's Issues

NodeJS FullStack Questions 2

下面是另一组 Node.js 全栈面试题:

一、JavaScript 深入理解

  1. 作用域和闭包
    • 解释 JavaScript 的作用域和作用域链。
    • 闭包在实际开发中的应用场景有哪些?
  2. 对象和类
    • 如何定义和继承 JavaScript 类?
    • 什么是 JavaScript 中的 getter 和 setter?
  3. 函数
    • 什么是高阶函数?
    • 请解释 call、apply 和 bind 方法的区别和使用场景。

二、Node.js 高级知识

  1. 异步编程模式
    • 什么是事件循环中的微任务和宏任务?
    • 如何处理 Node.js 中的异步错误?
  2. 流(Stream)
    • Node.js 中的 Stream 有哪些类型?
    • 如何使用 Stream 处理大文件?
  3. 集群(Cluster)
    • 什么是 Node.js 的集群模块?如何使用?

三、后端开发进阶

  1. 中间件高级用法
    • 如何编写自定义中间件?
    • Express 中间件的执行顺序是怎样的?
  2. 数据库事务
    • 如何在 SQL 数据库中实现事务处理?
    • MongoDB 中的事务如何使用?
  3. GraphQL
    • 什么是 GraphQL?它与 RESTful API 有何不同?
    • 如何在 Node.js 中实现 GraphQL 服务?

四、前端开发进阶

  1. 前端性能优化
    • 如何减少首屏加载时间?
    • 什么是代码分割(Code Splitting)?如何实现?
  2. 组件通信
    • React 中父子组件如何通信?
    • Vue 中的 Vuex 是什么?如何使用?
  3. 前端测试
    • 如何使用 React Testing Library 进行组件测试?
    • 什么是前端的自动化测试?

五、全栈开发综合

  1. WebSocket
    • 什么是 WebSocket?如何在 Node.js 中实现 WebSocket 服务器?
    • WebSocket 的应用场景有哪些?
  2. 服务端渲染(SSR)
    • 什么是服务端渲染?如何使用 Next.js 实现服务端渲染?
    • SSR 的优点和缺点是什么?
  3. OAuth2.0
    • 什么是 OAuth2.0?它的工作流程是怎样的?
    • 如何在 Node.js 应用中实现 OAuth2.0 身份验证?

六、性能和安全进阶

  1. 性能监控
    • 如何使用 Node.js 内置的性能监控工具?
    • 什么是 APM(应用性能管理)?有哪些常用工具?
  2. 缓存
    • 如何使用 Redis 缓存数据?
    • 什么是缓存穿透、缓存击穿和缓存雪崩?如何应对?
  3. 安全策略
    • 什么是安全头(Security Headers)?如何在 Express 中设置?
    • 如何防范 DDoS 攻击?

这些题目可以帮助进一步评估候选人在 Node.js 全栈开发方面的高级知识和实际应用能力。

IBM i introduction

an introduction from Wikipedia. https://zh.wikipedia.org/wiki/IBM_System_i

IBM i

  • IBM i 是个操作系统,它运行在 IBM Power System 机器上。
  • 1988年 6月21日,IBM i 被引入,那时名字视 AS/400
  • TIMI, Technology Independent Machine Interface, 用于描述 IBM i.
  • 以 Objects 的形式在 IBM i 上存储信息。
  • IBM i 使用 subsystem 子系统来隔离负载

Power

  • Power 是 IBM 开发的 reduced instruction set computer (RISC ) instruction set acchitecture (ISA)
  • RISC, 精简指令集计算,一种基于简化指令能够更快执行从而提高性能的CPU设计策略。
  • Logic partitions, LPARs 逻辑分组
  • 5250 模拟器能够连接到 IBM i.

Some feature for IBM i

  • Capacity on Demand (CoD) 按需容量,可在不影响用户操作的情况下轻松激活处理器和内存。
  • Asynchronous geopraphic mirror, 异步地理镜像,一种灾难恢复 (DR) 方案,可以让您有灾难恢复站点。
  • IBM Db2 Mirror for i, 为需要零恢复时间目标(ROT)的关键应用提供解决方案。Bb2 使用 Converged Ethernet放网络上的远程直接内存访问(RDMA)技术。

Object and File management

  • IBM i 操作系统和其他系统的一个不同点是 object 的概念。几乎所有在系统中的都是 object。任务执行在 object 上。
  • object 的类型决定了如何使用它,object 类型有 *PGM, *AUTL, *CLS, *CMD, *JOBQ, *USRPRF, *JRN 等。
  • Library,Library 包含 object,保存了 object 的名称,类型和位置。属于 Library 的object 不会连续存储在磁盘上,而是利用了 磁盘分区。
  • library 不能嵌套,即不能包含另一个library。
  • QSYS 包含系统上全部的 libraries,和 所有其他的安全信息,例如用户信息。
  • 多数的 IBM object 以 Q 开头;object 可能同名,但是同名的object,类型一定不同。
  • object 由两部分组成,description 和 contents

NodeJS FullStack Questions 1

Node.js 全栈面试题通常涵盖以下几个方面:

一、JavaScript 基础知识

  1. 数据类型和结构
  2. 异步编程
  3. DOM 操作

二、Node.js 基础知识

  1. 模块系统
  2. 事件驱动架构
  3. 文件系统操作
  4. HTTP 模块

三、后端开发

  1. Express 框架
  2. 数据库操作
    • 如何使用 MongoDB 连接并操作数据库? #58 (comment)
    • 如何使用 Sequelize 或 TypeORM 操作 SQL 数据库? #58 (comment)
  3. 身份验证和授权

四、前端开发

  1. React / Angular / Vue
    • 如何使用 Angular 创建组件?
    • Angular 中的状态管理如何实现?
    • Vue 的生命周期钩子有哪些?
  2. 前端路由
    • 如何使用 Angular Router 创建单页应用(SPA)?
  3. 状态管理
    • 什么是 Redux?如何使用?

五、全栈整合

  1. API 设计与调用
    • RESTful API 设计原则是什么?
    • 如何使用 Axios 或 Fetch 调用后端 API?
  2. DevOps
    • 如何使用 Docker 部署 Node.js 应用?
    • 持续集成/持续部署(CI/CD)流程如何实现?
  3. 测试
    • 如何使用 Jest 或 Mocha 进行单元测试?
    • 什么是端到端测试(E2E)?

六、性能优化和安全

  1. 性能优化
    • 如何优化 Node.js 应用的性能?
    • 什么是负载均衡?如何实现?
  2. 安全
    • 如何防止 SQL 注入攻击?
    • 如何防止跨站脚本攻击(XSS)和跨站请求伪造(CSRF)?

这些问题涵盖了全栈开发的主要方面,可以帮助面试官评估候选人在 Node.js 和全栈开发方面的知识和技能。

项目 配置文件说明

idcui项目说明

技术栈: Vue + iview

目录结构介绍

|-- build                            	// webpack配置文件
|-- config                           	// 项目打包路径
|-- dist                             	// 打包输出目录
|-- node_modules                     	// 项目依赖目录
|-- src                              	// 源码目录
|-- |-- assets                       	// 模块资源(由webapck进行处理)
|       |-- img                      
|       |-- json                     	// 接口模拟数据
|       |-- lib                      	// 插件
|       |-- style                    	// 公共样式  
|           |-- theme                	// 主题    
|   |-- components                   	// 组件
|       |-- Header.vue               	// 公共头部
|   |-- store                        	// vuex,全局状态
|		|-- views                   	   	// 主要路由页面
|       |-- index.vue                	// 首页
|   |-- App.vue                      	// 页面入口文件
|   |-- main.js                      	// 程序入口文件,加载各种公共组件
|-- static                           	// 纯静态资源(直接拷贝)
|-- .babelrc                         	// ES6语法编译配置
|-- .editorconfig                    	// 代码编写规格
|-- .eslintignore                    	// eslint忽略文件
|-- .eslinttrc.js                    	// eslint检查文件
|-- .gitignore                       	// 忽略的文件
|-- .postcssrc.js                    	// postcss配置文件
|-- index.html                       	// 入口html文件
|-- package.json                     	// 项目及工具的依赖配置文件
|-- README.md                        	// 说明

配置文件说明

|-- build                             // 构建文件
|   |-- build.js											//  
|   |-- check-versions.js							//
|   |-- dev-client.js  
|   |-- dev-server.js
|   |-- utils.js
|   |-- vue-loader.config.js
|   |-- webapck.base.conf.js
|   |-- webpack.dev.conf.js
|   |-- webpack.prod.conf.js
|   |-- webpack.test.conf.js
|-- config
|   |-- dev.env.js
|   |-- index.js
|   |-- prod.env.js
|-- package.json
|   |-- test.env.js

package.json

开发时我们常用的命令为 npm run devnpm run build
那么我们就从这两个命令入手。
这两行命令对应执行的是package.json文件中的script属性对应的命令。

"scripts": {
    "dev": "node build/dev-server.js",
    "start": "node build/dev-server.js",
    "build": "node build/build.js",
    "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
    "e2e": "node test/e2e/runner.js",
    "test": "npm run unit && npm run e2e",
    "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
  },

由此可见,在我们执行npm run dev/start实际上是node在执行build下的dev-server.js

dev-server.js

主要做的内容

  1. 检查node和npm版本
  2. 引入插件和配置
  3. 创建express服务器和webpack编译器
  4. 配置开发中间件(webpack-dev-middleware)和热重载中间件(wwebpack-hot-middleware)
  5. 挂载大力服务和中间件
  6. 配置静态资源
  7. 启动服务器并监听特定端口
  8. 自定打开浏览器,进入特定网址

说明:

express服务器提供静态文件服务,和一个http请求代理的中间件。前端开发需要请求后端api的话,可配置proxyTable来将相应的请求代理到专用api服务器。

详细:

// 检查版本
require('./check-versions')()

// 读取配置文件
var config = require('../config')
/**
* 如果不能判断当前环境为dev/production环境,则将环境设置为开发环境
* process对象是Node的一个全局对象,提供当前Node进程的信息。
* process.env返回一个对象,成员为当前Shell的环境变量,如process.env.HOME返回用户主目录
* NODE_ENV是自定义的环境变量,用来确定所处的开发阶段development/production/testing
**/
if (!process.env.NODE_ENV) {
  // 读取dev的config文件,将dev.env.NODE_ENV作为当前环境
  process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}

// 一个更好跨平台node-open,可以打开网站,文件、可执行文件
var opn = require('opn')
// nodejs的路径模块,用于路径处理
var path = require('path')
// 快速、包容、简约的nodejsweb框架,这里主要用于开发环境的调试
var express = require('express')
// 一个现代JavaScript应用程序的模块打包器
var webpack = require('webpack')
// Node.js的代理中间件,兼容Node.js服务器,轻松实现代理
var proxyMiddleware = require('http-proxy-middleware')
// 如果是测试或者生产环境,则使用webpack的生产环境配置,否则使用webpack的开发环境配置
var webpackConfig = (process.env.NODE_ENV === 'testing' || process.env.NODE_ENV === 'production')
  ? require('./webpack.prod.conf')
  : require('./webpack.dev.conf')

// 开发服务器对于入站流量监听的默认端口号,如果没有自定义端口,则使用开发配置的端口
var port = process.env.PORT || config.dev.port

// 自动打开浏览器,!!表示强制类型转换为Boolean,这里用来判断属性是否存在, !!null/!!undefined/!!""=>false
var autoOpenBrowser = !!config.dev.autoOpenBrowser

// 配置http代理到自定义的api后端
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable

// 使用express启动一个服务
var app = express()

// 启动webpack进行编译
var compiler = webpack(webpackConfig)

/**
* 启动webpack-dev-middleware,将编译的文件暂存到内存中
* ? webpack-dev-middleware:在开发环境中即时刷新,建议仅在开发中使用
**/
var devMiddleware = require('webpack-dev-middleware')(compiler, {
  // 打包文件将能够在浏览器的这个目录下找到
  publicPath: webpackConfig.output.publicPath,
  // 设置为true时,控制台(console)不会输出任何启动信息,并且webpack的错误、警告不可见
  quiet: true
})
/**
* 启动webpack-hot-middleware,热加载。
**/
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
  // 输出日志,默认是以console.log()的形式
  log: false,
  // 为了保持连接而向客户端发送心跳更新的间隔时间,通常为客户端超时时间的一半
  heartbeat: 2000
})

/*
* 当html-webpack-plugin模板改变时,强制页面重载
* html-webpack-plugin: 为了服务webpack的包而简化html文件创建的插件
**/
compiler.plugin('compilation', function (compilation) {
  // html-webpack-plugin-after-emit, 模板改变后的异步事件
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// 代理api请求,将proxyTable中的代理配置挂载到启动的express服务上
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  // 使用代理中间件
  app.use(proxyMiddleware(options.filter || context, options))
})


// 处理HTML5 history api的回退
// 使用 connect-history-api-fallback匹配资源,若不匹配则重新定向到指定地址
app.use(require('connect-history-api-fallback')())


// 将暂存在内存中的webpack编译后的文件挂载到express服务上
app.use(devMiddleware)

// 使热加载和状态保留生效
// 显示编译错误
// 将热加载挂载到express服务上
app.use(hotMiddleware)

// 拼接static文件夹的静态资源路径
// posix(Portable Operating System Interface)可移植操作系统接口
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
// 为静态资源提供响应服务
app.use(staticPath, express.static('./static'))

// 应用的地址信息
var uri = 'http://localhost:' + port

// Promise对象,异步操作,好处是将异步操作以同步操作的流程表达出来,避免层层嵌套的回调函数。
var _resolve
var readyPromise = new Promise(resolve => {
  _resolve = resolve
})

console.log('> Starting dev server...')
// 当打包文件生成,或者重新生成后,执行的回调函数
devMiddleware.waitUntilValid(() => {
  console.log('> Listening at ' + uri + '\n')
  // when env is testing, don't need open it
  // 当开发环境不是'testing'时,自动打开浏览器
  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
    opn(uri)
  }
  // 异步执行完成?
  _resolve()
})
// 启动express服务器并监听port端口
var server = app.listen(port)

module.exports = {
  ready: readyPromise,
  close: () => {
    server.close()
  }
}

webpack.base.conf.js

内容:

  1. 配置webpack编译入口,entry
  2. 配置webpack输出路径和命名规则
  3. 配置模块resolve规则
  4. 配置不同类型的模块的处理规则

说明:

这个配置中配置了js/vue/媒体/图片/字体等几类文件的处理规则,如果想处理其他文件可以在module.rules中配置

详细:

// Node.js path模块
var path = require('path')
// 自定义工具方法
var utils = require('./utils')
// 配置
var config = require('../config')
// vue-loader的配置项
var vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  // 编译入口
  entry: {
    app: './src/main.js'
  },
  // 输出出口
  output: {
    // 一个绝对路径,/dist
    path: config.build.assetsRoot,
    // 每个输出bundle的名称,输出到output.path指定的目录下
    filename: '[name].js',
    // 对于按需加载(on-demand-load)或加载外部资源(external resource)(如图片、文件等),output.publicPath是个重要选项,如果指定错误会出现404。
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  // 配置模块如何解析
  resolve: {
    // 自动解析确定的扩展,使用户引入时可以不带扩展
    extensions: ['.js', '.vue', '.json'],
    // 创建import或者require的别名,来确保引入模块变得简单
    alias: {
      // 在给定的件键后的末尾添加$,表示精准匹配
      // 所以 import Vue from 'vue/dist/vue.esm.js' = import Vue from 'vue'
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  // 配置项目中不同模块的处理方式
  module: {
    // 创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式,能够对模块应用loader,或者修改解析器(parser)。
    rules: [
      // 对src和test文件夹下的js/vue文件使用eslint-loader
      {
        // Rule.resource.test的简写,规则匹配
        test: /\.(js|vue)$/,
        // eslint是一个用来识别ECMAScript并且按照规则给出报告的代码检测工具,使用它可以避免低级错误和统一代码的风格。
        loader: 'eslint-loader',
        // 指定loader的种类(pre/inline/normal/post)(!,-!,!!)
        enforce: 'pre',
        // 在规则中,属性test/include/exclude/resource对resource匹配,当有多个条件时,都匹配。
        include: [resolve('src'), resolve('test')],
        // options可以为字符串或者对象,传递到loader中,为loader的选项
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      // 对vue文件使用vue-loader
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      // 对js文件使用babel-loader
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      // 对图片资源文件使用url-loader,options.name指明了输出的命名规则
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          // 图片当大小不超过limit设置的大小时,转为Data URL
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      // 媒体资源文件
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      // 字体资源文件
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}

webpack.dev.conf.js

内容:

  1. 将热重载的配置添加到entry chunks
  2. 合并基础的webpack配置
  3. 使用styleLoaders
  4. 配置source Maps
  5. 配置webpack插件

详细:

// 工具函数
var utils = require('./utils')
// webpack
var webpack = require('webpack')
// 配置文件
var config = require('../config')
// 一个可以合并数组和对象的插件
var merge = require('webpack-merge')
// webpack编译基本配置
var baseWebpackConfig = require('./webpack.base.conf')

// 一个用于生成HTML文件并自动注入依赖文件(link/script)的webpack插件
var HtmlWebpackPlugin = require('html-webpack-plugin')

// 错误提示插件
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')

// add hot-reload related code to entry chunks
// 给webpack入口文件配置热重载
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
  baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})

// 合并基础的webpack配置
module.exports = merge(baseWebpackConfig, {
  // 配置样式文件的处理规则
  module: {
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
  },

  // 配置source-map,在开发中使用cheap-module-eval-source-map更快
  devtool: '#cheap-module-eval-source-map',

  // 配置webpack插件
  plugins: [
    // 定义全局变量的插件,webpack打包时会对变量做替换。
    new webpack.DefinePlugin({
      'process.env': config.dev.env
    }),
    // 热替换插件,不要在生产环境中使用
    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
    new webpack.HotModuleReplacementPlugin(),
    // 在编译出错时,使用NoEmitOnErrorsPlugin来跳过输出阶段,这样可以确保输出资源不会包含错误
    new webpack.NoEmitOnErrorsPlugin(),
    // 一个生成HTML文件并自动注入依赖(script/link)的插件
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    // 有好的错误提示插件
    new FriendlyErrorsPlugin()
  ]
})

utils.js、vue-loader.conf.js

主要内容:

  • utils.js
    1. 配置静态资源路径
    2. 生成cssLoaders用于加载vue文件中的样式
    3. 生成styleLoaders用于加载不再vue文件中的单独存在的样式文件
  • vue-loader.cof.js
    1. 配置了css加载器

详细

vue-loader.conf.js

// 工具库
var utils = require('./utils')
// 配置文件
var config = require('../config')
// 判断是否为生产环境
var isProduction = process.env.NODE_ENV === 'production'

module.exports = {
  // css加载器,开发环境执行,utils.cssLoaders({sourceMap:false,extract: false}),正式环境执行utils.cssLoaders({sourceMap:true,extract: true})

  loaders: utils.cssLoaders({
    sourceMap: isProduction
      ? config.build.productionSourceMap
      : config.dev.cssSourceMap,
    extract: isProduction
  }),
  // 可以将路径直接赋给组件,不用提前require之后再传给组件。
  transformToRequire: {
    video: 'src',
    source: 'src',
    img: 'src',
    image: 'xlink:href'
  }
}

build.js

内容:

  1. loading动画
  2. 删除创建目标文件夹static
  3. webpack编译
  4. 输出信息

说明

删除目标文件夹之后再将webpack编译的内容输出到指定文件夹下。

详细

// 版本检查
require('./check-versions')()

// 改变shell环境变量为生产 "production"
process.env.NODE_ENV = 'production'

// 实现Node.js命令行的loading效果,显示各种状态的图标
var ora = require('ora')
// A `rm -rf` util for nodejs
var rm = require('rimraf')
// Node.js 路径模块
var path = require('path')
// 彩色命令行插件
var chalk = require('chalk')
// webpack
var webpack = require('webpack')
// 项目配置文件
var config = require('../config')
// webpack生产环境配置
var webpackConfig = require('./webpack.prod.conf')

var spinner = ora('building for production...')
spinner.start()
// 清空 /dist/static文件夹下的内容
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  webpack(webpackConfig, function (err, stats) {
    spinner.stop()
    if (err) throw err
    // process.stdout标准输出对象
    // process.stdout.write等同于console.log()
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      children: false,
      chunks: false,
      chunkModules: false
    }) + '\n\n')

    if (stats.hasErrors()) {
      console.log(chalk.red('  Build failed with errors.\n'))
      process.exit(1)
    }

    console.log(chalk.cyan('  Build complete.\n'))
    console.log(chalk.yellow(
      '  Tip: built files are meant to be served over an HTTP server.\n' +
      '  Opening index.html over file:// won\'t work.\n'
    ))
  })
})

webpack.prod.conf.js

内容:

  1. 配置webpack编译入口
  2. 配置webpack输出路径和命名规则
  3. 配置模块resolve规则
  4. 配置不同类型模块的处理规则

详细:

// Node.js模块
var path = require('path')
// 工具函数
var utils = require('./utils')
// webapck
var webpack = require('webpack')
// 配置
var config = require('../config')
// 用于合并对象、数组
var merge = require('webpack-merge')
// 基本的webpack编译配置
var baseWebpackConfig = require('./webpack.base.conf')
// 用于文件、文件夹复制
var CopyWebpackPlugin = require('copy-webpack-plugin')
// 生成HTML并将依赖自动注入文件中
var HtmlWebpackPlugin = require('html-webpack-plugin')
// 用于提取入口chunk中引用的css到独立的css文件中,以并行加载,提升速度。
var ExtractTextPlugin = require('extract-text-webpack-plugin')
// CSS优化,内部使用cssnano来优化css,cssnano内部调用postcss。可以分别了解下。
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

// 判断是测试环境是还是生产环境
var env = process.env.NODE_ENV === 'testing'
  ? require('../config/test.env')
  : config.build.env

// 合并webpack基础配置
var webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true
    })
  },
  // 配置source-map
  devtool: config.build.productionSourceMap ? '#source-map' : false,
  // 配置输出
  output: {
    // 编译输出目录
    path: config.build.assetsRoot,
    // 编译输出的文件名格式
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // 没有指定输出名的文件,输出的文件名格式
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  // 配置webpack插件
  plugins: [
    // 定义全局变量的插件,webpack打包时,会对变量做替换
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // 丑化压缩代码
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      },
      sourceMap: true
    }),
    // 提取组件中的CSS到单独的文件中
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css')
    }),

    // 压缩提取的CSS,将不同组件中重复的CSS去除
    new OptimizeCSSPlugin({
      cssProcessorOptions: {
        safe: true
      }
    }),
    // 为了解决缓存问题,使用正确的资源哈希码生成dist/index.html文件
    // 你可以通过编辑index.html文件自定义输出
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: process.env.NODE_ENV === 'testing'
        ? 'index.html'
        : config.build.index,
      template: 'index.html',
      // 自动注入js引用到index.html文件的body标签底部
      inject: true,
      minify: {
        // 移除注释
        removeComments: true,
        // 移除空格
        collapseWhitespace: true,
        // 尽可能删除属性的引号,删除可能有点问题
        removeAttributeQuotes: true
        // 更多配置项,可以在以下链接了解
        // https://github.com/kangax/html-minifier#options-quick-reference
      },

      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      // 尽可能多的使用 CommonsChunkPlugin 来工作 (说啥呢)
      chunksSortMode: 'dependency'
    }),

    // keep module.id stable when vender modules does not change
    // 当vender(第三方库)模块没有变化时,应该让module.id保持稳定
    new webpack.HashedModuleIdsPlugin(),

    // split vendor js into its own file
    // 拆分第三方库
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function (module, count) {
        // any required modules inside node_modules are extracted to vendor
        // 任何在node_modules中的模块将被提取到第三方库中
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),

    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    // 提取webpack运行时和模块清单提取到自己的文件中,防止当app打包文件更新时第三方库的hash也改变,减少编译时间
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      chunks: ['vendor']
    }),
    // 复制自定义静态文件
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

// 判断是否开启了Gzip,若开启则要引入compression插件
if (config.build.productionGzip) {
  var CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

// 在打包时分析生成js的库的构成情况,大小,方便我们做出调整。
if (config.build.bundleAnalyzerReport) {
  var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

webpack.test.conf.js

说明:

1.引入配置信息

详细:

// 测试单元的webpack编译配置

// 工具函数
var utils = require('./utils')
// webpack
var webpack = require('webpack')
// 合并对象、数组
var merge = require('webpack-merge')
// 基本webpack配置
var baseConfig = require('./webpack.base.conf')

// 合并配置
var webpackConfig = merge(baseConfig, {
  // use inline sourcemap for karma-sourcemap-loader
  // 使用sourceMap便于调试
  module: {
    rules: utils.styleLoaders()
  },
  devtool: '#inline-source-map',
  resolveLoader: {
    alias: {
      // necessary to to make lang="scss" work in test when using vue-loader's ?inject option
      // see discussion at https://github.com/vuejs/vue-loader/issues/724
      // 解决了一个设置lang="scss"的bug问题
      'scss-loader': 'sass-loader'
    }
  },
  plugins: [
    // 定义全局变量,webpack打包时会对变量做替换
    new webpack.DefinePlugin({
      'process.env': require('../config/test.env')
    })
  ]
})

// no need for app entry during tests
// 测试时将入口文件删除(用意?)
delete webpackConfig.entry

module.exports = webpackConfig

check-versions.js和dev-client.js

说明:

check-versions.js主要是对node和npm的版本检测
dev-client.js用来控制浏览器重新加载

详情:

check-versions.js

// 彩色命令行插件
var chalk = require('chalk')
// 语义化版本检查插件
var semver = require('semver')
// package.json文件
var packageConfig = require('../package.json')
// Unix命令行工具
var shell = require('shelljs')
// 开辟子进程执行cmd并返回结果
function exec (cmd) {
  return require('child_process').execSync(cmd).toString().trim()
}

// node和npm版本需求
var versionRequirements = [
  {
    name: 'node',
    // process.version 返回一个字符串,表示当前使用的node版本
    currentVersion: semver.clean(process.version),
    // 取出package.json中设定的node版本
    versionRequirement: packageConfig.engines.node
  }
]

// 判断系统的环境变量中是否有npm
if (shell.which('npm')) {
  versionRequirements.push({
    name: 'npm',
    currentVersion: exec('npm --version'),
    versionRequirement: packageConfig.engines.npm
  })
}

module.exports = function () {
  var warnings = []
  // 判断版本是否符合要求
  for (var i = 0; i < versionRequirements.length; i++) {
    var mod = versionRequirements[i]
    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
      warnings.push(mod.name + ': ' +
        chalk.red(mod.currentVersion) + ' should be ' +
        chalk.green(mod.versionRequirement)
      )
    }
  }

  // 输出警告
  if (warnings.length) {
    console.log('')
    console.log(chalk.yellow('To use this template, you must update following to modules:'))
    console.log()
    for (var i = 0; i < warnings.length; i++) {
      var warning = warnings[i]
      console.log('  ' + warning)
    }
    console.log()
    process.exit(1)
  }
}

dev-client.js

/* eslint-disable */
require('eventsource-polyfill')
// 调用插件的一个模块,传入参数(?)
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')

// 事件重写
hotClient.subscribe(function (event) {
  if (event.action === 'reload') {
    window.location.reload()
  }
})

config文件分析

config文件是项目项目相关配置信息,包含打包、开发设置

index.js

说明:

详细:

// 项目结构说明 http://vuejs-templates.github.io/webpack
var path = require('path')

module.exports = {
  // 构建时使用的配置
  build: {
    // 环境变量
    env: require('./prod.env'),
    // 获得index.html的绝对路径
    index: path.resolve(__dirname, '../dist/index.html'),
    // webpack编译输出的目标文件夹路径
    assetsRoot: path.resolve(__dirname, '../dist'),
    // webpack编译输出的(二级文件夹)静态资源文件夹
    assetsSubDirectory: 'static',
    // webpack编译输出的发布路径
    assetsPublicPath: '/',
    // 是否使用sourceMap
    productionSourceMap: true,
    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    // 默认不开启Gzip模式
    productionGzip: false,
    // Gzip模式下,需要压缩的格式
    productionGzipExtensions: ['js', 'css'],
    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    // 是否开启分析js文件的功能
    bundleAnalyzerReport: process.env.npm_config_report
  },
  // 开发时使用的配置
  dev: {
    // 编译环境变量
    env: require('./dev.env'),
    // dev-server监听的端口
    port: 8080,
    // 是否自动打开浏览器
    autoOpenBrowser: true,
    // webpack编译输出的二级文件夹
    assetsSubDirectory: 'static',
    // webpack编译输出的发布路径
    assetsPublicPath: '/',
    // 代理设置
    proxyTable: {},
    // CSS Sourcemaps off by default because relative paths are "buggy"
    // with this option, according to the CSS-Loader README
    // (https://github.com/webpack/css-loader#sourcemaps)
    // In our experience, they generally work as expected,
    // just be aware of this issue when enabling this option.
    // 是否开启cssSourceMap
    cssSourceMap: false
  }
}

dev.env.js/prod.env.js/test.env.js

说明:

用来设置环境变量,开发时、构建时、测试时。

详细:

dev.env.js

// 用于合并对象、数组
var merge = require('webpack-merge')
// 生产环境的环境变量
var prodEnv = require('./prod.env')

// 用"development"覆盖"production"
module.exports = merge(prodEnv, {
  NODE_ENV: '"development"'
})

prod.env.js

// 定义环境变量
module.exports = {
  NODE_ENV: '"production"'
}

test.env.js

// 用于合并对象、数组
var merge = require('webpack-merge')
// 取得开发时设置的环境变量
var devEnv = require('./dev.env')

// 使用"testing"覆盖"development"
module.exports = merge(devEnv, {
  NODE_ENV: '"testing"'
})

参考文档

  1. assets和static目录的区别
  2. 项目结构
  3. 别名和-D -S
  4. what's process
  5. opn
  6. node-open
  7. node/path
  8. webpack-dev-middleware[publicPath]
  9. Promise对象
  10. Data URL
  11. webpack.DefinePlugin
  12. Vue.js的实用技巧
  13. module.id保持稳定
  14. webpack构建之hash缓存的利用
  15. JavsScript Source Map 详解
  16. vue-cli的webpack模板项目配置文件解析
  17. vue-cli#2.0 webpack 配置分析

Building a Digital-First Strategy With IBM i

Origin article,
https://techchannel.com/SMB/06/2023/building-digital-first-strategy-with-ibm-i

When it comes to modernizing IBM i environments, often the biggest challenges aren't technical. Many enterprises simply don't know where to begin.

当现代化 IBM i 环境时,通常最大的挑战不是技术。许多企业只是简单的不知道从哪里开始。

Puneet Kohli sees it all the time. As president of application modernization at Rocket Software, Kohli is charged with helping clients navigate the challenges and recognize that IBM i is well-suited to help them achieve their modernization goals.

Puneet Kohli 看到很多这种情况。作为 Rocket Software application modernization 的主席,Kohli 负责帮助客户克服挑战并认识到 IBM i 适合帮助客户完成现代化目标。

This process typically starts with simple reassurance. That's because when meeting with IT personnel, Kohli says he can count on being told one of two things from the onset. “Anytime we talk to a customer about modernization, the first thing we'll hear is: 'Modernization is going to be too hard,' or 'I don't know where to start.'”

这个过程通常以简单的保证开始。这是因为和 IT 代表开会时,Kohli 说他能够想起其中的一两件事情。“任何时候我们跟客户谈到现代化时,我们第一个听到的就是, ‘现代化太难了’, 或者 ‘我不知道从哪里开始’”。

In March, tech industry analyst IDC published a white paper, “How IBM i Can Play a Pivotal Role in Supporting a 'Digital First' Strategy.” In the paper, which is sponsored by Rocket Software, IDC notes that IBM i systems, workloads and applications are often limited within or excluded from organizational modernization initiatives due to the perception that they are too costly and complex to manage.

3月份,技术行业分析 IDC 发布了一篇白皮书,“IBM i 如何能够在支持 ‘数字优先’的策略中版本重要角色”. 在 Rocket Software 赞助的白皮书中,IDC 说 IBM i 系统, 负载和应用程序经常在组织现代化初始时被限制或者排除,由于成本太高或者管理复杂的固有成见。

IDC then highlights key modernization requirements and details the platform's unique attributes, concluding: “In today's increasingly digital world…there is no avoiding including IBM i in a digital-first strategy. Too many critical applications run on IBM i for thousands of companies worldwide, and these applications require a platform that is included in continuous modernization in order to deliver new capabilities and services...IBM i is a solid, modern and future-proof platform for enterprise-class computing that organizations should invest in and integrate with their evolving digital-first IT landscape.”

IDC 高亮了关键现代化需求和细节了平台的唯一属性,总结出:“在今天的增长的数字世界…不能避免包含 IBM i 数字化第一的策略中。世界范围内数以千计的公司将很多重要的应用运行在 IBM i 上,这些应用需要一个持续现代化的平台,来提供新的能力和服务…对于企业级计算来说 IBM i 是坚实的,现代的和未来可持续平台,组织应该投资并与他们不断发展的数字化 IT 环境集成。”

Breaking Down Misconceptions About Modernization

打破现代化的错误概念

In dealing with clients, Kohli sees his role as providing information and education. Sometimes that requires addressing the misconceptions of someone unfamiliar with the IBM i platform. In an anecdote that will surely strike a chord with long-time programmers and administrators, he recalls a meeting a new CIO whose company had inherited an IBM i environment in an acquisition.

处理客户业务时,Kohli 认为自己的角色是提供信息和培训。有时需要纠正某些人关于 IBM i 平台的不公平的误解。在一个足以引起长期程序员和管理员共鸣的轶事中,他回忆起与一位新的CIO会面的情景,这位CIO的公司在一次收购中继承了一个IBM i环境。

“He's questioning why they're using this application that's 20-plus years old. He wants to move off of it,” he says. “And I just say 'look, it supports Python.' His eyes start to widen. 'So you're telling me these kids (programmers) that I have back there, they can take it?' I said, 'Absolutely they can.'”

“他问为什么他们在使用20多年的应用。他想移除它。”他说,“我说 ‘看,它支持 Python’ 他的眼睛开始睁大。“所以你是在告诉我那些孩子(程序员)能够接手这个项目?”我说:“当然他们能。”

“IBM i, RPG—they're not used to those terms," Kohli adds. "So you just have to help them through it, using terms that they hear.”

“IBM i、RPG——他们不习惯这些术语," Kohli 补充道。"所以你只需要使用他们熟悉的术语来帮助他们理解。”

As noted in the white paper, this lack of familiarity with IBM i also plays out in another way, when organizations that rely upon disparate computing platforms confine their modernization initiatives to Windows or Linux.

正如白皮书中所指出的那样,这种对IBM i不熟悉的情况也会以另一种方式表现出来,即依赖不同计算平台的组织将其现代化倡议限制在Windows或Linux上。

“There are also applications running on Windows and Linux that were written many moons ago, but people are more comfortable talking about Windows and Linux, so they put IBM i on the back burner,” Kohli says. “However, it's the same conversation, and if you don't also bring in the people who are running your IBM i, those conversations about modernization become even harder and you fall even further behind. It's important to break down those silos.”

“也有一些很多年前写的应用运行在 Windows 和 Linux 上,但是当讨论 Windows 和 Linux 时他们更舒服,所以他们将 IBM i 作为备用计划,“ Kohli 说。”然而,同样的对话,如果你不包含运行应用在 IBM i 上的人,这个关于现代化的对话甚至能苦难,你会跑的更远。打破这个结构非常重要。“

Modernization Is Possible

现代化是可能的

The angst is understandable. It is daunting to think of an application written more than 30 years ago still performing business-critical functions, knowing that the original owners have most likely long since left the organization. Kohli acknowledges the process isn't simple, but there is always a logical starting point. For example, with one client, Rocket was about to demonstrate that their monolithic application was only using 20% of the quote path. Another client was reminded that APIs (hello, Python) can be used to connect legacy applications with modern systems.

烦恼是站不住脚的。考虑一个写完了30年的应用仍然运行着重要业务功能是令人望而生畏的,原始的开发人员很可能已经离开这个组织很久了。Kohli 知道这个过程不简单,但是总是有个理想的开始的点。例如,一个客户,Rocket 给他们演示,他们的单体应用仅使用了 20% 的能力。另一个客户被提醒,APIs 能够用来将遗产应用和现代系统连接。

"Many times, it just starts from letting us, or letting someone else who does this sort of thing, give you a view into the application,” he says. “Once you can see what the application is doing, you can approach it like any other software application. So, follow the same software development principles. You may just take the front end and build a Reactor connector to it. Go from there, do your two-week sprints, and you start seeing progress. So yes, it's hard, but it's not that hard. You can get there.”

“许多次,从让我们或者让其他做这些事情的人给你一个应用程序的分析开始,” 他说。“一旦你能看到应用程序在做什么,你能够像其他应用一样接近他。所以,遵循同样的软件开发原则。你可能仅用前端建一个反应堆连接它。从这里开始,做一个两周的冲刺,你开始看这个过程。是的,它很困难,但是也没那么难。你能成功的。”

Angular v13~v16 upgrade discovery

公司项目是2021年开始的项目,Angular 用的是v13,现在是2023年,两年的时间 Angular 已经到了v16,各个方面都有提升;我们详细看下。

Angular 13

相较于Angular 12,Angular 13有些新的特性,原文在此(https://www.infoq.cn/article/v0v0cyr4i9lgr6c7us00),总结如下:

  • 弃用 View Engine, 使用 Ivy 代替,Ivy 是 Angular 下一代编译和渲染引擎。带来的改变是更快的编译。
  • Angular Package Format (APF) 的更改
  • Component API 的更新,Angular 13之前创建动态组件需要大量样板代码,Angular 13 通过使用 ViewContainerRef.createComponent 实例化组件的机会,无需创建关联的工厂。
  • 结束支持 IE11。Angular 12 版本维护到2022年11月。
  • Angular CLI 改进

Angular 14

Angular 14 有如下变化,原文在此

  • Standalone Component; 实验性的 standalone component,简化 Angular 应用程序的编写,不用声明 NgModule 即可开始项目。(https://stackblitz.com/edit/angular-standalone?file=src%2Fmain.ts)不过这个特性暂时还是 developer preview,所以生产环境不建议使用。
  • Typed Angular Forms; 针对响应式表单,实现了严格类型化。如果从13升级到14,这里会有个问题,原来没有定义类型的地方会报错;Angular考虑到了这个问题,推出了 Untyped 类型,不过这是为了升级方便,还是建议使用 Type forms.
  • Streamlined best practices; Angular v14 带来了内置功能,使开发人员能够构建高质量的应用程序,从路由到代码编辑器。
  • Streamlined page title accessibility; 将title定义在路由声明处,更加便捷。同时也支持自定义逻辑。
const routes: Routes = [{
  path: 'home',
  component: HomeComponent
  title: 'My App - Home'  // <-- Page title
}, {
  path: 'about',
  component: AboutComponent,
  title: 'My App - About Me'  // <-- Page title
}];
  • Extended developer diagnositics; 增强开发者诊断程序中的错误。
  • Tree-shakeable error messages; production版本简化了error信息的输出。
  • Experimental ESM Application Builds; an experimental esbuild-based build system for ng build, which compiles pure ESM output. Update angular.json to try this feature.
"builder": "@angular-devkit/build-angular:browser"

"builder": "@angular-devkit/build-angular:browser-esbuild"

Angular 15

原文在此; Angular 官方发布原文

Released in early November 2022.

  • Directive component API; 指令组合,避免了指令继承,使用起来更加便捷。
  • Stable standalone APIs; 在 Angular 14 中是 developer preview, 到 Angular 15 变为稳定版本。但是如何在生产环境中使用还是个问题,如果有个example项目出来,会更加有参考意义。
  • Image optimization directive (NgOptimizedImage); 由于 next.js 和 nuxt.js 已经支持图片优化,Angular 也加入这个feature。具体包含
    • 智能懒加载,比如滚动到用户可视范围内再加载图片
    • 支持调整重要图片的加载优先级
    • 支持CDN配置
    • 提示内部错误和警告
      详细的文章在此
  • Better stack traces for faster debugging; 更好的调用栈日志展示,如下
    image
  • Angular CDK Listbox; 锦上添花的功能,增加了网站的可访问性。
  • Language service improvement; 自动导入组件引用,提高开发者使用体验。
  • Default format options for date pipe; 全局日期格式化

Angular 16

重头戏来了,值得期待的更新;

we’re continuing the Angular Momentum with the biggest release since the initial rollout of Angular;(我们将继续推动角动量,这是自Angular首次推出以来最大的更新;)

  • Signals; Rethinking Reactivity; 它完全向后兼容并与当前系统互操作
    • 通过减少变更检测期间的计算次数来提高运行时性能。一旦 Angular Signals 完全推出,我们期望使用 Signals 构建的应用程序的 INP 核心 Web Vital 指标有显著改善。
    • 为响应性带来更简单的思维模型,清晰地表明视图的依赖关系以及数据在应用程序中的流动。
    • 启用细粒度反应性,这在未来的版本中将允许我们仅检查受影响的组件中的更改。
    • 通过使用 Signals 通知框架 model 已更改,使未来版本中Zone.js成为可选项.
    • 可以在每个变更检测周期中无需重新计算地提供计算属性
    • 通过概述引入响应式输入计划,实现与 RxJS 更好的互操作性
      Signals 使用示例:
@Component({
  selector: 'my-app',
  standalone: true,
  template: `
    {{ fullName() }} <button (click)="setName('John')">Click</button>
  `,
})
export class App {
  firstName = signal('Jane');
  lastName = signal('Doe');
  fullName = computed(() => `${this.firstName()} ${this.lastName()}`);

  constructor() {
		// 这里 effect 的作用类似 Vue 的watch,如果 fullname 
		// (或者 fullname 间接依赖的 firstName 和 lastName) 变化就会调用 console
    effect(() => console.log('Name changed:', this.fullName()));
  }

  setName(newName: string) {
    this.firstName.set(newName);
  }
}

Signal and Observable can convert to each other:

// signal to observable
import { toObservable, signal } from '@angular/core/rxjs-interop';

@Component({...})
export class App {
  count = signal(0);
  count$ = toObservable(this.count);

  ngOnInit() {
    this.count$.subscribe(() => ...);
  }
}

// observable to signal
import {toSignal} from '@angular/core/rxjs-interop';

@Component({
  template: `
    <li *ngFor="let row of data()"> {{ row }} </li>
  `
})
export class App {
  dataService = inject(DataService);
  data = toSignal(this.dataService.data$, []);
}
  • takeUntilDestroyed; 自动完成订阅,不需要在 onDestory 中手动 unsubscribe.
  • Server-side rendering and hydration enhanced; hydration 是恢复服务器端渲染应用程序的过程。这包括重用服务器端渲染DOM结构、持久化应用程序状态、传输服务器已经检索过的应用程序数据以及其他过程。在新的完整应用程序非破坏性hydration中,Angular不再从头开始重新渲染应用程序。相反,框架在构建内部数据结构时查找现有的DOM节点,并将事件侦听器附加到这些节点。
    • 对于终端用户,页面上没有内容闪烁
    • 在某些情况下,Web Core Vitals 更好
    • 未来可持续的架构,可以启用细粒度代码加载,使用我们将在今年晚些时候发布的基元。目前,这在渐进式延迟路由hydration 中表现出来。
    • 易于集成
    • 对于执行手动DOM操作的组件,在模板中使用ngSkipHydration属性逐步采用hydration
  • Advancing developer tooling;
    • the esbuild-based build system, this enters developer preview! Early tests showed over 72% improvement in cold production builds.
    • ng server, using Vite for development server, and esbuild powers both your development and production builds.

Summary

Angular 16 是个大的版本,有很多新的特性推出,理想的方式是一步到位,直接从13到16以上,也许等团队有时间考虑升级这个事情的时候,Angular 17已经出来了,这样对于某些16新出的特性更加的稳定,那样更加有利于项目。期待升级,但是升级节奏要稳。

Summary of debbie-saugen-ibm-i-backup-recovery

origin link https://techchannel.com/SMB/05/2023/debbie-saugen-ibm-i-backup-recovery

This article covers several crucial aspects of data backup and recovery strategies, particularly in the context of IBM i systems. Here are five key points from the article:

Increasing Value and Vulnerability of Information: Debbie Saugen emphasizes that information has always been valuable, but the growth and dispersion of data across various storage mediums have increased its vulnerability. Modern threats like ransomware, data corruption, and internal threats like disgruntled employees have escalated the need for robust bakcup and recovery strategies.

信息价值和脆弱性的增加:Debbie Saugen 强调,信息一直很有价值,但数据在不同存储介质中的增长和分散使其更加脆弱。像勒索软件、数据腐败和内部威胁(如不满的员工)等现代威胁,加剧了对强大备份和恢复策略的需求。

Interdependence of Backup and Recovery Strategies: The article highlights the importance of not just focusing on data backup but also on recovery strategies. A good backup plan is ineffective without a solid recovery strategy. This includes considering various factors like recovery time, recovery point, and testing the plan across different systems and networks.

备份和恢复策略的相互依存:文章强调了不仅关注数据备份而且还要关注恢复策略的重要性。没有坚实的恢复策略,良好的备份计划是无效的。这包括考虑恢复时间、恢复点和在不同系统和网络中测试计划等因素。

Cost-Effective Solutions and Air Gap Approach: Saugen discusses the need to find backup and recovery solutions that fit a company's budget. This may include physical tape drives, cloud backups, or virtual tape libraries. An essential aspect of modern backup strategies is the inclusion of an "air gap" solution, which involves keeping a backup disconnected from the network to prevent issues like ransomware and data corruption.

成本效益的解决方案和空中隙方法:Saugen 讨论了找到符合公司预算的备份和恢复解决方案的必要性。这可能包括物理磁带驱动器、云备份或虚拟磁带库。现代备份策略的一个重要方面是包含“空中隙”解决方案,即保持备份与网络断开连接,以防止勒索软件和数据腐败等问题。

Role of Various Stakeholders: Backup and recovery processes involve multiple stakeholders, including security personnel, admins, and business owners. Conducting a business impact analysis to assess the cost of potential downtime is crucial. The strategy needs to be tailored to each company's unique needs, considering factors like critical data that may not be included in current backup routines.

各种利益相关者的作用:备份和恢复过程涉及多个利益相关者,包括安全人员、管理员和业务所有者。进行业务影响分析以评估潜在停机时间的成本是至关重要的。策略需要针对每个公司的独特需求进行定制,考虑到当前备份例程中可能未包括的关键数据等因素。

Comprehensive Backup/Recovery Strategy Development: The article urges IBM i installations to develop and execute a comprehensive backup and recovery strategy. This strategy should address the growing complexity and threats faced in today's digital environment, ensuring the safety and recoverability of valuable data.

全面备份/恢复策略的开发:文章敦促 IBM i 安装开发和执行全面的备份和恢复策略。这个策略应该解决当今数字环境中面临的日益复杂和威胁,确保宝贵数据的安全和可恢复性。

巧妙的实现

入口

这是一个手机端组件库的演示项目,其中他的组件文档的实现方式非常巧妙。使用 markdown-it 一款markdown的解析器,所有的文档通过zh-cn的语言包管理,如果要做国际化,切换markdown文件的引用路径就可以,非常之方便。

近期我们的一期项目刚刚结束,leader让大家写项目说明文档、还有总结,PM计划是用markdown来写,当时就想如果用web项目来展示,不知如何解析markdown,markdown-it 的发现,正巧能解决我的问题。

链接

NodeJS FullStack Questions 3

下面是另一组更深入的 Node.js 全栈面试题:

一、JavaScript 进阶

  1. 异步编程进阶
    • 什么是事件循环的运行机制?可以用伪代码解释吗?
    • Promise.all、Promise.race、Promise.any 的区别是什么?
  2. 内存管理
    • JavaScript 是如何进行垃圾回收的?
    • 什么是内存泄漏?如何避免?
  3. 模块化
    • 什么是 ES 模块(ESM)和 CommonJS 模块?它们的区别是什么?
    • 如何使用动态导入(Dynamic Import)?

二、Node.js 深度理解

  1. 进程和线程
    • Node.js 是单线程的,但它是如何处理并发的?
    • 如何在 Node.js 中创建子进程?
  2. 进程间通信(IPC)
    • Node.js 中的进程间通信有哪些方式?
    • 如何使用 message 事件实现父子进程间的通信?
  3. 调试
    • 如何使用 Node.js 内置调试器进行调试?
    • 如何在生产环境中进行性能调优和调试?

三、后端开发高级

  1. 高级路由处理
    • 如何在 Express 中实现动态路由?
    • 什么是路由参数?如何使用?
  2. 高级数据库操作
    • 如何在 Node.js 中实现数据库的多表查询?
    • MongoDB 聚合(Aggregation)操作如何使用?
  3. 消息队列
    • 什么是消息队列?有哪些常见的消息队列系统?
    • 如何在 Node.js 中使用 RabbitMQ 或 Kafka?

四、前端开发高级

  1. 虚拟 DOM
    • 什么是虚拟 DOM?它的工作原理是什么?
    • React 如何通过虚拟 DOM 提高性能?
  2. 服务端渲染与静态生成
    • Next.js 中的 getServerSideProps 和 getStaticProps 的区别是什么?
    • 如何在 Vue 中实现服务端渲染(SSR)?
  3. 渐进式 Web 应用(PWA)
    • 什么是 PWA?它的主要特性有哪些?
    • 如何在现有的 Web 应用中添加 PWA 支持?

五、全栈开发高级

  1. API 网关
    • 什么是 API 网关?它的主要功能是什么?
    • 如何使用 Node.js 实现一个简单的 API 网关?
  2. 微服务架构
    • 什么是微服务架构?它的优缺点是什么?
    • 如何在 Node.js 中实现微服务通信?
  3. GraphQL 高级
    • 如何在 GraphQL 中实现订阅(Subscription)?
    • 如何在大型应用中设计 GraphQL 架构?

六、性能和安全高级

  1. 性能分析
    • 如何使用 Node.js 的性能分析工具进行性能分析?
    • 如何识别和优化应用中的性能瓶颈?
  2. 安全加固
    • 如何在 Node.js 应用中实现 HTTPS?
    • 什么是 CSP(Content Security Policy)?如何配置?
  3. DevOps 和部署
    • 如何使用 CI/CD 工具自动化 Node.js 应用的部署?
    • 如何在 Kubernetes 中部署 Node.js 应用?

这些题目能够帮助评估候选人在 Node.js 全栈开发中的高级技术和架构设计能力。

Jhipster Cache

Background

参与 iCluster Web 项目一年的时间,80%时间在写前端Angular,20%时间在边学习边写Java,剩下10%也在思考和设计功能。

Jhipster 支持6种缓存

  1. Ehcache, https://www.cnblogs.com/bronya0/p/14715732.html
    1. 纯 Java 的进程内缓存,直接在 JVM虚拟机中缓存,速度非常快。缓存有两级,内存储存完自动存到磁盘上。数据可持久化到磁盘,VM(虚拟机)重启后数据恢复。
    2. 速度超过 Redis, Redis 通过 socket 访问缓存数据,效率没 Ehcache 高。
    3. 在分布式和集群情况下的缓存恢复、数据缓存的支持不是很好。
    4. 如果是大型缓存,存在缓存共享、分布式部署,建议用 Redis。
    5. 可以和 Redis 共同使用。
  2. Caffeine, https://ghh3809.github.io/2021/05/31/caffeine/
    1. Java 高性能的本地缓存库。缓存命中率已接近最优值。
    2. 支持并发,支持O(1)时间复杂度的数据存取。
    3. 提供以下功能
      1. 自动加载条目到缓存中,支持异步加载
      2. 根据频率和最近访问情况,支持将缓存数量设为移除策略
      3. 根据最近访问和修改时间,支持将时间设为移除策略
      4. 过期条目再次访问时异步加载
      5. key自动包装为弱引用
      6. value自动包装为弱引用/软引用
      7. 条目移除通知
      8. 对外部资源的写入
      9. 累计缓存使用统计
  3. Hazelcast, https://www.bianchengbaodian.com/hazelcast
    1. 分布式内存数据网格,用于存储键值对的分布式缓存、用于创建和使用分布式数据结构的构造,以及在集群中的节点之间分配计算和查询的方法。
    2. 支持多种数据结构,如锁、信号量、队列、列表等。
    3. 快速R/W访问
    4. 高可用性,支持跨机器分发数据以及额外的备份支持。
    5. 高性能,提供了可用于在多个工作机器之前分配负载/计算/查询的构造。
    6. 易于使用,hazelcast 实现并扩展了许多 java.util.concurrent 结构,使它非常容易使用和与代码集成。
    7. 与 Redis 相比
      1. 开始就是为分布式环境构建,而Redis开始作为单机缓存。
      2. Simple cluster scale in/out。维护添加或删除节点的集群非常简单。
      3. 支持故障转移所需的资源更少。Redis 主从节点,hazelcast所有节点平等。
      4. 简单分布式计算。hazelcast 提供了一个简单的接口来将代码发送到数据以进行并行处理,减少在线数据传输。Redis 也支持,但是需要需要 Lua 脚本。
  4. Infinispan, https://www.infoq.cn/article/infinispan-gridfs
    1. Infinispan, 是 JBoss Cache 缓存框架的后续项目,是一个开源的数据网格平台,用于访问分布式状态的集群节点。
  5. Memcached, https://www.cnblogs.com/davidesun/p/12770114.html
    1. 自由开源,高性能,分布式内存键值对缓存系统,用来存储小块的任意数据(字符串、对象),可以是数据库调用、API 调用或者是页面渲染的结果。
    2. 协议简单,使用文本协议,使用换行符作为命令结束
    3. 基于 libevent 的事件处理
    4. 内置内存存储方式
    5. 使用客户端哈希的不互相通信的方式。
    6. 和 Redis 比较如下,http://www.yunweipai.com/35551.html
  6. Redis, https://www.yiibai.com/redis/redis_quick_guide.html
    1. 开源,高级的键值存储解决方案
      1. Redis 将其数据库完全保存在内存中,仅使用磁盘进行持久化。
      2. Redis 提供相对丰富的数据类型
      3. Redis 可以将数据复制到任意数量的从机中。
    2. 优点
      1. 异常快。每秒可以执行11万次SET操作,每秒可以执行81000次的GET操作。
      2. 支持丰富的数据类型。列表,集合,排序集和散列等
      3. 操作具有原子性。所有操作都是原子操作,确保了两个客户端并发访问,Redis 服务器能够接收到新的值。
      4. 多实用工具。如缓存,消息队列(Redis本地支持发布/订阅);应用中任何短期数据,如会话,网页命中计数等。
    3. 竞品比较
      1. 包含更多复杂数据类型
      2. 内存数据库,实现非常高的读写速度。
      3. 复杂数据结构和内存中存储表示更容易操作。

项目分析

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.io.Serializable;

@CacheConfig(cacheNames = {"replGroupsCaches"})
@Service
public class ReplGroupsCacheService implements Serializable {
    private final static Logger logger = LoggerFactory.getLogger(ReplGroupsCacheService.class);
    private static final long serialVersionUID = -2433804034194454964L;
    public final static String REPLGROUPSCACHE = "replgroups";

		// 这里的 #clusterId 是从参数取出来的值,
		// 这样key/value就和cluster绑定在一起了,是个聪明的做法。
		// 猜测这里使用 @Cacheable 注解后,response值会被自动缓存。
    @Cacheable(cacheNames="replgroups", key="#clusterId")
    public IcAgentService.GetReplicationGroupsResponse getCachedReplGroups(String clusterId, ICAgentServiceGrpc.ICAgentServiceBlockingStub agentStub) {
        Empty request = Empty.newBuilder().build();
        IcAgentService.GetReplicationGroupsResponse response;
        try {
            response = agentStub.getReplicationGroups(request);
        } catch (StatusRuntimeException e) {
            logger.error("getReplGroups RPC call failed: {}, {}", e.getStatus(), e.getMessage());
            Status status = e.getStatus();
            logger.debug("get groups gRPC failed: {}, {}", status.getCode(), status.getDescription());
            throw new GetReplicationGroupsException(status.getDescription());
        }

        return response;
    }
}

iCluster 中缓存使用的方式可能有些繁琐,暂时是这么觉得。待研究几个 Jhipster Redis best practise 后再回头看此结论是否能站住脚。

Summary

行文到此,初步发现项目中用的是 Redis,然后有一个 Groups 的使用例子。基于自己目前的认知,觉得使用方式还是不够简洁。后续在了解更多例子后,再看此结论是否能站住脚。

Jhipster Cacahe Demo

A new Repo to practise, https://github.com/llccing-demo/jhipster-redis-demo

Jhipster Redis Demo

Simple start

  1. Initialize the project.
    Follow the jhipster CLI

  2. 启动项目

# 注意,先启动 Redis,通过 docker 启动,命令如下:
docker-compose -f redis.yml -d

# 然后启动后端,这个命令同时也会 build 前端, Run your Spring Boot application:
./gradlew

# Start your Webpack development server with:
npm start
  1. simple try cache
    llccing-demo/jhipster-redis-demo@3c0d2c4
  • Define cacheName in CacheConfiguration.java
  • Cache the userList in UserService.java

通过上面两个步骤,即可实现 userList 的缓存,不过当前的缓存时间是1 hour,这个时间处理需要考虑一下。如果能够永不过期,然后异步更新其中的值,这样缓存永不过期,那么api永远是高性能的。

如果仅仅使用 @Cacheable 注解,这个例子实现起来还是很简单的,只不过如果过期时间是固定的,也应该有 update cache 的 logic。

Use Redisson client

Follow this article (https://dzone.com/articles/quickstart-how-to-use-spring-cache-on-redis) to define the Redisson client.

实验逻辑参照(https://www.51cto.com/article/743175.html)

llccing-demo/jhipster-redis-demo@7cd3ef9

Add Redisson simple demo

Try custom cache strategy

llccing-demo/jhipster-redis-demo@1c35d5b

使用 Async 注解来实现异步写入 Redis,这里需要注意 Transactional 和 Async 一起使用的问题([https://ynfatal.github.io/2018/11/02/SpringBoot2/SpringBoot2第二十四篇(三)@Async与事务/](https://ynfatal.github.io/2018/11/02/SpringBoot2/SpringBoot2%E7%AC%AC%E4%BA%8C%E5%8D%81%E5%9B%9B%E7%AF%87(%E4%B8%89)@Async%E4%B8%8E%E4%BA%8B%E5%8A%A1/))。

原本想利用 Redis 实现持久缓存,每次前端调用 api 都从缓存取结果,但是仔细想有个漏洞,如果如果用户通过其他方式更新了db数据,但是redis中没有缓存到,那么API会从Redis拿到空值并返回,然后异步更新Redis,那么用户下次刷新才能拿到新的值,这样也不合理。

所以如果这么做,需要确保所有更新DB的逻辑,同时异步的去更新 Redis;如果这样实现,需要仔细过一遍项目中的api,看哪些涉及了更新DB的逻辑。当然因为我们用 agent rpc (需要学习这两种技术,暂时是了解的范畴)所以很多涉及到用 command 的方式执行,所以也许只要处理几处 command 执行的函数就能够实现全部 更新DB同时异步更新Redis的逻辑。这里还需要去确认下。

Summary

对于Cache算是有个入门的了解,但是如果使用是最佳实践,还是需要时间去积累经验。

Other examples

@Override
public boolean doSecKill(String uid, String productId) {
    //1、uid和productId非空判断
    if (uid == null || productId == null) {
        return false;
    }

    //2、拼接key
    String kcKey = "sk:" + productId + ":qt";   //库存
    String userKey = "sk:" + productId + ":user";  //秒杀成功的用户

    //3、获取库存
    String kc = String.valueOf(redisTemplate.opsForValue().get(kcKey)) ;
    if (kc == null) {
        System.out.println("秒杀还没有开始,请等待");
        return false;
    }

    //4、判断用户是否已经秒杀成功过了
    if (redisTemplate.opsForSet().isMember(userKey, uid)) {
        System.out.println("已秒杀成功,不能重复秒杀");
        return false;
    }
    //5、如果库存数量小于1,秒杀结束
    if (Integer.parseInt(kc) <=0) {
        System.out.println("秒杀结束");
        return false;
    }

    //6、秒杀过程
    redisTemplate.opsForValue().decrement(kcKey);  //库存数量减1
    redisTemplate.opsForSet().add(userKey, uid);
    System.out.println("秒杀成功。。。");
    return true;
}

Tips

Only use Redisson

https://www.jianshu.com/p/e9b26c743cae

if we can use redis like the link example, we can do more things.

Redis command

# connected to Redis in Docker
docker exec -it xxxID redis-cli

# show all the keys in Reids
KEYS *

# return 'OK' if the cache was successfully cleared.
FLUSHALL 

# return the type of key
type key

# get hash value 
hgetall key

Java 乐观锁、悲观锁

https://segmentfault.com/a/1190000016611415

  • 乐观锁,乐观的认为操作不会产生并发问题,不上锁;但是会判断其他线程在这之前 是否对数据进行了修改。可以使用 版本号机制或者 CAS (compare and swap) 算法
  • 悲观锁,总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以会加(悲观)锁。加锁后,不同线程同时执行时,只能有一个线程执行,其他线程在入口处等待,直到锁被释放。悲观锁在 MySQL 和 Java 中有广泛使用
    • MySQL 的读锁、写锁、行锁 etc.
    • Java 的 synchronized 关键字。
  • 读得多,冲突几率小,乐观锁。写得多,冲突几率大,悲观锁。

Postman 压力测试

https://www.cnblogs.com/zjdxr-up/p/14604417.html

postman 的test选项支持压力测试。

Redisson

Annual summary

2016

初出茅庐

经过之前在百度、知乎平台一番搜索后,2016年2月15号,农历正月初八,我来到了帝都,下午3点多的动车到达北京站,感觉不像想象中的那么繁华,不过人是特别的多。在地铁口排队排了30分钟才坐上地铁,因为之前和同学联系他们可能住在菊园附近,不过他们这个时候还没过来,记得应该是初十过来,所以我就打算今天先去附近找个小旅店住一晚,因为那已经是北五环了,大学时学校附近都是些旅店,感觉菊园附近也应该有比较多的旅店,结果完全不是一个套路,看到了一家宾馆,结果还是没营业,门口贴的告示上写着十五左右营业。

是时候发挥我的聪明才智了,所以就从菊园做公交车再往西北方向走,也就是出城之路,做了大概7、8站,感觉应该到终点左右,车站对面是一处小区,感觉这里总该有个住的地方了吧,超市里的阿姨说这附近都没有,此时应该已经6点多了,我该怎么办呢?

回去!我又坐着公交车向城里走,繁华点的地方总该有住的地方了吧,就这样我在上地三街附近下了车,对了,我得重点提下我的行李箱,来的时候大连下雪,我的行李箱可能也比较脆弱,在放到出租车后备箱时,拉杆不是特别听话,就稍稍用了点劲儿,然后拉杆就进去了,真的进去了,想拉都拉不出来,我就是拖着这样的一个箱子在上地三街附近找着我的归宿。

走了一会儿,看到了一加速8,之前貌似在电视上看见过,别笑我,没见过什么世面,进去打听了一下,住一晚最便宜的200,几乎是想都没想,我就离开了,之前在学校上学出去唱歌想睡觉时,附近的宾馆才50一宿,大城市真的是大城市,价格贵的离谱,此时已经是9点左右了,我已经在这个诺大的城市漂泊了5个小时左右,之后又问了一下7天,也是200左右,不行,这样住一宿真的是太奢侈了。舍不得花这个钱的我,就走啊走,走啊走,有个龙泉湖大酒店,标间160,时间已经快10点了,不能再找了,而且价格也是相对低的了。

就这样我住到了这里,理所当然,没有窗户,但是有两张床,卫生间还有浴缸。给家里回个电话之后,然后我就去冲了个澡,怎么着也花了我160大洋呢,哈哈,原谅我就是这么屌丝。

半夜大概是睡到了2点左右,我就醒了,浑身烫的不行,我知道一定是晚上在路上走得比较急,出汗风吹,再加上想把钱花的更值的冲澡导致的,这可怎么办,半夜两点,药店都没开门呢,总不能打120吧,要是挺到早上还不烧糊涂了。此时我就想起了一句话,屋漏偏逢连夜雨,当然我也想起来我得药,安乃近,不知道有多少人知道这个药,可能有老乡用过,是一种退烧药,后来了解,据说副作用挺大的,所以不推荐大家使用,吃了两片之后,我就躺下了,今晚总算时熬过去了。

年轻人身体好,第二天早上我就又恢复了活力,然后我就搬到了同学家,一间7平左右的次卧,之前在学校时不是特别熟,但是这次跟他接触后,感觉是个挺讲究的人,可能是他帮了我才会有这种感觉,总之特别感谢他,收留了我3天。19号晚上我就搬到了新找到的住的地方,菊园附近的一间公寓,虽然破破烂烂,但是总算是有个落脚的地方了,着重带一笔,我是跟同学一起住的,后来想想我还真是特别后悔有这段经历,或者说特别感谢有这段经历。

找到住的地方之后,就开始投简历,19号、20号、21号(周五、周六、周日),3天时间应该投了100份左右,对了那个时候的我一门心思是准备做Android,感觉是个朝阳产业,可能因为之前在学校学习过java,加上用的Android手机,了解的也都是Android的内容,所以就开始朝着这个方向投,周一就开始有电话通知面试,有一家印象比较深,在我做完了自我介绍后,大哥直接对我说,我们招的是组长类型的开发,我觉得你应该去培训一下,然后再考虑找工作,然后还苦口婆心的跟我说了挺多的话,怎么说呢,心里比较急,五味杂陈。后面又面了两家培训公司,当然他要是告诉我是培训公司,我可能就不会去了。一家到了之后让我做了做题,然后都没看题答的怎么样,就跟我说培训如何如何,然后我就走了。另一家更是直接的说,我们给你培训然后怎样怎样。可能初来北京找工作的小伙伴都会有这样的经历。

虽然一周的时间还没过去,但是对于一个初来乍到菜鸟来说,确实很长了,在我感觉走投无路的时候,一家公司给我打电话去面试,少有的男的人事,后来才知道他是我们的经理。做了一些java基础的题,大哥拿去看了看,对我说及格了,好像也没再聊技术,问我要是开始不写Android而是Web,后面再写怎么样,这对于已经要走投无路的我来说,写什么不是写呢,我就说没问题,然后就开始了我的实习生活,记得特别清楚,周五那天去报到,领导了一台thinkpadT440S,当然这台电脑一直陪伴我到结束,当然说到结束已经是后话了。

在B公司的前两月,也就是3月份到5月初,都是在做一个Web的项目,和之前的两个Android哥们一起写,任务是大哥分配的,按功能模块分,部分前后端,前端是bootstrap+jQuery,后端是PHP,也就是说,我一个做Android的实习,去做了FE+php,当然还是特别感谢大哥能让我接触到web,或者说踏上这个坑。每天的工作就是和领导沟通模块的功能逻辑,然后自己去实现前后端,不知道别的小公司是什么方式,总之我们公司是这样的,当时公司有12个人左右,主要业务是GIS方向的政府项目,当时的实习薪水是3.5K,还要扣500的实习税,曾经较真的去网上查了查,你别说还真有实习税这么个说法。

两个月之后便请假回学校答辩,转眼学校事情结束,回到了北京,之前的住房经历然后比较后难受,所以就跟我的哥们住到了一起,两室一厅,我住大厅,哈哈,那段时间还是比较开心的,晚上到家就和哥们吐槽吐槽公司,看他打游戏,周末的时候他们的朋友过来大家一起涮火锅,能处理好关系的哥们真的是太厉害了,做一个让人感觉舒服的人对我来说真的是太难了,我现在仍是在学习的路上。

回到公司之后,领导对我的安排有了些变化,然后写Flex,次非彼,这个Flex在2016来说,已经过时了,但是公司的项目还是需要有人维护的,虽然不情愿但是还是做了,两周后大哥看我的进度还可以,就打算再给我安排些新的Flex的项目,虽然这两周我挣扎着熬过来了,但是内心已经有点排斥感了,就是希望尽早结束,这个时候给我任务已经让我不能够忍受。一方面之前的web开发让我对web、html5有了些了解,它可能才是趋势,一方面当时做的比较不错,有了一些自信心,想了几天,我就跟领导提了这个事,当时的我是抱着离开的心态去的,但是话到嘴边没直接说离开,只是说不太想做Flex,但是领导跟我说,公司现在没人,你要是不写可能工作就会耽误,我的态度比较坚决,领导看我没说话,想了想就说,那行,那你还是做web的工作吧,还有什么事情吗,当时的我也没说话,沉默了2秒钟,跟我说你的工资我之前跟经理提了,能给你涨,你看你觉得多少合适,我当时心里的想法是xK,但是话到嘴边又没了底气,就跟领导说x-0.5K,领导想了想说,好。开心之余,其实多少有点后悔,人心不足蛇吞象,可能差了0.5,就是留下与离开的区别了。

就这样经过了这样一个事,我顺利加薪做自己想做的工作,但是这个时候我发现经理对我的态度明显转变,见了面招呼也不打,俨然你是一个眼屎,说实话那个时候心里确实不是滋味。但是为了生存下去!

16年6月份开始到17年的8月份,在B公司做了不下5个项目,都是政府的项目,前端的技术从jQuery到Angular当然这个Angular也是我推动的,MVVM技术的优点显而易见,另一方面要是技术一直是jQuery的状态跟不上趋势,那应该是会被套牢在这家公司了。

2017

转折之年。

B公司接的项目更多了,人数也瞬间涨了起来,原本的经理做了总监,原本的组长做了经理,头衔的改变并不会带来思维方式的转变,所以依旧是以项目验收未目标,每天奔波在解决bug和创造bug的路上。这样的工作方式,已经然我不能忍受,我想有点改变,想去大公司,想去一个做产品的公司,想去一个有更多的靠谱的人的公司。

就这样8月22日我跟大哥提了离职,将离职原因总结了一下,第一,技术上的瓶颈,对自己做的东西没有信心,技术上没有大牛指引方向怕误入歧途,第二,工作的氛围,领导的无视始终是我心里挥之不去的一点。第三,浮躁的心态,在看大家都是通过跳槽涨工资的时候,我也按耐不住。领导的挽留比较无力,也就没有多费什么口舌。

当然呢,我还是特别感谢B公司,教会了我很多东西,除了技术上的,更多的是让我认识到了什么是社会,人情冷暖,世态炎凉,初入社会可能都会有这种感觉,在你有了一定的认知了之后也就习以为常了。大家都披上了重重的铠甲,密不透风,谁也不知道里面到底藏了一个怎样的内心。

离职的过程相对顺利,把上一年的年终奖的税扣了。离职之后,开始投简历,从24号到9月1日,两周左右时间面试了14家公司,最终有两家印象比较深的,一家是位于上地的创业公司,老板是个北大的创业老师,对我说,我觉得你的执行力还可以,但是你这个学历,你这个能力,想要这个工资是肯定不行的,你要是跟着我混我能够指导你,我是北大的,我很牛逼的。当然大致是这个意思,没有尊重在里面,有的仅仅是瞧不起。另一家公司是C公司,做家居装饰的,薪资虽然没到达预期,但是我能感觉到尊重,所以就选了这家,9月4号入职,工作一周后,一个突然到访的电话,扰乱我的计划,D的人事,联系到我
说是问我还考不考虑,曾经感慨要是能够进一个大公司该有多好,没想到竟然给我抛了橄榄枝,走了一些流程之后,我入职了D公司。

记得开始面试这家公司的时候是8月25日周四,当时说好的没什么问题的话,周五就能够顺利入职,然后此后就一直没了消息,直到9月11日。过了半个月人事再次联系我,估计应该是选了一圈人之后发觉我还是稍微能用,当然也可能是我内心险恶,真的就是流程稍微长了点,领导费了一番心思,才让我顺利入职。感谢领导。

从10月初到12月末,做的事情是公司现有平台的重构,技术栈是vue+iview,老板对前端的定位是造轮子的人,对于后端的定位是使用轮子的人,感觉理想很丰满,在技术还没到造轮子的水平的时候,强行造轮子也是着实尴尬,虽然最后的轮子问题百出,但是造轮子的这个过程还是收获很多,对于iview的组件的内部实现,封装思路有了一个更加清晰的了解,对于以后使用轮子的好处还是显而易见的。

总结一下,17年是我的幸运之年,工作上步入了正轨,感情上和丫头的关系在慢慢前进,举全家之力在三线小城市的郊区买了个房,成功转型房奴,几个事情加在一起17年还是收获慢慢的一年。

2018

18年是京东技术元年,也将是我抛开浮躁,踏实技术的技术元年。

在造轮子的路上要坚持住,正常的下场应该是不是在造轮子的路上,就是在学习造轮子的路上,或者是参见别人分享造轮子的路上。其实也不是非得纠结在造轮子这个事情,主要的是学习,成长,至于方式,造轮子算是其中之一。

新的一年工作上要更加全面、细致、把握机会,生活上,处理好和丫头的关系,照顾好爸妈,当然作为房奴的责任也不能抛弃,每个月会有人提醒的……。

18年定下几个计划:

技术:

  • Vue的项目要维护一个,这个不是难事。
  • 学习nodejs,维护一个项目。
  • 学习Reactjs,做一个像样的项目。

读书:

  • 《http权威指南》,通读一遍。
  • 《代码整洁之道》,培养自己成为一个有代码洁癖的人。
  • 《三体》,生活不仅仅是代码,还得有适合远方不是。

旅行:

  • 出去走走,至于去哪里,还在计划中。

浮躁的社会孕育了浮躁的我,而我需要竭尽全力使进入淡定的状态,沉淀。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.