腾讯IM实时通讯
开发指引:有体验版和开发版可以免费试试基础功能。文档很详细,api很多。
数字千位符分割,正则
'1234567.89'.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
-
/\B/g
: 匹配以下几种情况。正则中 单词字符指的是 字母、数字、下划线, 在这里,匹配数字和数字之间的位置。'1234567.89'.replace(/\B/g, ',')
输出结果是'1,2,3,4,5,6,7.8,9'
。 数字7,8
是单词字符,而小数点不是单词字符,7
和小数点,小数点和8
之间匹配不到。- 字符串第一个字符为非“字”字符
- 字符串最后一个字符为非“字”字符
- 两个单词字符之间
- 两个非单词字符之间
- 空字符串
-
x(?=y)
: 正向先行断言。 匹配x 仅当x后面是y时匹配; -
(\d{3})+
: 匹配三位数字一次或者多次 -
x(?!y)
: 正向否定查找, 匹配 x 仅当x后面不跟着 y的时候。所以(?!\d)
指的是仅当后面不跟着数字的时候 -
(?=(\d{3})+(?!\d))
: 指的是 匹配位置后有n
个连续三位数字,且n
个连续三位数字后不是数字,n>=1
匹配过程
-
位置0(字符
1
与2
之间)-
\B
:前后均为数字,匹配。 -
断言验证:
- 后面是
234567.89
,可分割为234
和567
(两组三位数)。 - 三位数组后是小数点(非数字),满足
(?!\d)
。
- 后面是
-
结果:在此处插入逗号
,
。
-
-
位置1(字符
2
与3
之间)-
\B
:匹配。 -
断言验证:
- 后面是
34567.89
,仅能分割为345
(剩余67
不足三位)。 - 匹配后紧跟数字
6
,不满足(?!\d)
。
- 后面是
-
结果:不匹配。
-
-
位置3(字符
4
与5
之间)-
\B
:匹配。 -
断言验证:
- 后面是
567.89
,可分割为567
(一组三位数)。 - 三位数组后是小数点,满足
(?!\d)
。
- 后面是
-
结果:在此处插入逗号
,
。
-
-
其他位置
后续位置无法满足断言条件(剩余数字不足三位或后面紧跟数字)。
path
- path.resolve: 方法是把一个路径或路径片段的序列解析为一个绝对路径。 如果传入的第一个参数是非根路径,会在基于当前脚本所在目录的基础上,拼接后续传入的路径片段。
- 注意点: path.resolve如果传入了 以 / 开头的片段,会直接跳转到脚本所在磁盘根部
path.resolve('a', '/b', 'c')===> E://b/c
- 若是要拼接其他盘的绝对路径,第一个参数需要传入其他盘的绝对路径
- 注意点: path.resolve如果传入了 以 / 开头的片段,会直接跳转到脚本所在磁盘根部
- path.join:用于连接路径, 主要用于连接相对路径。会把全部给定的 path 片段连接到一起,并规范化生成的路径。只是拼接传入路径片段
- __dirname 是被执行的js 文件的地址 ——文件所在目录.当前执行脚本所在的目录
- process.cwd() 是当前Node.js进程执行时的文件夹地址-工作目录。 如 node file.js,则这个时候process.cwd()就是file.js文件所在的目录地址
pnpm
performant npm
高性能的 npm ,是由`npm/yarn'衍生出来的,解决了'npm/yarn'潜在的一些bug。
- 特点: 速度快,节省磁盘空间,支持monorepo,安全性高
monorepo
用一个仓库管理多个项目项目代码的管理策略。
lerna
monorepo 可以利用 lerna 统一包管理 lerna 可以帮助解决 monorepo 因为多个子项目放在一个代码仓库,并且子项目之间又相互依赖时带来的两个棘手问题:
- 在多个子目录执行相同的命令时需要手动进入各个目录,并执行命令;
- 子项目更新后只能手动追踪依赖该项目的其他子项目,并升级其版本。
- vue组件 本组件内通过name可以自己调用自己做子组件, 树形结构
simplest-mock-server
一个开箱即用的搭建本地 mock 接口的工具
其他
- canonical 和 alternate 标签:
-
canonical:当一个网站有多个版本的内容时,例如同一个页面有多个URL,或者内容存在多种语言或版本,这些URL之间的内容可能会被搜索引擎认为是重复的,从而降低网站的排名。在这种情况下,使用canonical标签可以指示搜索引擎哪个URL应该被认为是原始的或主要的版本,避免因为重复内容而被搜索引擎降权
-
alternate:pc做简单的响应式可能不太满足需求,单独设置移动端站点。PC站点加alternate指向移动端站点
-
不同的URL对应分别对应pc和移动端的同一套内容网站时
<!-- PC端页面head标签内对应添加如下代码:(以职位百科详情页为例) -->
<meta name="applicable-device" content="pc">
<link rel=“alternate” media=“only screen and(max-width: 640px)” href="https://mbaike.51job.com/zhiwei/04021/" >
<!-- 移动端页面head标签内对应添加如下代码: -->
<meta name="applicable-device" content="mobile">
<link rel="canonical" href="https://baike.51job.com/zhiwei/04021/">
- webpack-dev-server
webpack-dev-server: 是一个本地开发服务器,会自动监听变化,自动打包构建,自动更新刷新浏览器
- 特点: 不会产生dist文件,将打包结果暂时存在内存中,内部的http-sever访问这些文件并读取数据,发送给浏览器 减少磁盘的读取,提高构建效率
- 写法:在webpack.config.js文件中,通过属性devServer: { } 设置与 webpack-dev-server相关的配置
- bin 目录是一个存放可直接运行文件的目录,里面有很多能够帮助我们提升效率的操作
- process 模块是 Node 环境的全局模块,属于全局对象,它好比浏览器环境上的 window 对象,不同的是它存在于 Node 环境中而已,它身上有很多方法与属性。
- process.argv: 能获取到命令行输入的一些参数,以空格分割
- file-save: 和
fs.writeFileSync(path, content)
类似,创建和写入文件的,对写入进行封装,可以链式调用
var fileSave = require('file-save');
// the first line will create a writeStream to the file path
fileSave('sample/test')
.write('this is the first line', 'utf8')
.write('this is the second line', 'utf8', function() {
console.log('writer callback')
})
.end('this is the end')
.error(function() {
console.log('error goes here')
})
.finish(function() {
console.log('write finished.')
})
- uppercamelcase 模块是将破折号/点/下划线/空格分隔的字符串转换为驼峰形式。
- JSON.stringify()第三个参数
- 只有一个参数,就是将数据转换成字符串
obj = {
name:'wxz',
age:78,
birth:'2020-02-15'
}
console.log(JSON.stringify(obj)); // {"name":"wxz","age":18,"birth":"2020-02-15"}
- 第二个参数:过滤对象中属性,需要转换返回的属性. 可以是一个数组或者一个函数
console.log(JSON.stringify(obj, ['name', 'age'])); // {"name":"wxz","age":18}
- 第三个参数:用于控制结果字符里的间距
console.log(JSON.stringify(obj, ['name','age'], '~')); // '{\n~"name": "wxz",\n~"age": 78\n}'
console.log(JSON.stringify(obj, ['name','age'], ' ')); // '{\n "name": "wxz",\n "age": 78\n}'
- json-templater 模块是一个字符串模板生成器,它能帮助我们更加快速、便捷的生成好字符串模板。
- charCodeAt 和 fromCharCode
// charCodeAt() 方法可返回指定位置的字符的 Unicode 编码,返回值是 0 - 65535 之间的整数
"HELLO WORLD".charCodeAt(0) // 72
// fromCharCode() 可接受一个指定的 Unicode 值,然后返回一个字符串。
String.fromCharCode(98) // b
-
经常碰到 Nginx: 301 Moved Permanently Nginx负责设置301 Moved Permanently状态码。但nginx.conf控制Nginx如何处理301 Moved Permanently状态码! 换句话说,要不要进行页面重定向,和怎么重定向,完全是用户配置的结果! Nginx主动设置301 Moved Permanently状态码只有一种情况,当用户在浏览器输入了一个url地址,末尾部分是一个文件目录且不以斜杠”/“结尾,比如 “www.test.com/index” 。 Nginx没有找到index这个文件,但发现了index是个目录。于是本次访问的返回状态码就会被设置成301 Moved Permanently。
-
GraphQL vs REST APIs
- REST: 它侧重于分配
http
请求方法(get,post,patch,delete,put) 和 url端点之间的关系- 缺点
- 获取过度:客户端收到了很多不必要的数据。比如:客户端请求获取用户名和头像,但返回了除这两个之外的 地址,年龄,电话,职业,状态等
- 获取不足: 向服务器发出大量额外请求以检索必要的数据。比如:客户端需要 用户名和头像外还需要 朋友列表。这可能需要两个接口才能拿到.
- 有点: 更易于理解,且有更简单和更可预测的数据结构。服务器响应速度相对好
- 缺点
- GraphQL:
- 查询(query): 查询语言发出请求来定义必要数据的结构。这些请求由一系列字段组成,这些字段定义了所请求数据的形状,每个字段都包含自己的一组子字段。然后,服务器使用与查询形状匹配的 JSON 对象响应请求
query { user(id: 1) { id name address friends { id name address } } }
- 变更(Mutations): 更改服务器数据,通常用于创建、更新和删除操作
mutation { createUser(input: { name: "Alice", address: "123 Main St" }) { id name address } }
- 相比于REST API v3,它最强大的优势在于,你能够精确的定义所需要的数据,并且毫无冗余。通过GraphQL,你只需要一次请求就能取到通过多个REST请求才能获得的数据。
- cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js
- cross-env: 能跨平台设置和使用环境变量
- cross-env BABEL_ENV=utils 作用: 设置 process.env.BABEL_ENV 的值为 utils
- babel src --out-dir lib --ignore src/index.js: 运行Babel指令编译src目录下文件,输出到 lib文件夹下,编译时忽略 src/index.js文件
Vue 相关
Props
- props属性申明时,如果是对象或者数组,设置默认值时,必须用函数返回,该函数接收组件所接收到的原始 prop 作为参数
- props 自定义类型校验函数
// 自定义类型校验函数
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 所有 prop 默认都是可选的,除非声明了 required: true。
// 除 Boolean 外的未传递的可选 prop 将会有一个默认值 undefined, 未传递的 Boolean 类型默认 false。
- 一个属性被允许是多个类型,且其一种是 Boolean时
<MyComponent disabled />
// disabled 将被转换为 true
defineProps({
disabled: [Boolean, Number]
})
// disabled 将被转换为 true
defineProps({
disabled: [Boolean, String]
})
// disabled 将被转换为 true
defineProps({
disabled: [Number, Boolean]
})
// disabled 将被解析为空字符串 (disabled="")
defineProps({
disabled: [String, Boolean]
})
- 使用一个对象绑定多个props
const data = {
id: 1,
title: 'My Journey with Vue'
}
<MyComponent v-bind="data" />
<!-- 相当于 -->
<MyComponent :id="data.id" :title="data.title" />
Vue.extend, 扩展Vue实例,以js的方式调用组件
import Vue from 'vue';
import store from '@/store';
import router from '@/router';
import shopVip from './shopVip.vue';
import chatAdd from './chatAdd.vue';
// 使用extend方法创建vue的子类
function globalExtend(data, Modal, callback) {
let instance = null;
if (!instance) {
const PoupDom = Vue.extend(Modal);
instance = new PoupDom({ store, router }).$mount();
}
document.body.appendChild(instance.$el);
Vue.nextTick(() => {
instance.showModalFun(data);
});
if (callback && typeof callback === 'function') {
callback();
}
}
export const vipModalDialog = function (data) {
globalExtend(data, shopVip);
};
export const chatAddModal = function (data) {
globalExtend(data, chatAdd);
};
Vue.prototype.$showVipModal = vipModalDialog; //vip购买弹框
Vue.prototype.$showChatAddModal = chatAddModal; //聊天增补包
其他
- 在双大括号或者指令中使用方法时,方法在组件每次更新时都会被重新调用,因此不应该产生任何副作用,比如改变数据或触发异步操作。
- vue模板使用表达式时,可用的内置全局对象
import { makeMap } from './makeMap'
const GLOBALS_ALLOWED =
'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' +
'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console'
export const isGloballyAllowed = /*#__PURE__*/ makeMap(GLOBALS_ALLOWED)
/** @deprecated use `isGloballyAllowed` instead */
export const isGloballyWhitelisted = isGloballyAllowed
- 指令的动态参数,比如
v-bind,v-on
的 (指令|参数|修饰符=值)
<a v-bind:[attributeName]="url"> ... </a>
<!-- 简写 -->
<a :[attributeName]="url"> ... </a>
<a v-on:[eventName]="doSomething"> ... </a>
<!-- 简写 -->
<a @[eventName]="doSomething">
<!-- attributeName 和 eventName 即是变量:它们的值应当是一个字符串或者null,为null即意味着移除该绑定 -->
<!-- 如果你需要传入一个复杂的动态参数,我们推荐使用计算属性替换复杂的表达式 -->
- Vue中initState()是在beforeCreate和created之间
if (opts.props) initProps(vm, opts.props)//初始化Props
if (opts.methods) initMethods(vm, opts.methods)//初始化methods
if (opts.data) {
initData(vm)} else {
observe(vm._data = {}, true /* asRootData */)}//初始化data
if (opts.computed) initComputed(vm, opts.computed)//初始化computed
手机网站支付功能
- 支付页面点 立即支付 后, 调后台接口创建订单
// 新增vip用支付接口 params, AndoroParams, iosParams, fromPage
export function createPayOrderVip(payParams) {
store.commit('payOrder/SET_LOADING', true);
const fromModule = payParams.fromModule;
if (equipmentSystem() == 9) {
// 安卓
const AndoroParams = { itemId: payParams.itemId, qty: payParams.qty, tag: payParams.tag, fromModule };
store
.dispatch('payOrder/createOrder', AndoroParams)
.then((data) => {
// 判断地址栏是否有pageType参数
let pageType = getQueryString('pageType');
const fromPage = payParams.fromPage || '';
router.push({
path: `/pay?orderId=${data}&pageType=${pageType}&fromModule=${fromModule}&fromPage=${fromPage}`,
});
})
.finally(() => {
store.commit('payOrder/SET_LOADING', false);
});
} else if (equipmentSystem() == 8) {
// 此处唤起苹果内购
const iosParams = { productid: payParams.productid, tag: payParams.tag, fromModule };
window.webkit.messageHandlers.model.postMessage(['buyProduct:', iosParams]);
setTimeout(() => {
store.commit('payOrder/SET_LOADING', false);
}, 1000);
}
}
createOrder(event) {
this.$emit('createOrder', event);
if (!isWeixin) {
//非小程序环境
const fromModule = getQueryString('fromModule') || '';
// 之前存在订单并且是安卓机器
if (this.checkedPop.tradeOrderCode && equipmentSystem() == 9) {
// 直接跳转支付页,不需要创建订单
this.$router.push({
path: `/pay?orderId=${this.checkedPop.tradeOrderCode}&fromModule=${fromModule}`,
});
} else {
/* 如果有赠品选择带赠品的商品Id, 否则取checked.id */
let params = {
fromModule: fromModule,
itemId: this.orderInfo.itemId,
qty: 1,
tag: this.orderInfo.tag,
productid: this.orderInfo.iosProductId,
fromPage: this.fromPage,
};
createPayOrderVip(params);
}
} else {
//小程序环境
this.appletPay();
}
},
//小程序支付
appletPay() {
this.$store.commit('payOrder/SET_LOADING', true);
this.$store
.dispatch('payOrder/createOrder', {
itemId: this.orderInfo.itemId,
qty: 1,
tag: this.orderInfo.tag,
fromModule: getQueryString('fromModule') || '',
})
.then((orderCode) => {
wx.miniProgram.navigateTo({
url: `/src/pages/pay/index?orderCode=${orderCode}`,
});
})
.finally(() => {
this.$store.commit('payOrder/SET_LOADING', false);
});
},
// 支付页面,确认支付调用方法
// 安卓支付
androidpay() {
let param = {
orderCode: this.orderId,
payMethod: this.payMethod,
};
this.$store.dispatch('payOrder/getPayInfo', param).then((res) => {
let url = '';
if (this.payMethod == 1) {
// 支付宝支付
// url = 'ali://wap/pay?' + 'orderId=' + this.orderId + '&' + 'orderInfo=' + res.vendor_data;
url = `ali://wap/pay?orderId=${this.orderId}&orderInfo=${res.vendor_data}`;
} else if (this.payMethod == 2) {
// 微信支付
/* let data = JSON.parse(res.vendor_data);
url = url + 'weixin://wap/pay?';
for (let item in data) {
url = url + item + '=' + data[item] + '&';
}
url = url + 'orderId=' + this.orderId; */
let data = JSON.parse(res.vendor_data);
let weixin = '';
for (let item in data) {
weixin = `${weixin}${item}=${data[item]}&`;
}
url = `weixin://wap/pay?${weixin}orderId=${this.orderId}`;
}
window.location = url;
});
},
// 支付页面:还需要轮询 调接口查订单状态,确认支付成功后跳转
trtc
rtc-detect 用来检测当前环境对 TRTC SDK 的支持度。 trtc-js-sdk是腾讯云实时音视频通讯解决方案的 Web 端 SDK,它是通过 HTML 网页加载的 JavaScript 库。Web 开发者可以使用 TRTC Web SDK 提供的 API,在您的业务网站上实现实时音视频通话、直播等功能。
js创建事件的原生方法 document.createEvent
// 创建事件
const event = document.createEvent('Event');
// 初始化事件
event.initEvent('myEvent', true, true);
// 监听事件
document.addEventListener('myEvent', () => {
console.log('myEvent 触发了');
});
// 触发事件
document.dispatchEvent(event);
创建自定义事件
function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined }
var evt = document.createEvent('CustomEvent')
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
return evt
}
// 触发事件
window.dispatchEvent(new CustomEvent('modeChange', { detail: { name: 'wxz', age: 28 } }))
// 可以监听这个事件
window.addEventListener('modechange', (ev) => {console.log('wxz', ev)})
app原生和H5的通信
android 添加方法给 H5 调用
主要通过WebView的addJavascriptInterface实现
- 定义类
// 定义类
public class WebAppInterface {
private Context context;
private WebView webView;
public WebAppInterface(Context context, WebView webView) {
this.context = context;
this.webView = webView;
}
@JavascriptInterface
public void showToast(String message) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
- 配置WebView并注入对象: 在Activity中启用JavaScript并将对象注入WebView。
WebView webView = findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
// 注入对象,H5通过window.Android访问
webView.addJavascriptInterface(new WebAppInterface(this, webView), "Android");
// addJavascriptInterface该方法:第一个参数是注入的对象实例,第二个参数是对象在H5中调用的名称。
webView.loadUrl("file:///android_asset/index.html"); // 加载本地或远程H5页面
- H5调用原生方法: H5页面通过window.Android调用暴露的方法。
// 调用显示Toast
window.Android.showToast('Hello from H5!');
ios 添加方法给 H5 调用
使用的WKScriptMessageHandler
// ios手机,h5调用类似下面: 其中 nativeBridge以及传参形式 是app的同学定义的
window.webkit.messageHandlers.nativeBridge.postMessage({
action: 'showToast',
message: 'Hello from H5!'
});