7.其他

备注:这个栏目,存放一些过小的问题(不适合单独成一章的),如果后期拓展的很多,就可以考虑单独抽离出去一部分知识


🌟第一部分:Git && GitHub

1.Git常用命令

# 创建新仓库
git init

# 检出仓库
git clone username@host:/path/to/repository

# 来一次添加所有改变的文件
git add -A . 

# 表示添加所有内容
git add -A 

# 表示添加新文件和编辑过的文件不包括删除的文件
git add . 

# 表示添加编辑或者删除的文件,不包括新添加的文件
git add -u

# 你的改动已经提交到了 HEAD,但是还没到你的远端仓库
git commit -m '代码提交信息'

# 推送改动
git push origin master



# 查看状态
git status 


# 查看每个分支的最新提交记录
git branch -av

# 查看每个分支属于哪个远程仓库
git branch -vv

# 下载Git项目
git clone 地址


# 查看用户名
git config user.name

# 查看用户邮箱
git config user.email

# 修改用户名
git config --global user.name "Your_username"

# 修改用户邮箱
git config --global user.email "Your_email"

2.shields.io使用(给项目添加徽章)

  • MIT使用举例:https://shields.io/category/license/ (要选GitHub哦)
  • 使用说明:https://www.sohu.com/a/134390224_655394

💫第二部分:包管理工具(npm)

1.npm基本使用

# 初始化项目(也可以写 npm init -y)
npm init

# 安装(简写 npm i)
npm install

# 帮助(简写 npm -h)
npm --help

# 全局标志(简写 -g)
--global

# 保存为开发依赖(简写 -D)
--save-dev

# 保存为生产依赖(简写 -d)
--save

# 卸载npm安装的包
npm uninstall 包的名称 -g

设置 npm init属性,这样在初始化的时候,就不用每次都写了

npm config set init.author.name "Zhu Kunpeng" npm config set init.author.email "270750933@qq.com" npm config set init.author.url "itzkp.com" npm config set init.license "MIT"

让脚本跨平台兼容:cross-env

  • 安装:npm i -D cross-env
// 然后在任何环境变量之前包括关键字cross-env,如下
{
  "scripts": {
    "dev": "cross-env node app.js"
  }
}

参考掘金文章,作者小智,感谢


2.发布包到NPM上


# 登录(会让你输入账号密码,邮箱)
npm adduser

# 上传(注意package.json里面的name不要和npm仓库重复)
npm publish

✨第三部分:前端工程化工具(Webpack)

  • 首先肯定要 npm init
  • 安装webpack:npm i webpack webpack-cli -D
  • 新建 webpack.config.js 文件
  • 常用loader
    • npm i file-loader -D (用于移动字体文件等)
    • npm i url-loader -D(用于打包图片等)
    • npm i css-loader style-loader -D(打包基础CSS样式文件)
    • npm i node-sass sass-loader -D(打包scss样式文件)
    • npm i postcss-loader autoprefixer -D(自动添加厂商前缀,需要建立一个新的文件 postcss.config.js )
  • 常用plugin
    • npm i html-webpack-plugin -D (会在打包完成之后,自动生成一个HTML文件,并把打包之后的JS自动引入到这个HTML文件中)
  • 其他
    • SourceMap
    • WebpackDevServer
    • Hot Module Replacement
// webpack.config.js 配置示例

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');

export default {
      
    // 环境(默认是production,开启压缩等默认配置 || development是开发环境,不压缩...)
    mode: 'production',
    
    // 有了这个在就能在出错的时候,准确定位到是哪里的问题,这个devtool 还有很多配置,具体查阅文档
    devtool: 'cheap-module-eval-source-map',

    // 启动一个serve:npx webpack-dev-server
    devServer: {
      contentBase: './dist', // 启动的目录
      open: true, // 自动打开浏览器
      hot: true, // 启动热更新
      hotOnly: true // 不刷新页面
    }, 

    // 入口:简写 entry: '入口文件地址'
    entry: {
        main: '入口文件地址',
        // main2: '入口文件2地址' 多文件入口
    },

    // 模块规则
    module: {
      rules: [
        
        // file-loader
        {
          test: /\.(字体文件后缀)$/,
          use: {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]', // 
              outputPath: 'font/' // 打包输出文件夹
            }
          }
        },

        // url-loader
        {
          test: /\.(jpg|png|gif)$/,
          use: {
            loader: 'url-loader',
            options: {
              name: '[name].[ext]', // 
              outputPath: 'images/', // 打包输出文件夹
              limit: 2048 // 如果小于 2kb,就会打包进JS,如果大于就打包到文件夹中
            }
          }
        },

        // 打包静态样式
        {
          test: '/\.css$/',
          use: ['style-loader', 'css-loader', 'sass-loader', 'postcss-loader']
        }
      ]
    }

    // 插件
    plugins: [
      new HtmlWebpackPlugin({
        // 配置,index.html 模板,以此为模板进行注入
        template: './test/index.html'
      })
    ]

    // 输入
    output: {
        filename: '输入文件名', // 输出文件夹名称, 可以使用占位符, 例如'[name]_[hash].[ext]'
        publicPath: 'https://cdn.com.cn', // 配置引入前缀,如果静态资源在CDN上这个很有用
        path: path.resolve(__dirname, '输入文件夹') // 输入目录
    },

}

