随笔
使用node.js的process
内置模块和fs
文件模块创建了建议的命令行工具,能快速创建私人定制的h5项目(使用rem适配 + zepto.js + swiper.js + h5兼容代码)
js
技巧
对象合并技巧
若字段相等则会都被后一个的值覆盖
Object.assign(obj1, obj2)
参数为简单类型则深拷贝,引用类型则浅拷贝。{...obj1, ...obj2}
数组转对象:
{...arr}
for(var key in arr){obj[key] = arr[key]}
深拷贝
- 判断类型+创建空数据递归赋值(还是最靠谱)
JSON.parse(JSON.strighfy(obj))
,若元素有函数等非JSON格式对象,如正则、Error、NaN,则会丢失或出错,Data类型会成为字符串
- 浅拷贝,除直接赋值外。元素为简单类型则深拷贝,引用类型则浅拷贝,本质上还是浅拷贝
- 对象可用
{...obj}
, - 数组可用
[...arr]
、new Array(...arr)
、Array.from(arr)
,slice
、concat
- 对象可用
指数运算
- es5
Math.pow(2, 10)
- es7
2 ** 10
- es5
浮点数转整数(位运算),都是向下取整
~~6.95
~
按位非,就是每一位取反6.95 >> 0
>>
位运算右移6.95 << 0
>>
位运算左移6.95 | 0
|
位运算或6.95 >>> 0
>>>带符号位的右移
不可取整负数
隐式转换规则:
运算, 如
+
- 包装类型在运算的时候,会先调用
valueOf
方法,如果valueOf
返回的还是包装类型,会默认再调用toString
方法。**数组.toString
** 默认会将数组各项使用逗号 “,” 隔开, 比如[1,2,3].toSting
变成了"1,2,3"
,空数组 toString 就是空字符串
- 包装类型在运算的时候,会先调用
比较, 如
==
,除开会进行上文运算过程,还有:- 如果非
number
与number
比较,会将其转换为number
。 - 如果
boolean
与其他类型比较,那么会先将boolean
转换为number
- 如果非
1
2
3
4
5
6
7
8
9
10
11
121 + '1' //'11'
true + 0 //1
{}+[] //[Object Object] 包装类型被valueOf,而后toString
4 + {} //'4[Object Object]'
4 + [1] //'41'
'a' + + 'b' //'aNaN'
console.log ([] == 0) //true 先将[]转为0
console.log (![] == 0) //true ![]先转为false,false再转为0
console.log ([] == ![]) //true ![]先转为false,false再转为0:0==[] =>true
console.log ([] == []) //false 直接比较内存地址
console.log({} == !{}) //false !{}先转为false,再转为0:{}==0 =>false
console.log({} == {}) //false 直接比较内存地址undefined
隐式转换为number
是NaN
,null
是0
:2 * undefined = NaN
、2 * null = 0
在函数定义形参时若知道参数是对象,且清除它的属性,可以直接:
1
2
3
4function fn({attr}) {
//相当于只接收obj.attr作为参数,使用时不用obj来调用
}
fn(obj)es6的方法写法也可以结合async:
1
2
3const obj = {
async 方法名(){}
}JS不能通过number的单个
.
判断数字是否结束,因此:123.toString()
//err123..toString()
//ok(123).toString()
//ok123.0.toString()
//ok
复制到粘贴板
1
2
3
4
5
6
7
8
9
10
11
12
13copy(content) {
const select = window.getSelection();
const cDom = document.createElement("div");
const body = document.body;
cDom.innerText = content;
body.appendChild(cDom);
//选中要粘贴的元素
select.selectAllChildren(cDom);
document.execCommand("Copy");
//复制后取消选中
select.removeAllRanges();
body.removeChild(cDom);
}若选中失败有可能是:
1.selectAllChildren传入的元素不是原生dom元素
2.点击事件冲突
3.全局的css设置了
-webkit-user-select:none;
,将其设置为text
、all
即可用
delete
删除数组元素时数组长度不会发生变化,不会影响之后的索引位置瀑布流: 按列存放数据, 每次将数据添加到高度最小的一列的数据中。必要时可以根据图片和屏幕宽度用js计算每一列宽度和列数
使用父元素flex布局会使短的一列跟长的一列高度相等,父元素添加属性align-items: flex-start;可解决,记住要给子元素分配flex:1或固定宽度
关键字
function
比var
预解析的变量提升更优先,且声明时赋值offsetTop
获取元素距离父元素顶部的距离函数内
形参
与var
定义的参数重名、且var
定义的参数未赋值时,取到的值不是undefined
而是形参
的值 (应该当作一个变量)函数this取决于它的调用者。即使从对象的方法中赋到值,调用时没有调用者this还是指向window
任何对象转为布尔值,都为得到 true(切记!在 JS 中,只有
0,-0,NaN,"",null,undefined
这六个值转布尔值时,结果为false
,new Boolean(false)
为true…..setTimeout
来自于window对象,不属于js全局函数数组扁平化: str为
JSON.strigfy(arr)
arr.flat(Infinity)
str.replace(/\[|\]/g, '').split(',')
,缺点是结果数组内元素都是字符串str = '[' + str.replace((\[|\]/g, '') + ']'
,再JSON.parse(str)
递归赋值
1
2
3
4
5
6//reduce + 递归
function flatten(arr) {
return arr.reduce((pre, cur) => {
return [].concat(pre, Array.isArray(cur) ? flatten(cur) : cur)
})
}扩展运算符
1
2
3
4//扩展运算符
while(arr.some(Array.isArray)) {
arr = [].concat(...arr)
}
与数组的map方法对应,有flatMap方法,将返回的数组扁平化,适合递归对多级数组处理并返回扁平化的数组
生成guid(全局唯一标识符)
方式一
1
2
3
4
5
6function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
function newGuid() {
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}方式二
1
2
3
4
5
6
7
8
9
10function newGuid() {
var guid = "";
for (var i = 1; i <= 32; i++){
var n = Math.floor(Math.random()*16.0).toString(16);
guid += n;
if((i==8)||(i==12)||(i==16)||(i==20))
guid += "-";
}
return guid;
}
微软提供的网页端文件预览: https://view.officeapps.live.com/op/view.aspx?src=filePath 但是我用此地址预览PDF时提示找不到文件,其他暂未发现问题
手写call方法:
1
2
3
4
5
6//apply只需要把形参arg前的`...`去掉
Function.prototype.myCall = function(context, ...arg) {
const _this = context || window
_this._fn = this //获取调用的函数
return _this._fn(...arg)
}手写bind: 在call的基础上返回一个闭包函数,只有这样才能在调用时留住this指向的改变
1
2
3
4
5
6
7Function.prototype.myBind = function(context, ...arg) {
const _this = context || window
_this._fn = this
return function() {
return _this._fn(...arg)
}
}
call、apply方法第一个参数传
null
或undefined
,相当于普通调用,this取决于调用者不论对象、数组、字符串还是
new String
创建的包装类型字符串,其key
值其实都是string
类型,但是使用时内部没有全等判断所以也可以用number
类型索引。1
2
3
4
5
6
7
8
9let a = new String('ssss')
for (let key in a) {
if (key === '1') { //key是string类型,判断能成立
console.log(key)
a[key] = 'dd' //无效。字符串不能索引赋值,严格模式会报错
console.log('success')
}
}
console.log(a) //包装类型String {'ssss'}url传参时,若有中文的参数,最好使用
encodeURIComponent
和decodeURIComponent
进行编码和解码。由于游览器会自动作一次解码,需要使用两次encodeURIComponent
才有效str.padStart(num, str2)
str小于num位时在前面补str2str.endsWith(str2)
str是否以str2结尾函数的声明提前有块级作用域(不会将函数直接提升到全局),执行到函数这一步时才会将块级作用域内的该变量提升到全局:
1
2
3
4
5
6
7
8console.log(a);//undefined
if (true) {
a = 1;
function a() {}//此时将a覆盖到全局,即使a已经是1不是函数了
a = 5;//此时只是块级作用域内的改动,全局的a仍为1
console.log(a);//5
}
console.log(a);//1
pdf转图片
1 | // 2.9.359以上版本才修复显示签章的技术问题,当前使用2.12.313 |
css
扩散动画
1 | &.active .circle { |
尖角圆弧正方形
1 | .box { |
三线旋转加载动画
1 | .load { |
双球加载动画
1 | .load { |
动态修改样式(伪元素)
本质上就是利用 行内样式+css3变量
- 给祖先元素动态添加行内样式定义css变量如:
--color: red
- 在样式表中使用css变量:
background: var(--color)
css变量(var函数)
除了传一个参数使用变量外,var()
函数还可以有更多用法:
var()
第二个参数第二个参数表示默认值,若变量不存在就使用此默认值- 如何变量值是字符串,可以直接与其他字符串拼接:
'字符串'var(--变量)
- 若变量值有单位,不能加
''
写成字符串
css变量作用域就在定义变量的元素内,权重类似选择器
table-cell实现居中
设置元素样式:
1
2
3
4//其行内子元素会被居中,子元素不确定宽高和数量时,特别实用
display: table-cell;
vertical-align: middle;
text-align: center;设置父元素样式
1
2
3//若不设置display: table;宽度设置可能影响不了子元素
display: table;
width: 100%;
clip-path剪裁图形
clip-path: polygon(x1 y1, x2 y2...)
绘制多边形clip-path: circle(半径 at 圆心x 圆心y)
圆型clip-path: circle(x半径 y半径 at 圆心x 圆心y)
椭圆clip-path: inset(距上 距下 距左 距右 round 左上圆角 右上圆角 右下圆角 左下圆角)
圆角在兄弟元素中,其中一兄弟的子元素层级设置到普通文档流之上时,另一个兄弟单独讲子元素设置层级再高也无法覆盖,需要给这另一个兄弟元素自身提高层级
整站黑白
1 | html { |
网络请求
- https默认端口
443
, http默认80
,在端口为默认端口时也就不会跨域
vue
关于数据响应
当组件data
中的数据是Object,若初始时未赋该属性,新添加该属性需要$set
。还有一种方式是初始赋空该属性,在实例创建时就会开始监听该属性。
代理跨域
vue-cli2.x.x config->index.js->dev中:
1
2
3
4
5
6
7
8
9
10proxyTable: {
'/api': {
target: 'http://api.certificate.uuudoo.com', //接口源地址
changeOrigin: true, //是否跨域
secure: true,
pathRewrite: {
'^/api': '' //路径重写
}
}
},vue-cli3.x.x vue.config.js中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16module.exports = {
devServer: {
port: 8000,
proxy: {
"/api": {
target: 'http://api.certificate.uuudoo.com',
changeOrigin: true, //是否跨域
secure: true,
pathRewrite: {
'^/api': '' //路径重写
}
}
}
},
},
};
之后请求的接口由vue开启的服务代理,以/api
开头请求,无需其他操作:
1 | Axios.defaults.baseURL = '/api' |
vue-cli3配置别名
vue.config.js中
1
2
3
4
5
6
7
8
9module.exports = {
configureWebpack: {
resolve: {
alias: {
'components': '@/components'
}
}
}
}
vue-cli3关闭prettier代码格式化
package.json
=>eslintConfig
=>:1
2
3"rules": {
"prettier/prettier": "off"
}或者在
vue.config.js
中:1
2
3module.exports={
lintOnSave: false
}
添加sass-loader
安装
node-sass@4.14.1
,sass-loader@8.0.2
。目前这两个版本稳定,最新版互相不兼容webpack.base.config.js
的module
中配置loader
规则:1
2
3
4{
test: /\.scss$/,
loaders: ['style', 'css', 'sass']
},
样式穿透
在vue中定义scoped
后样式产生作用域,但是一些ui组件需要我们局部修改样式,在scoped
内修改经常不生效。除了新增一个没有scoped
的style
标签外,还可以选择穿透scoped
:
.parent >>> .ui-component
1
2
3
4
5
6
- 有些Sass 之类的预处理器无法正确解析 `>>>`。更建议使用 `/deep/`或`::v-deep` :
```css
.parent /deep/ .ui-component
.parent ::v-deep .ui-component
注意:穿透不是成为全局样式,新增没有scoped的style标签才会成为全局样式污染全局。因此穿透仅在父子关系时在父组件使用有效,兄弟组件无效
热更新配置
在package.json
=>scripts
=>dev
中:
npm run dev
后自动在游览器开启:--open
- 开启服务,可在同一wifi下的PC和移动端游览器查看:
--host 192.168.0.144
- 开启服务的端口
--port 3000
(vue-cli项目直接在config=>index.js=>dev=>port设置更好)
过渡动画
默认前缀是v
,给transition
组件添加name
如:slide
1 | //路由切换时从最右往左平移动画 |
1 | //底部弹出框 |
1 | //切换路由时上下淡入淡出动画,父元素y方向最好不要超出隐藏 |
1 | /* 弹出框左上角缩放显示 */ |
还可以使用animate.css,改变transition
的enter-active-class
属性设置入场,改变leave-active-class
设置离场动画(还是需要给父元素overflow:hidden
):
1 | <transition duration="1000" |
封装Axios请求
vue中使用axios发起post请求要么每次请求都要配置header,要么必须使用urlencoded格式传参:a=1&b=2
,可封装为表单序列化的post请求:
1 | //axios挂载到vue原型方式 |
组件通信方法
父子组件
props和$emit(常用)
v-model
父组件通过
v-model
传递值给子组件时,会自动传递一个value
的prop,在子组件中用props
接收,并通过this.$emit(‘input',val)
自动修改v-model绑定的值。涉及到双向绑定原理如input:input动态绑定一个value,通过input事件动态修改这个value
在vue2.2以后,新增了
model
选项来自定义v-model
绑定的prop和事件:1
2
3
4model: {
prop: "val", //将不会使用默认的value
event: "change" //将不会使用默认的input
}$parent和$children
ref操作子组件
$attrs和$listeners – 祖先
组件实例.$attrs
获取祖先组件传递来的所有未被props接收的属性可以通过给下一个后代组件
v-bind="$attrs"
将未接收的属性继续传递下去组件实例.$listerners
获取祖先组件传递来的所有自定义方法可以通过给下一个后代组件
v-on="$listeners"
祖先组件传来的自定义方法继续传递下去,后代组件就可以直接通过$emit
调用
provide和inject(vue 2.2+) – 祖先
父组件中通过
provide
(obj)来提供变量,然后在子组件中通过inject
(arr)来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provide中的数据。缺点就是修改祖先提供的值不方便provide的普通数据并不是响应式的,。将已经定义好的响应数据对象传入,然后此数据内部才能响应
若provide定义为对象,里面
this
是拿不到vue实例的,必须定义为方法且返回提供的数据对象1
2
3
4
5
6//obj是data中的响应数据,其内部的属性是响应的。直接传内部的普通数据不会响应,必须将obj整个传入,子组件注入后使用其属性
provide() {
return {
provideData: this.obj
}
}
非父子组件间也适用
中央事件总线
新建一个Vue实例bus,然后通过
bus.$emit
触发事件,bus.$on
监听触发的事件。若组件通过
router-view
切换后被销毁,可使用keep-alive
保留状态vuex
使用前需要Vue.use(Vuex)
创建仓储对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23var store=new Vuex.Store({
state: {相当于data属性存储数据},
mutations: {相当于methods存储方法
//mutations中的方法最多有两个参数: 1.state属性 2.传入的参数
},
actions: {
fn(context,传入的参数){
context.commit('mutations中的方法')//自定义触发mutations中的方法,使其可异步
}
},
getters:{ //包装state中的数据
自定义方法: function(state){
return 包装后的数据
}
},
//模块化管理模式。
modules:{
a: {
state: {},
getters: {}...
}
}
})
再将仓储对象挂载vue实例的
store属性
- 组件中访问
store
中的数据必须this.$store.state.数据
- 组件中访问
调用
mutations
中的方法必须this.$store.commit('方法名', 传入的参数)
使用
getters
包装后的数据通过this.$store.getters.自定义方法
getters只对外包装数据,修改实际state数据还是找mutations
如果用到state的数据,用到的数据被修改会立即重新计算包装
使用
actions
中的方法通过this.$store.dispatch('方法名', 传入的参数)
actions
中方法第一个参数为context,第二个参数为传入的参数。可通过context.commit(mutations中的方法)
配合异步操作修改数据,如Promise+async / await(mutations内不能异步操作),
Vuex 允许我们将 store 分割成模块,每个模块拥有自己的 state、mutation、action、getter, modules。使用
modules
如下:- 组件内调用a模块的状态:
this.$store.state.a.数据
mutations
和actions
使用方式与非模块相同,actions
中context.state
是模块状态,context.rootState
才是根节点状态mutations
和getters
中的方法第一个参数是模块自身的state
getters
第三个参数是rootState
- 组件内调用a模块的状态:
mapGetters
和mapActions
映射1
2
3
4
5
6
7
8
9//通过import从vuex中引入:
import {mapGetters} from 'vuex'
import {mapActions} from 'vuex'
//在computed中使用: 将对应getter直接当做组件计算属性使用
...mapGetters(['getterName'...])
...mapGetters({'getterNewName': 'getterName'}) //重命名
//在methods中使用: 将对应action直接当做组件方法使用
...mapActions(['actionName'...])
...mapActions({'actionNewName': 'actionrName'}) //重命名此外还有mapState=>computed,mapMutations=>methods用法和效果类似
多行文本截断
vue-clamp
实现多行文本截断并在末尾同一行最后加入按钮等元素,支持插槽。
如果需要自己实现,可以通过:
- 最外部块级元素。文本容器应该是行内元素(文本跟末尾/头部按钮也要包在一个行内元素内)。
- 计算内容需要的总高度:
最外部总高度 = 内容行高 * 行数
。要特别注意:- 末尾/头部按钮高度如果大于行高,会影响该行的高度
- 最外部如果设置了
padding
,高度需要算上paddingTop
和paddingBottom
- 以上计算可以使用
getComputedStyle
拿到实际样式值
- 文本容器内尾部/头部加入的按钮等元素,设置
display: inline-block
才能和文本文字同一行 - 设置文本容器
visibility: hidden
,并循环文本字符串逐个添加到文本容器(尾部可能加上...
等省略符),直到实际总高度超过之前计算的总高度,就取上一次的字符串 - 最后再设置文本容器
visibility: visible
展示。
已通过vue实现该组件,需要源码的可以联系我,乐意分享
浅谈富文本
除表单元素外,其他元素默认不可编辑。html5提供了contenteditable
属性定义元素为可编辑状态,可借此实现富文本编辑器。
定义
contenteditable
后聚焦时默认会出现外边框使用css取消:1
[contenteditable]:focus{outline: none;}
没有
change
事件,通过元素的keyup
事件监听修改使用
getSelection()
获取光标起始位置信息,其getRangeAt
方法获取range对象,addRange
和removeAllRanges
向当前选取添加一个 range 对象和 删除所有 range 对象1
let range = selection.getRangeAt(0);
range对象的主要属性:
startContainer
: range 范围的起始节点。endContainer
: range 范围的结束节点startOffset
: range 起点位置的偏移量。endOffset
: range 终点位置的偏移量。commonAncestorContainer
: 返回包含 startContainer 和 endContainer 的最深的节点。collapsed
: 返回一个用于判断 Range 起始位置和终止位置是否相同的布尔值。操作range节点的主要方法:
setStart()
: 设置 Range 的起点
setEnd()
: 设置 Range 的终点selectNode()
: 设定一个包含节点和节点内容的 Range
collapse()
: 向指定端点折叠该 RangeinsertNode()
: 在 Range 的起点处插入节点。
cloneRange()
: 返回拥有和原 Range 相同端点的克隆 Range 对象
vue-quill-editor使用
安装:
npm i -S vue-quill-editor
样式引入:
1
2
3import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'注册
全局
1
2import VueQuillEditor from 'vue-quill-editor'
Vue.use(VueQuillEditor, {placeholder: '请输入'})组件内
1
2
3
4
5import {quillEditor, Quill} from 'vue-quill-editor'
//实例内
components: {
quillEditor
}
配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//工具栏配置
const toolbarOptions = [
["bold", "italic", "underline", "image"], // toggled buttons
[{ header: 1 }, { header: 2 }],
[{ color: [] }]
]
//data中:
content: '内容',
editorOption: {
modules: {
toolbar: {
container: toolbarOptions, // 工具栏
handlers: {
image: function() {
//监听工具栏图片点击
}
}
}
}
}使用
1
<quill-editor class="quil" ref="QuillEditor" v-model="content" :options="editorOption" @change="onEditorChange($event)"></quill-editor>
- 使用
v-model
+change事件
可自定义富文本展示的内容,此外还有focus
、blur
事件
- 使用
使用quill-image-extend-module上传插件总会使新图片插入在最前面,光标也在最前面,因此打算用input自定义图片的上传和插入,见下文
- input文件上传并插入
图片上传按钮触发:
1
2
3
4
5
6
7
8
9
10
11
12
13//工具栏中配置handler
toolbar: {
container: toolbarOptions, // 工具栏
handlers: {
image: function(value) {
if (value) {
document.querySelector('#upload').click()
} else {
this.quill.format('image', false)
}
}
}
}html添加隐藏的文件input:
1
<input type="file" name="img" id="upload" @change="up" style="display:none;">
给隐藏的文本input添加
change
事件监听上传1
2
3
4
5
6
7
8
9
10
11
12
13
14
15inputChange(e) {
let formData = new FormData()
formData.append('file', e.target.files[0])
this.axios.post('/upload', formData)
.then(res=>{
this.upSuccess(res.data.data.url)
})
},
//上传成功后在富文本最后添加对应url的图片标签
upSuccess(url) {
let quill = this.$refs.QuillEditor.quill
let length = quill.getSelection().index
quill.insertEmbed(length, 'image', url)
quill.setSelection(length + 1)
}
vue中使用ueditor
使用
在public下的static目录存放UEditor目录
npm i vue-ueditor-wrap@2.x
v2只支持vue2引入组件并使用:
template中
1
2
3
4
5
6<vue-ueditor-wrap
:config="editorConfig"
v-model="richText"
@beforeInit="初始化前回调"
@ready="初始化完成回调"
/>模板中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98import VueUeditorWrap from "vue-ueditor-wrap";
//组件option中注册
components: {
"vue-ueditor-wrap": VueUeditorWrap
},
//data中添加
editorConfig: {
// 编辑器不自动被内容撑高
autoHeightEnabled: false,
// 初始容器高度
initialFrameHeight: 500,
// 初始容器宽度
initialFrameWidth: "100%",
// 语言
lang: "zh-cn",
// 设置自带工具栏
toolbars: [
[
"undo",
"redo",
"|",
"bold",
"italic",
"underline",
"fontborder",
"strikethrough",
"superscript",
"subscript",
"removeformat",
"formatmatch",
"autotypeset",
"blockquote",
"pasteplain",
"|",
"forecolor",
"backcolor",
"insertorderedlist",
"insertunorderedlist",
"selectall",
"cleardoc",
"|",
"rowspacingtop",
"rowspacingbottom",
"lineheight",
"|",
"customstyle",
"paragraph",
"fontfamily",
"fontsize",
"|",
"directionalityltr",
"directionalityrtl",
"indent",
"|",
"justifyleft",
"justifycenter",
"justifyright",
"justifyjustify",
"|",
"touppercase",
"tolowercase",
"|",
"imagenone",
"imageleft",
"imageright",
"imagecenter",
"|",
"pagebreak",
"|",
"horizontal",
"date",
"time",
"spechars",
"|",
"inserttable",
"deletetable",
"insertparagraphbeforetable",
"insertrow",
"deleterow",
"insertcol",
"deletecol",
"mergecells",
"mergeright",
"mergedown",
"splittocells",
"splittorows",
"splittocols",
"|",
"preview",
"searchreplace"
]
],
// 上传接口地址
serverUrl: "",
// UEditor资源目录
UEDITOR_HOME_URL: "./static/UEditor/"
},添加自定义图片上传按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94//beforeInit回调传入下面方法自定义图标,手动触发隐藏的文件域点击并上传图片
beforeInitHandle: function(editorId) {
// console.log(`${editorId}的配置参数:${JSON.stringify(config)}`);
window.UE.registerUI(
//按钮标识,上传图片后需要这个标识调用editor.execCommand插入图片
this.editorInsertImage,
(editor, uiName) => {
this.editor = editor;
// 注册按钮执行时的 command 命令,使用命令默认就会带有回退操作
editor.registerCommand(uiName, {
execCommand: (name, data) => {
editor.execCommand("insertimage", {
src: data.imgUrl,
height: "100"
});
}
});
// 创建按钮
const btn = new window.UE.ui.Button({
// 按钮的名字
name: uiName,
// 提示
title: "插入图片",
// 需要添加的额外样式,可指定 icon 图标,图标路径参考常见问题 2
cssRules:
background-image: url(../images/icons.png); background-position: -380px 0px;"
// 单击
onclick: () => {
const ueditorSelectImg = this.$refs["ueditorSelectImg"];
if (ueditorSelectImg) {
ueditorSelectImg.click();
}
}
});
// 当点到编辑内容上时,按钮要做的状态反射
editor.addListener("selectionchange", () => {
const state = editor.queryCommandState(uiName);
if (state === -1) {
btn.setDisabled(true);
btn.setChecked(false);
} else {
btn.setDisabled(false);
btn.setChecked(state);
}
});
return btn;
},
// 指定添加到工具栏上的哪个位置,默认时追加到最后
49,
// 指定这个 UI 是哪个编辑器实例上的,默认是页面上所有的编辑器都会添加这个按钮
editorId
);
},
//隐藏文本域的change事件
editorFileChangeHandle: function(e) {
const files = e.target.files;
if (files.length === 0) {
return;
}
const file = files[0];
const right = checkUploadImg(file, this.checkImageCallBack);
if (!right) {
return;
}
const loaded = res => {
// 清空富文本编辑器文件
this.clearEditorFile();
//上传图片
this.editorUploadImage = true;
const params = {
base64: res,
group: "richText"
};
saveImg(params).then(res => {
this.editorUploadImage = false;
const code = dataIsNullNumber(res.code);
if (code === 0) {
const imgFileUrl = dataIsNullStr(res.data);
if (this.editor && this.editor.execCommand) {
//上传后将返回的图片插入到编辑器
this.editor.execCommand(this.editorInsertImage, {
imgUrl: `${GETIMG}${imgFileUrl}`
});
}
}
});
};
const loadfailed = msg => {
// 清空富文本编辑器文件
this.clearEditorFile();
this.$root.showMessage("error", msg);
};
// 获取图片base64
getImageBase64(file, loaded, loadfailed);
在vue-ueditor-wrap内部初始化之前修改v-model绑定的数据可能导致出现不止一个富文本编辑器,造成页面和数据错误。需要在ready事件中异步获取数据
回流和重绘
reflow回流,如某个子元素样式发生改变,直接影响到了其父元素以及往上追溯很多祖先元素(包括兄弟元素),这个时候浏览器要重新去渲染这个子元素相关联的所有元素的过程称为回流。下面情况会导致reflow发生
改变窗口大小
改变文字大小
内容的改变,如用户在输入框中敲字
激活伪类,如:hover
操作class属性
脚本操作DOM
计算offsetWidth和offsetHeight
设置style属性
repaint重绘,重绘速度快于reflow。只是改变某个元素的
背景色、文 字颜色、边框颜色
等等不影响它周围或内部布局的属性,将只会引起浏览器 repaint
VSCode
霓虹灯主题
windows下:
- 安装 SynthWave ‘84
- ctrl + shift + P 输入 Enable custom CSS and JS 并点击
- 再次 ctrl + shift + P 输入 Enable Neon Dreams 点击
- 重启vscode
打字特效
安装 Power Mode
配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23"powermode.enabled": true,
//样式
// 火焰
// "powermode.presets": "flames",
// 炸裂
// "powermode.presets": "exploding-rift",
// 爆炸
// "powermode.presets": "simple-rift",
// 粒子
"powermode.presets": "particles",
// 烟花
// "powermode.presets": "fireworks",
// 魔法
// "powermode.presets": "magic",
// 回形针
// "powermode.presets": "clippy",
// 时间间隔
"powermode.comboTimeout": 0,
// 抖动
"powermode.enableShake": false,
// "powermode.shakeIntensity": 3
// 随字体颜色变化
// "powermode.backgroundMode": "mask"
- 本文作者: MR-QXJ
- 本文链接: https://mr-qxj.github.io/2021/10/14/其他/随笔/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!