项目中遇到过的问题与技巧

记录开发过程中遇到的一些问题和一些技巧。

坑点

  • 使用vue的ui框架时,比如element,iview等,需要给组件绑定原生事件,可以在事件后加.native。例如element的input组件绑定键盘事件可以这么写@keyup.enter.native=”add” 详见vue的官方教程

面试题

  • 不用循环生成100长度都为1的数组,详见es6
1
2
3
new Array(100).fill(1)
Array.from({length: 1}, () => 1)
Array.from({length: 1}).map(() => 1)

webpack中css预处理器的配置

1
2
3
sass: cnpm install node-sass sass-loader --save-dev
less: cnpm install less less-loader --save-dev
stylus: cnpm install stylus stylus-loader --save-dev

使用node和chrome调试

进入到项目当前文件,比如要调试 d/Leetcode/045/main.js 文件,git bash进入到 d/Leetcode/045/, 输入命令

1
node --inspect-brk main

使用npm启动项目(查看端口占用)

在windows本地调试时使用 npm run dev 启动项目,用 ctrl + c 停止,会导致进程没有结束,这时需要在windows找到该项目的进程并结束

1
2
netstat -ano | findstr 8080 //找到8080端口正在运行的进程
taskkill /f /pid xxx // 强行结束pid为xxx的进程

还有一种方法,使用yarn代替npm,yarn run dev 启动项目,不会产生上述的问题

mac 中查看端口占用并结束进程的方法

1
2
lsof -i:8080
kill xxx

mongoose 里保存 mixed 类型的数据

在 mongoose 对 type 为 mixed 的数据作修改时,save 操作时会保存不进去。

1
2
3
contest.ranklist = ranklist
contest.markModified('ranklist')
await contest.save()

保存时对改变的字段进行 markModified 操作,再 save 后可以保存对数据的操作。

react 中批量导入图片

1
2
const requireContext = require.context('../img', true, /^\.\/.*\.png$/)
const images = requireContext.keys().map(requireContext)

该方法的缺点是导入顺序是文件里的图片顺序,image 数组保存的是图片的 base64 编码,无法在代码中通过名字确认是哪张图

linux 命令行常用快捷键

Ctrl + A // 光标移到行首
Ctrl + E // 光标移到行尾
Ctrl + K // 删除之行尾

linux 常用指令

1
2
echo $(cat a) > b  // 将文件a输出至文件b
echo $(cat a) >> b // 将文件a追加至文件b

npm 部分操作

npm >= 5.0 时更新包

1
npm install xxx@yyy --save-dev  // 手动指定安装版本

1
2
3
npm install -g xxx  // npm 全局安装
npm uninstall -g xxx // 卸载全局安装的包
npm list -g --depth 0 // 查看全局安装过的包

reset.css

做 PC 端时,不同的浏览器的默认样式之间存在差别,使用 reset.css 来将浏览器的默认样式全部覆盖掉,达到统一样式的效果。
reset.css

使用 proxy 代替 Object.defineProperty

对象级别的优化已经很极致了,工程代码中也没有机会帮助 JS 引擎做得更好,值得注意的是不要对数组使用 Object 对象下的方法,尤其是 defineProperty,因为这会让 JS 引擎在存储数组元素时,使用 Dictionary Elements 结构替代 Elements,而 Elements 结构是共享 PropertyDescriptor 的。

但也有难以避免的情况,比如使用 Object.defineProperty 监听数组变化时,就不得不破坏 JS 引擎渲染了。

使用 proxy 监听数组变化,这并不会改变 Elements 的结构,所以这也从另一个侧面证明了使用 proxy 监听对象变化比 Object.defineProperty 更优,因为 Object.defineProperty 会破坏 JS 引擎对数组做的优化。

原文可参考 JS 引擎基础之 Shapes and Inline Caches

reduce 函数使用技巧

计算数组 array 的累加和

1
2
3
4
5
const add = array
.reduce((sum, val) => {
sum += val
return sum
}, 0)

可以简化为

1
2
const add = array
.reduce((sum, val) => (sum += val), 0)

极简版函数柯里化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function curry(fn, args = []) {
const length = fn.length
return function() {
const _args = args.slice(0)
Array.prototype.push.apply(_args, Array.prototype.slice.call(arguments))
if (_args.length === length) {
return fn.apply(this, _args)
}
return curry.call(this, fn, _args)
}
}

