首页

vue el-upload 和form表单一起提交

前端达人

图片上传和表单同时一起提交

1. <!-- 弹出框 --> <el-dialog :title="titleMap[dialogStatus]" :visible.sync="dialogFormVisible"> <el-form :model="form" :rules="rules" ref="form"> <el-form-item label="商品名称" prop='goodsName' style="width:40%"> <el-input v-model="form.goodsName" auto-complete="off" placeholder="请输入商品名称" size="medium"></el-input> </el-form-item> <el-form-item label="商品图片" prop='goodsImg'> <el-upload action="uploadAction" list-type="picture-card" :on-preview="handlePictureCardPreview" :on-remove="handleRemove" :limit="1" :show-file-list="true" name="img" ref="upload" :data="form" accept="image/png,image/gif,image/jpg,image/jpeg" :headers="headers" :on-exceed="handleExceed" :auto-upload="false" :on-error="uploadError" :before-upload="handleBeforeUpload" :on-change="changeFile"> <i class="el-icon-plus"></i> </el-upload> <el-dialog :visible.sync="dialogVisible"> <img width="50px" :src="dialogImageUrl" alt=""> </el-dialog> </el-form-item> <el-form-item label="商品对应积分" prop='goodsIntegral'> <el-input v-model="form.goodsIntegral" auto-complete="off" placeholder="请输入积分" size="medium" type="number"></el-input> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="cancel">取 消</el-button> <el-button type="primary" @click="uploadFileFun('form')">确 定</el-button> "注意uploadFileFun('form')  括号内记得带引号不然可能会报validate is not defined" </div> </el-dialog> 
                    
  • 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