postcss.config.js 配置示例

// 默认是这样写的
module.exports = {
  parser: 'sugarss',
  plugins: {
    'postcss-import': {},
    'postcss-cssnext': {},
    'cssnano': {}
  }
}

// 我们也可以这么写
// 这么写的作用是,当我们使用 postcss-loader 的时候,会使用 autoprefixer 这个插件
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

.babelrc 配置示例



☀️第四部分:测试(Jest)

1.Jest 常用匹配器速查


// ========== 和真假相关的匹配器 ===========

expect(1).toBe(1) // 通过:有点类似于 === 

expect({ name: '朱昆鹏' }).toEqual({ name: '朱昆鹏' }) // 通过:匹配内容相等

expect(null).toBeNull() // 通过:匹配是否是 null

expect(undefined).toBeUndefined() // 通过:匹配是否是 undefined

expect(true).toBetruthy() // 通过:匹配 是否为真,填 1 也行

expect(0).toBeFalsy() // 通过:匹配是否为假,填 false 也行

expect(0).not.toBeFalsy() // 不通过:可以通过 not 进行取反

// ...


// ========== 和数字相关的匹配器 ===========

expect(10).toBeGreaterThan(9) // 通过,匹配的是 10 是否 比 9 大

expect(10).toBeLessThan(11) // 通过,匹配的是 10 是否 比 11 小

expect(10).toBeGreaterThanOrEqual(10) // 通过,匹配的是 10 是否 大于等于 10

expect(11).toBeLessThanOrEqual(11) // 通过,匹配的是 11 是否小于等于 11

expect(0.1 + 0.2).toBeCloseTo(0.3) // 通过,判断小数是否相同,直接比较的话会有精度缺失的问题,比较不了

// ...


// ========= 和字符串相关的匹配器 ===========

expect('朱昆鹏').toMatch('昆鹏') // 通过,匹配的是 '朱昆鹏' 中是否有 '昆鹏' 字符 | 还可以写正则表达式

// ...


// ========= 和数组相关的匹配器 ===========

expect([1,2,3]).toContain(2) // 通过,匹配的是 数组中 是否包含 2 这一项,也可以判断 Set, Map 这种类型的

// ...


// ========= 和异常相关的匹配器 ===========

expect(throw new Error('出现错误')).toThrow() // 通过,匹配的是 是否出现异常

// ...



❄️第五部分:CSS预处理器


💥第六部分:跨平台开发(Electron)


🔥第七部分:日常开发中总结出来的组件(移动端)

1.弹框组件(DialogGray.vue)

使用方法

<dialog-gray v-model="dialogVisible" :opacity="opacity" :is-click="isClick">
    //opacity 蒙层透明度 不传默认为.5
    //isClick 蒙层是否可点击 默认可点击,传false即不可点击
    //阻止点击冒泡在蒙层内部加@click.stop
    //阻止滑动冒泡在蒙层内部加@touchmove.stop
    //弹窗过大,内部div加class="part-scroll" 去掉滚动条:.part-scroll::-webkit-scrollbar {display: none;}
</dialog-gray>

<template>
  <div
    v-if="dialogVisible"
    ref="dialogGray"
    class="dialogSt"
    :style="background"
    @click="closeDia"
    @touchmove.stop.prevent
  >
    <slot />

  </div>