const func = curry((a, b, c) => [a, b, c])

console.log(func(1, 2, 3)) // [ 1, 2, 3 ]
console.log(func(1)(2)(3)) // [ 1, 2, 3 ]

函数的 length 表示该函数参数的数量

MAC 操作 mariadb

进入 mariadb 启动目录,启动 mariadb,之后登录

1
2
3
cd /usr/local/Cellar/mariadb/10.3.9/bin
mysql.server start
mysql -u root -p

创建数据库

1
create database database_name;

创建数据表

1
2
3
4
5
6
7
create table table_name(
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
author VARCHAR(40) NOT NULL,
date DATE,
PRIMARY KEY ( id )
);

使用 sequelize-cli 创建数据表

创建一个名字为 auth 的表,字段有 id(int),title(varchar)

1
npx sequelize model:create --name auth --attributes id:integer,title:string

将文件中 migration 的数据表同步到本地的数据库中

1
npx sequelize db:migrate

mac 启动数据库

启动 mongodb

1
mongod

启动 redis

1
redis-server

brew 的基本操作

安装 brew

1
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

使用 brew 安装

1
2
3
4
brew search xxx  // 搜索
brew install xxx // 安装
brew uninstall xxx // 卸载
brew list // 查看所有已安装的

使用 brew services 管理服务 (比如 mysql)

1
2
3
4
brew services start mysql  // 启动 mysql 服务
brew services stop mysql // 停止 mysql 服务
brew services restart mysql // 重启 mysql 服务
brew services list // 查看使用 brew 安装的服务列表

react 中 background-image 使用变量

当尝试赋值给 backgroundImage 带空格的 url 时设置值会无效
尝试把空格转义,然后设置给 backgroundImage 就没问题了

1
2
img = img.replace(/\s/g, encodeURIComponent(' '))
<div className="infoImg" style={{backgroundImage:{"url("+img+")"}}}>

使用 babel7 更新依赖

使用了babel7.0以上的版本,依赖不再是安装 babel-core 这种,命名空间改为了 @babel-core
所有原来的依赖都要更改为新的命名空间

1
2
"@babel/core": "^7.1.2",
"@babel/preset-env": "^7.1.5",

babel 升级工具修改配置

1
2
3
4
5
npx babel-upgrade --write

# 或是安装 babel-upgrade -g
npm install babel-upgrade -g
babel-upgrade --write

SSR 时 node 返回的 html 带上 js 报错

在服务端使用 webpack 编译代码时,可能导致 dirname 和 filename 返回 ‘/‘ 路径
在 webpack 里加上这些,使 dirname 和 filename 返回正常路径

1
2
3
4
5
target: 'node',
node: {
__dirname: false,
__filename: false
}

项目依赖升级

1
2
npm update  // 如果报错,先把 node_modules 删掉重装
npm install

如果此时提示

1
run `npm audit fix` to fix them, or `npm audit` for details

那么按照提示 输入

1
npm audit fix

lodash 按需打包

webpack 的 Tree Shaking 不能很好的做到按需打包,还是会将整个 lodash 库打包。
这边推荐两个简单的方法:

使用子包

lodash 中的每个函数在 NPM 中都有一个单独的发布模块
如果你只需要使用 _.cloneDeep, 那么只需安装 lodash.cloneDeep 模块,然后引入

1
2
const cloneDeep = require(lodash.cloneDeep)
const copyObj = cloneDeep(obj)

使用插件

还是安装 lodash 整个包,但是需要配置
安装 babel-plugin-lodash 插件
在 bebel 的 plugin 里添加

1
"plugins": ["lodash"]

安装 lodash-webpack-plugin 插件
在 webpack 作如下配置

1
2
3
4
5
6
7
8
9
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');

module.exports = {
mode: 'production',
...
plugins: [
new LodashModuleReplacementPlugin(),
]
}

browserslist

命令行运行以下命令,判断项目支持的浏览器

1
npx browserslist

参考资料

管理 npm 源

1
2
npm config get registry  // 获取 npm 源地址
npm config set registry http://registry.npmjs.org // 设置 npm 源地址