肖虎威个人博客 - Web Program
type: channel
link: https://blog.xiaohuwei.cn/category/Web-Program/
items: 前端的事务处理和任务队列
items: NodeORM 数据库插件 Sequelize 基本使用
items: 使用微信交易组件打造自己的小商城
items: Vue3快速上手
items: Taro + TS 封装pick组件 实现省市县三级联动
items: 优雅解决项目中重复多次important
items: 嘿！ 这是一篇前端小心得～
items: 原生JS实现复制文本
items: 简单理解JS函数柯里化
items: 手摸手教你 Taro+dva+Hooks快速开发小程序

前端的事务处理和任务队列
type: item
date: 2023-03-28T22:28:00+08:00
description: 事务处理 重要 事务处理解释 事务处理是一种将多个操作作为一个单独的、原子性的操作执行的机制。在事务处理中，多个操作被组合成一个单元，这个单元被视为单个不可分割的操作。这个单元中的所有操作都必须成功完成，否则整个事务将被回滚，回滚是指将所有操作撤销回到事务开始之前的状态。 事务处理通常应用于需要对多个数据进行修改的场景，如数据库管理系统中。在这种场景下，如果一个操作失败，所有已经完成的操作都必须撤销，以确保数据的一致性和完整性。事务处理通常包括以下四个属性：原子性（Atomicity）：整个事务作为一个单独的、不可分割的操作执行，要么全部成功，要么全部失败。一致性（Consistency）：事务执行前和执行后，系统的状态应该保持一致。隔离性（Isolation）：并发的事务之间应该相互隔离，以防止数据损坏和冲突。持久性（Durability）：一旦事务被提交，它的结果应该是永久性的，即使系统发生故障也应该如此。事务处理是一种重要的机制，它可以帮助保障数据的完整性和一致性，并且可以提高系统的可靠性和可用性。在前端的应用场景某个APP可能需要依次按照用户操作的特定顺序实现分步修改某些数据,假如步骤是3步,如果最后一步失败了,我需要吧第一步操作的结果进行回滚,这里就需要用到事务.在 JavaScript 中，原生的异步函数和 Promise 不支持回滚（rollback）操作，因为这些操作都是异步执行的，无法像关系型数据库一样实现回滚操作。但是，我们可以通过一些技巧来实现简单的回滚操作。例如，在执行事务之前，我们可以备份一些数据，以便在事务失败时恢复数据。如果事务成功，则删除备份数据。如果事务失败，则恢复备份数据以回滚事务。可以用到下面的代码实现事务处理和模拟简单的回滚:function performTransaction(transactionFunctions) { let backupData = null; return new Promise((resolve, reject) => { const results = []; const transactionPromises = transactionFunctions.map(fn => fn()); Promise.all(transactionPromises) .then(data => { results.push(...data); backupData = null; // 删除备份数据 resolve(results); }) .catch(error => { if (backupData) { // 恢复备份数据以回滚事务 // 在实际情况下，你可能需要使用更安全的备份机制，如深拷贝或序列化/反序列化等 console.log('Rolling back transaction...'); // 恢复备份数据 } reject(error); }); }); } function transaction1() { return new Promise((resolve, reject) => { // 备份数据 if (!backupData) { backupData = 'backup'; } setTimeout(() => { console.log('Transaction 1 completed'); resolve(1); }, 1000); }); } function transaction2() { return new Promise((resolve, reject) => { setTimeout(() => { console.log('Transaction 2 completed'); resolve(2); }, 2000); }); } function transaction3() { return new Promise((resolve, reject) => { setTimeout(() => { console.log('Transaction 3 failed'); reject(new Error('Transaction failed')); }, 500); }); } performTransaction([transaction1, transaction2, transaction3]) .then(results => { console.log('All transactions completed successfully'); console.log(results); }) .catch(error => { console.error('Transaction failed'…

NodeORM 数据库插件 Sequelize 基本使用
type: item
date: 2023-03-18T11:39:00+08:00
description: Sequelize 是一个基于 Node.js 的 ORM（Object-Relational Mapping）框架，可以与多种数据库进行交互，如 MySQL、PostgreSQL、SQLite 等。它的使用简单，支持多种数据库操作，并且可以轻松地进行数据迁移。 本篇博文将介绍如何在 Node.js 环境下使用 Sequelize 进行数据库操作，包括创建表、增删改查等基本操作。安装 Sequelize在使用 Sequelize 之前，需要先安装它及其对应的数据库驱动程序。npm install sequelize mysql2其中，mysql2 是 Sequelize 的 MySQL 驱动程序，可以根据实际需求选择对应的驱动程序。连接数据库首先需要创建一个 Sequelize 实例，用于连接数据库。可以通过以下代码进行创建：const Sequelize = require('sequelize'); ​ const sequelize = new Sequelize('database', 'username', 'password', { host: 'localhost', dialect: 'mysql', logging: false, });上述代码中，database、username、password 分别为数据库的名称、用户名和密码。host 表示数据库的主机地址，dialect 则表示使用的数据库类型。定义模型在 Sequelize 中，一个表对应一个模型。定义模型的方式如下：const { DataTypes } = require('sequelize'); ​ const User = sequelize.define('User', { username: { type: DataTypes.STRING, allowNull: false, }, email: { type: DataTypes.STRING, allowNull: false, unique: true, }, password: { type: DataTypes.STRING, allowNull: false, }, });上述代码中，定义了一个名为 User 的模型，包含了三个属性：username、email 和 password。其中，type 表示属性的类型，allowNull 表示该属性是否允许为空，unique 则表示该属性是否唯一。同步数据库在定义完模型后，需要通过以下代码将模型同步到数据库中：sequelize.sync();如果需要将模型同步到指定的数据表中，可以在定义模型时指定表名：const User = sequelize.define('user', {...}, { tableName: 'users' });增删改查创建记录创建记录有两种方式，一种是使用 create 方法，另一种是使用 build 方法，后者只是将要创建的实例对象保存在内存中，需要使用 save 方法将其持久化到数据库中。// 创建一条新记录 const user = await User.create({ name: 'John Doe', email: 'john.doe@example.com' }); ​ // 创建一条新记录，但不保存到数据库中 const user = User.build({ name: 'John Doe', email: 'john.doe@example.com' }); await user.save();查询记录查询记录有多种方式，可以使用 findAll 方法查询所有记录，使用 findOne 方法查询单条记录，使用 findByPk 方法根据主键查询记录，还可以使用 findAll 方法的选项参数进行条件查询、排序、分页等。const users = await User.findAll(); ​ // 根据主键查询记录 const user = await User.findByPk(1); ​ // 条件查询 const users = await User.findAll({ where: { name: 'John Doe' }, order: [ ['created_at', 'DESC'] ], limit: 10, offset: 0 });更新记录使用 update 方法更新记录。// 更新一条记录 await User.update({ name: 'Jane Doe' }, { where: { id: 1 } });删除记录使用 destroy 方法删除记录。// 删除一条记录 await User.destroy({ where: { id: 1 } })…

使用微信交易组件打造自己的小商城
type: item
date: 2022-05-09T11:26:42+08:00
description: 说明本项目采用Taro-React进行开发。UI参考来自老孙的店。先上效果图初始化小程序购物组件app.config.ts plugins:{ 'mini-shop-plugin':{ version:'latest', provider:'wx34345ae5855f892d' } },app.tsxconst miniShopPlugin = Taro.requirePlugin('mini-shop-plugin'); //下面的代码在componentDidMount执行 //需要异步执行 否则拿不到小程序当前小程序实例 setTimeout(()=>{ miniShopPlugin.initApp(Taro.getApp(), Taro) },1)至此已经初始化成功到这里就可以自己开发啦请参考文档 小程序商业化能力

Vue3快速上手
type: item
date: 2022-03-15T14:10:00+08:00
description: Vue3快速上手1.Vue3简介2020年9月18日，Vue.js发布3.0版本，代号：One Piece（海贼王）耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者github上的tags地址：https://github.com/vuejs/vue-next/releases/tag/v3.0.02.Vue3带来了什么1.性能的提升打包大小减少41%初次渲染快55%, 更新渲染快133%内存减少54%......2.源码的升级使用Proxy代替defineProperty实现响应式重写虚拟DOM的实现和Tree-Shaking......3.拥抱TypeScriptVue3可以更好的支持TypeScript4.新的特性Composition API（组合API）setup配置ref与reactivewatch与watchEffectprovide与inject......新的内置组件FragmentTeleportSuspense其他改变新的生命周期钩子data 选项应始终被声明为一个函数移除keyCode支持作为 v-on 的修饰符......一、创建Vue3.0工程1.使用 vue-cli 创建官方文档：https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create## 查看@vue/cli版本，确保@vue/cli版本在4.5.0以上 vue --version ## 安装或者升级你的@vue/cli npm install -g @vue/cli ## 创建 vue create vue_test ## 启动 cd vue_test npm run serve2.使用 vite 创建官方文档：https://v3.cn.vuejs.org/guide/installation.html#vitevite官网：https://vitejs.cn什么是vite？—— 新一代前端构建工具。优势如下：开发环境中，无需打包操作，可快速的冷启动。轻量快速的热重载（HMR）。真正的按需编译，不再等待整个应用编译完成。传统构建 与 vite构建对比图## 创建工程 npm init vite-app <project-name> ## 进入工程目录 cd <project-name> ## 安装依赖 npm install ## 运行 npm run dev二、常用 Composition API官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html1.拉开序幕的setup理解：Vue3.0中一个新的配置项，值为一个函数。setup是所有Composition API（组合API）“ 表演的舞台 ”。组件中所用到的：数据、方法等等，均要配置在setup中。setup函数的两种返回值：若返回一个对象，则对象中的属性、方法, 在模板中均可以直接使用。（重点关注！）若返回一个渲染函数：则可以自定义渲染内容。（了解）注意点：尽量不要与Vue2.x配置混用Vue2.x配置（data、methos、computed...）中可以访问到setup中的属性、方法。但在setup中不能访问到Vue2.x配置（data、methos、computed...）。如果有重名, setup优先。setup不能是一个async函数，因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。（后期也可以返回一个Promise实例，但需要Suspense和异步组件的配合）2.ref函数作用: 定义一个响应式的数据语法: const xxx = ref(initValue)创建一个包含响应式数据的引用对象（reference对象，简称ref对象）。JS中操作数据： xxx.value模板中读取数据: 不需要.value，直接： {{xxx}} 备注：接收的数据可以是：基本类型、也可以是对象类型。基本类型的数据：响应式依然是靠Object.defineProperty()的get与set完成的。对象类型的数据：内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。3.reactive函数作用: 定义一个对象类型的响应式数据（基本类型不要用它，要用ref函数）语法：const 代理对象= reactive(源对象)接收一个对象（或数组），返回一个代理对象（Proxy的实例对象，简称proxy对象）reactive定义的响应式数据是“深层次的”。内部基于 ES6 的 Proxy 实现，通过代理对象操作源对象内部数据进行操作。4.Vue3.…

Taro + TS 封装pick组件 实现省市县三级联动
type: item
date: 2021-11-10T18:40:00+08:00
description: 直接贴代码，省市级数据来自自定义的仓库index.tsximport { View, PickerView, PickerViewColumn, Text } from "@tarojs/components"; import "./index.scss"; import React, { FC, useEffect, useState } from "react"; import Row from "@/component/Row"; import { useStores } from "@/store"; import { observer } from "mobx-react-lite"; import classNames from "classnames"; interface AddressPickerProps { //父组件接值回调 onSure?: (address: [string, string][]) => void; //组件初始值 defaultValue?: [string, string][]; } const AddressPicker: FC<AddressPickerProps> = (props) => { //接受一个回调的通知函数 和组件初始值 const { onSure, defaultValue } = props; //连接仓库 const { userStore } = useStores(); const address = userStore.address; //选定之后显示给用户的文本 const [adressText, setAdressText] = useState(""); //是否打开选择面板 const [pickerShow, setPickerShow] = useState(false); //关闭面板函数 const onClosd = () => setPickerShow(false); //地市级下标 const [state, setState] = useState([0, 0, 0]); //监听三列滑动改变 const cityChange = (e) => { if (e.detail.value[0] !== state[0]) { //省份发生改变 return setState([e.detail.value[0], 0, 0]); } if (e.detail.value[1] !== state[1]) { //市级发生改变 return setState([state[0], e.detail.value[1], 0]); } setState(e.detail.value); }; //省市级数组 格式 [['100001',''北京],[‘100002’,'天津']] const currentProvinces = Object.entries(address[86]); //当前地级市数组 const currentCitys = Object.entries(address[currentProvinces[state[0]][0]]); //当前县数组 const currentAreas = Object.entries(address[currentCitys[state[1]][0]]); //返回最终结果 const handleSure = () => { const tempAreaInfo = [ currentProvinces[state[0]], currentCitys[state[1]], currentAreas[state[2]], ]; //关闭弹框 onClosd(); //设置显示文字 setAdressText(tempAreaInfo.map((item) => item[1]).join("")); //通知调用的组件 吧最终结果返回 onSure && onSure(tempAreaInfo); }; //classNames 动态声明 const labelClasses = classNames("address-picker-primary", { "address-picker-active": !!adressText, }); const pickerClasses = classNames("address-picker-container", { "show address-picker-container-active": pickerShow, }); //完成初始化赋值 useEffect(() => { if (defaultValue) …

优雅解决项目中重复多次important
type: item
date: 2021-10-27T22:55:00+08:00
description: 适用场景说明当我们在React/Vue等使用webpack构建项目中需要大量使用important 来引入我们所需要的模块时。就像下面这样。例如React/Vue 的路由或者store部分。import A from 'components/A' import B from 'components/B' import C from 'components/C' import D from 'components/D' // ...解决方法require.context(directory, useSubdirectories, regExp)directory: 要查找的文件路径（String）useSubdirectories: 是否查找子目录（为布尔值）regExp: 要匹配文件的正则就像下面一样使用//寻找当前目录下所有ts文件（包含子目录） const modulesFiles = require.context("./", true, /\.ts$/); //一个webpack的api,通过执行require.context函数获取一个特定的上下文,主要用来实现自动化导入模块值得注意的是require.context函数执行后返回的是一个函数,并且这个函数有3个属性keys: 返回匹配成功模块的名字组成的数组resolve: 接受一个参数request，request返回这个匹配文件相对于整个工程的相对路径。id: 执行环境的id，返回的是一个字符串，主要用在module.hot.accept，应该是热加载。当然上面的我也没看懂 ，直接整活就Vans了在我的项目中使用到了mobx作为全局状态管理 ，把store要挂载到全局时，我是这么写的。/* * @Author: XiaoHuwei * @Date: 2021-10-27 20:04:33 * @LastEditors: XiaoHuwei * @LastEditTime: 2021-10-27 22:27:50 * @FilePath: /txcb_mini/src/store/index.ts */ import React from "react"; //遍历同级目录包含ts子文件 自动加载./store下面所有store 实现全量注册 const modulesFiles = require.context("./", true, /\.ts$/); //组成store的集合 const allStore = modulesFiles.keys().reduce((allStore, modulePath) => { //根据模块路径取到该模块 const value = modulesFiles(modulePath); //不包含本文件 if (modulePath !== "./index.ts") { //获取到仓库名 获取该模块下面所有初始值 allStore[value.default.storeName] = value.default; } return allStore; }, {}); const StoresContext = React.createContext({ ...allStore, }); export const useStores = () => React.useContext(StoresContext);我的一个store之一代码如下/* * @Author: XiaoHuwei * @Date: 2021-10-11 11:47:36 * @LastEditors: XiaoHuwei * @LastEditTime: 2021-10-27 21:46:59 * @FilePath: /txcb_mini/src/store/home/counter.ts */ import { observable } from "mobx"; import { getLogin } from "@/service/test"; const counterStore = observable({ //////仓库名 重要部分！！storeName 该变量每次声明仓库必填！ storeName: "counterStore", counter: 0, loading: "false", Login(data) { this.loading = "true"; getLogin(data) .then((res) => { console.log(res.data); this.loading = "true"; }) .catch((error) => console.log(error)); }, increment() …

嘿！ 这是一篇前端小心得～
type: item
date: 2020-07-06T17:28:00+08:00
description: 说一下最近公司项目的需求实现方式视频预加载与本地化如果页面有若干个视频，我们想做到页面刚进来的时候就把所有的视频缓存在本地，下次进页面就可以直接读取缓存而不是向服务器重复请求。实现方法const saveVideo = src => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest() xhr.open('GET', src, true); xhr.responseType = 'blob'; xhr.onload = function () { if (this.status === 200) { const videoBlob = this.response; const blobSrc = URL.createObjectURL(videoBlob); // IE10+ resolve(blobSrc) } }; xhr.send(); }) };使用saveVideo(`xxxx.mp4`).then(res=>console.log(res))监听localStorage变化需要重写 window.localStorage.setItem() var orignalSetItem = localStorage.setItem; window.localStorage.setItem = function(key,newValue){ var setItemEvent = new Event("setItemEvent"); setItemEvent.key = key; setItemEvent.newValue = newValue; window.dispatchEvent(setItemEvent); orignalSetItem.apply(this,arguments); };使用window.addEventListener("setItemEvent", function(){console.log(e)}) window.localStorage.setItem(name, value)//console.log(e)给你的React 加上动画npm i animate.css -S使用import 'animate.css' 云屏编号：100006 这样就可以了。更多效果请参阅 animate.css图片未加载完成添加loading画面{ test.boxList.data.map(item => { return ( GotoDetail(item)} key={item.image} className={`${styles.imgbox} animate__animated animate__fadeInUp`}> //关键部分 ) }) }方法//判断浏览器 var Browser = new Object(); Browser.userAgent = window.navigator.userAgent.toLowerCase(); Browser.ie = /msie/.test(Browser.userAgent); Browser.Moz = /gecko/.test(Browser.userAgent); window.Imagess = (url, callback, error) => { var val = url; var img = new Image(); if (Browser.ie) { img.onreadystatechange = function () { if (img.readyState == "complete" || img.readyState == "loaded") { callback(img); } } } else { img.onload = function () { if (img.complete == true) { callback(img); } } } //如果因为网络或图片的原因发生异常，则显示该图片 if (error) { img.onerror = error; } else { img.onerror = function () { img.src = imgloading } } img.src = val; } //调用方法 let imglist = document.querySelectorAll('.imgLoading') for (let i = 0; i < imglist.length; i++) { let tt = imglist[i]; //防止重复加载 if (tt.loading == …

原生JS实现复制文本
type: item
date: 2020-05-29T14:50:00+08:00
description: 公司最近一个需求 需要通过变量实现复制效果请双击我 哈哈哈哈代码window.copyText = item => {//挂载到全局 var textArea = document.createElement('textArea'); textArea.innerHTML = item; textArea.value = item; document.body.appendChild(textArea); textArea.select(); try{ if(document.execCommand("Copy")){ alert(`${item} 复制成功！`) }else{ alert('复制失败！请手动复制！') } } catch(err){ alert('复制错误！请手动复制！') } document.body.removeChild(textArea); }使用 window.copyText('你好')

简单理解JS函数柯里化
type: item
date: 2020-04-26T11:05:00+08:00
description: 介绍柯里化其实是函数式编程的一个过程，在这个过程中我们能把一个带有多个参数的函数转换成一系列的嵌套函数。它返回一个新函数，这个新函数期望传入下一个参数。简单的说就是把一个你写好的具有多个参数的函数可以一级级（递归+闭包）调用（每次传入一个参数）。应用场景我们希望用一个通用的正则模板实现项目中各种类型检查，包括但不限于手机号，邮件地址... { //定义主要柯里化主要逻辑处理 const curry = fn => { const _c = (restNum, argsList) => restNum === 0 ? fn(...argsList) : x => _c(restNum - 1, [...argsList, x]); return _c(fn.length, []); } //定义正则验证函数 regExp =>规则 value=>待检验对象 const MyReg = (regExp, value)=>console.log(regExp.test(value)); MyReg(/^1\d{10}$/, '18642838455') // 校验电话号码 true const _check = curry(MyReg) //进行柯里化 const checkPhone = _check(/^1\d{10}$/) //生成工具函数，验证电话号码 checkPhone('18642838455') // 这样就实现了函数柯里化 true }关键部分是 const curry = fn => { const _c = (restNum, argsList) => restNum === 0 ? fn(...argsList) : x => _c(restNum - 1, [...argsList, x]); return _c(fn.length, []); }理解首先curry函数接收一个函数，这个函数是我们真正的处理逻辑的函数然后通过闭包，声明一个函数_c，_c接收两个参数，一个是真正函数的参数列表长度的计数器，一个是传入参数的列表在_c函数中判断真正函数的参数列表是否等于0，等于0代表参数已经全部都传入进来了，可以调用真正的函数去进行计算，并返回fn函数进行计算然后返回结果；如果参数列表不等于0，则把参数列表的计数器-1，并把传入参数进行合并，然后使用返回_c函数供尾递归调用。上面就是柯里化的大体思路，主要实现是依赖闭包和尾递归来实现的，所以在性能上有优化的同时也是有一定的损失，对于性能要求很苛刻的程序可能需要酌情考虑，一般情况下合理使用柯里化是可以把代码的可读性提高很多。

手摸手教你 Taro+dva+Hooks快速开发小程序
type: item
date: 2019-12-02T11:36:00+08:00
description: 最近在研究小程序这方面的东西，使用到了京东基于 React 的小程序开发框架 Taro ，相比 mpvue 和 uni-app 这两个框架来说，Taro 显得更优雅，更稳重，大厂在迭代，就是不一样，一句话 ，就是坑少一点～前置知识看本篇教程前需要你掌握 dva ,React Hooks ,Redux ,还有小程序的开发流程。希望你知其然，并知其所以然。如果你不具备以上任意一点，建议先停下来，下面我给出地址，先自行学习之后再过来看我这篇文章 。Redux-React全局状态管理工具之一dva-让你的全局状态管理更简单React-用于构建用户界面的 JavaScript 库React Hooks-更优雅的React应用书写方式小程序Taro-基于React的小程序框架正式开始安装 Taro脚手架# 使用 npm 安装 CLI $ npm install -g @tarojs/cli # OR 使用 yarn 安装 CLI $ yarn global add @tarojs/cli # OR 安装了 cnpm，使用 cnpm 安装 CLI $ cnpm install -g @tarojs/cli因为我电脑安装了 cnpm 镜像源，所以下文我都会用 cnpm脚手架完成后使用脚手架初始化一个新项目在你需要建立项目的文件夹打开 CMDtaro init test//我们演示的项目名叫test然后跟着流程走，这边为了减少你的学习成本，建议不要选择 Typescript ,css选择less,模版选择默认模版。 最后成功之后，用你的编辑器打开test目录。你的目录结构跟我的应该是一样的 先删除 ./src/pages 下面的 index 文件夹。然后命令行在test 目录打开，安装我们需要用的扩展cnpm install --save @tarojs/async-await //没想到吧 在Taro里面使用 async和 await 需要安装这个扩展 cnpm install --save redux @tarojs/redux @tarojs/redux-h5 redux-thunk redux-logger //Redux全家桶 cnpm install --save dva-core dva-loading //dva cnpm install --save taro-axios //在Taro里面使用axios 需要安装他这四个命令安装完之后，打开./src 目录 ，新建两个文件夹 ，一个叫 utils 一个叫 models 注意这两个文件夹应该跟你的 pages 文件夹同级。在 utils 文件夹下面新建文件 dva.js 写上下面的内容 注册dvaimport { create } from 'dva-core'; import { createLogger } from 'redux-logger'; import createLoading from 'dva-loading'; let app; let store; let dispatch; function createApp(opt) { // redux日志 // opt.onAction = [createLogger()]; app = create(opt); app.use(createLoading({})); if (!global.registered) opt.models.forEach(model => app.model(model)); global.registered = true; app.start(); store = app._store; app.getStore = () => store; dispatch = store.dispatch; app.dispatch = dispatch; return app; } export default { createApp, getDispatch() { return app.dispatch; } }在 utils 文件夹下面新建文件 request.js 写上下面的内容 封装一个简单的axios 实际你的项目中可能不一样import axios from "taro-axios"; const baseURL = `https://blog.xiaohuwei.cn` const service = axios.create({ baseURL: baseURL, withCredentials: true, timeout: 300000 }); service.interceptors.response.use( response => { return resp…