</template>
<script>
export default {
    name: 'DialogGray',
    model: {
        prop: 'dialogVisible',
        event: 'change'
    },
    props: {
        dialogVisible: {
            type: Boolean,
            default: false
        },
        opacity: {
            type: Number,
            default: 0.5
        },
        isClick: {
            type: Boolean,
            default: true
        }
    },
    data() {
        return {
            top: '',
            background: {
                'background': `rgba(0,0,0,${this.opacity})`
            }
        };
    },
    watch: {
        dialogVisible: {
            immediate: true,
            handler(newval) {
                const bodyEl = document.body;
                if (newval) {
                    this.$nextTick(() => {
                        bodyEl.appendChild(this.$refs['dialogGray']);
                    });
                    this.top = window.scrollY;
                    bodyEl.style.position = 'fixed';
                    bodyEl.style.top = -this.top + 'px';
                } else {
                    bodyEl.style.position = '';
                    bodyEl.style.top = '';
                    window.scrollTo(0, this.top); // 回到原先的top
                }
            }
        }
    },
    methods: {
        closeDia() {
            if (!this.isClick) {
                return;
            }
            this.$emit('change', false);
        }
    }
};
</script>
<style scoped>
.dialogSt {
  position: fixed;
  width: 100%;
  height: 100%;
  z-index: 99;
  left: 0;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

2.奖励轮播组件(RewardWheel.vue)

使用方法

<reward-wheel
  :wheel-row-height="23"
  :wheel-rem-size="37.5"
  :wheel-time="4000"
  :wheel-number="playMsg.length - 2">
  <!-- 书写业务代码区域 -->
  <div></div>
</reward-wheel>
  • wheel-row-height:滚动的距离
  • wheel-rem-size:rem相对单位(适配用)
  • wheel-time:滚动一个的时间
  • wheel-number:滚动的数量

<template>
  <div :style="loopStyle">
    <slot />
  </div>
</template>

<script>
export default {
    props: {
        // 默认轮播数量
        wheelNumber: {
            type: Number,
            default: 0
        },
        // 轮播一个需要的时间
        wheelTime: {
            type: Number,
            default: 3000
        },
        // 每次滚动的距离
        wheelRowHeight: {
            type: Number,
            default: 10
        },
        // 当前环境rem对px的值
        wheelRemSize: {
            type: Number,
            default: 32
        }
    },
    data() {
        return {
            // 中奖信息(轮播)
            loopStyle: {
                transform: '',
                transition: 'none'
            },
            activeIndex: 0
        };
    },
    watch: {
        activeIndex(val) {
            const transition = this.activeIndex === 0
                ? 'none'
                : 'all .5s';
            const top = ((-val * this.wheelRowHeight) / this.wheelRemSize) + 'rem';
            this.loopStyle = {
                transform: `translateY(${top})`,
                transition: transition
            };
        }
    },
    mounted() {
        this.loopListTool(); // 自动轮播函数
        this.distanceAdaptationTool(); // 自动适配各个屏幕滚动距离
    },
    methods: {
        // 自动轮播函数
        loopListTool() {
            this.$nextTick(() => {
                window.clearInterval(this.interval);
                this.interval = setInterval(() => {
                    if (this.activeIndex < this.wheelNumber - 1) {
                        this.activeIndex += 1;
                    } else {
                        this.activeIndex = 0;
                    }
                }, this.wheelTime);
            });
        },
        // 自动适配各个屏幕滚动距离
        distanceAdaptationTool() {
            const rem = this.wheelRowHeight / this.wheelRemSize;
            this.wheelRemSize = document.documentElement.clientWidth / 10;
            this.wheelRowHeight = rem * this.wheelRemSize;
        }
    }
};
</script>
  • end-time-data: 结束时间 | String(xxxx-xx-xx xx:xx:xx)或者 Number(时间戳)
  • @time-end: 倒计时结束的时候触发

3.倒计时组件(CountDown.vue)

使用方法

<!-- end-time-data:2020-01-30 00:00:00 -->
<div style="padding: 5px; border: 1px solid red;" >
  <count-down 
    :end-time="1580313600000"
    @time-end="timeEndEvent">
  </count-down>
</div>

<template>
  <div>
    {{ time }}
  </div>
</template>

<script>
export default {
    props: {
        // 结束时间
        endTime: {
            type: [Number, String],
            default: null
        }
    },
    data() {
        return {
            time: '', // 时间展示
            flag: false // 是否启动时间
        };
    },
    mounted() {
        this.visibilitychangeEvent();
        this.init();
    },
    methods: {
        init() {
            if (this.endTime === null) return;
            const time = setInterval(() => {
                if (this.flag == true) {
                    clearInterval(time);
                    return;
                }
                this.timeDown();
            }, 500);
        },
        // 倒计时组件 核心逻辑
        timeDown() {
            // 结束时间
            let endTimeData = null;
            typeof this.endTime === 'number'
                ? endTimeData = new Date(this.endTime)
                : endTimeData = this.timeStrToCorrectTool(this.endTime);

            // 当前时间(默认是系统当前时间)
            const nowTime = new Date();

            // 相差时间
            const leftTime = parseInt((endTimeData.getTime() - nowTime.getTime()) / 1000);

            if (leftTime <= 0) {
                this.time = `0小时 0分 0秒`;
                this.flag = true;
                this.$nextTick(() => {
                    this.$emit('time-end');
                });
                return;
            }

            const d = parseInt(leftTime / (24 * 60 * 60));
            const h = this.formateTool(parseInt(leftTime / (60 * 60) % 24 + d * 24));
            const m = this.formateTool(parseInt(leftTime / 60 % 60));
            const s = this.formateTool(parseInt(leftTime % 60));
            this.time = `${h}小时 ${m}${s}`;
        },
        // 将小于10的数 转为 0开头的
        formateTool(time) {
            return time >= 10
                ? time
                : `0${time}`;
        },
        // 兼容IOS 日期字符串 格式NaN的问题
        timeStrToCorrectTool(endTime) {
            const arr = endTime.split(/[- :]/);
            const endTimeData = new Date(arr[0], arr[1] - 1, arr[2], arr[3], arr[4], arr[5]);
            return endTimeData;
        },
        // 息屏检测事件,防止定时器关闭
        visibilitychangeEvent() {
            document.addEventListener('visibilitychange', () => {
                this.init();
            }, false);
        }
    }
};
</script>

📚参考列表(致敬)