- 🔭 I’m currently working on Rocket Software.
- 🌱 I’m currently learning angular/ts/jhipster
- 🤔 I’m enjoy current work.
llccing / frontend Goto Github PK
View Code? Open in Web Editor NEW:100: 学习记录文档
Home Page: https://llccing.github.io/FrontEnd/
:100: 学习记录文档
Home Page: https://llccing.github.io/FrontEnd/
下面是另一组 Node.js 全栈面试题:
这些题目可以帮助进一步评估候选人在 Node.js 全栈开发方面的高级知识和实际应用能力。
an introduction from Wikipedia. https://zh.wikipedia.org/wiki/IBM_System_i
Node.js 全栈面试题通常涵盖以下几个方面:
这些问题涵盖了全栈开发的主要方面,可以帮助面试官评估候选人在 Node.js 和全栈开发方面的知识和技能。
|-- 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
开发时我们常用的命令为 npm run dev
和npm 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
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()
}
}
这个配置中配置了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]')
}
}
]
}
}
// 工具函数
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()
]
})
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'
}
}
删除目标文件夹之后再将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'
))
})
})
// 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
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主要是对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文件是项目项目相关配置信息,包含打包、开发设置
// 项目结构说明 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
// 用于合并对象、数组
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"'
})
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.”
“许多次,从让我们或者让其他做这些事情的人给你一个应用程序的分析开始,” 他说。“一旦你能看到应用程序在做什么,你能够像其他应用一样接近他。所以,遵循同样的软件开发原则。你可能仅用前端建一个反应堆连接它。从这里开始,做一个两周的冲刺,你开始看这个过程。是的,它很困难,但是也没那么难。你能成功的。”
公司项目是2021年开始的项目,Angular 用的是v13,现在是2023年,两年的时间 Angular 已经到了v16,各个方面都有提升;我们详细看下。
相较于Angular 12,Angular 13有些新的特性,原文在此(https://www.infoq.cn/article/v0v0cyr4i9lgr6c7us00),总结如下:
Angular 14 有如下变化,原文在此。
const routes: Routes = [{
path: 'home',
component: HomeComponent
title: 'My App - Home' // <-- Page title
}, {
path: 'about',
component: AboutComponent,
title: 'My App - About Me' // <-- Page title
}];
"builder": "@angular-devkit/build-angular:browser"
"builder": "@angular-devkit/build-angular:browser-esbuild"
Released in early November 2022.
重头戏来了,值得期待的更新;
we’re continuing the Angular Momentum with the biggest release since the initial rollout of Angular;(我们将继续推动角动量,这是自Angular首次推出以来最大的更新;)
@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$, []);
}
Angular 16 是个大的版本,有很多新的特性推出,理想的方式是一步到位,直接从13到16以上,也许等团队有时间考虑升级这个事情的时候,Angular 17已经出来了,这样对于某些16新出的特性更加的稳定,那样更加有利于项目。期待升级,但是升级节奏要稳。
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 的发现,正巧能解决我的问题。
下面是另一组更深入的 Node.js 全栈面试题:
这些题目能够帮助评估候选人在 Node.js 全栈开发中的高级技术和架构设计能力。
参与 iCluster Web 项目一年的时间,80%时间在写前端Angular,20%时间在边学习边写Java,剩下10%也在思考和设计功能。
[CacheConfiguration.java](http://CacheConfiguration.java) 定义类
Docker redis.yml 配置文件
实际使用的一个例子
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 后再回头看此结论是否能站住脚。
行文到此,初步发现项目中用的是 Redis,然后有一个 Groups 的使用例子。基于自己目前的认知,觉得使用方式还是不够简洁。后续在了解更多例子后,再看此结论是否能站住脚。
A new Repo to practise, https://github.com/llccing-demo/jhipster-redis-demo
Initialize the project.
Follow the jhipster CLI
启动项目
# 注意,先启动 Redis,通过 docker 启动,命令如下:
docker-compose -f redis.yml -d
# 然后启动后端,这个命令同时也会 build 前端, Run your Spring Boot application:
./gradlew
# Start your Webpack development server with:
npm start
通过上面两个步骤,即可实现 userList 的缓存,不过当前的缓存时间是1 hour,这个时间处理需要考虑一下。如果能够永不过期,然后异步更新其中的值,这样缓存永不过期,那么api永远是高性能的。
如果仅仅使用 @Cacheable
注解,这个例子实现起来还是很简单的,只不过如果过期时间是固定的,也应该有 update cache 的 logic。
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
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的逻辑。这里还需要去确认下。
对于Cache算是有个入门的了解,但是如果使用是最佳实践,还是需要时间去积累经验。
@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;
}
https://www.jianshu.com/p/e9b26c743cae
if we can use redis like the link example, we can do more things.
# 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
https://segmentfault.com/a/1190000016611415
https://www.cnblogs.com/zjdxr-up/p/14604417.html
postman 的test选项支持压力测试。
初出茅庐
经过之前在百度、知乎平台一番搜索后,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的状态跟不上趋势,那应该是会被套牢在这家公司了。
转折之年。
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年还是收获慢慢的一年。
18年是京东技术元年,也将是我抛开浮躁,踏实技术的技术元年。
在造轮子的路上要坚持住,正常的下场应该是不是在造轮子的路上,就是在学习造轮子的路上,或者是参见别人分享造轮子的路上。其实也不是非得纠结在造轮子这个事情,主要的是学习,成长,至于方式,造轮子算是其中之一。
新的一年工作上要更加全面、细致、把握机会,生活上,处理好和丫头的关系,照顾好爸妈,当然作为房奴的责任也不能抛弃,每个月会有人提醒的……。
18年定下几个计划:
技术:
读书:
旅行:
浮躁的社会孕育了浮躁的我,而我需要竭尽全力使进入淡定的状态,沉淀。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.