- Published on
螺丝是怎么拧成的
- Authors

- 作者
- Quelennuii
目录
- vue2
- 路由跳转
- :star:调用函数中,有几句不执行,通过点击事件传参
- websocket
- 监听输入框改变
- 传值双向绑定
- 祖孙传值
- 网页终端 xterm
- 动态绑定 class(多条件)
- 判断对象是否为空
- 根据 id 查找对象并把找到的对象存入新数组
- 将 value 相同的对象合并
- v-for 循环导致表单验证失效
- prop 传参有延迟
- 一行有两个 input,不用单独写样式
- 路由带参数传递,参数为 number 格式
- 单独调用分页组件,发现挂载时会额外发送一次请求
- 序号从 1 开始
- sortable.js 拖拽
- 修改表格样式(child 伪元素)
- 选项循环
- 路由监听
- 表格跳转到第一行
- vue3
- 可能为 undefined 的时候八成是又忘了写.value 了
- 图片缩放
- 计算属性
- 左右按钮切换的值以及事件传递给父级
- 对象 key 不定的时候读取变量
- 按钮切换状态
- defineExpose 父调用子方法和变量
- defineEmit 子调用父方法和变量
- 祖孙组件通信
- 给数组添加 value 属性
- 表格里拼接字符串
- 动态 ref
- 折叠后选择和 allid 都在
- 最里层折叠有问题
- 子组件传给父组件没问题,父组件转发时 undefined
- 第一行展开获取不到数据,unmounted 里没法读到 prop 的数据,传过来的数组显示无定义
- 阻止点击事件冒泡
- 图片详情弹窗层级混乱
- 没有数据的时候展开折叠会频繁掉接口
- 删除后弹窗刷新数据,并改变 activeindex
- 显示 undefined
- v01.2.1.zip 提取名字(v01.2.1)
- 用 key 强制刷新组件
- 枚举
- 文字垂直
- draggable.js
- 表格跳转到最后一行
- TS 语法
- 代码优化
- git
- 正则表达式
- React
- npm 包
- ide 插件
vue2
路由跳转
private jumpToTerminal (ip) {
this.$router.push({ path: '/server/terminal', query: { ip } });
}
private ip = this.$route.query.ip
:star:调用函数中,有几句不执行,通过点击事件传参
<i class="el-icon-refresh-left" @click="reset(true)" />
public reset (clickFlag = false) {
//不执行的
if (!clickFlag) {
this.activeForm = defaultForm; // 切回灰度图
}
//需要执行的
this.isEdit = false;
……
}
websocket
挂载在 main.js
// 建立链接
if (!(store.getters['websocket/getExhibitWs'])) {
store.dispatch('websocket/initExhibitSocket');
}
监听输入框改变
上面的选择框决定下面的输入框内容:输入框@change 事件,不要用 watch 监听
传值双向绑定
@PropSync('formModel', { type: Object })
private formModelData!: MatchRelationModel
:visibility.sync = visible
祖孙传值
都是写在中间层
v-bind="$attrs"用于祖向孙传
v-on="$listeners"用于孙向祖传
网页终端 xterm
"xterm": "^5.1.0",
"xterm-addon-attach": "^0.8.0",
"xterm-addon-fit": "^0.7.0"
<template>
<div id="terminal" />
</template>
<script lang="ts">
import Vue from 'vue';
import { Component } from 'vue-property-decorator';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { AttachAddon } from 'xterm-addon-attach';
import 'xterm/css/xterm.css';
@Component
export default class WebTerminal extends Vue {
private ip: string=this.$route.query.ip as string
//不这么写会报错
private term: Terminal|null=null
private socket: WebSocket|null = null
private mounted () {
const term = new Terminal();
const fitAddon = new FitAddon();
const socket = new WebSocket(`ws://${window.location.host}${window.location.pathname}/api/ws?ip=${this.ip}`);
const attachAddon = new AttachAddon(socket); // 建立websocket链接
term.loadAddon(attachAddon);
term.loadAddon(fitAddon);
term.open(document.getElementById('terminal') as HTMLElement);
fitAddon.fit();// 适应页面大小
term.focus();
socket.onopen = () => { socket.send('\n'); }; // 当连接建立时向终端发送一个换行符,不这么做的话最初终端是没有内容的,输入换行符可让终端显示当前用户的工作路径
window.onresize = function () { // 窗口尺寸变化时,终端尺寸自适应
fitAddon.fit();
};
this.term = term;
this.socket = socket;
}
private beforeDestroy () {
this.socket && this.socket.close();
//不这么写会报错
this.term && this.term.dispose();
}
}
</script>
<style lang="scss" scoped>
#terminal{
height: 100%;
}
</style>
动态绑定 class(多条件)
:class="{ 'not-use': item.system_id === 0,'lock': item.lock }"
not-use 为类名,item.system_id === 0 为执行该类的条件
判断对象是否为空
//方法一:for in 循环
function isEmptyObject(obj) {
//key表示前面的name 如果要获取value则 obj[key]
for (key in obj) {
return false
}
return true
}
//方法二:ES6 的 Object.keys() -->获取对象身上的所有value
function isEmptyObject(obj) {
var arr =Object.keys(obj)
if(arr.length==0)return true
return false
}
根据 id 查找对象并把找到的对象存入新数组
data 里存放所有数据,selectedIds 存放查找的 id
this.selectedIds.forEach((item) => {
const selectItem = this.data.find((x) => x.id === item);
if (selectItem) {
this.selectList.push(selectItem);
}
});
将 value 相同的对象合并
// 合并camera名称相同的对象
const dataInfo = {};
this.selectList.forEach((item) => {
const { camera } = item;
if (!dataInfo[camera]) {
dataInfo[camera] = {
...defaultForm,
camera,
point_name: []
};
}
dataInfo[camera].point_name.push(item.point_name);
const sortPointName = dataInfo[camera].point_name.map(Number).sort((a, b) => { return a - b; });
// 字符串转换为数字后,001会变成1,要补零
const sortList = sortPointName.map(String).map((item) => item.padStart('3', 0));
v-for 循环导致表单验证失效
数据结构
这里的 model 必须绑定一个对象,所以给数组外面套了一个
<el-form
ref="form"
:model="formModel"
>
<el-form-item
label="阈值"
:prop="`pointList.${index}.threshold`"
:rules="{ required: true, pattern: /^(?:(?:[1-9](?:\.[0-9])?)|0\.[1-9]|10|10.0)$/, message: '输入范围为0.1-10' }"
>
<div class="threshold-input">
<basic-input
v-model="item.threshold"
/>
</div>
</el-form-item>
prop 传参有延迟
直接点击事件传参,定义变量接受 子组件 list 触发
private toEdit (id: number) {
this.$emit('setParameter', id);
}
父组件
public setParameter (id: number) {
if (typeof id === 'number') {
this.$refs.parameterDialog.editParameter(id);
return;
}
if (!this.selectedIds.length) {
this.$message.error('请选择要配置的数据');
return;
}
this.$refs.parameterDialog.batchEdit();
}
一行有两个 input,不用单独写样式
注意得写到一个 div 里
.input-item{
display: flex;
justify-content: space-between;
::v-deep .el-input {
&:first-child {
width: 20%;
}
&:nth-child(2) {
width: 75%
}
}
}
路由带参数传递,参数为 number 格式
需要改成 string
private toDetail (id: number) {
this.$router.push({ path: '/update/detail', query: { id: id.toString() } });
}
单独调用分页组件,发现挂载时会额外发送一次请求
系统将初始化判定为页码改变
解决:
<pagination
v-if="total"
:page-params="pageParams"
:total="total"
page-layout="prev, pager, next"
class="pagination"
@current-change="handlePageChange"
/>
序号从 1 开始
<el-table-column
min-width="15%"
label="调整后序号"
>
<template slot-scope="{$index}">
{{ $index+1 }}
</template>
sortable.js 拖拽
row-key="id"必须写
<basic-table
border
:data="dataList"
row-key="id"
>
<el-table-column
prop="id"
min-width="15%"
label="调整前序号"
/>
<el-table-column
min-width="15%"
label="调整后序号"
>
<template slot-scope="{$index}">
{{ $index+1 }}
</template>
</el-table-column>
<el-table-column
prop="name"
min-width="57%"
label="数据组名称"
>
<template slot-scope="{row}">
<text-popover :content="row.name" />
</template>
</el-table-column>
<el-table-column
min-width="13%"
class-name="drag"
>
<template>
<i
class="iconfont icon-menu"
/>
</template>
</el-table-column>
</basic-table>
引入
import Sortable from 'sortablejs';
注意 nexttick
// open方法
public async open () {
this.visible = true;
// 防止初次打开时获取不到dom元素
this.$nextTick(() => {
this.setSort();
});
}
private setSort () {
const tbody = document.querySelector('.el-table__body-wrapper tbody');
const _this = this;
Sortable.create(tbody, {
// 仅class-name=“drag”那一列可拖拽
handle: '.drag',
animation: 180,
delay: 0,
onEnd ({ newIndex, oldIndex }) {
const currRow = _this.dataList.splice(oldIndex, 1)[0];
_this.dataList.splice(newIndex, 0, currRow);
// _this.dataList.forEach((item, index) => {
// this.newList[index] = {
// id: index + 1,
// name: item.name
// };
// });
this.newList = _this.dataList;
}
});
}
修改表格样式(child 伪元素)
某一列
::v-deep .el-table thead tr th:nth-child(2) .cell, ::v-deep .el-table .el-table__row td:nth-child(2) .cell {
display: flex;
justify-content: center;
}
::v-deep .el-table thead tr th:last-child .cell, ::v-deep .el-table .el-table__row td:last-child .cell {
display: flex;
justify-content: flex-end;
.iconfont {
margin-right: 0;
}
}
多列修改
::v-deep .el-table thead tr, ::v-deep .el-table .el-table__row {
td:first-child{
color: $color-input
}
th:nth-child(2) .cell,td:nth-child(2) .cell{
display: flex;
justify-content: center;
}
td:last-child {
.cell {
display: flex;
justify-content: flex-end;
.iconfont {
margin-right: 0;
}
}
}
td:nth-child(3),th:nth-child(3){
border-right: 0 !important;
}
}
选项循环
需求:这样写太麻烦
private statusOptions: Option[] = [
{ value: 1, label: 1 },
{ value: 2, label: 2 },
{ value: 3, label: 3 }
];
this.carriageList = new Array(6).fill(null).map((i, index) => {
return {
label: `${index + 1}车`,
value: (index + 1)
};
});
路由监听
@Watch('$route')
private offlineChanged (to, from) {
const offlineDetailPath = '/offline/taskRecord';
const taskDetailPath = '/tasks/taskRecord';
const taskPath = '/tasks';
const offlinePath = '/offline';
// 不是从任务详情跳转回任务记录、离线详情跳转回离线记录,刷新列表
if ((from.path === taskDetailPath && to.path === taskPath) || (from.path === offlineDetailPath && to.path === offlinePath) || to.name === '任务详情') {
return;
}
this.taskListData = [];
this.searchForm.reset();
// this.getTaskData();
}
表格跳转到第一行
表格记得写 ref
public backToTop () {
(this.$refs.table as any).bodyWrapper.scrollTop = 0;
(this.$refs.table as any).bodyWrapper.scrollLeft = 0;
}
vue3
可能为 undefined 的时候八成是又忘了写.value 了
图片缩放
以宽度为标准缩放图片
动态绑定 width
<div
v-for="(cameraInfo, index) in cameraInfoList"
:key="index"
:class="`imgs-content-item ${activeCamera.length ? 'one-camera' : ''}`"
:style="`width:${imgWidth}`"
>
设定宽度
const photoContentWidth = ref<number>(0); // photo-content的宽
计算图片尺寸
// 一个相机图片的高
const imgHeight = computed(() => (activeCamera.value.length ? photoContentHeight.value : photoContentHeight.value / 2));
const imgWidth = computed(() => (activeCamera.value.length ? photoContentWidth.value : photoContentWidth.value / 2));
加载时计算缩放比例
const imageLoaded = (e: any) => {
imageRate.value = e.target.naturalWidth / e.target.naturalHeight;
imageRate.value = e.target.naturalHeight / e.target.naturalWidth;
naturalWidth.value = e.target.naturalWidth;
naturalHeight.value = e.target.naturalHeight;
resetActivePoint(); // 图片有时加载缓慢,需要重新选中
activeCameraOptions.value.unshift({ value: '', label: '', icon: 'icon-menu' });
}
// 避免有些时候没有高度
!photoContentHeight.value && updateContentStyle();
!photoContentWidth.value && updateContentStyle();
},
{ deep: true }
);
监听
watch(
() => drawerStyleWidth.value,
() => drawerStyleHeight.value,
async (newValue: number) => {
const xRate = newValue / naturalWidth.value;
const yRate = imgHeight.value / naturalHeight.value;
await imageDrawersOperation('updateCanvasStyle', newValue, imgHeight.value, xRate, yRate);
const xRate = imgWidth.value / naturalWidth.value;
const yRate = newValue / naturalHeight.value;
await imageDrawersOperation('updateCanvasStyle', imgWidth.value, newValue, xRate, yRate);
// 再次选中
resetActivePoint();
}
);
图片缩放,依据高度计算宽度横竖颠倒后缩放比例不对:因为 rate 计算没有修改
计算属性
带参数的计算属性
const imageDrawerSrc = computed(() => (cameraInfo: any) => {
const leftPhotoSrc =
activeForm.value === ActivePhotoForm.灰度图 ? cameraInfo.texture_image_url : cameraInfo.rgb_image_url;
return leftPhotoSrc;
});
不带参数的计算属性
const isGreyOrColor = computed(
() => activeForm.value === ActivePhotoForm.灰度图 || activeForm.value === ActivePhotoForm.彩色图
);
左右按钮切换的值以及事件传递给父级
<i
:class="`iconfont icon-expandleft ${activeSide === 'left' ? 'active-side' : ''}`"
@click="setActiveSide('left')"
/>
<i
:class="`iconfont icon-expandright ${activeSide === 'right' ? 'active-side' : ''}`"
@click="setActiveSide('right')"
/>
设置样式
.active-side {
color: $color-primary;
}
初始化并暴露
const activeSide = ref<string>('');
type EmitType = {
(e: 'setActiveSide', activeSide: string): void;
};
const emit = defineEmits<EmitType>();
const setActiveSide = (side: string) => {
activeSide.value = side;
};
watch(displayShapes, () => {
emit('setDisplayShapes', displayShapes.value);
});
defineExpose({
activeSide
});
父组件接受
<drawer-operation
ref="drawerOperationRef"
@setActiveSide="setActiveSide"
/>
调用
// 左右拓展
const setActiveSide = (activeSide: string) => {
imageDrawersOperation('setActiveSide', activeSide);
};
读取子组件中的数据:drawerOperationRef.value.activeSide = '';
对象 key 不定的时候读取变量
对象:
list = [
{
id:1,
left_extend_image_url:qwer
},
{
id:3,
right_extend_image_url:asdf
},
{
id:2,
left_extend_image_url:zxcv
},
]
读取 id 用 list.id,但是_extend_image_url 是个变量,不能用. 来读,用
if (respItem.length) {
item[`${changeKey}_extend_image_url`] = respItem[0][`${changeKey}_extend_image_url`];
}
按钮切换状态
需求,两个按钮只能激活一个,并且点一下激活点两下重置

