blog
blog copied to clipboard
开发技巧挑战 100 楼
目录
- 001. JS 剔除对象中不需要的属性 + 惰性载入函数
- 002. JS-根据条件给对象插入属性
- 003. Node.js 的 Error-First Callbacks
- 004. 数组,对象映射优化 if... else...
- 005. 从数组循环的 forEach 方法中删除元素
- 006. 优化表单验证逻辑判断
- 007. JS 的加减乘除运算
- 008. VSCode - ESLint, Prettier & Airbnb Setup
- 009. Cannot use arrow keys to choose options on Windows
- 010. Markdown 使用
- 011. Git commit log
- 012. 代码片段编写
- 013. Ve2x base64 解密
- 014. require 自动导入
- 015. package.json 文件添加注释
- 016. 给你的select添加上placeholder
- 017. 微信内置浏览器缓存清理
- 018. 接口 API 参考文档
- 019.在 Markdown 文档显示 diff 效果
- 020. Markdown展开折叠功能
- 021 markdown里怎么加引用注释或脚注
- 022 什么是好的代码?
- 023 vue中为什么要使用js调用单文件组件?怎么实现js调用组件?
001. JS 剔除对象中不需要的属性 + 惰性载入函数
剔除对象中不需要的属性
var original_params = {
username: "Rainbow",
age: "25",
pwd: "123456",
girlFriend: true,
friend: ["heizi", "niu"]
};
提问
获取 username
与 pwd
obj
// 方法1: delete object[key]
let result = original_params;
delete result.age;
delete result.girlFriend;
delete result.friend;
// 方法2:使用解构删除不必要属性
const { friend, age, girlFriend, ...result2 } = original_params; // Rest element must be last element
惰性载入函数
在某个场景下我们的函数中有判断语句,这个判断依据在整个项目运行期间一般不会变化,所以判断分支在整个项目运行期间只会运行某个特定分支,那么就可以考虑惰性载入函数
function foo() {
if (a !== b) {
console.log("aaa");
} else {
console.log("bbb");
}
}
// 优化后
function foo() {
if (a != b) {
foo = function() {
console.log("aaa");
};
} else {
foo = function() {
console.log("bbb");
};
}
return foo();
}
第一次运行之后就会覆写这个方法,下一次再运行的时候就不会执行判断了。当然现在只有一个判断,如果判断很多,分支比较复杂,那么节约的资源还是可观的。
举一反三,既然在函数内部定义与原函数同名函数导出可以覆写原函数,在仅需让函数方法执行一次的场景也同样使用,将原函数覆写为空函数就可以了
参考
- https://juejin.im/post/5b51e5d3f265da0f4861143c
002. JS-根据条件给对象插入属性
对一个原始类型的值执行扩展运算符,它总会返回一个空对象。比如{ ...true }
相当于{}
。当然字符串除外,因为字符串拥有某些数组的特性。
那这个奇技淫巧的原理是什么呢?
其实是因为&&
运算符的优先级比...
高,所以{ ...name && { name } }
相当于{ ...(name && { name }) }
,这样一转化就好理解了。
扩展运算符的优先级很低,仅高于,
。
// 通常我们都这么写
const profile = { age: 18, gender: "男" };
if (name) {
profile.name = name;
}
// if 可以简化一下
if (name) profile.name = name;
// 你不能这样写,因为逻辑与运算符后面只能跟表达式,不能跟语句
name && profile.name = name;
// 现在你可以这样写
const profile = { age: 18, gender: "男", ...(name && { name }) };
参考
- https://github.com/veedrin/hacks
003. Node.js 的 Error-First Callbacks
- 1.回调函数的第一个参数保留给一个错误
error
对象,如果有错误发生,错误将通过第一个参数 err 返回。 - 2.回调函数的第二个参数为成功响应的数据保留,如果没有错误发生,err 将被设置为 null, 成功的数据将从第二个参数返回。
Page({
// 获取用户可以领取的优惠券
getMyCoupons(callback) {
fetch._post(API.get_coupons, {}).then(res => {
let err, result;
if (res.response_data.error_code) {
err = res.response_data;
} else {
result = res.response_data.lists;
}
typeof callback === "function" ? callback(err, result) : "";
});
}
});
004. 数组,对象映射优化 if... else...
-
switch-case
function getWeekDay() {
let day = new Date().getDay();
switch (day) {
case 0:
return "天";
case 1:
return "一";
case 2:
return "二";
case 3:
return "三";
case 4:
return "四";
case 5:
return "五";
case 6:
return "六";
}
}
console.log(`今天是星期` + getWeekDay());
-
数组映射
function getWeekDay() {
let day = new Date().getDay();
return ["天", "一", "二", "三", "四", "五", "六"][day];
}
console.log(`今天是星期` + getWeekDay());
-
对象映射
function resolveWeekday() {
let string = "今天是星期",
date = new Date().getDay(),
dateObj = {
0: ["天", "休"],
1: ["一", "工"],
2: ["二", "工"],
3: ["三", "工"],
4: ["四", "工"],
5: ["五", "工"],
6: ["六", "休"]
};
dayType = {
休: function() {
// some code
console.log(string + "为休息日");
},
工: function() {
// some code
console.log(string + "为工作日");
}
};
return {
string: string + dateObj[date][0],
method: dayType[dateObj[date][1]]
};
}
resolveWeekday().method();
参考
- https://juejin.im/post/5cf8be116fb9a07edc0b4710
005. 从数组循环的 forEach 方法中删除元素
let data = [
{
name: "蔬菜类",
thumb_img: "",
price: 30,
desc: "茄子",
goods_id: "4",
num: 1
},
{
name: "越南进口红心火龙果 4个装",
thumb_img: "",
price: 0.01,
goods_id: "7",
num: 1
}
];
let ids = ["4", "7"];
- 方法一:
反向删除 for while
let index = data.length - 1;
while (index >= 0) {
if (ids.includes(data[index]["goods_id"])) {
data.splice(index, 1);
}
index--;
}
for (var i = data.length - 1; i >= 0; i--) {
if (ids.includes(data[i]["goods_id"])) {
data.splice(i, 1);
}
}
- 方法二:
浅拷贝数组:倒序修正 index 指向
// 使用 slice 浅拷贝 object 是真实的
data.slice().forEach((v, index, object) => {
ids.includes(v["goods_id"]) ? data.splice(object.length - 1 - index, 1) : "";
});
-
双重 forEach
data.slice().forEach((v, index, object) => {
ids.forEach(m => {
v.goods_id === m ? data.splice(object - 1 - index, 1) : "";
});
});
参考
- https://codeday.me/bug/20180524/170511.html
006. 优化表单验证逻辑判断
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
<div id="app">
<input type="text" v-model="username" placeholder="请输入你的用户名" />
<input type="text" v-model="phone" placeholder="请输入你的手机号" />
<input type="text" v-model="pwd" placeholder="请输入你的密码" />
<button @click="submit">提交</button>
</div>
- 方法一:
数组遍历判断
var vm = new Vue({
el: "#app",
data: {
username: "",
phone: "",
pwd: ""
},
methods: {
submit: function() {
const { username, phone, pwd } = this;
let valids = [
{
valid: username,
err: "请输入你的用户名"
},
{
valid: phone,
err: "请输入你的手机号码"
},
{
valid: /^1[3|4|5|6|7|8][0-9]{9}$/.test(phone),
err: "手机号码格式有误"
},
{ valid: pwd, err: "请输入你的密码" }
];
for (let val of valids) {
if (!val.valid) {
alert(val.err);
return;
}
}
alert("提交成功");
}
}
});
- 方法二:
Validator
Class 公共类封装
class Validator {
constructor(conditions) {
setTimeout(() => {
for (let item of conditions) {
const res = item.fn();
if (!res) {
alert(item.err);
return;
}
}
this.callback();
}, 0);
}
then(callback = () => {}) {
this.callback = callback;
}
}
var vm = new Vue({
el: "#app",
data: {
username: "",
phone: "",
pwd: ""
},
methods: {
submit: function() {
const { username, phone, pwd } = this;
let rules = [
{
fn: _ => username,
err: "请输入你的用户名"
},
{
fn: _ => phone,
err: "请输入你的手机号码"
},
{
fn: _ => /^1[3|4|5|6|7|8][0-9]{9}$/.test(phone),
err: "手机号码格式有误"
},
{ fn: _ => pwd, err: "请输入你的密码" }
];
new Validator(rules).then(() => {
alert("提交成功");
});
}
}
});
参考
- https://juejin.im/pin/5d2f1de0e51d456cecc490b7
007. JS 的加减乘除运算
将浮点数 toString 后记录小数位的长度,然后将小数点抹掉,完整的代码如下:
Math.pow() 函数返回基数(base 例:10)的指数(exponent 例:2)次幂,Math.pow(10,2) = 100
/**
* 加法
* @param arg1
* @param arg2
* @returns
**/
function accAdd(arg1, arg2) {
var r1, r2, m;
try {
r1 = arg1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return (arg1 * m + arg2 * m) / m;
}
/**
* 减法
* @param arg1
* @param arg2
* @returns
*/
function accSub(arg1, arg2) {
var r1, r2, m, n;
try {
r1 = arg1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
//动态控制精度长度
n = r1 >= r2 ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
function accMul(arg1, arg2) {
var m = 0,
s1 = arg1.toString(),
s2 = arg2.toString();
try {
m += s1.split(".")[1].length;
} catch (e) {}
try {
m += s2.split(".")[1].length;
} catch (e) {}
return (
(Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) /
Math.pow(10, m)
);
}
function accDiv(arg1, arg2) {
var t1 = 0,
t2 = 0,
r1,
r2;
try {
t1 = arg1.toString().split(".")[1].length;
} catch (e) {}
try {
t2 = arg2.toString().split(".")[1].length;
} catch (e) {}
with (Math) {
r1 = Number(arg1.toString().replace(".", ""));
r2 = Number(arg2.toString().replace(".", ""));
return (r1 / r2) * pow(10, t2 - t1);
}
}
//给Number类型增加一个add方法,调用起来更加方便。
Number.prototype.add = function(arg) {
return accAdd(arg, this);
};
Number.prototype.sub = function(arg) {
return accSub(arg, this);
};
Number.prototype.Mul = function(arg) {
return accMul(arg, this);
};
Number.prototype.Dev = function(arg) {
return accMul(arg, this);
};
008. VSCode - ESLint, Prettier & Airbnb Setup
1. Install ESLint & Prettier extensions for VSCode
Optional - Set format on save and any global prettier options
2. Install Packages
npm i -D eslint prettier eslint-plugin-prettier eslint-config-prettier eslint-plugin-node eslint-config-node
npx install-peerdeps --dev eslint-config-airbnb
3. Create .prettierrc for any prettier rules (semicolons, quotes, etc)
4. Create .eslintrc.json file (You can generate with eslint --init if you install eslint globally)
{
"extends": ["airbnb", "prettier", "plugin:node/recommended"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"no-unused-vars": "warn",
"no-console": "off",
"func-names": "off",
"no-process-exit": "off",
"object-shorthand": "off",
"class-methods-use-this": "off"
}
}
Reference
- VSCode - ESLint, Prettier & Airbnb Setup , Youtube Video
- ESLint Rules - https://eslint.org/docs/rules/
- Prettier Options - https://prettier.io/docs/en/options.html
- Airbnb Style Guide - https://github.com/airbnb/javascript
- Demo - King-of-glory
009. Cannot use arrow keys to choose options on Windows
- 输入选项
number
, Enter
Reference
010. Markdown 使用
- 首行缩进
两个  
即可
- 图片尺寸
<img width="100%" src="http://ww1.sinaimg.cn/large/df551ea5ly1g865qolvofj20dw08zacz.jpg"></img>
参考
- https://www.zhihu.com/question/21420126
- https://stackoverflow.com/questions/14675913/changing-image-size-in-markdown
011. Git commit log
- Head
- type: feat 新特性, fix 修改问题, docs 文档, style 格式, refactor 重构, test 测试用例,revert 还原, chore 其他修改, 比如构建流程, 依赖管理.
- scope:影响范围, 比如: route, component, utils, build... 可省略
- subject:简短的提交信息
- Body
- what:详细做了什么
- why: 为什么这样做
- how: 有什么后果
- Footer
- 相关链接
012. 代码片段编写
import Taro, { Component } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import './index.scss'
export default class Index extends Component {
config = {
navigationBarTitleText: '首页'
}
componentWillMount () { }
componentDidMount () { }
componentWillUnmount () { }
componentDidShow () { }
componentDidHide () { }
render () {
return (
<View className='index'>
<Text>1</Text>
</View>
)
}
}
转换过程
符号 | 含义 |
---|---|
\n |
换行 |
\t |
缩进 |
{
"tarcs": {
"prefix": "tarc",
"body": [
"import Taro from '@tarojs/taro';",
"import { View, Text } from '@tarojs/components';",
"import styles from './index.scss'",
"",
"export default class ${1:Test} extends Taro.Component {",
" render() {",
" return (",
" <View className={styles.${2:main}}>",
" <Text> ${1:测试输入} </Text>",
" </View>",
" );",
" }",
"}",
""
],
"description": "taro页面模板带状态"
},
}
013. Ve2x base64 解密
浏览器 F12
, 在 console
面板里输入
# 加密
btoa('13122223333') => MTMxMjIyMjMzMzM=
# 解密
atob('Y3JhY2sybkBnbWFpbC5jb20=') => [email protected]
参考
- https://www.v2ex.com/t/514639
- https://github.com/ttop5/issue-blog
014. require 自动导入
/**
* @desc webpack打包入口文件
*/
let moduleExports = {};
const r = require.context('./', true, /^\.\/.+\/.+\.js$/);
r.keys().forEach(key => {
let attr = key.substring(key.lastIndexOf('/') + 1, key.lastIndexOf('.'));
moduleExports[attr] = r(key);
});
module.exports = moduleExports;
参考
- https://github.com/proYang/outils/issues/27
015. package.json 文件添加注释
不能加在"dependencies"里,但是可以加为package.json的最顶层项:
{
"//": "This is comment 1",
"//": "This is comment 2"
}
或者
{
"//": [ "Line 1 of Comments", "Line 2 of Comments" ]
}
来源
- https://cnodejs.org/topic/53a72406a087f45620b9659d
- https://github.com/npm/npm/issues/4482
016. 给你的select添加上placeholder
为 select 下拉框添加类似 input 的placeholder 的功能呢?
<select name="source" id="source">
<option value="" disabled="" selected="" hidden="">请选择来源</option>
<option value="1">个人</option>
<option value="2">学校</option>
<option value="3">平台</option>
</select>
来源
- https://github.com/youngwind/blog/issues/38
- https://stackoverflow.com/questions/5805059/how-do-i-make-a-placeholder-for-a-select-box/5859221#5859221
017. 微信内置浏览器缓存清理
之前做过很多公众号的项目,项目写完后给客户看项目,客户一而再再而三的修改元素向左挪1px,向右挪2px。改好之后让客户看,客户说我特泽发克,你啥都没有修改,你竟然骗我!!!
这其实就是微信内置浏览器的缓存在作祟啦,那么如何清理微信内置浏览器的缓存呢?
你们是否知道 ios版微信 和 android版微信 的内置浏览器的内核是不一样的呢?
android版微信内置浏览器(X5内核) 在安卓版微信内打开链接 http://debugx5.qq.com
拉到调试页面的最底端,勾选上所有的缓存项目,点击清除。
点击确定之后即可完成清除微信浏览器缓存的操作。
ios版微信内置浏览器(WKWebView) ios版微信内置浏览器内核并不是 X5内核,而是使用的ios的浏览器内核WKWebView,所以安卓手机的那种方案对ios手机用户不生效,因为那个链接压根打不开
只要微信用户退出登录,然后重新登录,ios版微信内置浏览器内核即可清除,不行的话,你们回来打我
有人说了:“IOS中 设置—通用----存储空间 就会看到“正在计算空间”计算完了会清理一点清理即可”,这种办法当然也可以,但是这种办法不光是清理微信内置浏览器的缓存,同时也清理其他的一些数据,比如朋友圈的视频图片和聊天记录等等缓存,而且容易误删某些想留下的数据,对于开发而言,我认为退出重新登录是最好的解决办法。
来源
- https://developers.weixin.qq.com/community/develop/article/doc/000620cda68ef8d70f79dfc0c5b813
018. 接口 API 参考文档
一、基本规则
1.1 接⼝规则
协议规则:
描述 | 说明 |
---|---|
传输⽅式 | 采⽤HTTP、HTTPS |
提交⽅式 | 采⽤POST⽅法提交 |
请求参数类型 | Content-Type: application/json |
响应参数类型 | Content-Type: application/json |
字符编码 | 统⼀采⽤ UTF-8 字符编码 |
签名算法 | MD5().toUpperCase() |
1.2 请求参数公共字段
字段名 | 变量名 | 必填 | 类型 | 描述 |
---|---|---|---|---|
调⽤⽅帐号 | appId | 是 | String(32) | 分配给调⽤⽅的帐号 id |
随机字符串 | randomStr | 是 | String(32) | 随机字符串,不⻓于 32 位 |
签名 | sign | 是 | String(32) | 签名 |
签名请求时间戳(毫秒级) | gmtRequest | 是 | Long | 请求时间,Unix 时间戳格式,如1514782861001,代表‘2018-01-01 13:01:01.001’,仅允许请求时间与系统时间五分钟内的请求 |
1.3 返回参数字段格式
字段名 | 变量名 | 必填 | 类型 | 描述 |
---|---|---|---|---|
数据实体 | data | 否 | json | 接⼝返回的数据放这⾥ |
异常信息 | msg | 否 | string | 接⼝调⽤返回的消息 |
异常码 | code | 否 | int(11) | 接⼝调⽤返回异常,异常码 |
接⼝调⽤状态 | success | 是 | boolean(1) | 接⼝调⽤状态 true-成功,false-失败 |
二、接⼝列表
2.1 查询积分
应⽤场景:查询⽤户积分余额
接⼝链接:/
请求⽅式:POST
请求参数:
字段名 | 变量名 | 必填 | 类型 | 描述 |
---|---|---|---|---|
⽤户id | uid | 是 | int | ⽤户ID |
返回参数:
字段名 | 变量名 | 必填 | 类型 | 描述 |
---|---|---|---|---|
账户积分 | score | 是 | int | 会员账户积分 |
请求参数示例:
{
"uid": 10001,
"appId": "xh161344685917",
"gmtRequest": 1603702285590,
"randomStr": "621465f968e0d9022f",
"sign": "278967FACE51E3BD45D7EB028E0CE5C8"
}
返回参数示例:
{
"data": {
"uid": 10001,
"data": 120
}
"code": 0,
"success": true
}
收到如下返回时结束请求:
{
"msg": "用户不存在",
"code": 100,
"success": false
}
019.在 Markdown 文档显示 diff 效果
function addTwoNumbers (num1, num2) {
- return 1 + 2
+ return num1 + num2
}
参考
- https://blog.alispit.tel/create-a-git-diff-in-markdown/
020. Markdown展开折叠功能
默认展开
details 标签默认是折叠状态,如果想默认为展开的话,可以添加一个 open
属性:
TestA1
TestB1
- Test B1
TestB2
TestC1
- Test C1
参考
- https://immwind.com/folding-nesting-in-markdown-with-blockquote/
markdown里怎么加引用注释或脚注
一般我们只是在markdown添加链接,但怎么在markdown里加脚注呢?下面来看看
这里有一个注脚[^1],这段话的还有其他意思[^2]在里面
[^1]:这里是注脚内容
[^2]:这里是其他意思的注脚
注脚放到中间也可以,下面是具体效果
md中链接的另一种写法
我是一段文字,[baidu][1]、[qq][2]里面有链接
[1]: http://baidu.com "baidu"
[2]: http://qq.com "qq"
参考
- http://www.zuo11.com/blog/2020/7/md_ref.html
什么是好的代码?
在web前端方面,什么是好的代码?好的代码应该包含以下两个特性
- 高性能,低时延(性能优化)
- 熟悉数据结构与算法,减少时间复杂度或空间复杂度
- 熟悉浏览器渲染基本原理、熟悉HTTP请求与响应细节、熟悉前端框架源码、减少不必要的渲染开销,提高加载速度
- 可读性、可维护性、可扩展性
- 熟悉设计模式,封装变化。代码高内聚、低耦合、指责单一、高度复用。写出好维护、好迭代、好扩展的代码
- 化繁为简,形成特定代码规范,注意命名、注释。写出人能看懂的代码,不做骚操作。尽量保持简单、易懂,在可扩展性和简单之间寻找平衡
前端只要不是写框架,性能问题会很少遇到。简单来讲,在实现功能的基础上,代码简单、易懂、好维护迭代就很好了。技术始终是为业务需求服务的。基础建设是很重要的一个环节,这样有利于快速迭代开发
参考
- http://www.zuo11.com/blog/2020/10/good_code.html
vue中为什么要使用js调用单文件组件?怎么实现js调用组件?
如果自己写一个组件。一般情况下,vue项目中在某个组件里调用另一个组件,至少需要修改三个位置
- 在 template 里写引入组件,加上传参等
- 在 components 里声明组件(如果全局引入了,可以省去这一步)
- data 里面写对应的传参数
代码对应如下,这种组件对于使用地方比较多时候,我们就需要想办法直接使用js来调用组件,而不是每次都要在 template 里面声明对应的组件,这样会有很多重复代码,可维护性较差。
<template>
<el-button @click="showToast">打开toast</el-button>
<!-- template中使用组件 -->
<toast v-model="showToast"></toast>
</template>
<script>
export default {
components: {
Toast: () => import('./Toast.vue')
}
data() {
return {
showToast: false
}
}
}
</script>
较早之前使用js加载组件尝试
在较早之前,用js写过一个直接挂载组件到当前dom上的一个方法,分三步:
- 使用 Vue.extend,处理需要用js调用的vue单文件组件,返回该vue组件的一个子类
- new对应的组件子类,生成对应的组件实例,并使用
.$mount()
挂载组件,返回对应组件的 vm - 这样可以通过 vm.$el 拿到组件dom,append到当前组件dom里,即可完成加载
具体写法如下
// 假设写好了 showInfo.vue 组件,执行clickShow函数直接显示dialog
// 组件中 dialog :visible.sync="dialogTableVisible"初始值设置为true
// demo.vue 在需要调用的vue文件中引入该组件
import ShowInfo from 'showInfo.vue'
// ...
clickShow() {
const Component = Vue.extend(ShowInfo)
// 挂载后返回对应组件的vm
let showInfoVue = new Component().$mount()
// 将组件vm的dom,append到当前页面
this.$el.appendChild(showInfoVue.$el)
}
// ...
具体参考:使用js调用vue单文件组件
但它有一个缺点,常规调用组件时,我们会向子组件里面传入参数或事件。而这种情况不能向调用的子组件传入参数。下面来看另一种方法
使用js将单文件组件挂载到body的通用方法
我们来看看下面的create方法,其实和上面的方法流程基本一致,只是改变了创建对应vue单文件组件实例的方法。这里用render函数来替代之前的Vue.extend来创建对应组件实例,这样可以通过render函数的createElement函数向子组件内部传参数,传方法等。
// create.js
import Vue from "vue";
export default function create(Component, props) {
// 先创建实例
const vm = new Vue({
render(h) {
// h就是createElement,它返回VNode
return h(Component, { props });
}
}).$mount();
// 手动挂载
document.body.appendChild(vm.$el);
// 销毁方法
const comp = vm.$children[0];
comp.remove = function() {
document.body.removeChild(vm.$el);
vm.$destroy();
};
return comp;
}
上面的例子中,create函数参数接收一个单文件组件对象,以及在调用组件时需要传递给组件的参数,来看看具体使用例子
<template>
<div>
<el-button @click="showToast">打开toast</el-button>
</div>
</template>
<script>
import Toast from "./Toast.vue";
import create from "./create";
export default {
methods: {
showToast() {
console.log("show toast");
let toast = create(Toast, {
show: true,
message: "我是错误信息",
type: "error"
});
// 等价于 <toast :show="true" message="xx" :type="error"></toast>
console.log(toast);
setTimeout(() => {
toast.remove();
}, 2000);
}
}
};
</script>
Toast.vue 单文件组件代码如下
<template>
<div class="my-toast" v-if="show">
<div :class="type">{{ message }}</div>
</div>
</template>
<script>
export default {
props: {
message: {
type: String,
required: true
},
type: {
type: String,
default: "error"
}
show: {
type: Boolean,
}
}
};
</script>
<style lang="less" scoped>
.my-toast {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 300px;
border: 1px solid #ccc;
text-align: center;
.error {
color: red;
}
.success {
color: green;
}
}
</style>
再进一步封装
上面的例子中,我们引入了 create.js 以及 对应的单文件组件。它还是不够简洁,我们可以再封装一次,只需要调用一个js就搞定。如果我们将它在main.js里面引入并挂载到vue实例属性,那么调用就非常方便了。
// 在main.js里注册实例属性
import showDialog from '@/views/jsDialog/index.js'
Vue.prototype.$showDialog = showDialog
// 其他地方直接使用 this.$showDialog(options) 即可调用组件
下面来看看实现思路,关于render函数createElement的options的配置,参见 createElement 参数 - 深入数据对象
// showDialog/index.js
import Vue from "vue";
import DialogComponent from '@/views/jsDialog/src/index.vue'
let TheDialog = null
export default function showDialog(options) {
// 如果未移除,先移除
TheDialog && TheDialog.remove()
TheDialog = create(DialogComponent, {
on: {
// 单文件组件内部可以emit该事件,销毁TheDialog组件
'close-dialog': () => {
TheDialog.remove()
}
},
props: {
// 需要传入的属性,单文件组件需要使用props接收
title: '标题',
content: '内容'
}
// 其他参数
...options
})
function create(Component, options) {
// 先创建实例
const vm = new Vue({
render(h) {
// h就是createElement,它返回VNode
return h(Component, options);
}
}).$mount();
// 手动挂载
document.body.appendChild(vm.$el);
// 销毁方法
const comp = vm.$children[0];
comp.remove = function() {
document.body.removeChild(vm.$el);
vm.$destroy();
};
return comp;
}
}
注意,虽然js调用更方便了,但js处理、render组件的传参复杂度会增加。它和普通组件各有各的优缺点。
参考
- http://www.zuo11.com/blog/2020/9/vue_why_js_comp.html