2.data部分

 data() { return { //新增和编辑弹框标题 titleMap: { addStore: '新增商品', editStore: "修改商品" }, dialogStatus: "", //新增编辑弹出框 dialogFormVisible: false, dialogVisible: false, //表格数据 tableData: [], form: { id: '', goodsName: '', goodsImg: '', goodsIntegral: '', }, rules: { goodsName: [{ required: true, message: '请输入商品名称', trigger: 'blur' }], goodsIntegral: [{ required: true, message: '请输入商品积分', trigger: 'blur' }], }, headers: { Authorization: 'Bearer ' + window.localStorage.getItem("token") }, dialogImageUrl: '', //图片回显 uploadFiles: '', //formData img 文件 goodsId: '', //判断是新增还是编辑 }
                    
  • 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
  1. methods部分 手动上传图片 **var fd = new FormData()**添加
// 上传文件之前的钩子 handleBeforeUpload(file) { console.log('按钮', this.titleMap) if (!(file.type === 'image/png' || file.type === 'image/gif' || file.type === 'image/jpg' || file.type === 'image/jpeg')) { this.$notify.warning({ title: '警告', message: '请上传格式为image/png, image/gif, image/jpg, image/jpeg的图片' }) } let size = file.size / 1024 / 1024 / 2 if (size > 2) { this.$notify.warning({ title: '警告', message: '图片大小必须小于2M' }) } }, //图片上传超过数量限制 handleExceed(files, fileList) { this.$message.error("上传图片不能超过1张!"); }, handleRemove(file, fileList) { this.$message.error("删除成功!"); }, // 图片上传失败时 uploadError() { this.$message.error("图片上传失败!"); }, //预览图片 handlePictureCardPreview(file) { this.dialogImageUrl = file.url; this.dialogVisible = true; }, //文件改变时 on-change方法 ,fileList[0].raw 就是传给后端的值 //filelist这个对象里面有很多属性,我们上传文件时,实际上传的是filelist列表中每一项的raw,只有raw可以正常上传, 获取到文件后,需要定义变量保存文件。所以需要获取filelist中的raw进行保存。 //这里我用的formdata上传多文件,console打印formdata,文件在控制台中显示的格式为binary。 changeFile(file, fileList) { this.uploadFiles = fileList[0].raw }, uploadFileFun(formName) { this.$refs[formName].validate((valid) => { let fd = new FormData(); fd.append('id', this.form.id); fd.append('goodsName', this.form.goodsName); fd.append('goodsIntegral', this.form.goodsIntegral); fd.append('img', this.uploadFiles); let config = { headers: { 'Content-Type': 'multipart/form-data' } } 根据goodsID判断是编辑提交还是新增提交,主要针对新增编辑使用同一个弹框 if (valid && !this.goodsId) { this.$axios.post("接口地址", fd, config).then(res => { if (res.data.success == true) { this.dialogFormVisible = false this.$message({ message: res.data.msg, type: 'success' }); //新增完清空表单内容 setTimeout(() => { this.$refs.form.resetFields(); }, 200) this.reload() } else { this.$message.error(res.data.msg); } }).catch(res => { console.log(res) }) } else { this.$axios.post("接口地址", fd, config).then(res => { if (res.data.success == true) { this.dialogFormVisible = false this.$message({ message: res.data.msg, type: 'success' }); //编辑完清空表单内容 setTimeout(() => { this.$refs.form.resetFields(); }, 200) this.reload() } else { this.$message.error(res.data.msg); } }).catch(res => { console.log(res) }) } }) }, add() { this.dialogStatus = "addStore" this.dialogFormVisible = true this.goodsId = "" //新增商品是商品ID为空 }, // 取消 cancel() { this.dialogFormVisible = false this.$refs[formName].resetFields() }, //编辑 handleEdit(index, row) { this.form = this.tableData[index] this.dialogStatus = "editStore" this.goodsId = row.id this.currentIndex = index this.dialogFormVisible = true }, 
                    
  • 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
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
文章


蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。


分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司


使用Node.js的formidable模块实现文件上传

前端达人

在我上一篇写的Node.js实现简单的POST请求
里面POST请求接受参数需要写两个事件,这难免有些不太方便
在这里插入图片描述

如果我们用formidable来接受参数的话,会变得特别方便。

下面我们来写一个Demo,来利用formidable来实现图片上传

1.下面来看一眼 目录结构

在这里插入图片描述

2.先来写一个简单的前端上传页面

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form class="upload" action="shangchuan" enctype="multipart/form-data" method="post"><!--  上传接口是/shangchuan -->
        <p>
            请上传一个头像
            <input type="file" name="wenjian">
        </p>
        <p>
            <input type="submit" value="提交">
        </p>
    </form>
</body>
</html> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2.package.json 文件

安装依赖,执行下面这三句npm语句

npm install finalhandler --save
npm install serve-static --save
npm install formidable --save

之后会自动生成下面这个package.json文件

{
  "dependencies": {
    "finalhandler": "^1.1.1",
    "formidable": "^1.2.1",
    "serve-static": "^1.13.2"
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.post.js

var finalhandler = require('finalhandler')
var http = require('http')
var serveStatic = require('serve-static')
var url = require('url')
var fs = require('fs')
var querystring = require('querystring')
var formidable = require('formidable')
var path = require('path')
// Serve up public/ftp folder
//配置静态资源服务器,将public文件夹静态化出来

var serve = serveStatic('public', {'index': ['index.html', 'index.htm']})

// Create server
var server = http.createServer(function onRequest (req, res) {
    //路由
    var pathname = url.parse(req.url).pathname;
    if(pathname == '/shangchuan'){
        //创建一个表单的实例,formidable
        var form = new formidable.IncomingForm();
        //设置上传的文件存放在哪里
        form.uploadDir = './uploads';
        //处理表单
        form.parse(req,(err,fields,files) => {
            //fields 表示普通控件
            //files 表示文件控件
            if(!files.wenjian){
                return;
            }
            if(!files.wenjian.name){
                return;
            }
            var extname = path.extname(files.wenjian.name);获取文件的扩展名,便于下面修改上传后的文件名字
            //改名
            fs.rename(files.wenjian.path, files.wenjian.path + extname,() => {
                res.end('上传成功')
            })
            // console.log(fields);
        })
        return;
    }
    serve(req, res, finalhandler(req, res))
})

// Listen
server.listen(3000);
console.log('服务已经启动在3000端口'); 
  • 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

4.最后找到post文件的目录,然后开始node post.js

会看到这个页面

在这里插入图片描述

然后选择任意文件点击提交
会发现在很短的时间内你的文件会提交成功在你的uploads文件夹下。




蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。


分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司

关于vue.js element ui 表单验证 使用this.$refs[formName].validate()

前端达人

实现form表单提交验证功能

model : 绑定整个表单model
rules : 整个表单校验规则
ref :获取该表单form组件
prop : 绑定每个表单的规则,写在el-form-item
validate : 对整个表单进行校验的方法
valid : 每个必填表单项都提交为true,否则为false

1、简易版 表单提交

//使用element-ui 页面组件 <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item label="活动名称" prop="name"> <el-input v-model="ruleForm.name"></el-input> </el-form-item> <el-form-item label="活动区域" prop="region"> <el-select v-model="ruleForm.region" placeholder="请选择活动区域"> <el-option label="区域一" value="shanghai"></el-option> <el-option label="区域二" value="beijing"></el-option> </el-select> </el-form-item> </el-form> <el-form-item> <el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button> </el-form-item> //data中定义form表单中每项表单model值、和每个表单校验的规则 <script> export default { data() { return { ruleForm: { name: '', region: '', }, rules: { name: [ { required: true, message: '请输入活动名称', trigger: 'blur' }, { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' } ], region: [ { required: true, message: '请选择活动区域', trigger: 'change' } ], }; }, //定义表单提交方法 methods: { submitForm(formName) { this.$refs[formName].validate((valid) => { if (valid) { alert('submit!'); } else { console.log('error submit!!'); return false; } }); }, } } 
        
  • 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

2、复杂版 表单提交

  • 对于页面有多个整体的form提交,每个form表单里类型为文本框、下拉框、单选框、复选框等类型,可对类型进行封装,使用循环遍历使页面呈现不同类型表单项
<el-form v-if="fromHtmlList.deviceInfo.sitsb.length" :model="fasj" :rules="rules" label-position="left" label-width="125px" ref='sitsb'> <el-form-item label-width="100%" > <div slot="label" class="titleInfo">局端设备</div> </el-form-item> <el-form-item v-for="(item, i) in fromHtmlList.deviceInfo.sitsb" :key="`F_key_${i}`" :label="`${item.label}:`" :class="item.class" :label-width="item.labelWidth" :prop="`${item.rule ? 'deviceInfo.sitsb.'+item.rule : ''}`"> <el-select v-if="item.el === 'slt'" v-model="fasj.deviceInfo.sitsb[item.model]" placeholder="请选择" style="width:200px;" clearable> <el-option v-for="(op, n) in options[item.option]" :key="`vop_key_${n}`" :label="op.label" :value="op.value"> </el-option> </el-select> <el-input  v-if="item.el === 'ipt'" v-model="fasj.deviceInfo.sitsb[item.model]" @focus="()=>{ item.focus && handleFocus('sitsb', item.model)}" :placeholder="`${item.placeholder ? item.placeholder : '请输入'}`" style="width:200px;"></el-input> <span v-if="item.el === 'txt'">{{fasj.base[item.model]}}</span> </el-form-item> </el-form> <div class="btn-list"> <el-button type="primary" @click="checkRequired">提 交</el-button> </div> <script> export default { data() { return { fasj: { base: {}, // 基本信息 deviceInfo: {}, }, //定义 页面表单结构 fromHtmlList: { baseInfo: [], deviceInfo: { ywzsb: [], sitsb: [] } }, //定义 表单校验规则 rules: { 'deviceInfo.sitsb.IPType': [ { required: true, message: '请输入', trigger: 'blur' } ], 'deviceInfo.sitsb.IPAddr': [ { required: true, message: '请输入', trigger: 'blur' } ], 'deviceInfo.sitsb.netmask': [ { required: true, message: '请输入', trigger: 'blur' } ], 'deviceInfo.sitsb.vlanId': [ { required: true, message: '请输入', trigger: 'blur' } ], }, options: { IPType: [ {label: 'ipv4', value: 'ipv4'}, {label: 'ipv6', value: 'ipv6'} ], rmSite: [], room: [], }, } }, created(){ const orderType = '业务开通' ;//页面初始加载用到的变量值,可通过组件传值获取 //created中初始定义页面表单结构 this.fromHtmlList = { baseInfo: orderType === '业务开通' ? [ {label: '接入方式', labelWidth: '100px', rule: '' , model: 'serviceType', option: '', el: 'txt', class: 'w2'}, {label: '标准九级地址', labelWidth: '120px', rule: '', model: 'aendAdreesName', option: '', el: 'txt', class: 'w2'}, {label: '城乡类型', labelWidth: '100px', rule: 'cityType', model: 'cityType', option: 'cityType', el: 'slt', class: 'w2'}, {label: '方案设计附件', labelWidth: '120px', rule: '', model: 'fileId', option: '', el: 'btn', class: 'w2'}, ] : orderType === '移机' ? [ {label: '接入方式', labelWidth: '100px', rule: '' , model: 'serviceType', option: '', el: 'txt', class: 'w2'}, {label: '标准九级地址', labelWidth: '120px', rule: '', model: 'aendAdreesName', option: '', el: 'txt', class: 'w2'}, {label: '城乡类型', labelWidth: '100px', rule: 'cityType', model: 'cityType', option: 'cityType', el: 'slt', class: 'w2'}, {label: '方案设计附件', labelWidth: '120px', rule: '', model: 'fileId', option: '', el: 'btn', class: 'w2'}, {label: '移机场景', labelWidth: '100px', rule: 'moveScene', model: 'moveScene', option: 'moveScene', el: 'slt', class: 'w2'}, {label: '是否需要网络调整', labelWidth: '', rule: 'netChange', model: 'netChange', option: 'netChange', el: 'slt', class: 'w2'} ] : [ {label: '接入方式', labelWidth: '100px', rule: '' , model: 'serviceType', option: '', el: 'txt', class: 'w2'}, {label: '标准九级地址', labelWidth: '120px', rule: '', model: 'aendAdreesName', option: '', el: 'txt', class: 'w2'}, {label: '城乡类型', labelWidth: '100px', rule: 'cityType', model: 'cityType', option: 'cityType', el: 'slt', class: 'w2'}, {label: '方案设计附件', labelWidth: '120px', rule: '', model: 'fileId', option: '', el: 'btn', class: 'w2'}, {label: '是否需要网络调整', labelWidth: '', rule: 'netChange', model: 'netChange', option: 'netChange', el: 'slt', class: 'w2'}, {label: '是否更换末端设备', labelWidth: '', rule: 'devChange', model: 'devChange', option: 'devChange', el: 'slt', class: 'w2'} ], deviceInfo: { ywzsb: serviceType === 'PON' ? [ // 默认PON {label: '机房', labelWidth: '90px', rule: '' , model: 'siteRoom', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: '设备名称', labelWidth: '90px', rule: '', model: 'deviceName', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: '设备端口', labelWidth: '90px', rule: '', model: 'devicePort', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: '延伸设备', labelWidth: '90px', rule: '', model: 'deviceExtend', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'} ] : [ // 裸光纤、传输下沉、传输延伸(ywzsb) {label: '机房', labelWidth: '', rule: '' , model: 'siteRoom', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: '站点', labelWidth: '', rule: '', model: 'site', option: '', el: 'ipt', class: 'w2'}, {label: '设备名称', labelWidth: '', rule: '', model: 'deviceName', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: '设备端口', labelWidth: '', rule: '', model: 'devicePort', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, ], sitsb: serviceType === 'PON' ? [ {label: '机房名称', labelWidth: '', rule: '' , model: 'siteRoom', option: '', el: 'ipt', class: 'w2'}, {label: '站点名称', labelWidth: '', rule: '', model: 'site', option: '', el: 'ipt', class: 'w2'}, {label: '汇聚交换机', labelWidth: '', rule: '', model: 'switch', option: '', el: 'ipt', class: 'w2'}, {label: '汇聚交换机端口', labelWidth: '', rule: '', model: 'switchPort', option: '', el: 'ipt', class: 'w2'}, {label: 'SR/BARS', labelWidth: '', rule: '' , model: 'srbars', option: '', el: 'ipt', class: 'w2'}, {label: 'SR/BARS端口', labelWidth: '', rule: '', model: 'srbarsPort', option: '', el: 'ipt', class: 'w2'}, {label: 'IP地址类型', labelWidth: '', rule: 'IPType', model: 'IPType', option: 'IPType', el: 'slt', class: 'w2'}, {label: 'IP地址', labelWidth: '', rule: 'IPAddr', model: 'IPAddr', option: '', el: 'ipt', class: 'w2'}, {label: '子网掩码', labelWidth: '', rule: 'netmask', model: 'netmask', option: '', el: 'ipt', class: 'w2'}, {label: 'VLAN ID', labelWidth: '', rule: 'vlanId', model: 'vlanId', option: '', el: 'ipt', class: 'w2'} ] : serviceType === '裸光纤' ? [ // 裸光纤 {label: '机房名称', labelWidth: '', rule: '' , model: 'siteRoom', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: '站点名称', labelWidth: '', rule: '', model: 'site', option: '', el: 'ipt', class: 'w2'}, {label: 'SR', labelWidth: '', rule: '', model: 'SR', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: 'SR端口', labelWidth: '', rule: '', model: 'SRPort', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: 'BARS', labelWidth: '', rule: '' , model: 'BARS', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, {label: 'BARS端口', labelWidth: '', rule: '', model: 'BARSPort', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}, ] : [ // 传输下沉、传输延伸 ] } } }, methods:{ async checkRequired() { let checkName = ['base','sitsb'];//表单校验定义ref let text; for(let key of checkName){ if(this.$refs[key]){ this.$refs[key].validate((valid) => { if(!valid){ text = '请检查必填项'; }else { return false; } }); } } if(text){ this.$message.warning(text); return false; } let _res_ = await this.saveFasjData('', true);//页面表单要保存后才能提交 if(!!_res_ && _res_.code === 200){ this.commit(_param, _url);//保存表单再提交 }else{ this.$message.error(_res_.msg) } }, async commit(param, url) { this.loadings.body = true; let _res = await postDataRequest(url, param); this.loadings.body = false; if (_res && _res.code === 200) { this.$message.success("提交成功"); this.$router.go(-1); } } } } </script> 
        
  • 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
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178

什么是 Vite?

前端达人

一、什么是 Vite 

Vite 是 vue 的作者尤雨溪在开发 vue3.0 的时候开发的一个 web 开发构建工具。由于其原生 ES 模块导入方式,可以实现闪电般的冷服务器启动。

二、Vite 和 Webpack 区别

Vite 优势:

  1. vite 开发服务器启动速度比 webpack 快
    webpack 会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。
    vite 在启动开发服务器时不需要打包,也就意味着不需要分析模块的依赖、不需要编译,因此启动速度非常快。当浏览器请求某个模块时,再根据需要对模块内容进行编译。这种按需动态编译的方式,极大的缩减了编译时间,项目越复杂、模块越多,vite的优势越明显。
  2. 由于现代浏览器本身就支持ES Module,会自动向依赖的Module发出请求。vite充分利用这一点,将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像webpack那样进行打包合并。
  3. vite 热更新比 webpack 快
    在 HMR 方面,当改动了一个模块后,vite仅需让浏览器重新请求该模块即可,不像webpack那样需要把该模块的相关依赖模块全部编译一次,效率更高。
  4. vite 使用esbuild(Go 编写) 预构建依赖,比 webpack 的 nodejs,快 10-100 倍。

Vite 劣势:

  1. 生态不及webpack,加载器、插件不够丰富
  2. 打包到生产环境时,vite使用传统的 rollup(也可以自己手动安装webpack来)进行打包
  3. 项目的开发浏览器要支持 ES Module,而且不能识别 CommonJS 语法

其他区别:

     1. 打包原理的区别

webpack
webpack打包原理
 
vite打包原理

     2. 项目入口文件的区别
         
项目根目录的 index.html 是 Vite 项目的入口文件,而 webpack 的入口文件是 webpack 配置 entry 中指定的 js 文件。

三、Esbuild

Esbuild 是一款基于 Go 语言开发的 javascript 打包工具,最大的一个特征就是

通过官网提供的一张图,我们可以清晰的看到 Esbuild 的表现是多么优秀:

Esbuild 之所以能这么快,主要原因有两个:

  • Go 语言开发,可以多线程打包,代码直接编译成机器码;

    Webpack 一直被人诟病构建速度慢,主要原因是在打包构建过程中,存在大量的 resolveloadtransformparse 操作(详见 为什么有人说 vite 快,有人却说 vite 慢?- 快速的冷启动 ),而这些操作通常是通过 javascript 代码来执行的。要知道,javascript 并不是什么高效的语言,在执行过程中要先编译后执行,还是单线程并且不能利用多核 cpu 优势,和 Go 语言相比,效率很低。

  • 可充分利用多核 cpu 优势;

关键 API - transfrom & build

Esbuild 并不复杂,它对外提供了两个 API:transform 和 build,使用起来非常简单。

transfrom,转换的意思,将 tsjsxtsx 等格式的内容转化为 js。 transfrom 只负责文件内容转换,并不会生成一个新的文件。

build,构建的意思,根据指定的单个或者多个入口,分析依赖,并使用 loader 将不同格式的内容转化为 js 内容,生成一个或多个 bundle 文件。

Esbuild 在 Vite 中的巧妙使用

我们来看看Vite 是怎么利用 Esbuild 来做预构建和内容转换的。

预构建

为什么要做预构建?原因有两点:

  • 将非 ESM 规范的代码转换为符合 ESM 规范的代码;

  • 将第三方依赖内部的多个文件合并为一个,减少 http 请求数量;

middlewares 中内容转换

Vite 中源文件的转换是在 dev server 启动以后通过 middlewares 实现的。

当浏览器发起请求以后,dev sever 会通过相应的 middlewares 对请求做处理,然后将处理以后的内容返回给浏览器。

middlewares 对源文件的处理,分为 resolveloadtransformparser 四个过程:

  1. resolve - 解析 url,找到源文件的绝对路径;

  2. load - 加载源文件。如果是第三方依赖,直接将预构建内容返回给浏览器;如果是业务代码,继续 transformparser

  3. transfrom - 对源文件内容做转换,即 ts -> jsless -> css 等。转换完成的内容可以直接返回给浏览器了。

  4. parser - 对转换以后的内容做分析,找到依赖模块,对依赖模块做预转换 - pre transform 操作,即重复 1 - 4

    pre transform 是 Vite 做的一个优化点。预转换的内容会先做缓存,等浏览器发起请求以后,如果已经完成转换,直接将缓存的内容返回给浏览器。

Vite 在处理步骤 3 时,是通过 esbuild.transform 实现的,对比 Webpack 使用各个 loader 处理源文件,那是非常简单、快捷的。

四、关于 Vite 的一些其他知识

  • 在工程中不是所有的引用模块都是ES写法,可能是CommonJS 和 UMD 、AMD 等等,这个时候Vite 会进行预构建,将其转换为ESM模块,以支持Vite。
  • 对于JSX、或者TS 等需要编译的文件,Vite是用esbuild来进行编译的。

五、Webpack 和 Rollup 区别

经验法则:对于应用使用 webpack,对于类库使用 Rollup。

如果你需要代码拆分(Code Splitting),或者你有很多静态资源需要处理,再或者你构建的项目需要引入很多CommonJS模块的依赖,那么 webpack 是个很不错的选择。

如果您的代码库是基于 ES2015 模块的,而且希望你写的代码能够被其他人直接使用,你需要的打包工具可能是 Rollup 。

蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。


分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司

nodejs--图片上传

前端达人

前端部分

html部分


  1. <div class="add">
  2. <p>商品名称: <input type="text" class="productName"></p>
  3. <p>商品价格: <input type="number" class="price" ></p>
  4. <!-- multiple: 允许同时上传多张图片 -->
  5. <!-- <p>商品图片: <input type="file" class="file" multiple="multiple"></p> -->
  6. <p>商品图片: <input type="file" class="file"></p>
  7. <p><button class="add-btn">添加商品</button></p>
  8. </div>
  9. <script src="js/axios.js"></script>
  10. <script src="js/util.js"></script>
  11. <script src="js/upload.js"></script>

upload.js


  1. // 新建表单数据对象,用来存储上传的文件及上传的其它数据
  2. let param = new FormData()
  3. $(".file").onchange = function(){
  4. //获取图片信息
  5. let file = this.files[0]
  6. //"file"为前后端约定vb的属性名
  7. param.append("file",file)
  8. }
  9. $(".add-btn").onclick = function(){
  10. let productName = $(".productName").value;
  11. let price = $(".price").value;
  12. param.append("productName",productName)
  13. param.append("price",price)
  14. axios.post("/product",param,{
  15. headers: {
  16. // 默认提交的类型
  17. // "content-type": "application/json"
  18. // 复杂的表单数据(只要上传文件,就必须是下面的类型)
  19. "content-type": "multipart/form-data"
  20. }
  21. })
  22. .then((res)=>{
  23. console.log(res.data);
  24. })
  25. }

postman

 

Express后端部分

使用multer实现图片上传

下载

npm i multer -S

引入


  1. const multer = require("multer")
  2. const path = require("path")

配置

注意: 该文件在/router文件夹中,而uploads存放上传图片文件夹在根目录


  1. var storage = multer.diskStorage({
  2. // 配置文件上传后存储的路径
  3. destination: function (req, file, cb) {
  4. // NodeJS的两个全局变量
  5. // console.log(__dirname); //获取当前文件在服务器上的完整目录
  6. // console.log(__filename); //获取当前文件在服务器上的完整路径
  7. cb(null, path.join(__dirname,'../uploads'))
  8. },
  9. // 配置文件上传后存储的路径和文件名
  10. filename: function (req, file, cb) {
  11. console.log('file',file);
  12. cb(null, Date.now() + path.extname(file.originalname))
  13. }
  14. })
  15. var upload = multer({ storage: storage })

在路由中使用


  1. //添加商品
  2. router.post("/product",upload.single("file1"),(req,res)=>{
  3. //接收普通文本参数
  4. let {productName,price} = req.body;
  5. 、接收上传文件数据 -->
  6. let imgUrl = req.file.filename;
  7. let sql = "insert into product (productName,price,imgUrl) values (?,?,?)"
  8. conn.query(sql,[productName, price , imgUrl],(err,result)=>{
  9. if (err){
  10. console.log('增加失败');
  11. return;
  12. }
  13. let data;
  14. if (result.affectedRows === 1){
  15. data = {
  16. code: 0,
  17. msg: '添加成功'
  18. }
  19. }else{
  20. data = {
  21. code: 1,
  22. msg: '添加失败'
  23. }
  24. }
  25. res.send(data)
  26. })
  27. })

如果抽离路由模块中的处理函数upload.single("file1")写在Router模块

实现写在抽取的模块 

 NODEJS--express图片上传 - 简书

如果req.body为空,可以用nodejs后台文件上传模块connect-multiparty

使用方法如下:

1.  安装模块

npm install connect-multiparty --save

2.  引入模块

var multipart = require('connect-multiparty');

var multipartMiddleware = multipart();

3.  使用模块

const express = require('express')

const router = express.Router()

router.post('/formdata',multipartMiddleware, function (req,res) {

console.log(req.query)

//分别返回body,文件属性,以及文件存放地址

});
————————————————
原文链接:https://blog.csdn.net/zjwengyidong/article/details/51407903




蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。


分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司

HTML生日快乐-生日祝福(烟花+粒子动画)

前端达人

一、HTML页面制作

1、更换title

在HappyBirthday/HappyBirthday.html中的title换成相应人的名字

 
  1. <head>
  2. <meta charset="utf-8">
  3. <title>XXX生日快乐</title>
  4. <style>
  5. html,body{
  6. margin:0px;
  7. width:100%;
  8. height:100%;
  9. overflow:hidden;
  10. background:linear-gradient(to left top,blue, #ffc0cb);
  11. }
  12. </style>
  13. <link href="favicon.ico" rel="shortcut icon">
  14. </head>

2、粒子名字

在粒子展示祝福的名字进行更换

 在HappyBirthday/js/index.js#44行处修改

 
  1. if (i !== -1) {
  2. S.UI.simulate(decodeURI(action).substring(i + 3));
  3. } else {
  4. S.UI.simulate('|#countdown 3||祝|XXX|生日快乐|祝你|生日快乐|祝你幸福|祝你健康|前途光明|祝你|生日快乐!|#icon heart|#icon heart-empty|#icon heart');
  5. }

3、粒子颜色

修改粒子动画展示的颜色,视频中使用了粉色(255,192,203)

HappyBirthday/js/index.js#417行处修改

 
  1. S.Dot = function (x, y) {
  2. this.p = new S.Point({
  3. x: x,
  4. y: y,
  5. z: 5,
  6. a: 1,
  7. h: 0
  8. });
  9. this.e = 0.07;
  10. this.s = true;
  11. this.c = new S.Color(255, 192, 203, this.p.a);
  12. this.t = this.clone();
  13. this.q = [];
  14. };

4、设备兼容

在原版代码中,仅仅在电脑浏览器有一个较为好的展示效果,在手机浏览器上字显示效果不佳以及延时不足,但是无法正常显示,主要调整了粒子间距和延时时间

粒子间距:先设置默认间距为8(手机较好显示),然后判断屏幕是否大于手机一般尺寸,调整大一点13(平板和电脑较好显示)。

粒子间距变小,数量变多,加载起来就慢。

HappyBirthday/js/index.js#525行处修改

 
  1. if ((window.innerWidth>500 && window.innerHeight>500)){
  2. gap = 13;
  3. }

延时时间:当粒子数量变多,加载慢, 按照原作者设置的时间来展示,可能上一个字没展示完就要去展示下一个字,导致变成一坨。

HappyBirthday/js/index.js#119行处修改

HappyBirthday/js/index.js#177行处修改

 
  1. // 118行
  2. var delay1,delay2;
  3. delay1 = 3000;
  4. delay2 = 5000;
  5. // 177行
  6. if (window.innerWidth>500 && window.innerHeight>500){
  7. delay1 = 1000;
  8. delay2 = 2000;
  9. }

5、音乐播放

由于在某些设备上,无法自动播放音乐,需要通过点击触发,增加点击爱心,开始播放。

二、阿里云部署

通过部署在阿里云,可以通过网址进行访问。

我租了一个阿里云,通过简单部署静态页面就可以访问。

(如果有兄弟紧急使用,也可以叫我帮忙部署一下,哈

1、开放80端口

找到自己的实例,点击完全组,配置开放一个80端口 

开放80端口

2、安装httpd

yum -y install httpd

 3、启动httpd

 
  1. service httpd start
  2. service httpd status

 启动之后可以看到如下画面

 4、移动httpd.conf

默认会发布var/www/html下面的网页

cp /etc/httpd/conf/httpd.conf /var/www/html

5、上传资源到var/www/html

 
  1. # 解压压缩包
  2. unzip HappyBirthday.zip
  3. # 删除压缩包
  4. rm -rf HappyBirthday.zip

6、重启hhtpd

 service httpd restart

7、地址访问

http://8.130.106.21/HappyBirthday/HappyBirthday.html

蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。

分享此文一切功德,皆悉回向给文章原作者及众读者. 免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司

Vue3中全局配置 axios 的两种方式

前端达人

目录

一、回顾 Vue2 的全局引用方式

  1. 简单项目的全局引用

2. 复杂项目的三步封装

二、Vue3 中的使用 

1. provide/inject 方式

2. getCurrentInstance 组合式API引入


 

 

一、回顾 Vue2 的全局引用方式

  1. 简单项目的全局引用

    如果只是简单几个页面的使用,无需太过复杂的配置就可以直接再 main.js 中进行挂载


  1. import Vue from "vue";
  2. /* 第一步下载 axios 命令:npm i axios 或者yarn add axios 或者pnpm i axios */
  3. /* 第二步引入axios */
  4. import axios from 'axios'
  5. // 挂载一个自定义属性$http
  6. Vue.prototype.$http = axios
  7. // 全局配置axios请求根路径(axios.默认配置.请求根路径)
  8. axios.defaults.baseURL = 'http://yufei.shop:3000'

   页面使用


  1. methods:{
  2. getData(){
  3. this.$http.get('/barry').then(res=>{
  4. console.log('res',res)
  5. )}
  6. }
  7. }

2. 复杂项目的三步封装

  ① 新建 util/request.js (配置全局的Axios,请求拦截、响应拦截等)

        关于 VFrame 有疑问的同学可以移步  前端不使用 il8n,如何优雅的实现多语言?


  1. import axios from "axios";
  2. import { Notification, MessageBox, Message } from "element-ui";
  3. import store from "@/store";
  4. import { getToken } from "@/utils/auth";
  5. import errorCode from "@/utils/errorCode";
  6. import Cookies from "js-cookie";
  7. import VFrame from "../framework/VFrame.js";
  8. import CONSTANT from '@/CONSTANT.js'
  9. axios.defaults.headers["Content-Type"] = "application/json;charset=utf-8";
  10. // 创建axios实例
  11. const service = axios.create({
  12. // axios中请求配置有baseURL选项,表示请求URL公共部分
  13. baseURL: process.env.VUE_APP_BASE_API,
  14. // 超时
  15. timeout: 120000
  16. });
  17. // request拦截器
  18. service.interceptors.request.use(
  19. config => {
  20. // 是否需要设置 token
  21. const isToken = (config.headers || {}).isToken === false;
  22. if (getToken() && !isToken) {
  23. config.headers["Authorization"] = "Bearer " + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
  24. }
  25. var cultureName = Cookies.get(CONSTANT.UX_LANGUAGE);
  26. if (cultureName) {
  27. config.headers[CONSTANT.UX_LANGUAGE] = cultureName; // 让每个请求携带自定义token 请根据实际情况自行修改
  28. }
  29. // get请求映射params参数
  30. if (config.method === "get" && config.params) {
  31. let url = config.url + "?";
  32. for (const propName of Object.keys(config.params)) {
  33. const value = config.params[propName];
  34. var part = encodeURIComponent(propName) + "=";
  35. if (value !== null && typeof value !== "undefined") {
  36. if (typeof value === "object") {
  37. for (const key of Object.keys(value)) {
  38. let params = propName + "[" + key + "]";
  39. var subPart = encodeURIComponent(params) + "=";
  40. url += subPart + encodeURIComponent(value[key]) + "&";
  41. }
  42. } else {
  43. url += part + encodeURIComponent(value) + "&";
  44. }
  45. }
  46. }
  47. url = url.slice(0, -1);
  48. config.params = {};
  49. config.url = url;
  50. }
  51. return config;
  52. },
  53. error => {
  54. console.log(error);
  55. Promise.reject(error);
  56. }
  57. );
  58. // 响应拦截器
  59. service.interceptors.response.use(
  60. res => {
  61. // 未设置状态码则默认成功状态
  62. const code = res.data.code || 200;
  63. // 获取错误信息
  64. const msg = errorCode[code] || res.data.msg || errorCode["default"];
  65. if (code === 401) {
  66. MessageBox.alert(
  67. VFrame.l("SessionExpired"),
  68. VFrame.l("SystemInfo"),
  69. {
  70. confirmButtonText: VFrame.l("Relogin"),
  71. type: "warning"
  72. }
  73. ).then(() => {
  74. store.dispatch("LogOut").then(() => {
  75. location.href = "/index";
  76. });
  77. });
  78. } else if (code === 500) {
  79. Message({
  80. message: msg,
  81. type: "error"
  82. });
  83. if (res.data.data) {
  84. console.error(res.data.data)
  85. }
  86. return Promise.reject(new Error(msg));
  87. } else if (code !== 200) {
  88. Notification.error({
  89. title: msg
  90. });
  91. return Promise.reject("error");
  92. } else {
  93. if (res.data.uxApi) {
  94. if (res.data.success) {
  95. return res.data.result;
  96. } else {
  97. Notification.error({ title: res.data.error });
  98. console.error(res);
  99. return Promise.reject(res.data.error);
  100. }
  101. } else {
  102. return res.data;
  103. }
  104. }
  105. },
  106. error => {
  107. console.log("err" + error);
  108. let { message } = error;
  109. if (message == "Network Error") {
  110. message = VFrame.l("TheBackEndPortConnectionIsAbnormal");
  111. } else if (message.includes("timeout")) {
  112. message = VFrame.l("TheSystemInterfaceRequestTimedOut");
  113. } else if (message.includes("Request failed with status code")) {
  114. message =
  115. VFrame.l("SystemInterface") +
  116. message.substr(message.length - 3) +
  117. VFrame.l("Abnormal");
  118. }
  119. Message({
  120. message: VFrame.l(message),
  121. type: "error",
  122. duration: 5 * 1000
  123. });
  124. return Promise.reject(error);
  125. }
  126. );
  127. export default service;

  ② 新建 api/login.js (配置页面所需使用的 api)


  1. import request from '@/utils/request'
  2. // 登录方法
  3. export function login(username, password,shopOrgId,counter, code, uuid) {
  4. const data = {
  5. username,
  6. password,
  7. shopOrgId,
  8. counter,
  9. uuid
  10. }
  11. return request({
  12. url: '/login',
  13. method: 'post',
  14. data: data
  15. })
  16. }
  17. // 获取用户详细信息
  18. export function getInfo() {
  19. return request({
  20. url: '/getInfo',
  21. method: 'get'
  22. })
  23. }
  24. // 退出方法
  25. export function logout() {
  26. return request({
  27. url: '/logout',
  28. method: 'post'
  29. })
  30. }

  ③ 页面使用引入


  1. import { login } from "@/api/login.js"
  2. 接下来不用多说,相信大家已经会使用了

二、Vue3 中的使用 

 上面回顾完 Vue2 中使用 axios 我们来一起看看 Vue3 中axios的使用( 简单Demo,前台使用Vue3,后台使用 node.js ),仅供学习!

1. provide/inject 方式

    ① main.js 中 使用 provide 传入


  1. import {
  2. createApp
  3. } from 'vue'
  4. import App from './App.vue'
  5. import router from './router'
  6. import store from './store'
  7. import "lib-flexible/flexible.js"
  8. import axios from "@/util/request.js"
  9. const app = createApp(App);
  10. app.provide('$axios', axios)
  11. app.use(store).use(router).mount('#app');

    ② 需要用到的页面使用 inject 接受


  1. import { ref, reactive, inject, onMounted} from "vue";
  2. export default {
  3. setup() {
  4. const $axios = inject("$axios");
  5. const getData = async () => {
  6. data = await $axios({ url: "/one/data" });
  7. console.log("data", data);
  8. };
  9. onMounted(() => {
  10. getData()
  11. })
  12. return { getData }
  13. }
  14. }

这个就是借助 provide 做一个派发,和 Vue2 中的差距使用方法差距不大 

 

2. getCurrentInstance 组合式API引入

 ① main.js 中挂载


  1. import {
  2. createApp
  3. } from 'vue'
  4. import App from './App.vue'
  5. import router from './router'
  6. import store from './store'
  7. import "lib-flexible/flexible.js"
  8. import axios from "@/util/request.js"
  9. const app = createApp(App);
  10. /* 挂载全局对象 */
  11. app.config.globalProperties.$axios = axios;
  12. app.use(store).use(router).mount('#app');

/* 挂载全局对象 */
app.config.globalProperties.$axios = axios;

重点就是上面这句

② 需要用的页面使用 Composition Api -- getCurrentInstance 拿到


  1. <script>
  2. import { reactive, onMounted, getCurrentInstance } from "vue";
  3. export default {
  4. setup() {
  5. let data = reactive([]);
  6. /**
  7. * 1. 通过getCurrentInstance方法获取当前实例
  8. * 再根据当前实例找到全局实例对象appContext,进而拿到全局实例的config.globalProperties。
  9. */
  10. const currentInstance = getCurrentInstance();
  11. const { $axios } = currentInstance.appContext.config.globalProperties;
  12. /**
  13. * 2. 通过getCurrentInstance方法获取上下文,这里的proxy就相当于this。
  14. */
  15. const { proxy } = currentInstance;
  16. const getData = async () => {
  17. data = await $axios({ url: "/one/data" });
  18. console.log("data", data);
  19. };
  20. const getData2 = async () => {
  21. data = await proxy.$axios({ url: "/one/data" });
  22. console.log("data2", data);
  23. };
  24. onMounted(() => {
  25. getData()
  26. });
  27. return { getData };
  28. },
  29. };
  30. </script>

下图可以看到我们确实调用了 2次 API 

其实通过 Composition API 中的 getCurrentInstance 方法也是有两种方式的

 1. 通过 getCurrentInstance 方法获取当前实例,再根据当前实例找到全局实例对象appContext,进而拿到全局实例的config.globalProperties。        


    
  1. const currentInstance = getCurrentInstance();
  2. const { $axios } = currentInstance.appContext.config.globalProperties;

 2. 通过getCurrentInstance方法获取上下文,这里的proxy就相当于this。


    
  1. const currentInstance = getCurrentInstance();
  2. const { proxy } = currentInstance;
  3. const getData2 = async () => {
  4. data = await proxy.$axios({ url: "/one/data" });
  5. console.log("data2", data);
  6. };

 


蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。


分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司

用 JavaScript 检测浏览器在线/离线状态(JavaScript API — navigator.onLine)

前端达人

如今HTML5 移动应用或 Web app 中越来越普遍的使用了离线浏览技术,所以用 JavaScript 检测浏览器在线/离线状态非常常见。

无论浏览器是否在线,navigator.onLine 属性都会提供一个布尔值。 如果浏览器在线,则设置为 true ,否则设置为 false 

 if(navigator.onLine) { // true|false // ... }

online 和 offline 事件:

当浏览器脱机或上线时,浏览器还支持 online 和 offline 事件

window.addEventListener('online', function(e){console.log('online')});
window.addEventListener('offline', function(e){console.log('offline');});

你可以使用几种熟悉的方式来注册事件:

  • 在 windowdocument,或 document.body 上使用 addEventListener
  • 将 document 或 document.body 的 ononline 或 onoffline 属性设置为一个 JavaScript Function 对象。(注意:由于兼容性原因,不能使用 window.ononline 或 window.onoffline。)
  • 在 HTML 标记中的 body 标签上指定 οnοnline=”…” 或 οnοffline=”…” 特性。

注意事项:

  • IE8中需要给document.body绑定事件而不是window
  • 在线离线的变化指的是物理上的网络链接变化,如果是在控制台将网络限制为 offline 则不会触发相应的事件。

实例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用 JavaScript 检测浏览器在线/离线状态(JavaScript API — navigator.onLine)</title>
    <style type="text/css"> #status {
            position: fixed;
            width: 100%;
            font: bold 1em sans-serif;
            color: #FFF;
            padding: 0.5em;
        }
        #log {
            padding: 2.5em 0.5em 0.5em;
            font: 1em sans-serif;
        }
        .online {
            background: green;
        }
        .offline {
            background: red;
        } </style>
</head>
<body>
<div id="status"></div>
<div id="log"></div>
<button type="button" id="test">检查状态</button>
<script> window.addEventListener('load', function () { var testBtn = document.getElementById("test"); var status = document.getElementById("status"); var log = document.getElementById("log");

    function updateOnlineStatus(event) { var condition = navigator.onLine ? "online" : "offline";
        status.className = condition;
        status.innerHTML = condition.toUpperCase();

        log.insertAdjacentHTML("beforeend", "Event: " + (event?event.type:"-") + "; Status: " + condition+ " | ");
    }

    window.addEventListener('online', updateOnlineStatus);
    window.addEventListener('offline', updateOnlineStatus);
    testBtn.addEventListener("click", updateOnlineStatus);
    updateOnlineStatus();
}); </script>
</body>
</html>
    

总结:

1、navigator.online属性提供浏览器是否在线的布尔值

2、浏览器脱机或上线还支持online和offline事件(IE8需要给document.body绑定事件而不是window)

蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。


分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司

【小程序开发】uniapp引入iconfont图标及使用方式

前端达人

第一步:打开iconfont官网新建项目并添加自己所需要的图标

这里是iconfont的网址链接: iconfon官网
在这里插入图片描述

新建项目

不 要 勾 选 彩 色 , 会 导 致 在 项 目 中 无 法 修 改 字 体 颜 色 及 样 式 , 本 人 亲 测 , 找 了 半 天 解 决 办 法 最 终 悔 恨 不 已 \textcolor{red} {不要勾选彩色,会导致在项目中无法修改字体颜色及样式,本人亲测,找了半天解决办法最终悔恨不已}

  • App下原生导航栏的按钮如果使用字体图标,注意检查字体库的名字(font-family)是否使用了默认的 iconfont,这个名字是保留字,不能作为外部引入的字体库的名字,需要调整为自定义的名称,否则无法显示
  • 按钮上显示的文字。使用字体图标时 unicode 字符表示必须 ‘\u’ 开头,如 “\ue123”(注意不能写成"\e123")。

选择自己需要的图标加入购物车再添加到项目中

在这里插入图片描述
在这里插入图片描述

第二步:下载我们所需要的iconfont.css文件并引入到项目中

在这里插入图片描述

下载项目并解压

在这里插入图片描述

将iconfont.css文件复制放到我们的项目中去,一般放在static静态文件目录下

第三步:修改iconfont.css文件中的内容并全局引用

在这里插入图片描述
在这里插入图片描述

需要注意的是,当我们在项目中新添加了图标后,需要重新复制修改iconfont.css中的内容,要不然新添加的图标是找不到的

在这里插入图片描述

在移动端引用的时候要在App.vue文件中进行全局注册,而不是main.js中

第四步:使用iconfont图标进行开发

在开发中我们常用的有两种方式,这两种方式以及注意事项我在以下内容都有演示:

  • 在页面文件中直接使用标签
  • 使用APPplus原生的自定义导航栏iconfont右侧自定义图标

1.在页面文件中直接使用标签

在这里插入图片描述

两种方式代码的获取方式如下图所示:

  • 使用uniCode码
  • Font Class 名称

在这里插入图片描述

在这里插入图片描述

2.使用APPplus原生的自定义导航栏iconfont右侧自定义图标

使用iconfont图标的文件内容(忽略css样式):
在这里插入图片描述
需要配合static目录下的iconfont.tff文件,这个文件在我们下载到本地的时候那个目录中,与iconfont.css在一个目录中:
在这里插入图片描述
pages.json文件中配置iconfont图标:
在这里插入图片描述

这三步完成,我们配置自定义原生导航栏的自定义图标就完成啦!

五:需要注意的点写在这里:

  1. iconfont官网创建项目的时候,不要勾选彩 色 \textcolor{red} {彩色} 那个多选框,否则使用的时候不能更改颜色
  2. 如果项目重新添加了新的图标,需要重新生成在线链接,更新iconfont.css文件
  3. 自定义原生导航栏使用iconfont图标时,必须配合iconfont.ttf文件使用


作者:彩云sky
来源:人人都是产品经理
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。


分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司

web3:区块链常见的几大共识机制及优缺点

前端达人

什么是共识?

我们所说的共识,是指达成了广泛的一致。 比如,一群人去吃晚饭, 如果他们对于提议“吃某火锅”没有任何异议,那么就可以说共识达成了。 如果存在异议,那么他们就必须通过某种方法决定吃什么。 在极端情况下,这群人就会分开。


什么是共识机制?

区块链要成为一个难以攻破的、公开的、不可篡改数据记录的去中心化诚实可信系统,需要在尽可能短的时间内做到分布式数据记录的安全、明确及不可逆,提供一个最坚实且去中心化的系统。共识机制在区块链中成为了重要的因素之一。

区块链分布式记账的方式使得每个人手上都有一本完整的账本,全网共有。但是随着节点的不断增多,数据越多,账本也越安全,难以摧毁。除此之外,任意一个或者部分节点的账本被篡改,都不可能被全网认同,除非你能控制51%的节点,即51%攻击,但是这耗能巨大,几乎是不可能的。同时随着节点不断增加,谁来记账,如何选择合适的人来记账成为一个问题,而制定一个记账人的选择方式以及规定,让大家来遵守这个规定,达成共识,这就是区块链里面的共识机制。

共识机制是区块链节点就区块信息达成全网一致共识的机制,说得更直白一些就是要解决所谓去中心化的信任问题,因为每个节点之间默认是不认识且不可靠的,同时每个节点都不能知道其他节点是否宕机或者背叛的情况下,尽可能的保证记录信息的准确性以及安全性。同时节点越分散,效率越低,网络对于信息的满意度越高,越安全。


共识机制的目标

区块链作为一种按时间顺序存储数据的数据结构,可支持不同的共识机制。共识机制是区块链技术的重要组件。区块链共识机制的目标是使所有的诚实节点保存一致的区块链视图,同时满足两个性质:
1)一致性:所有诚实节点保存的区块链的前缀部分完全相同。
2)有效性:由某诚实节点发布的信息终将被其他所有诚实节点记录在自己的区块链中。


为什么需要共识机制?

在分布式系统中,各个不同的主机通过异步通信方式组成网络集群。为了保证每个主机达成一致的状态共识,就需要在主机之间进行状态复制。异步系统中,可能会出现各样的问题,例如主机出现故障无法通信,或者性能下降,而网络也可能发生拥堵延迟,类似的种种故障有可能会发生错误信息在系统内传播。因此需要在默认不可靠的异步网络中定义容错协议,以确保各主机达成安全可靠的状态共识。所以,利用区块链构造基于互联网的去中心化账本,需要解决的首要问题是如何实现不同账本节点上的账本数据的一致性和正确性。

这就需要借鉴已有的在分布式系统中实现状态共识的算法,确定网络中选择记账节点的机制,以及如何保障账本数据在全网中形成正确、一致的共识。


如何评价一个共识机制的优劣:

  1. 安全性:能否有效防止二次支付,私自挖矿
  2. 扩展性:当系统成员和待确认交易数量增加时,所带来的系统负载和网络通信量的变化,通常以网络吞吐量来衡量
  3. 性能效率:每秒可以处理的交易数量
  4. 资源消耗:达成共识过程中,所要消耗的CPU、内存等计算资源

共识机制分类

没有一种共识机制是完美无缺的,各共识机制都有其优缺点,有些共识机制是为解决一些特定的问题而生。

BTC作为区块链的第一个应用,它的共识机制PoW共识机制曾经一枝独秀,但是随着区块链技术的不断发展,各类不同的共识机制开始不断涌现,各有千秋,各有拥趸。

常见的共识就机制包括:POW(工作量证明机制)、POS(权益证明机制)、POW+POS(混合共识机制)、DPOS(股份授权证明)等等,另外还有Pool验证池、Ripple瑞波共识协议、PBFT(使用拜占庭容错算法)等等

PoW( Proof of Work)工作量证明:多劳多得

在这里插入图片描述

最早(也是第一个)被应用的共识机制,最先被BTC采用并且获取了巨大成功,它支撑了BTC系统长达 10 多年无重大故障使其平稳运行。而且 PoW 构想也符合创始人中本聪最初的设想:人人皆可挖矿、按劳分配、公平公正。

PoW 属于按劳分配,多劳多得,就如同大家在BTC系统中一起进行数学运算,最先运算出的才能获得奖励。是一种衡量计算机工作量的共识机制。BTC使用的就是工作量证明机制。

工作量证明主要通过哈希计算找出合理数据的步骤来完成:将区块头数据带入哈希函数计算公式,不断调整区块头数据中的随机数,直到计算出满足特定标准的哈希值,工作量证明就会完成。

简单来说就是多劳多得,谁的算力强,计算得就更快,获得记账权的概率就越高,算力竞争的胜者将获得相应区块记账权和BTC奖励。 因此,矿机芯片的算力越高,挖矿的时间更长,就可以获得更多的数字货币。进行运算获得奖励的过程称之为挖矿,参与挖矿的人们称之为矿工。这种证明方式决定了其验证过程需要大量的数据计算,而其他节点却很容易验证计算结果是否正确,因此 区块链系统无法被恶意节点所欺骗。但是这种证明方式需要消耗大量能源(电力及计算硬件损耗),很不 环保。并且在理论上,如果集合了全网51%的算力即可对区块链网络进行有效攻击,因此许多基于比特币 代码产生的、市值较小的山寨币很容易遭受攻击。

代表token:BTC、BCH、LTC等。

优点

  • 公平公正:去中心化程度高,人人都可以参与获得记账权;
  • 安全系数高:全网算力越高,其安全程度也越高,如破坏该系统需要投入巨大成本;
  • 算法简单,容易实现

缺点

  • 资源浪费:因每一笔交易都需要通过多数矿工的确认,在这些矿工进行运算的过程中消耗大量的电力资源等。
  • 效率低下:区块的确认时间难以缩短;容易产生分叉,需要等待多个确认。
  • 算力集中:根据往年数据显示,在资本大量进入的情况下,过去一年矿池算力份额排名前五位的矿池占据了BTC总算力份额的 65%,马太效应逐渐显现,而算力过度集中还存在着 51% 攻击的风险。
  • 永远没有最终性,需要检查点机制来弥补最终性。

PoS(Proof of Stake)股权证明算法:持有越多,获得越多

在这里插入图片描述

因 PoW 存在的问题,PoS 在主流算法一路畅通的“杀了出来”,成为了最具有挑战者。近几年,基于 PoS共识打造的区块链项目越来越多,如目前市值保持第二的ETH也加入了 PoS。“Staking经济”在 2019年成为了热门词语,同时也被交易所和钱包大力追捧。

POS机制采用类似股权证明与投票的机制,选出记帐人,由它来创建区块。持有股权愈多则有较大的特权,且需负担更多的责任来产生区块,同时也获得更多收益的权力。 POS 机制中一般用币龄来计算记账权,每个币持有一天算一个币龄,比如 持有 100 个币,总共持有了 30 天,那么此时的币龄就为 3000。在 POS机制下,如果记账人发现一个 POS 区块, 他的币龄就会被清空为 0,每被清空 365 币龄,将会从区块中获得 0.05 个币的利息(可理解为年利率 5%)。

PoS权益证明同样需要通过计算找出合理的哈希值来完成。 但不同的是权益证明机制通过节点持有加密货币的时间和数量来判断节点的权益大小。根据权益大小不同,用户之间看到的计算目标值也不同。权益大的节点,获得目标值更加简单,更容易获得下一个区块的记账权。 这种方式不需要每个节点都进行大量的运算,节省了电力能源。同时全网51%的算力攻击在权益证明机制下是无效的,因为发起这种攻击反而会损害自身的利益。但是可能会出现币种持有数量大的节点权力过 大,对区块链记账享有绝对支配权的情况,容易引发信任问题。

在 PoS 机制中,是不需要消耗电力来进行运算,而是通过抵押 token 来获得打包区块的权利。当一笔交易发生时,系统会对打包区块和验证区块的节点来进行奖励,奖励则是增发或者解锁的 token。

代表token:ADA、ONT、ATOM等。

优点

  • 不浪费资源、效率高:因其不需要拼算力挖矿,同时缩短了共识达成的时间,转账效率提高了;
  • 弱化了中心矿池规模经济的需求:算力集中垄断的情形也得到了缓解,个体竞争力差别相对减小;

缺点

  • 被动形成中心化:因去中心化程度,容易出现强者恒强的情况,会导致富者越富,资源越来越集中的情况。
  • 安全隐患:PoS机制实现较为复杂,容易产生安全漏洞。
  • 无权益问题(Nothing at Stake):用户在PoS中可以同时在两个分叉上面下注;无论哪一个分叉后面被公认为主链,该用户都可以获得奖励而没有机会成本的损失。这样也在事实上会干扰共识的形成。
  • 还是需要挖矿,本质上没有解决商业应用的痛点。

DPOS(Delegated Proof-of-Stake)股份授权证明

在这里插入图片描述

DPoS 机制是在 PoS 的基础上进行了改良,举例来说就是大家公认的投出选票,选举出一定数量的代表,让这些代表进行验证和记账等,可以理解为PoS 的升级版。与PoS的主要区别在于持币者投出一定数量的节点,代理他们进行验证和记账。其合规监管、性能、资源消耗和容错性与PoS相似。

DPoS的工作原理为:每个股东按其持股比例拥有影响力,51%股东投票的结果将是不可逆且有约束力的。其挑战是通过及时而高效的方法达到51%批准。为达到这个目标,每个股东可以将其投票权授予一名代表。获票数最多的前100位代表按既定时间表轮流产生区块。每名代表分配到一个时间段来生产区块。所有的代表将收到等同于一个平均水平的区块所含交易费的10%作为报酬。如果一个平均水平的区块含有100股作为交易费,一名代表将获得1股作为报酬。DPoS的投票模式可以每30秒产生一个新区块。

简单点说:DPoS 委托权益证明通过由持币人投票选举出一定数量的代表来达成共识。 每个持币人的投票所占的比重 与他持有的币种数量有关,持有的越多,所占的比重越大。被选出的代表可拥有记账权,轮流进行记账;未能很好履行职责的代表还会被投票除名。这一任期结束后,新的代表会再次通过投票产生。

代表token:EOS、TRX等。

优点

  • 比 PoS 机制拥有更高的效率和性能:相比于 PoS 机制,DPoS 大幅缩小了参与验证和记账的节点数量,可以达到秒级的共识验证。

缺点

  • 整个共识机制还是依赖于token,很多商业应用是不需要代币存在的。
  • 去中心化程度低:相比较于只能说是弱中心化;
  • 安全问题严重:类 PoS 机制的通病,相信大家都看到过报道,如被黑客攻击等等。

PoC(Proof of Capacity)容量证明机制

在这里插入图片描述

PoC 机制早在 2014年存在了,但只是一直处于“落魄阶段”,简单说就是没火,无人问津。2019年随着POC一大公链Yottachain的崛起,越来越多的矿工加入了POC硬盘挖矿这个行业大军了。它是POW共识机制的一种,以硬盘作为共识参与者,它的特点是牺牲性能获得安全可信,相对POW减少了非常多的安全和信任成本,更低成本解决了全局信任和安全,几乎不耗电力资源,并且可共享和复用的信任生态。

PoC 机制是通过普通硬盘挖矿的共识机制。简单来说就是利用计算机硬盘中的闲置空间来进行存储进行挖矿获取收益,硬盘空间越大,存储的内容越多获得的收益就越大。 它更多地关注内存而不是处理能力。 从某种意义上说,这是对PoW的改进,即使在挖掘开始之前,容量证明也要求节点将预先计算的哈希值存储在其硬盘驱动器和其他内存单元上,这个过程称为绘图,绘图使容量证明成为比工作证明更快的机制。这种方法的另一个优点是它可以节省大量能源,这与工作量证明机制不同。更不用说,硬盘存储更多哈希值的任何技术改进也将为不在区块链中的人改进技术,这与许多制造商制造的专用芯片不同,后者除了采矿之外什么都不做。

IPFS 也类似,但不同的是 IPFS 衍生的区块链项目(激励层Filecoin)是一种去中心化存储服务的 Marketplace(撮合交易的市场),它的重点在于如何在系统参与者互不信任的条件下,实现存储和检索工作的量化;PoC 是一种底层共识机制,与 PoW、PoS一样都是去中心化网络达成一致性状态的算法。由此来看,两者是完全不同的概念,唯一的共同点就是都可以使用硬盘向网络贡献价值来换取收益。

代表token:BTT、BHD等。

优点

  • 挖矿门槛较低:只要有硬盘就可以进行挖矿,大大降低了挖矿门槛;PoC的矿机耗电量低、噪音小、成本低,适合家庭挖矿和人人挖矿。
  • 去中心化程度较高:抵押机制提高了算力集中化的门槛,即便有超级矿工出现,普通散户也可以继续获得收益。
  • 能源消耗低,节能:相比于PoW,POC挖矿将算力替换成硬盘空间,在很大程度的杜绝了POW挖矿造成的资源浪费以及对环境不友好等问题,同时降低了挖矿成本,让矿工从挖矿中赚取更多的利润。
  • 反垄断:用硬盘容量替代了算力,天然具有抗ASIC的属性

缺点

  • 未来发展的局限性可能较大
  • 可能会有政策性风险
  • PoC并没有实现真正的平等

DAG(Directed acyclic graph)有向无环图:无区块链概念

在这里插入图片描述

有向无环图是计算机科学中众所周知的数据结构。事实上,区块链也是DAG的一个例子,因为它有一个明确的方向,没有任何循环,并且是一个图。1OTA使用的Tangle也是DAG共识机制的一种形式。在这种机制中,每个块必须有两个父块。所以,为了通过DAG共识机制完成一笔交易,用户需要验证自己之前的两笔交易。这种机制的最大优势是它可以减少延迟和交易费用。然而,这种共识模型对提高可扩展性几乎没有任何作用,而且极易受到攻击,因为任何攻击只需要34%的哈希算力就可以破坏系统。

DAG最初出现就是为了解决区块链的效率问题。其通过改变区块的链式存储结构,通过DAG的拓扑结构来存储区块。在区块打包时间不变的情况下,网络中可以并行的打包N个区块,网络中的交易就可以容纳N倍。

之后DAG发展成为脱离区块链,提出了blockless无区块的概念。新交易发起时,只需要选择网络中已经存在的并且比较新的交易作为链接确认,这一做法解决了网络宽度问题,大大加快了交易速度。

代表token:IOTA、byteball等。
前段时间国内首个基于DAG的物联网区块链项目ITC万物链也取得了不小的涨幅。

优点

  • 交易速度快;
  • 无需挖矿;
  • 极低的手续费

缺点

  • 网络规模不大,导致极易成为中心化;
  • 安全性低于PoW机制

PBFT(Practical Byzantine Fault Tolerance)实用拜占庭容错:分布式一致性算法

实用拜占庭容错在保证活性和安全性(liveness & safety)的前提下提供了(n-1)/3的容错性。
在分布式计算上,不同的计算机透过讯息交换,尝试达成共识;但有时候,系统上协调计算机(Coordinator / Commander)或成员计算机 (Member /Lieutanent)可能因系统错误并交换错的讯息,导致影响最终的系统一致性。拜占庭将军问题就根据错误计算机的数量,寻找可能的解决办法,这无法找到一个绝对的答案,但只可以用来验证一个机制的有效程度。而拜占庭问题的可能解决方法为:在 N ≥ 3F + 1 的情况下一致性是可能解决。其中,N为计算机总数,F为有问题计算机总数。信息在计算机间互相交换后,各计算机列出所有得到的信息,以大多数的结果作为解决办法。

优点

  • 系统运转可以脱离币的存在,pbft算法共识各节点由业务的参与方或者监管方组成,安全性与稳定性由业务相关方保证。
  • 共识的时延大约在2~5秒钟,基本达到商用实时处理的要求。
  • 共识效率高,可满足高频交易量的需求。

缺点

  • 当有1/3或以上记账人停止工作后,系统将无法提供服务;
  • 当有1/3或以上记账人联合作恶,且其它所有的记账人被恰好分割为两个网络孤岛时,恶意记账人可以使系统出现分叉,但是会留下密码学证据

实用拜占庭容错主要应用于央行的数字货币以及布萌区块链。


dBFT(delegated BFT)授权拜占庭容错算法

小蚁采用的dBFT机制,是由权益来选出记账人,然后记账人之间通过拜占庭容错算法来达成共识。dBFT和PBFT的关系类似于PoS和DPoS的关系。

dBFT在PBFT基础上进行了以下改进:

  1. 将C/S架构的请求响应模式,改进为适合P2P网络的对等节点模式;
  2. 将静态的共识参与节点改进为可动态进入、退出的动态共识参与节点;
  3. 为共识参与节点的产生设计了一套基于持有权益比例的投票机制,通过投票决定共识参与节点(记账节点);
  4. 在区块链中引入数字证书,解决了投票中对记账节点真实身份的认证问题。

优点

  • 专业化的记账人;
  • 可以容忍任何类型的错误;
  • 记账由多人协同完成,每一个区块都有最终性,不会分叉;
  • 算法的可靠性有严格的数学证明;

缺点

  • 当有1/3或以上记账人停止工作后,系统将无法提供服务;
  • 当有1/3或以上记账人联合作恶,且其它所有的记账人被恰好分割为两个网络孤岛时,恶意记账人可以使系统出现分叉,但是会留下密码学证据;

以上总结来说,dBFT机制最核心的一点,就是最大限度地确保系统的最终性,使区块链能够适用于真正的金融应用场景。


Pool验证池——私有链专用

基于传统的分布式一致性技术,加上数据验证机制;之前曾是行业链大范围在使用的共识机制,但是随着私有链项目的逐渐减少渐渐开始势微。

优点

不需要token也可以工作,在成熟的分布式一致性算法(Pasox、Raft)基础上,实现秒级共识验证。

缺点

去中心化程度不如bictoin;更适合多方参与的多中心商业模式。

参考

拜占庭将军问题

拜占庭将军问题是一个协议问题,拜占庭帝国军队的将军们必须全体一致的决定是否攻击某一支敌军。问题是这些将军在地理上是分隔开来的,并且将军中存在叛徒。叛徒可以任意行动以达到以下目标:欺骗某些将军采取进攻行动;促成一个不是所有将军都同意的决定,如当将军们不希望进攻时促成进攻行动;或者迷惑某些将军,使他们无法做出决定。如果叛徒达到了这些目的之一,则任何攻击行动的结果都是注定要失败的,只有完全达成一致的努力才能获得胜利。

这一问题是一种对现实世界的模型化,尤指网络当中由于软硬件错误、网络阻塞及恶意攻击导致的各种未知行为。
显然,在此处默认了将军们在达成一致的过程中正确的传递出了自己的决定,也就是说叛徒只存在于将军当中,不存在于传令兵当中。故要让拜占庭将军问题有解,必须要具备一个重要前提,即信道必须是安全可靠的。关于信道可靠问题,会引出两军问题。两军问题的结论是,在一个不可靠的通信链路上试图通过通信以达成一致是基本不可能或者十分困难的。

拜占庭容错

拜占庭将军问题提出后,有很多的算法被提出用于解决这个问题。这类算法统称拜占庭容错算法(BFT: Byzantine Fault Tolerance)。简略来说,拜占庭容错(BFT)不是某一个具体算法,而是能够抵抗拜占庭将军问题导致的一系列失利的系统特点。 这意味着即使某些节点出现缺点或恶意行为,拜占庭容错系统也能够继续运转。本质上来说,拜占庭容错方案就是少数服从多数。

拜占庭将军问题的原始论文给出了一些解决思路,但其更注重理论上的可行性。算法效率不高,算法复杂度为指数级,且文中明确指出时间成本及消息传递数量很大。因此不具备太大的实用价值。

拜占庭容错系统需要达成如下两个指标
● 安全性:任何已经完成的请求都不会被更改,它可以在以后请求看到。在区块链系统中,可以理解为,已经生成的账本不可篡改,并且可以被节点随时查看。
● 活性:可以接受并且执行非拜占庭客户端的请求,不会被任何因素影响而导致非拜占庭客户端的请求不能执行。在区块链系统中,可以理解为,系统需要持续生成区块,为用户记账,这主要靠挖矿的激励机制来保证。

拜占庭系统目前普遍采用的假设条件包括:
● 拜占庭节点的行为可以是任意的,拜占庭节点之间可以共谋;
● 节点之间的错误是不相关的;
● 节点之间通过异步网络连接,网络中的消息可能丢失、乱序、延时到达;
● 服务器之间传递的信息,第三方可以知晓 ,但是不能窜改、伪造信息的内容和验证信息的完整性;

科普 | 什么是共识机制?

详解区块链中常见的八大共识机制!

区块链的几大共识机制及优缺点




作者:彩云sky
来源:人人都是产品经理
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请加蓝小助,微信号:ben_lanlan,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系01063334945。


分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务UI设计公司、界面设计公司、UI设计服务公司、数据可视化设计公司、UI交互设计公司、高端网站设计公司、UI咨询、用户体验公司、软件界面设计公司

日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档