作者: Allison

  • 迁移monorepo保留原仓库提交记录

    将代码仓库转移到monorepo如果直接将代码复制到大仓中就会丢失原本的代码提交记录。而大多数情况下我们希望保留提交记录以方便后期问题定位等情况。

    • 在原仓库中进行以下操作,这里注意文件移动需要时间,确保全部文件移动完成后再进行下一步提交操作
    # 移动文件
    git mv -k * projects/{你的项目名}
    git mv -k .* projects/{你的项目名} 
    
    # 提交仓库
    git commit && git push
    • 在迁移目标仓库中进行以下操作
    git remote add -f my-app {原仓库git地址}
    
    git merge my-app/{分支名} --allow-unrelated-histories
    
    git remote remove my-app
    git push
  • parseInt

    parseInt永远将输入解析为字符串

    parseInt({toString: () => 2, valueOf: () => 1}); // -> 2
    Number({toString: () => 2, valueOf: () => 1}); // -> 1
    

    一个常见的栗子

    parseInt("apple"); // -> NaN
    parseInt("apple", 16); // -> 10
    

    上面这个例子是因为parseInt会一个个解析字符直到解析不了,所以解析十六进制中的a为10作为结果返回。

    parseInt Infinity

    parseInt("Infinity", 10); // -> NaN
    // ...
    parseInt("Infinity", 18); // -> NaN...
    parseInt("Infinity", 19); // -> 18
    // ...
    parseInt("Infinity", 23); // -> 18...
    parseInt("Infinity", 24); // -> 151176378
    // ...
    parseInt("Infinity", 29); // -> 385849803
    parseInt("Infinity", 30); // -> 13693557269
    // ...
    parseInt("Infinity", 34); // -> 28872273981
    parseInt("Infinity", 35); // -> 1201203301724
    parseInt("Infinity", 36); // -> 1461559270678...
    parseInt("Infinity", 37); // -> NaN
    

    parseInt null

    parseInt(null, 24); // -> 23
    

    上面的结果都是一样的解析过程,按字母表排序,每个字符对应数字是固定的。比如,I对应18,在18进制中只能解析0-17,所以parseInt("Infinity", 18); // -> NaN,在19进制中就能解析出结果:parseInt("Infinity", 19); // -> 18

    n对应23,在23进制中解析不了,只返回18,在24进制中就是18 * (24 ^ 5) + 23 * (24 ^ 4)+ 15 * (24 ^ 3)+ 18 * (24 ^ 2) + 23 * 24 + 18 = 151176378,其他依此类推。

    八进制

    parseInt("08"); // -> 8, 支持ES5
    parseInt("08"); // -> 0, 不支持ES5
    

    以0开头的数字可以被解析为10进制或8进制,ECMAScript 5规定为10进制,但不是所有浏览器都支持它。所以使用parseInt时指定进制是好习惯,有时可以避免不必要的bug。

    浮点数

    parseInt(0.000001); // -> 0
    parseInt(0.0000001); // -> 1
    parseInt(1/1999999); // -> 5
    

    parseInt 接受一个字符串参数并返回一个指定基数的整数。 parseInt 还会去除字符串参数中第一个非数字之后的任何内容。0.000001转换为字符串后是"0.000001",0.0000001转换后是"1e-7",所以返回1,1/1999999转换后是5.00000250000125e-7,返回结果是5。

  • 本地开发debug npm包

    使用pnpm包管理

    1. 在本地npm包目录下执行

    pnpm link --global

    2. 如果是cli,可直接在其他项目目录下调用

    3. 如果需要在其他项目中引入npm包,在其他项目目录下执行

    pnpm link --global npm-package-name

    使用npm包管理

    1. 在npm包目录下执行

    npm link

    2. 在其他目录使用

    npm link 本地npm包绝对路径

    官方文档:https://docs.npmjs.com/cli/v9/commands/npm-link

  • async/await class constructor

    背景:期望在新建类时调后台接口对类进行配置,配置成功后才可以执行其他操作。

    尝试封装类的构造函数为async/await,报错

    Class constructor may not be an async method

    原因:async函数返回值为promise,而构造函数返回值为object,同一个函数不可能返回值同时为promiseobject

    解决:参考 jQuery's ready()方法

    1. 类实现

        class myClass {
            constructor () {
    
            }
    
            async ready (callback) {
                await yourHttpRequest();
                // 回调函数绑定作用域
                callback.bind(this)();
            }
        }

    2. 调用

        const myObj = new myClass();
        myObj.ready(function() {
            // 保证配置完才能执行其他操作
        });
    

    参考:https://stackoverflow.com/questions/43431550/async-await-class-constructor

  • Vue 3 TypeScript项目报错类型{}不存在属性

    搜了一下tsconfig.json文件需要配置"jsx": "preserve",但我的config文件原先就有这个配置。因为项目一开始是可以正常报错的,迁移到大仓后就有问题了。中间也考虑了是否是配置文件位置的问题,调整了位置也没有解决问题。

    最后看到tsconfig.json文件另一个报错“找不到node的类型定义文件”

    原因是我没有安装@types/node,装上后重新打开IDE”类型{}不存在属性“和“找不到node的类型定义文件”报错都没有了。

    // npm
    npm i @types/node --save-dev
    
    // pnpm
    pnpm add @types/node

  • 【Rust】Hello World

    创建main.rs文件来编写一个将文本打印到屏幕上的小程序Hello, world!

    fn main() {
        println!("Hello, world!");
    }

    Rust 文件总是以 .rs 扩展名结尾。如果在文件名中使用多个单词,惯例是使用下划线将它们分开。例如,使用hello_world.rs而不是helloworld.rs。

    编译和运行

    Linux或macOS

    rustc main.rs
    ./main
    编译
    编译成功后,Rust 输出二进制可执行文件
    运行

    Windows

    rustc main.rs

    .\main.exe

    编译后,使用 Windows 上的 PowerShell执行 ls 或者 使用 CMD 时执行

     dir /B %= the /B option says to only show the file names =%

    都可以看到三个文件:

    main.exe
    main.pdb
    main.rs

    扩展名为.rs的源代码文件、可执行文件(在 Windows 上为main.exe,但在所有其他平台上为main),以及使用 Windows 时包含调试信息且扩展名为.pdb的文件。

    程序剖析

    函数main始终是每个可执行 Rust 程序中运行的第一段代码。

    1. 函数体包裹在{}。Rust 要求所有函数体都用大括号括起来。
    2. Rust 风格是用四个空格缩进,而不是制表符。
    3. 使用 a!意味着您正在调用宏而不是普通函数,并且宏并不总是遵循与函数相同的规则。上面代码中println!调用 Rust 宏,如果它调用了一个函数,输入为println(不带!)
    4. 分号结束一行,表示表达式已结束

    编译和运行是分开的步骤

    Rust 是一种提前编译语言,这意味着可以编译一个程序并将可执行文件提供给其他人,他们可以在没有安装 Rust 的情况下运行它。

    如果给别人一个.rb.py.js文件,他们需要安装 Ruby、Python 或 JavaScript 实现(分别)。但是在这些语言中,只需要一个命令来编译和运行程序。一切都是语言设计的权衡。

  • 微信小程序保存图片到本地相册

    2022/10/29 更新

    可以利用canvas来绕过下载图片到本地受微信下载白名单的限制

    方案:

    1. canvas 绘制图片
    2. wx.canvasToTempFilePath拿临时路径
    3. wx.saveImageToPhotosAlbum 保存图片

    代码片段:https://developers.weixin.qq.com/s/iKZ2ymm87uDj

    原文章

    小程序使用wx.saveImageToPhotosAlbum保存图片到本地相册,该方法不能直接保存网络路径地址

    如果页面渲染的图片链接是网络地址,可以通过wx.downloadFile获取文件的本地路径 / 临时文件路径后,再调用保存图片方法

    除了保存图片到本地,如果还需要用到图片其他信息,可以使用wx.getImageInfo,该方法是对wx.downloadFile方法的封装,也会返回图片的本地路径,两个方法都需要先在管理后台配置下载白名单

  • Element Plus自定义命名空间

    使用Vue 3 + Element Plus开发新项目时,因为要引入的公共组件是Vue 2 + Element UI实现的。同一个项目中既有Element UI,又有Element Plus,会造成CSS冲突。

    官方给出的解决方案是给Element Plus修改命名空间,如:原来的都是el-开头,我们可以改成ep-开头。

    • 使用 ElConfigProvider 包装您的根组件
    <!-- App.vue -->
    <template>
      <el-config-provider namespace="ep">
        <!-- ... -->
      </el-config-provider>
    </template>
    
    • 在src目录下创建 styles/element/index.scss
    // styles/element/index.scss
    // we can add this to custom namespace, default is 'el'
    @forward 'element-plus/theme-chalk/src/mixins/config.scss' with (
      $namespace: 'ep'
    );
    // ...
    
    • vite.config.ts 中导入 styles/element/index.scss
    import { defineConfig } from 'vite'
    // https://vitejs.dev/config/
    export default defineConfig({
      // ...
      css: {
        preprocessorOptions: {
          scss: {
            additionalData: `@use "~/styles/element/index.scss" as *;`,
          },
        },
      },
      // ...
    })
    

    完全按照教程在全新的项目中试验会发现虽然类名都变成ep-开头了,但是element 源码中的类依然是el-

    原因:

    1. 官方教程中使用了~别名,如果你在vite / webpack中没有配置~别名,自定义命名空间就不会生效
    2. 如果是全部导入,需要在styles/element/index.scss中引入Element源码
    // we can add this to custom namespace, default is 'el'
    @forward "element-plus/theme-chalk/src/mixins/config.scss" with (
      $namespace: "ep"
    );
    
    @use "element-plus/theme-chalk/src/index.scss" as *;
    注意:如果是按需导入组件,需要使用ElementPlusResolver,具体配置参考官方模版
    
    import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
    
    Components({
          resolvers: [
            ElementPlusResolver({
              importStyle: 'sass',
            }),
          ]
    })
    
  • 在Vue模版中定义临时变量

    使用场景:需要在Vue中多次用到同一个表达式,表达式太长影响代码可读性且不方便放在data中

    可以使用void 表达式,不会影响DOM

    <template>
      <div>
        {{ void (value1 = 1, value2 = 2) }} // 不会渲染额外的 DOM 
      </div>
    </template>
    

    使用:set定义临时变量占位符,不能用在方法中

    <div id="app">
      <div
        v-for="id in users"
        :key="id"
        :set="user = getUser(id)"
      >
        {{ user.name }}: {{ user.age }} years old
      </div>
    </div>
    
    
    const app = new Vue({
      el: '#app',
      data: {
        users: [1, 2, 3, 4],
        usersData: {
          1: {name: 'Mohamed', age: 29},
          2: {name: 'Ahmed', age: 27},
          3: {name: 'Zizo', age: 32},
          4: {name: 'John', age: 13},
        }
      },
      methods: {
        getUser(id) {
          return this.usersData[id];
        },
      },
    });

    举个例子:后台返回表格的数据中有一个字段是数组,要求默认只展示前三个,超过三个支持展开收起。效果如下:

    代码如下:

    <el-table ref="table">
    <el-table-column label="文件名">
        <template slot-scope="scope">
            <div v-if="scope.row.fileLists && scope.row.fileLists.length">
                {{ void (length = scope.row.fileLists.length) }}
                <div
                    :style="
                        scope.row.isOverflow
                            ? { height: '69px', overflow: 'hidden' }
                            : { height: 'auto' }
                    "
                >
                    <p v-for="(item, index) in scope.row.fileLists" :key="item">
                        {{ item }}
                        <span v-if="index === length - 1">({{ length }})</span>
                    </p>
                </div>
                <el-button
                    v-if="length > 3"
                    type="text"
                    @click.prevent="scope.row.isOverflow = !scope.row.isOverflow; $refs.table.doLayout()"
                >
                    {{ scope.row.isOverflow ? '展开全部' : '收起' }}
                </el-button>
            </div>
            <p v-else>暂无文件</p>
        </template>
    </el-table-column>
    </el-table>

    参考链接:https://forum.vuejs.org/t/topic/30395/8,https://stackoverflow.com/questions/43999618/how-to-define-a-temporary-variable-in-vue-js-template

  • append vs appendChild

    append和appendChild都用于在DOM中添加新的元素,区别如下:

    1. append传参可以为DOMString和Node节点,appendChild传参只能为Node节点
    // 1. append传Node节点
    const parent = document.createElement('div');
    const child = document.createElement('p');
    parent.append(child);
    
    // 2. append传文本,被插入的 DOMString对象等价为 Text 节点
    const parent = document.createElement('div');
    parent.append('Appending Text');
    
    // 3. appendChild传Node节点
    const parent = document.createElement('div');
    const child = document.createElement('p');
    parent.appendChild(child);
    
    // 4. 报错:appendChild传文本
    const parent = document.createElement('div');
    parent.appendChild('Appending Text');
    appendChild不支持传字符串

    2. append没有返回值,appendChild返回被创建的Node节点

    const parent = document.createElement('div');
    const child = document.createElement('p');
    const appendValue = parent.append(child);
    console.log(appendValue) // undefined
    
    const appendChildValue = parent.appendChild(child);
    console.log(appendChildValue) // <p><p>
    

    3. append可以同时添加多个元素,appendChild同时只能添加一个

    const parent = document.createElement('div');
    const child = document.createElement('p');
    const childTwo = document.createElement('p');
    parent.append(child, childTwo, 'Hello world'); // Works fine
    
    parent.appendChild(child, childTwo, 'Hello world');
    // Works fine, but adds the first element and ignores the rest
    

    4. 浏览器兼容区别,appendChild在各浏览器中兼容性较好

    其他:DOMString是一个UTF-16字符串。由于JavaScript已经使用了这样的字符串,所以DOMString 直接映射到 一个String