<i
:class="`iconfont icon-expandleft ${activeSide === 'left' ? 'active-side' : ''}`"
@click="setActiveSide('left')"
/>
<i
:class="`iconfont icon-expandright ${activeSide === 'right' ? 'active-side' : ''}`"
@click="setActiveSide('right')"
/>
const setActiveSide = (side: string) => {
activeSide.value = activeSide.value === side ? '' : side;
};
不可以赋先在判断,因为可能点完左再点右
defineExpose 父调用子方法和变量
在 setup 函数中,存在 defineExpose 语法糖,用于完成子向父传参数,不调用方法
子组件暴露数据:
defineExpose({
activeSide
})
父组件定义 ref 用于调用
<drawer-operation
ref="drawerOperationRef"
/>
const drawerOperationRef = ref<InstanceType<typeof DrawerOperation>>();
调用直接drawerOperationRef.value.activeIndex,调用方法的话在后面加上括号
defineEmit 子调用父方法和变量
父组件定义
const handleDeletePhoto = async (id: number, stop_frame_id: number) => {
const stopTableDetailRef = stopTableDetailRefs.value[stop_frame_id];
if (stopTableDetailRef) {
await stopTableDetailRef?.batchDelete(id);
}
};
父组件传递
<image-detail-dialog
ref="imageDetailDialogRef"
@handle-delete-photo="handleDeletePhoto"
/>
子组件定义
type EmitType = {
(e: 'handleDeleteItem', id: number): void;
};
const emit = defineEmits<EmitType>();
子组件调用
const handleDelete = () => {
emit('handleDeleteItem', photoItem.value?.id as number);
};
祖孙组件通信
provide inject(记得注册)
爷组件:
export default defineComponent({
// ...
setup () {
// provide一个ref
const msg = ref<string>('Hello World!');
provide('msg', msg);
// provide一个reactive
const userInfo: Member = reactive({
id: 1,
name: 'Petter'
});
provide('userInfo', userInfo);
}
孙组件:
export default defineComponent({
setup () {
// 获取数据
const msg = inject('msg');
const userInfo = inject('userInfo');
// 打印刚刚拿到的数据
console.log(msg);
console.log(userInfo);
}
})
给数组添加 value 属性
headFrameOptions 是个数组,但是选项需要 label 和 value 两个属性
const open = () => {
headFrameOptions.value = props.headFrameList?.map((item, index) => {
return {
value: index,
label: item
};
});
dialogFormRef.value?.open();
};
表格里拼接字符串
<el-table-column
prop="carriage_name"
label="关联车厢"
>
<template #default="{ row }">{{ row.carriage_name }} 车厢</template>
</el-table-column>
动态 ref
解决展开后的 table 绑顶的是相同数据
<stop-table
:ref="el => setStopDetailRef(el, item.id)"
:region-id="item.id"
/>
const stopTableRefs = ref<any>({});
const setStopDetailRef = (el: any, id: number) => {
if (el) {
stopTableRefs.value[id] = el;
}
};
// 遍历取出各个ref中所有的id并拼接
const allStopIds = computed<number[]>(() => {
const ids: number[] = [];
activeNames.value.map((regionId: number) => {
if (stopTableRefs.value[regionId]?.allDataIds.length) {
ids.push(...stopTableRefs.value[regionId].allDataIds);
}
});
return ids;
});
折叠后选择和 allid 都在
调用 reset 方法无效,因为用的的引入的 allIds 和 selectedIds,本质上是一样的,所以这里不能引入,要单独定义
最里层折叠有问题
在 get 请求的时候,需要赋值一个变量 disexpand,不然表格无法判断是展开还是折叠
子组件传给父组件没问题,父组件转发时 undefined
get 请求先后顺序,父组件转发时子组件还没赋值。
解决: watch 看变量的变化,变了就赋值给新变量然后传递
第一行展开获取不到数据,unmounted 里没法读到 prop 的数据,传过来的数组显示无定义
因为子组件加载比父组件早,所以传值时父组件还没有值
解决:定一个新变量=prop.regionlist,虽然会显示不是 ref 但是后面都调用这个就都没问题了
不是 ref 可以通过 toRef 解决
阻止点击事件冒泡
@click.stop写在子级阻止冒泡
图片详情弹窗层级混乱
详情列表包括图片列表和图片弹框,渲染时每一行都会拥有自己的图片弹窗,这时父级修改样式导致样式穿透
解决:弹窗写在表格层,而不是图片列表层
没有数据的时候展开折叠会频繁掉接口
在 get 请求里写一个 flag,默认为 false,调用后变为 true,只有为 false 时才调用接口
删除后弹窗刷新数据,并改变 activeindex
第一个后移,中间的前移,没有直接关闭
// 删除时候需要监听 用于刷新数据
watch(
() => photoList.value.length,
() => {
activeIndex.value = !activeIndex.value ? activeIndex.value : activeIndex.value - 1;
imageSrc.value = photoItem.value?.texture_image_url as string;
dialogCancel();
!photoList.value.length && cancel();
}
);
带有默认值的 props
const props = withDefaults(defineProps<{ photoList: PhotoListModel[] }>(), {
photoList: () => []
});
显示 undefined
提前判断 存在才走
if(){
return
}
v01.2.1.zip 提取名字(v01.2.1)
不能用点分割,不然会变成 v01
const lastPointIndex = file.name.lastIndexOf('.');
// 上传文件的格式
const splitFormat = file.name.substring(lastPointIndex + 1, file.name.length);
// 上传文件的名字
const splitName = file.name.substring(0, lastPointIndex);
用 key 强制刷新组件
<cy-image-collapse
v-if="!index && standardImageList.length"
:key="key"
:min-height="159"
:max-height="520"
>
<div class="image-list">
<image-item
v-for="(item, index) in standardImageList"
:key="index"
:data="item"
@click="itemClick(index)"
/>
</div>
</cy-image-collapse>
const searchImage = async () => {
await getStandardImageList();
// 获取数据后刷新组件
key.value++;
};
枚举
enum LineStatus {
进行中,
已通过,
未通过,
忽略测试结果
}
因为枚举默认从 0 开始,并且累加,写成这样也可以
enum LineStatus {
进行中=0,
已通过=1,
未通过=2,
忽略测试结果=3
}
应用:
<span
:class="
row.test_result === LineStatus.已通过 ? 'pass' : row.test_result === LineStatus.未通过 ? 'failed' : ''
"
>
变量可以用
{{ LineStatus[row.test_result] }}
读取
文字垂直
writing-mode: vertical-lr;
text-orientation: upright;
letter-spacing: 4px;
draggable.js
import draggable from 'vuedraggable';
<draggable
:list="item.component"
:animation="200"
item-key="name" //必须写
class="component"
>
//插槽必须写 并且只能含一个子标签,哪怕是注释也不行
<template #item="{ element }">
<div class="component-item">
<div class="top" />
<div class="name">{{ element.name }}</div>
</div>
</template>
</draggable>
表格跳转到最后一行
表格记得写 ref
const jumpToBottom = () => {
setTimeout(() => {
const ScrollTop = 55 * props.data.length;
//1、50为每一行的高度;2、tableData.data.length为数据数量
table.value?.setScrollTop(ScrollTop);
table.value?.setScrollLeft(0);
}, 100);
};
TS 语法
非必传参数用?:,有时候读不到参数就?
组件传值不用private改成public
判断字符串是否为空,不用drawerOperationRef.value?.activeSide===''用drawerOperationRef.value?.activeSide.length
在父组件里设置子组件的ref=drawerOperationRef,可以直接读取子组件中的数据:drawerOperationRef.value.activeSide ;
代码优化
length 判断不用if (ids.length>1) {},用if (ids.length) {}
用三目运算,不用 if else
可以提前 return 就 return 出去
有的时候用 some 或者 every 可以少写一层 if
git
合并远程 master 上的代码
git pull origin master
撤回已经 push 的代码
git log查看需要撤回的版本的上一个版本的版本号
git reset 版本号 --soft
git push origin xxx --force
正则表达式
0.1-10:/^(?:(?:[1-9](?:\.[0-9])?)|0\.[1-9]|10|10.0)$/
正整数:/^[+]{0,1}(\d+)$/
1-99 整数:/^(([1-9][0-9])|[1-9])$/
正整数:/^[0-9]*[1-9][0-9]*$/
大小写,数字,(_ -.),10 个字符以内: /^[A-Za-z0-9-_.]{1,10}$/
React
发请求在 useEffect 里,[]就是挂载时发送
useState 太多了就用 useReducer,相当于一个 switch
严格模式下 useEffect 就是执行两次,不用管
set 的时候直接 push 数组没用,得...拼接
特别神奇,then 和 await 不一样,useEffect 不用 then 会出现数据加载不全的情况(感觉像请求还没完事但那边已经渲染完了)
没事别用 useContext
useColums 的时候不传弹窗组件,用父子事件的方式触发弹窗
less 的样式穿透是/deep/
tailwind 的 class 是有顺序的,找不到复制搜(为什么会有这么恶心的东西)
Next.js 实现 SSR 是通过 getServerSideProps 和 getStaticProps,getServerSideProps 类似于 getStaticProps,但两者的区别在于 getServerSideProps 在每次页面请求时都会运行,在构建时不运行,两者执行时机相反
Next.js 不允许在除 _app.js 文件以外的任何文件中导入全局 CSS,可以把 css 文件改名,.css 改为 .module.css 就能正常使用了,CSS Modules 允许你在组件级别导入 CSS,而不会影响全局样式,但是实际引入的时候那确实是不知道什么原因,反正引入不了,还是放在全局样式了,起了个不会被穿透的名字
npm 包
在 utils 里调用函数是可以被执行的,都得判断项目环境再搞
搞个 story 调试很方便
chrome 插件安装好才能调试,必须判断当前页面是不是 active
如果想要在页面中插入元素需要用原生 DOM 去写,啥 tsx react 的都不行
cssText 可以用字符串的形式定义样式,按钮得用 pointer 老忘
localstorage 读数据会跨域,searchURLParams 可以获取 url 里?后面的东西
keytoValue 不用映射到 i18n 给的那个存储对象上,直接 window[xx] = keytoValue
判断是否具备该功能是 url 和所处环境两个条件
ide 插件
调试的时候必须切换到子项目!!!!!cd 没用!!!!!!!!卡了四天啊!!!!!!!!!!我不是弱智谁是弱智!!!!!!!!!!!!!!
读文件用 node 的 fs 读,写也是,字符串正则匹配两次 replace
搜了一下还可以用 AST 搞,直接在打包的时候改不影响开发代码(听起来怎么比我们这个好)
如果文字回车了,不仅会/n,还会(很多空格)/n(很多空格),正则不好写