bjkb / blog Goto Github PK
View Code? Open in Web Editor NEW文章记录以及一些项目总结
文章记录以及一些项目总结
WeixinJSBridge
的 JS SDK
。
微信分享
,调用原生相册
之类的原生功能传统的混合APP
传统的混合APP,主要依赖于端上内置的JS运行容器
,在其上运行混合APP
代码,其本质仍然属于传统的JavaScript
运行机制,也就是单线程运行的,并且JS引擎线程
会跟GUI渲染线程
互斥,导致执行长时间的JS
操作的时候,页面会失去响应,动画掉帧等,影响用户体验,这也是混合APP
最大的一个弊端
小程序的运行环境
运行环境 | 逻辑层 | 渲染层 |
---|---|---|
iOS | JavaScriptCore | WKWebView |
安卓 | V8 | chromium定制内核 |
小程序开发者工具 | NWJS | Chrome WebView |
视图层和逻辑层通过系统层的JSBridage
进行通信,逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。这就解决了混合APP最大的一个问题
小程序与传统编写的区别
HTML+CSS+JS
的方式WXML+WXSS+JS
,然后偷偷摸摸的编译一下,本质还是HTML+CSS+JS
依赖于微信平台的Native组件
由于依赖于微信平台,所以在小程序里面,能使用微信提供的原生组件,也就是Native层绘制的组件,Native层绘制的组件会在WebView
层之上
所以 微信小程序的本质是由WebView
与Native
两者组合渲染完成的
小程序的优势
小程序的问题
虽然小程序为业务提供了一种新的展示形式,但对于开发者来说,开发体验则显得并不那么友好。在前端工程化深入人心的今天,小程序那落后的三件套编写模式,大大的浪费时间。
Taro 采用了 React 语法来作为自己的语法标准,配合前端工程化的**,为小程序开发打造了更加优雅的开发体验。
我们目前这边所使用的前端框架也就是 React
,基本只需要很少的学习,就能无缝切换到小程序的开发当中去。
taro诞生的本质**跟编译原理有点类似,就是通过编译的方式,将taro代码,通过一系列的操作,转换成目标代码
Taro的优势
但是这对于多端开发是远远不够的,因为每一个平台都有自己的特性,比如小程序跟WEB
编译转换核心
Taro 的核心部分就是将代码编译成其他端(H5、小程序、React Native 等)代码。一般来说,将一种结构化语言的代码编译成另一种类似的结构化语言的代码包括以下几个步骤:
具体的操作
假设我们需要更改 foo
的名字 为 bar
jsonStr.replace(/(?<=")foo(?="\s*:)/i, 'bar')
这就是具体的操作了,实现parse
的部分是整个框架的核心,肯定不可能这么简单了,但是自己去实现一个parse
也是工作量非常巨大的,还好业界有非常著名的 babel
Babylon(@babel/parser)
将代码转换成AST
import * as babylon from "babylon";
const code = `n * n`;
babylon.parse(code);
然后通过 Babel-traverse (@babel/traverse) 可以遍历生成的树,实现添加,删除,替换的操作。
Taro 组件的 setState 行为最终会对应到小程序的 setData。Taro 引入了如 nextTick ,编译时识别模板中用到的数据,在 setData 前进行数据差异比较等方式来提高 setState 的性能。
taro的不足
React
的一些新特性,某些方法的使用也遭到了阉割这里只简单介绍了一下小程序的历史以及taro主要的架构,在开发过程中依然有无数的坑在等着你,这个只有等具体实践的时候,再逐步积累了。
自己对正则表达式的使用,还停留在校验手机号、身份证这种级别,趁着中秋假期,梳理一波正则表达式的使用。
这里列举一些基本的,完整的查看正则表达式MDN
字符 | 含义 |
---|---|
\ | 表示下一个字符不是特殊字符,常用来做转义 |
^ | 表示以什么开头,必须要在开头的位置才行,eating,用这个匹配 a 肯定没效果 |
$ | 表示以什么结尾,必须要在结尾的位置才行,跟上面同理 |
. | 表示换行符之外的任意单个字符 |
* | 表示匹配 0 - 多次,类似 {0,} |
+ | 表示匹配 1- 多次,类似{1,} |
? | 表示匹配 0-1次,类似{0,1} |
(x) | 会记住匹配项 x |
(?:x) | 不会记住匹配项x |
x(?=y) | 匹配x,只有当x后面是y的时候,先行断言 |
(?<=y)x | 匹配x,只有当x前面是y的时候,后行断言 |
x(?!y) | 匹配x,只有当x后面不是y的时候,正向否定查找 |
(?<!y)x | 匹配x,只有当x前面不是y的时候,反向否定查找 |
x|y | 匹配x或者y |
通过这些题目,对使用正则做一个综合性的测试
题目来源:
手写题:9道字符串类高频面试题(掘金)
var url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果
{ user: 'anonymous',
id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
city: '北京', // 中文需解码
enabled: true, // 未指定值得 key 约定为 true
}
*/
解法:
function parseParam(url){
let obj = {};
if(!url) return obj;
let searchParams = url.match(/(?<=\?).*/g)[0]; // 匹配 ?号之后的内容
searchParams.split('&').forEach((item)=>{
let [key, value ] = item.split('=');
if(value){
value = /^\d+$/.test(value)?parseFloat(value): decodeURIComponent(value); // 判断是否是数字以及解码
}else{
value = true
}
if(obj[key]){ // 判断是否已经存在了这个key,存在就得推入数组里
obj[key] = Array.isArray(obj[key])? obj[key].push(value): obj[key] = [obj[key],value];
return;
}
obj[key] = value;
})
return obj;
}
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
name: '姓名',
age: 18
}
compile(template, data); // 我是姓名,年龄18,性别undefined
解法一:
// 正则替换{{}}里面的内容
function compile(template,data){
return template.replace(/{{\w+}}/g,item=>data[item.match(/\w+/)]);
}
上面的方法有个问题是,只能替换简单key,复杂一点的,比如 {{obj.name}},就GG了
题目有个变种:
let template = compile(template);
template({name:'姓名',age:18});
// 可以将字符串写成字符串表达式,类似下面这样
let template = '我是'+obj.name+',年龄'+obj.age+',性别'+obj.sex+'';
解法二:
// 要使用这种方式,得借助一下 new Function(),创建一个新的函数
function compile(template){
// 将 template 替换成 '我是'+obj.name+',年龄'+obj.age+',性别'+obj.sex+'';
let str = template.replace(/{{\w+}}/g,item=>`'+obj.${item.match(/\w+/)}+'`);
let newFn = new Function('obj',`return '${str}'`); // 第一个参数,第二个 return 返回值
/*
function anonymous(obj){
return '我是'+obj.name+',年龄'+obj.age+',性别'+obj.sex+'';
}
*/
return newFn;
}
let str = "get-element-by-id"
// 转化为 getElementById
解法:
// 匹配 -跟后面的单个字符,再利用 replace 替换掉
str = str.replace(/-\w/g,x=>x.slice(1).toUpperCase())
例: abbcccddddd -> 字符最多的是d,出现了5次
let str = "abcabcabcbbccccc";
let num = 0;
let char = '';
// 使其按照一定的次序排列
str = str.split('').sort().join('');
// "aaabbbbbcccccccc"
// 定义正则表达式
let re = /(\w)\1+/g; // 匹配单字符,并记住它
replace具体方法参数可以参考 String.prototype.replace
参数 | 含义 |
---|---|
match | 匹配的子串 |
p1,p2,... | 匹配括号中的内容,依次推 |
offset | 偏移量 |
string | 原字符串 |
str.replace(re,(match,p1) => {
if(num < match.length){
num = match.length;
char = p1;
}
});
console.log(`字符最多的是${char},出现了${num}次`);
a='34';b='1234567'; // 返回 2
a='35';b='1234567'; // 返回 -1
a='355';b='12354355'; // 返回 5
isInclude(a,b);
function isInclude(a,b){
const result = b.match(new RegExp(a));
return result === null ? -1 : result.index;
}
parseToMoney(123456789); // return '123,456,789'
function parseToMoney(num){
let str = '';
let [integer,decimal] = String.prototype.split.call(num,'.');
str = integer.replace(/(?<=\d)(?=(\d{3})+$)/g,','); // 以数字开头,3个数字为一组,匹配多次
return decimal?str+'.'+decimal:str;
}
通过这一次的正则做题,让自己对正则有了一个清晰的认识,也巩固了以往正则的基础,收获非常大。
将几个组件提取到公共组件之后, 开发效率从2小时开发一个基本的后台CRUD模块,提高到了不到 1 小时就能完成。于是,日常的开发就变成了 复制,粘贴,修改表单 fields
,增加Router
,增加对应菜单的权限key
。然后一套写下来差不多 30,40分钟的样子,在暗自窃喜自己又提高了开发效率的一周后,突然感觉到了一阵的空虚,索然无味。重复的创建相同的文件夹,重复的修改文件name
,重复的写相似的Router
,简直让我感觉到崩溃
这重复的劳动,能不能一键就生成了,我只关注于业务代码的逻辑跟质量,行否?
这念头一起,瞬间四肢百骸 涌起一阵阵的波动,这,这,这,难道便是新的境界?
背景
目标
Page
文件中的PageName
model
文件 中的 namespace
Page
目录下的 index.js
文件中 export
创建的文件configs
文件中,新增对应的权限 key
Layout
目录下新增对应的 Router
VSCode
插件扩展在充分了解了各个项目的情况之后,选择使用VSCode
插件扩展的方式,去实现这一次的功能。
官方的文档对开发扩展有详细的文档说明,所以下面就大概提一下
环境安装
npm i -g yo generator-code // 官方插件开发脚手架
yo code // 执行脚手架命令
根据步骤创建一个New Extension
这边选的是
JavaScript
,所以下文都是JavaScript
的
这个新建的Hello Word
扩展,点击调试,就能运行了,这里需要注意的是 package.json
中vscode
的版本
打包成.vsix
文件
npm i -g vsce
vsce vscode-plugin-demo
如果没有vscode
的 Access Token
需要申请一个 vscode 推送准备
Node.js
开发准备由于 VSCode
的扩展里面,内置了 Node.js
,所以 fs
模块就能直接用了
遇到的问题,以及解决
本来想直接使用 inquirer
这个交互式工具去完成的,结果直接写 命令调用是没用的,需要通过 VSCode
提供的 vscode.window.createTerminal
的命令去创建一个终端,执行自己的 shell
代码。在查看API
的过程中,发现可以直接利用input
弹框获取输入值,所以就用弹框的形式了
在创建文件夹的时候,需要获取 项目的根路径
,当时还折腾了下,都不太满意,没想到VSCode
里面就直接有命令可以使用获取到。
对Page/index.js
文件内容进行添加操作的时候,需要获取对应的行数,进行添加。思考了下,将fs.readFile
读取出来的字符串以\r\n
的方式进行数组分割,再匹配最后一个 export的位置,然后进行插入
大概的操作
shelljs
命令,cp
模板文件,放到对应的文件路径下面,判断文件是否存在,给出提示。Node.js
的 fs
模块,去读写文件。然后修改文件名,增加对应的语句等...VSCode
开发扩展极其的方便,几乎是0
学习成本VSCode
扩展里。比如扩展公共组件的 参数提示之类的。公司需要一个便捷的方式获取员工的照片以制作工牌,靠人力收集的话,则无法保证图片的效果以及标准,浪费人力时间。制作成H5,方便大家上传规范的图片。
公司推荐使用的技术栈有:React
,Jquery
,ES6
,这里考虑到这个项目比较轻量,没有过多的DOM操作,直接选择了原生的ES6开撸
实现的难点在于 放大,缩小,裁剪,旋转这一项功能。
当时查看组件库中无此组件,项目周期只有一周(前端开发只有2天),根本不够撸一个稳定版组件。只能退而求其次,选择第三方组件,最后经过,issues
,star
,pkgSize
,操作性
四个方面的考量,选择了cropperjs
项目还是比较简单的,这里略过不提。
localstorage
无法保存需要保存是因为需要满足 裁剪之后,虽然没有上传,但是也不用重新选择照片再次裁剪的功能
具体问题
IndexedDB
的方式进行存储,但是这个API对低版本的IOS
,Android
不支持。解决方案
localStorage
),一张用于备用(localStorage
),一张用于上传(IndexedDB
)。IndexedDB
,不支持就降级,选择备用的图片(localStorage
)IndexedDB
呢,因为产品要求,她想要最好的...Canvas.toDataURL
有时候得到的base64图片是白色的具体问题
解决方案
img.onload
未完成就转换,将其简单修改crossorigin
或者日期属性base64
的图片数据,网络请求一直无响应,页面崩溃具体问题
base64
图片,会让浏览器卡死解决方案
base64
的字符串太长,上传的时候造成浏览器的内存增加,然后服务器没响应,就卡死了base64
的图片转换成file(blob)
文件上传在进行业务开发的时候,往往能抽离出很多相似的模块跟组件。自己负责的项目一般也不止一个,如果只是将其手动复制到不同的项目当中,组件跟模块往后的更新与迭代将会很困难。
大多数人都会这么干,那就是将公共的组件、模块、工具方法、发布到 npm私有仓库上进行统一的管理跟迭代...
npm
仓库当中,所以就造成了 npm
公共组件仓库体积的急剧增加。最开始的解决方案,将公共操作方法,公共组件,公共模块弄成了三个仓库单独发布
自从这样实行以来,上面的问题得到了初步的解决,本以为满心欢喜,结果却遇到了更棘手的情况。
pull request
,负责管理公共仓库的小华,review
以后觉得相当的不错,于是打上了 tag
更新了版本。那能不能一个仓库管理多个包,但是这些包相对于这个仓库,又是独立的呢?
答案是可以的,在调研之后,发现业界早已存在解决方案,那就是 monorepo
管理方案,本文基于monorepo
方案的开源框架 lerna
进行简单的讲解。
lerna
的简单使用Lerna
是一个管理多个 npm 模块的工具,是 Babel 自己用来维护自己的 Monorepo 并开源出的一个项目。优化维护多包的工作流,解决多个包互相依赖,且发布需要手动维护多个包的问题。
npm i -g lerna
git init lernaExample
cd lernaExample
lerna init
然后根据提示,项目当中生成的 package.json
跟 lerna.json
如下
// package.json
{
"name": "root",
"private": true, // 私有的,不会被发布,是管理整个项目,与要发布到npm的解耦
"devDependencies": {
"lerna": "^3.15.0"
}
}
// lerna.json
{
"packages": [
"packages/*"
],
"version": "0.0.0" // 仓库的版本
}
npm
包这里有个注意点是,lerna
中的仓库名 @lernaExample
必须一致
lerna create @lernaExample/utils
lerna create @lernaExample/components
lerna create @lernaExample/decorator
文件目录
lerna add react // 为所有 package 增加 react 模块 (像这种公共的包可以使用 lerna bootstrap --hoist 提升到项目的工程目录)
lerna add classnames--scope @lernaExample/components // 为 @lernaExample/components 增加 classnames 模块
lerna add @lernaExample/utils--scope @lernaExample/components // 增加内部模块之间的依赖
// 想删除某个依赖项
lerna clean
lerna publish
// 按照提示发布就行
// 如果发布报错,可能是没有相对于仓库的权限
lerna
完整示例在上文中,只是简单的了解 lerna
的基本使用,在项目当中使用的话还存在下面的几个问题。
es6
文件,则很有可能浏览器中不能兼容eslint
格式校验commit
校验,changlog
文件生成等babel
环境需要下载的 babel
包
"dependencies": {
"@babel/runtime": "^7.6.2",
"@lernaExample/utils": "^0.0.0",
"classnames": "^2.2.5",
"react": "^16.8.0"
},
"devDependencies": {
"@babel/cli": "^7.6.0",
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-decorators": "^7.6.0",
"@babel/plugin-transform-regenerator": "^7.4.5",
"@babel/plugin-transform-runtime": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0",
"babel-plugin-import": "^1.12.2"
},
.babelrc
文件配置
{
"presets": [
[
"@babel/preset-env",
{
"modules": false // 模块使用 es modules ,不使用 commonJS 规范
}
],
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
["@babel/plugin-transform-regenerator"],
["@babel/plugin-proposal-class-properties"],
[
"@babel/plugin-transform-runtime",
{
"corejs": false, // 默认值,可以不写
"helpers": true, // 默认,可以不写
"regenerator": true, // 通过 preset-env 已经使用了全局的 regeneratorRuntime, 不再需要 transform-runtime 提供的 不污染全局的 regeneratorRuntime
"useESModules": true // 使用 es modules helpers, 减少 commonJS 语法代码
}
],
[
"import",
{
"libraryName": "antd"
}
] // 通过手动引入antd样式
]
}
package.json
文件中增加编译命令
"main": "lib/index.js", // 文件入口
"scripts": {
"compile": "babel src --out-dir lib",
...
},
使用方式 npm run compile
eslint
环境我这边直接使用的 vscode
扩展的 eslint
工具,如果团队内部对 eslint
有自己的定义,可以单独下载 eslint
包,对其文件进行配置。
commit
以及 changelog
环境自动校验commit规范
使用 husky
包,相关依赖项
"devDependencies": {
"@commitlint/cli": "^7.0.0",
"@commitlint/config-conventional": "^7.0.1",
"husky": "^0.14.3",
}
创建 commitlint.config.js
文件
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'bug',
'docs',
'style',
'refactor',
'test',
'chore',
'version',
'build',
'ci',
'perf',
'revert',
]
],
'scope-case': [2, 'always', ['lower-case', 'snake-case', 'upper-case', 'kebab-case']],
'subject-case': [0]
}
};
自动生成changelog
记录
npm i cz-lerna-changelog
下载 lerna
专用的 changelog
包
npm run compile
commit
内容,如果内容不符合,会自动打回lerna version
,打上tag
生成 changlog
记录lerna updated
,然后执行 lerna publish
lerna publish
git push
lerna
之后,package
之间存在互相的依赖项,lerna
会自动对其管理,不需要手动更新 package.json
中依赖项的版本号。lerna
使用介绍A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.