Commit 22ab7159 authored by wangxuelai's avatar wangxuelai

'ss'

parent 087f6d56
......@@ -22,6 +22,7 @@ import {
} from '../../../service/common.js';
Page({
data: {
editType: 1,
maxLength: 500,
maxImgCount: 9,
maxVideoCount: 9,
......@@ -52,10 +53,11 @@ Page({
publishing: false,
},
onLoad: function (options) {
const { id, sid } = options;
const { id, sid, editType } = options;
this.setData({
id: id || 0,
sid: sid
sid: sid,
editType,
})
if(this.data.id!=0){
this.getThemeAdDetail();
......@@ -163,6 +165,7 @@ Page({
this.data.publishing = true;
const sjdMediaEditor = this.selectComponent('#sjd-media-editor');
let content = JSON.parse(JSON.stringify(sjdMediaEditor.data.content));
console.log(content, 'content');
let _content = content.filter((ele) => ele.value.trim() != '');
_content.forEach((ele) => {
if (ele.type == 'video' && ele.poster) {
......
......@@ -7,6 +7,7 @@ var app = getApp();
Page({
mixins: [require("../../../mixins/myMins/audioCallback")],
data: {
editType: 1,
localImageRoot: "../../../images/",
imageRoot: app.globalData.imageRoot,
imageVersion: app.globalData.imageVersion,
......@@ -31,17 +32,22 @@ Page({
isexpand: true,
modalContact: false,
modelContactAnimate: false,
showRefresh: false,
},
onLoad: function(options, b) {
const { id, sid } = options;
const { id, sid, editType } = options;
this.setData({
id,
sid,
editType,
});
this.getThemeAdDetail();
},
onShow: function(options) {
if (this.data.showRefresh) {
this.data.showRefresh = false;
this.getThemeAdDetail();
}
},
onReady: function() {
},
......@@ -59,6 +65,7 @@ Page({
}).then((res) => {
const { code, data } = res;
if (code == 200) {
console.log(data, 'datadatadatadata');
this.setData({
landPageInfo: {
cover: data.cover,
......@@ -336,19 +343,30 @@ Page({
const themeeditorPage = currentPages.find(ele => ele.route == 'business/pages/themeeditor/index');
const themeeditorPageIndex = currentPages.findIndex(ele => ele.route == 'business/pages/themeeditor/index');
const delta = currentPages.length - themeeditorPageIndex - 1;
themeeditorPage.setData({
themeAdInfo: {
id: this.data.id,
title: this.data.landPageInfo.title,
}
})
if (this.data.editType == 1) {
themeeditorPage.setData({
themeAdInfo: {
id: this.data.id,
title: this.data.landPageInfo.title,
}
})
} else if (this.data.editType == 2) {
themeeditorPage.setData({
calendarThemeAdInfo: {
id: this.data.id,
title: this.data.landPageInfo.title,
}
})
} else if (this.data.editType == 3) {
}
wx.navigateBack({
delta,
})
},
goedit () {
wx.navigateTo({
url: `/business/pages/themead/index?id=${this.data.id}&sid=${this.data.sid}`
url: `/business/pages/themead/index?id=${this.data.id}&sid=${this.data.sid}&editType=${this.data.editType}`
})
}
});
......@@ -15,13 +15,15 @@ Page({
emptyPage: false,
list: [],
total: 0,
showRefresh: false
showRefresh: false,
editType: 1,
},
onLoad: function(options) { // Do some initialize when page load.
const { sid, id } = options;
const { sid, id, editType } = options;
this.setData({
sid,
id: id || 0
id: id || 0,
editType,
});
this.getThemeAdList('init');
},
......@@ -154,25 +156,35 @@ Page({
const themeeditorPage = currentPages.find(ele => ele.route == 'business/pages/themeeditor/index');
const themeeditorPageIndex = currentPages.findIndex(ele => ele.route == 'business/pages/themeeditor/index');
const delta = currentPages.length - themeeditorPageIndex - 1;
themeeditorPage.setData({
themeAdInfo: {
id: item.id,
title: item.title,
}
})
if (this.data.editType == 1) {
themeeditorPage.setData({
themeAdInfo: {
id: item.id,
title: item.title,
}
})
} else if (this.data.editType == 2) {
themeeditorPage.setData({
calendarThemeAdInfo: {
id: item.id,
title: item.title,
}
})
} else if (this.data.editType == 3) {
}
wx.navigateBack({
delta,
})
},
toEditLandpage () {
wx.navigateTo({
url: `/business/pages/themead/index?id=0&sid=${this.data.sid}`
url: `/business/pages/themead/index?id=0&sid=${this.data.sid}&editType=${this.data.editType}`
})
},
viewThemeAd (e) {
const { item } = e.currentTarget.dataset;
wx.navigateTo({
url: `/business/pages/themead/landpage?id=${item.id}&sid=${this.data.sid}`
url: `/business/pages/themead/landpage?id=${item.id}&sid=${this.data.sid}&editType=${this.data.editType}`
})
}
})
\ No newline at end of file
This diff is collapsed.
{
"navigationBarTitleText": "新建作业打卡",
"navigationBarTitleText": "",
"usingComponents": {
"sjd-media-editor": "../../../components/newsjdmediaeditor",
"sound-recording": "../../../components/soundrecording",
......@@ -8,6 +8,7 @@
"w-cell": "/dist/w-cell/index",
"w-cell-group": "/dist/w-cell-group/index",
"w-pane": "/dist/w-pane/index",
"expiredTip": "../../components/expiredTip"
"expiredTip": "../../components/expiredTip",
"calender":"../../components/calendar"
}
}
\ No newline at end of file
......@@ -108,7 +108,7 @@
<view class="section-item">
<view class="form-item">
<view class="item-l">高级设置</view>
<view class="item-r" bindtap="bindMoreSetting">
<view class="item-r">
<image class="icon-arr" src="{{imageRoot}}2b/organizationalmgt/right.png?{{imageVersion}}"/>
</view>
</view>
......@@ -168,7 +168,7 @@
<view class="item-l">打卡开始时间</view>
<view class="item-r">
<view class="label">
<picker mode="multiSelector" range="{{datePickerArr}}" value="{{startDateInfoArr}}" bindchange="startTimeChange" bindcolumnchange="startDatePickerChange">
<picker mode="multiSelector" range="{{datePickerArr}}" value="{{calendarStartDateInfoArr}}" bindchange="startTimeChange" bindcolumnchange="startDatePickerChange">
{{calendarparams.startDate}} {{calendarparams.startTime}}
</picker>
</view>
......@@ -182,7 +182,7 @@
<view class="item-l">打卡结束时间</view>
<view class="item-r">
<view class="label">
<picker mode="multiSelector" range="{{datePickerArr}}" value="{{endDateInfoArr}}" bindchange="endTimeChange" bindcolumnchange="endDatePickerChange" style="line-height: 40rpx;">
<picker mode="multiSelector" range="{{datePickerArr}}" value="{{calendarEndDateInfoArr}}" bindchange="endTimeChange" bindcolumnchange="endDatePickerChange" style="line-height: 40rpx;">
{{calendarparams.endDate}} {{calendarparams.endTime}}
</picker>
</view>
......@@ -191,6 +191,53 @@
</view>
</view>
<view class="section-tip" style="color: #999999;margin-bottom:12px;">打卡时间结束后将无法提交打卡!</view>
<view class="section-wrap pdlpdr24" style="margin-bottom: 0;">
<view class="section-item">
<view class="form-item">
<view class="item-l">不用打卡的日期</view>
<view class="item-r" bindtap="showCalender">
<view class="label extrarestdate" wx:if="{{selectRestDays&&selectRestDays.length>0}}">
<text wx:for="{{selectRestDays}}" wx:key="index" class="" selectable="false" space="false" decode="false">{{item.show}},</text>
</view>
<view class="label" wx:else>
增加额外休息日
</view>
<image class="icon-arr" src="{{imageRoot}}2b/organizationalmgt/right.png?{{imageVersion}}"/>
</view>
</view>
<view class="week-select" >
<view class="week-item {{item.isSlect?'selected-item':''}}" bindtap="selectrestdays" data-item="{{item}}"
wx:for="{{weeks}}" wx:key="index"
style="{{index==6?'margin:0':'1'}}">
{{item.name}}
</view>
</view>
</view>
</view>
<view class="section-tip" style="color: #999999;margin-bottom:12px;">选中的日期,学生不用打卡!</view>
<view class="section-wrap" bindtap="goyinxiaoPage">
<view class="section-item">
<view class="form-item">
<view class="item-l">打卡营销页</view>
<view class="item-r">
<view class="label">{{calendarThemeAdInfo.id != 0 ? calendarThemeAdInfo.title : '请选择营销页'}}</view>
<image class="icon-arr" src="{{imageRoot}}2b/organizationalmgt/right.png?{{imageVersion}}"/>
</view>
</view>
</view>
<view class="section-tip" style="color: #999999;">可在学生加入打卡前展示, 此功能可收集有意向学生的联系方式 <text style="color: #16B0FD;">了解详情</text></view>
<view class="section-wrap" bindtap="bindMoreSetting">
<view class="section-item">
<view class="form-item">
<view class="item-l">高级设置</view>
<view class="item-r">
<image class="icon-arr" src="{{imageRoot}}2b/organizationalmgt/right.png?{{imageVersion}}"/>
</view>
</view>
</view>
<view class="section-tip" style="color: #999999;">设置学生打卡参与方式、作业要求、打卡模式等</view>
</view>
</view>
</block>
<block wx:if="{{editType == 3}}">
闯关打卡
......@@ -207,6 +254,21 @@
initial-time="0" autoplay="false" loop="false" bindplay="" bindpause="" bindended=""
bindtimeupdate="" bindwaiting="" binderror="" bindfullscreenchange="screenchange"></video>
</view>
<view class="calender-box" wx:if="{{calenderShow}}" bindtap="hiddeCalender" data-id="hidde">
<view class="content-box">
<view class="content">
<calender calendar="{{calendar}}" gesture="{{gesture}}"></calender>
</view>
<view class="footer">
<view class="cancle" bindtap="hiddeCalender" data-id="hidde">
取消
</view>
<view class="sure" bindtap="selectDays">
完成
</view>
</view>
</view>
</view>
<view class="modal-dialog success-dailog" wx:if="{{successDailogShow}}" catchtouchmove="true">
<view class="modal-mask"></view>
<view class="modal-box">
......
......@@ -281,6 +281,12 @@ page{
font-size: 26rpx;
color: #686868;
}
.form-item .item-r .label.extrarestdate {
max-width: 423rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.form-item .item-r .icon-arr{
width: 12rpx;
height: 20rpx;
......@@ -445,3 +451,67 @@ page{
padding-top: 28rpx;
text-align: center;
}
.week-select{
display: flex;
}
.week-select .week-item{
width:78rpx;
height:78rpx;
background:rgba(255,255,255,1);
border:1px solid rgba(203,203,203,1);
border-radius:50%;
font-size:22rpx;
font-family:PingFang-SC-Regular;
font-weight:400;
color:rgba(0,0,0,1);
display:flex;
align-items:center;
justify-content:center;
margin-right: 26rpx;
}
.week-select .selected-item{
background:rgba(101,184,244,1);
border: none;
color:rgba(255,255,255,1);
}
.calender-box{
background: rgba(0,0,0,.5);
position: fixed;
width: 100%;
height: 100%;
z-index: 20;
left: 0;
bottom: 0;
}
.calender-box .content-box{
background: #fff;
position: absolute;
width: 100%;
left: 0;
bottom: 0;
border-radius:20rpx 20rpx 0px 0px;
}
.calender-box .footer{
display: flex;
padding: 85rpx 0 48rpx 0;
}
.calender-box .footer .sure{
flex: 1;
text-align: center;
line-height: 1;
font-size:26rpx;
font-family:PingFang-SC-Bold;
font-weight:bold;
color:rgba(101,184,244,1);
}
.calender-box .footer .cancle{
flex: 1;
text-align: center;
line-height: 1;
font-size:26rpx;
font-family:PingFang-SC-Bold;
font-weight:bold;
color:rgba(101,184,244,1);
border-right: 1rpx solid rgba(0,0,0,.1);
}
\ No newline at end of file
......@@ -18,6 +18,9 @@ Page({
video_require_num:0,
audio_require_status:2,
audio_require_num:0,
supplement_status: 2,
supplement_num: 0,
editType: 1,
tipObj: [
{type: '1', title: '防作弊模式', content: ['开启后学员打卡后','才能看到其他同学的打卡作品!']},
{type: '2', title: '加密模式', content: ['开启后非本校在读人员','看不到老师的主题内容的!']},
......@@ -37,6 +40,7 @@ Page({
class_ids: '',
classes: [],
psdshow: false,
editType: 1,
},
onLoad: function (options) {
let that = this;
......@@ -59,6 +63,9 @@ Page({
class_ids,
sid,
classes,
supplement_status,
supplement_num,
editType,
} = options;
that.setData({
is_cheat,
......@@ -79,7 +86,10 @@ Page({
join_secret,
class_ids,
sid,
classes: JSON.parse(decodeURIComponent(classes))
supplement_status: supplement_status || 2,
supplement_num: supplement_num || 0,
classes: JSON.parse(decodeURIComponent(classes)),
editType,
})
},
remindswitch() {
......@@ -168,6 +178,14 @@ Page({
force_num: Number(value),
})
},
supplementInput(e) {
const {
value
} = e.detail;
this.setData({
supplement_num: Number(value),
})
},
textInput(e) {
const {
value
......@@ -268,7 +286,9 @@ Page({
join_rule_type,
join_secret,
class_ids,
classes
classes,
supplement_num,
editType,
} = this.data
let currPage = null; //当前页面
let prevPage = null; //上一个页面
......@@ -276,25 +296,49 @@ Page({
currPage = pages[pages.length - 1]; //当前页面
prevPage = pages[pages.length - 2]; //上一个页面
}
prevPage.setData({
"params.is_cheat": Number(is_cheat),
// "params.is_encrypt": Number(is_encrypt),
"params.force_status": Number(force_status),
"params.force_num": Number(force_num),
"params.text_require_status": Number(text_require_status),
"params.text_require_num": Number(text_require_num),
"params.image_require_status": Number(image_require_status),
"params.image_require_num": Number(image_require_num),
"params.audio_require_status": Number(audio_require_status),
"params.video_require_num": Number(video_require_num),
"params.video_require_status": Number(video_require_status),
"params.audio_require_num": Number(audio_require_num),
"params.join_rule_type": Number(join_rule_type),
"params.join_secret": join_secret,
"params.class_ids": classes.map(ele => ele.id).join(','),
"params.push_time": push_time,
"params.push_status": push_status,
});
if (editType == 1) {
prevPage.setData({
"params.is_cheat": Number(is_cheat),
// "params.is_encrypt": Number(is_encrypt),
"params.force_status": Number(force_status),
"params.force_num": Number(force_num),
"params.text_require_status": Number(text_require_status),
"params.text_require_num": Number(text_require_num),
"params.image_require_status": Number(image_require_status),
"params.image_require_num": Number(image_require_num),
"params.audio_require_status": Number(audio_require_status),
"params.video_require_num": Number(video_require_num),
"params.video_require_status": Number(video_require_status),
"params.audio_require_num": Number(audio_require_num),
"params.join_rule_type": Number(join_rule_type),
"params.join_secret": join_secret,
"params.class_ids": classes.map(ele => ele.id).join(','),
"params.push_time": push_time,
"params.push_status": push_status,
});
} else if (editType == 2) {
prevPage.setData({
"calendarparams.is_cheat": Number(is_cheat),
// "params.is_encrypt": Number(is_encrypt),
"calendarparams.force_status": Number(force_status),
"calendarparams.force_num": Number(force_num),
"calendarparams.text_require_status": Number(text_require_status),
"calendarparams.text_require_num": Number(text_require_num),
"calendarparams.image_require_status": Number(image_require_status),
"calendarparams.image_require_num": Number(image_require_num),
"calendarparams.audio_require_status": Number(audio_require_status),
"calendarparams.video_require_num": Number(video_require_num),
"calendarparams.video_require_status": Number(video_require_status),
"calendarparams.audio_require_num": Number(audio_require_num),
"calendarparams.join_rule_type": Number(join_rule_type),
"calendarparams.join_secret": join_secret,
"calendarparams.class_ids": classes.map(ele => ele.id).join(','),
"calendarparams.push_time": push_time,
"calendarparams.push_status": push_status,
"calendarparams.supplement_num": supplement_num,
});
} else if (editType == 3) {
}
wx.navigateBack({
delta: 1
})
......
......@@ -62,7 +62,6 @@
<image class="icon-arr" src="{{imageRoot}}2b/organizationalmgt/right.png?{{imageVersion}}"/>
</picker>提醒
</view>
<!-- <text class="tip" data-type="1" bindtap="showTipModal" wx:if="{{is_cheat==0}}">什么是防作弊?</text> -->
</view>
<view class="item-r">
<w-switch slot="content" value="{{ push_status }}" color="#16B0FD" size="small" bind:onChange="remindswitch" />
......@@ -71,22 +70,19 @@
<view class="usedesc" data-type="4" wx:if="{{push_status!=2}}">已关注“轻校家”公众号且未打卡的学生将收到公众号提醒通知</view>
<view class="section-tip" style="color: #999999;" wx:if="{{push_status!=2}}"><text style="font-size: 22rpx;color: #16B0FD;">了解更多</text></view>
</view>
<!-- <view class="section-item">
<view class="form-item">
<view class="item-l">打卡提醒
<text class="tip" wx:if="{{params.push_status==2}}">(开启后定时提醒学员打卡)</text>
<view class="num-box" wx:else>
<picker mode="time" value="{{params.push_time}}" bindchange="tipTimeChange">
{{params.push_time}}
<image class="icon-arr" src="{{imageRoot}}2b/organizationalmgt/right.png?{{imageVersion}}"/>
</picker>提醒
</view>
<view class="section" wx:if="{{editType == 2}}">
<view class="form-itembox borderbottom1px">
<view class="form-item ">
<view class="item-l">
<text class="title">补打卡设置</text>
</view>
<view class="item-r">
<input type="number" placeholder-class="placeholderclass" class="iteminput" maxlength="5" value="{{supplement_num}}" bindinput="supplementInput" placeholder=""></input>次
</view>
</view>
<view class="item-r">
<w-switch slot="content" value="{{params.push_status}}" color="#16B0FD" size="small" bind:onChange="remindswitch" />
</view>
</view>
</view> -->
<view class="usedesc" data-type="4">学生忘记打卡,可以补打卡几次</view>
</view>
</view>
<view class="bigtitle">打卡模式</view>
<view class="section">
......@@ -102,15 +98,6 @@
</view>
<view class="usedesc" data-type="4" wx:if="{{is_cheat!=0}}">学生打卡后才能看到其他学生的打卡内容</view>
</view>
<!-- <view class="form-item">
<view class="item-l">
<text class="title">加密模式</text>
<text class="tip" data-type="2" bindtap="showTipModal" wx:if="{{is_encrypt==0}}">什么是加密模式?</text>
</view>
<view class="item-r">
<w-switch slot="content" isBoolean="{{1}}" value="{{ is_encrypt }}" color="#16B0FD" size="small" bind:onChange="encryptswitch" />
</view>
</view> -->
<view class="form-itembox">
<view class="form-item">
<view class="item-l">
......@@ -127,24 +114,6 @@
<view class="usedesc" data-type="4" wx:if="{{force_status!=2}}">学生打卡将先阅读设置的作业内容,阅读时长达到设定的时间后,才能提交作业</view>
</view>
</view>
<!-- <view class="section-wrap">
<view class="section-item">
<view class="form-item">
<view class="item-l">打卡提醒
<text class="tip" wx:if="{{params.push_status==2}}">(开启后定时提醒学员打卡)</text>
<view class="num-box" wx:else>
<picker mode="time" value="{{params.push_time}}" bindchange="tipTimeChange">
{{params.push_time}}
<image class="icon-arr" src="{{imageRoot}}2b/organizationalmgt/right.png?{{imageVersion}}"/>
</picker>提醒
</view>
</view>
<view class="item-r">
<w-switch slot="content" value="{{params.push_status}}" color="#16B0FD" size="small" bind:onChange="remindswitch" />
</view>
</view>
</view>
</view> -->
<view class="bigtitle">学生作业要求</view>
<view class="section">
<view class="form-itembox borderbottom1px">
......
......@@ -83,6 +83,14 @@ picker .icon-arr{
margin-left: 20rpx;
color: #9B9B9B;
}
.iteminput{
width: 69rpx;
border-bottom: 2rpx solid #E2E2E2;
margin-right: 26rpx;
font-size: 26rpx;
color: #16B0FD;
text-align: center;
}
.form-item .item-l .num-box .iteminput{
width: 69rpx;
border-bottom: 2rpx solid #E2E2E2;
......
const computedBehavior = require('miniprogram-computed');
import {
wxChooseImage,
wxUploadFile,
......@@ -16,10 +17,14 @@ import {
} from '../../utilities/index.js';
var app = getApp();
Component({
behaviors: [computedBehavior],
properties: {
content: {
type: Array,
value: [],
observer: (value) => {
console.log(this);
}
},
placeholder: {
type: String,
......@@ -104,6 +109,11 @@ Component({
})
}, 200)
},
// observers: {
// 'exchangeIndex': function () {
// consoel.log('dasdas');
// }
// },
methods: {
textareaChange (e) {
const {currentTarget, detail} = e;
......@@ -410,18 +420,14 @@ Component({
exchangeIndex: index
})
setTimeout(() => {
// const content = this.data.content;
// content.splice(index-1, 2, content[index], content[index - 1]);
this.triggerEvent('exchange', index)
this.setData({
// content,
exchangeIndex: -1
})
}, 500)
},
prevImg (e) {
const {dataset} = e.currentTarget;
// const images = this.data.params.img;
const current = dataset.src;
wxPreviewImage({
urls: [current],
......
module.exports = (function() {
var __MODS__ = {};
var __DEFINE__ = function(modId, func, req) { var m = { exports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = { exports: {} }; __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); if(typeof m.exports === "object") { __MODS__[modId].m.exports.__proto__ = m.exports.__proto__; Object.keys(m.exports).forEach(function(k) { __MODS__[modId].m.exports[k] = m.exports[k]; var desp = Object.getOwnPropertyDescriptor(m.exports, k); if(desp && desp.configurable) Object.defineProperty(m.exports, k, { set: function(val) { __MODS__[modId].m.exports[k] = val; }, get: function() { return __MODS__[modId].m.exports[k]; } }); }); if(m.exports.__esModule) Object.defineProperty(__MODS__[modId].m.exports, "__esModule", { value: true }); } else { __MODS__[modId].m.exports = m.exports; } } return __MODS__[modId].m.exports; };
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
__DEFINE__(1585194071421, function(require, module, exports) {
var isArray = Array.isArray;
var keyList = Object.keys;
var hasProp = Object.prototype.hasOwnProperty;
module.exports = function equal(a, b) {
if (a === b) return true;
if (a && b && typeof a == 'object' && typeof b == 'object') {
var arrA = isArray(a)
, arrB = isArray(b)
, i
, length
, key;
if (arrA && arrB) {
length = a.length;
if (length != b.length) return false;
for (i = length; i-- !== 0;)
if (!equal(a[i], b[i])) return false;
return true;
}
if (arrA != arrB) return false;
var dateA = a instanceof Date
, dateB = b instanceof Date;
if (dateA != dateB) return false;
if (dateA && dateB) return a.getTime() == b.getTime();
var regexpA = a instanceof RegExp
, regexpB = b instanceof RegExp;
if (regexpA != regexpB) return false;
if (regexpA && regexpB) return a.toString() == b.toString();
var keys = keyList(a);
length = keys.length;
if (length !== keyList(b).length)
return false;
for (i = length; i-- !== 0;)
if (!hasProp.call(b, keys[i])) return false;
for (i = length; i-- !== 0;) {
key = keys[i];
if (!equal(a[key], b[key])) return false;
}
return true;
}
return a!==a && b!==b;
};
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
return __REQUIRE__(1585194071421);
})()
//# sourceMappingURL=index.js.map
\ No newline at end of file
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\n\nvar isArray = Array.isArray;\nvar keyList = Object.keys;\nvar hasProp = Object.prototype.hasOwnProperty;\n\nmodule.exports = function equal(a, b) {\n if (a === b) return true;\n\n if (a && b && typeof a == 'object' && typeof b == 'object') {\n var arrA = isArray(a)\n , arrB = isArray(b)\n , i\n , length\n , key;\n\n if (arrA && arrB) {\n length = a.length;\n if (length != b.length) return false;\n for (i = length; i-- !== 0;)\n if (!equal(a[i], b[i])) return false;\n return true;\n }\n\n if (arrA != arrB) return false;\n\n var dateA = a instanceof Date\n , dateB = b instanceof Date;\n if (dateA != dateB) return false;\n if (dateA && dateB) return a.getTime() == b.getTime();\n\n var regexpA = a instanceof RegExp\n , regexpB = b instanceof RegExp;\n if (regexpA != regexpB) return false;\n if (regexpA && regexpB) return a.toString() == b.toString();\n\n var keys = keyList(a);\n length = keys.length;\n\n if (length !== keyList(b).length)\n return false;\n\n for (i = length; i-- !== 0;)\n if (!hasProp.call(b, keys[i])) return false;\n\n for (i = length; i-- !== 0;) {\n key = keys[i];\n if (!equal(a[key], b[key])) return false;\n }\n\n return true;\n }\n\n return a!==a && b!==b;\n};\n"]}
\ No newline at end of file
This diff is collapsed.
module.exports = (function() {
var __MODS__ = {};
var __DEFINE__ = function(modId, func, req) { var m = { exports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = { exports: {} }; __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); if(typeof m.exports === "object") { __MODS__[modId].m.exports.__proto__ = m.exports.__proto__; Object.keys(m.exports).forEach(function(k) { __MODS__[modId].m.exports[k] = m.exports[k]; var desp = Object.getOwnPropertyDescriptor(m.exports, k); if(desp && desp.configurable) Object.defineProperty(m.exports, k, { set: function(val) { __MODS__[modId].m.exports[k] = val; }, get: function() { return __MODS__[modId].m.exports[k]; } }); }); if(m.exports.__esModule) Object.defineProperty(__MODS__[modId].m.exports, "__esModule", { value: true }); } else { __MODS__[modId].m.exports = m.exports; } } return __MODS__[modId].m.exports; };
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
__DEFINE__(1585194071422, function(require, module, exports) {
module.exports = rfdc
function rfdc (opts) {
opts = opts || {}
if (opts.circles) return rfdcCircles(opts)
return opts.proto ? cloneProto : clone
function cloneArray (a, fn) {
var keys = Object.keys(a)
var a2 = new Array(keys.length)
for (var i = 0; i < keys.length; i++) {
var k = keys[i]
var cur = a[k]
if (typeof cur !== 'object' || cur === null) {
a2[k] = cur
} else if (cur instanceof Date) {
a2[k] = new Date(cur)
} else {
a2[k] = fn(cur)
}
}
return a2
}
function clone (o) {
if (typeof o !== 'object' || o === null) return o
if (o instanceof Date) return new Date(o)
if (Array.isArray(o)) return cloneArray(o, clone)
var o2 = {}
for (var k in o) {
if (Object.hasOwnProperty.call(o, k) === false) continue
var cur = o[k]
if (typeof cur !== 'object' || cur === null) {
o2[k] = cur
} else if (cur instanceof Date) {
o2[k] = new Date(cur)
} else {
o2[k] = clone(cur)
}
}
return o2
}
function cloneProto (o) {
if (typeof o !== 'object' || o === null) return o
if (o instanceof Date) return new Date(o)
if (Array.isArray(o)) return cloneArray(o, cloneProto)
var o2 = {}
for (var k in o) {
var cur = o[k]
if (typeof cur !== 'object' || cur === null) {
o2[k] = cur
} else if (cur instanceof Date) {
o2[k] = new Date(cur)
} else {
o2[k] = cloneProto(cur)
}
}
return o2
}
}
function rfdcCircles (opts) {
var refs = []
var refsNew = []
return opts.proto ? cloneProto : clone
function cloneArray (a, fn) {
var keys = Object.keys(a)
var a2 = new Array(keys.length)
for (var i = 0; i < keys.length; i++) {
var k = keys[i]
var cur = a[k]
if (typeof cur !== 'object' || cur === null) {
a2[k] = cur
} else if (cur instanceof Date) {
a2[k] = new Date(cur)
} else {
var index = refs.indexOf(cur)
if (index !== -1) {
a2[k] = refsNew[index]
} else {
a2[k] = fn(cur)
}
}
}
return a2
}
function clone (o) {
if (typeof o !== 'object' || o === null) return o
if (o instanceof Date) return new Date(o)
if (Array.isArray(o)) return cloneArray(o, clone)
var o2 = {}
refs.push(o)
refsNew.push(o2)
for (var k in o) {
if (Object.hasOwnProperty.call(o, k) === false) continue
var cur = o[k]
if (typeof cur !== 'object' || cur === null) {
o2[k] = cur
} else if (cur instanceof Date) {
o2[k] = new Date(cur)
} else {
var i = refs.indexOf(cur)
if (i !== -1) {
o2[k] = refsNew[i]
} else {
o2[k] = clone(cur)
}
}
}
refs.pop()
refsNew.pop()
return o2
}
function cloneProto (o) {
if (typeof o !== 'object' || o === null) return o
if (o instanceof Date) return new Date(o)
if (Array.isArray(o)) return cloneArray(o, cloneProto)
var o2 = {}
refs.push(o)
refsNew.push(o2)
for (var k in o) {
var cur = o[k]
if (typeof cur !== 'object' || cur === null) {
o2[k] = cur
} else if (cur instanceof Date) {
o2[k] = new Date(cur)
} else {
var i = refs.indexOf(cur)
if (i !== -1) {
o2[k] = refsNew[i]
} else {
o2[k] = cloneProto(cur)
}
}
}
refs.pop()
refsNew.pop()
return o2
}
}
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
return __REQUIRE__(1585194071422);
})()
//# sourceMappingURL=index.js.map
\ No newline at end of file
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\nmodule.exports = rfdc\n\nfunction rfdc (opts) {\n opts = opts || {}\n\n if (opts.circles) return rfdcCircles(opts)\n return opts.proto ? cloneProto : clone\n\n function cloneArray (a, fn) {\n var keys = Object.keys(a)\n var a2 = new Array(keys.length)\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i]\n var cur = a[k]\n if (typeof cur !== 'object' || cur === null) {\n a2[k] = cur\n } else if (cur instanceof Date) {\n a2[k] = new Date(cur)\n } else {\n a2[k] = fn(cur)\n }\n }\n return a2\n }\n\n function clone (o) {\n if (typeof o !== 'object' || o === null) return o\n if (o instanceof Date) return new Date(o)\n if (Array.isArray(o)) return cloneArray(o, clone)\n var o2 = {}\n for (var k in o) {\n if (Object.hasOwnProperty.call(o, k) === false) continue\n var cur = o[k]\n if (typeof cur !== 'object' || cur === null) {\n o2[k] = cur\n } else if (cur instanceof Date) {\n o2[k] = new Date(cur)\n } else {\n o2[k] = clone(cur)\n }\n }\n return o2\n }\n\n function cloneProto (o) {\n if (typeof o !== 'object' || o === null) return o\n if (o instanceof Date) return new Date(o)\n if (Array.isArray(o)) return cloneArray(o, cloneProto)\n var o2 = {}\n for (var k in o) {\n var cur = o[k]\n if (typeof cur !== 'object' || cur === null) {\n o2[k] = cur\n } else if (cur instanceof Date) {\n o2[k] = new Date(cur)\n } else {\n o2[k] = cloneProto(cur)\n }\n }\n return o2\n }\n}\n\nfunction rfdcCircles (opts) {\n var refs = []\n var refsNew = []\n\n return opts.proto ? cloneProto : clone\n\n function cloneArray (a, fn) {\n var keys = Object.keys(a)\n var a2 = new Array(keys.length)\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i]\n var cur = a[k]\n if (typeof cur !== 'object' || cur === null) {\n a2[k] = cur\n } else if (cur instanceof Date) {\n a2[k] = new Date(cur)\n } else {\n var index = refs.indexOf(cur)\n if (index !== -1) {\n a2[k] = refsNew[index]\n } else {\n a2[k] = fn(cur)\n }\n }\n }\n return a2\n }\n\n function clone (o) {\n if (typeof o !== 'object' || o === null) return o\n if (o instanceof Date) return new Date(o)\n if (Array.isArray(o)) return cloneArray(o, clone)\n var o2 = {}\n refs.push(o)\n refsNew.push(o2)\n for (var k in o) {\n if (Object.hasOwnProperty.call(o, k) === false) continue\n var cur = o[k]\n if (typeof cur !== 'object' || cur === null) {\n o2[k] = cur\n } else if (cur instanceof Date) {\n o2[k] = new Date(cur)\n } else {\n var i = refs.indexOf(cur)\n if (i !== -1) {\n o2[k] = refsNew[i]\n } else {\n o2[k] = clone(cur)\n }\n }\n }\n refs.pop()\n refsNew.pop()\n return o2\n }\n\n function cloneProto (o) {\n if (typeof o !== 'object' || o === null) return o\n if (o instanceof Date) return new Date(o)\n if (Array.isArray(o)) return cloneArray(o, cloneProto)\n var o2 = {}\n refs.push(o)\n refsNew.push(o2)\n for (var k in o) {\n var cur = o[k]\n if (typeof cur !== 'object' || cur === null) {\n o2[k] = cur\n } else if (cur instanceof Date) {\n o2[k] = new Date(cur)\n } else {\n var i = refs.indexOf(cur)\n if (i !== -1) {\n o2[k] = refsNew[i]\n } else {\n o2[k] = cloneProto(cur)\n }\n }\n }\n refs.pop()\n refsNew.pop()\n return o2\n }\n}\n"]}
\ No newline at end of file
MIT License
Copyright (c) 2017 Evgeny Poberezkin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# fast-deep-equal
The fastest deep equal
[![Build Status](https://travis-ci.org/epoberezkin/fast-deep-equal.svg?branch=master)](https://travis-ci.org/epoberezkin/fast-deep-equal)
[![npm version](https://badge.fury.io/js/fast-deep-equal.svg)](http://badge.fury.io/js/fast-deep-equal)
[![Coverage Status](https://coveralls.io/repos/github/epoberezkin/fast-deep-equal/badge.svg?branch=master)](https://coveralls.io/github/epoberezkin/fast-deep-equal?branch=master)
## Install
```bash
npm install fast-deep-equal
```
## Features
- ES5 compatible
- works in node.js (0.10+) and browsers (IE9+)
- checks equality of Date and RegExp objects by value.
## Usage
```javascript
var equal = require('fast-deep-equal');
console.log(equal({foo: 'bar'}, {foo: 'bar'})); // true
```
## Performance benchmark
Node.js v9.11.1:
```
fast-deep-equal x 226,960 ops/sec ±1.55% (86 runs sampled)
nano-equal x 218,210 ops/sec ±0.79% (89 runs sampled)
shallow-equal-fuzzy x 206,762 ops/sec ±0.84% (88 runs sampled)
underscore.isEqual x 128,668 ops/sec ±0.75% (91 runs sampled)
lodash.isEqual x 44,895 ops/sec ±0.67% (85 runs sampled)
deep-equal x 51,616 ops/sec ±0.96% (90 runs sampled)
deep-eql x 28,218 ops/sec ±0.42% (85 runs sampled)
assert.deepStrictEqual x 1,777 ops/sec ±1.05% (86 runs sampled)
ramda.equals x 13,466 ops/sec ±0.82% (86 runs sampled)
The fastest is fast-deep-equal
```
To run benchmark (requires node.js 6+):
```bash
npm install
node benchmark
```
## License
[MIT](https://github.com/epoberezkin/fast-deep-equal/blob/master/LICENSE)
declare module 'fast-deep-equal' {
const equal: (a: any, b: any) => boolean;
export = equal;
}
'use strict';
var isArray = Array.isArray;
var keyList = Object.keys;
var hasProp = Object.prototype.hasOwnProperty;
module.exports = function equal(a, b) {
if (a === b) return true;
if (a && b && typeof a == 'object' && typeof b == 'object') {
var arrA = isArray(a)
, arrB = isArray(b)
, i
, length
, key;
if (arrA && arrB) {
length = a.length;
if (length != b.length) return false;
for (i = length; i-- !== 0;)
if (!equal(a[i], b[i])) return false;
return true;
}
if (arrA != arrB) return false;
var dateA = a instanceof Date
, dateB = b instanceof Date;
if (dateA != dateB) return false;
if (dateA && dateB) return a.getTime() == b.getTime();
var regexpA = a instanceof RegExp
, regexpB = b instanceof RegExp;
if (regexpA != regexpB) return false;
if (regexpA && regexpB) return a.toString() == b.toString();
var keys = keyList(a);
length = keys.length;
if (length !== keyList(b).length)
return false;
for (i = length; i-- !== 0;)
if (!hasProp.call(b, keys[i])) return false;
for (i = length; i-- !== 0;) {
key = keys[i];
if (!equal(a[key], b[key])) return false;
}
return true;
}
return a!==a && b!==b;
};
{
"_from": "fast-deep-equal@^2.0.1",
"_id": "fast-deep-equal@2.0.1",
"_inBundle": false,
"_integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"_location": "/fast-deep-equal",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "fast-deep-equal@^2.0.1",
"name": "fast-deep-equal",
"escapedName": "fast-deep-equal",
"rawSpec": "^2.0.1",
"saveSpec": null,
"fetchSpec": "^2.0.1"
},
"_requiredBy": [
"/miniprogram-computed"
],
"_resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"_shasum": "7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49",
"_spec": "fast-deep-equal@^2.0.1",
"_where": "F:\\wechatapp\\wechatapp.shangjiadao.com\\node_modules\\miniprogram-computed",
"author": {
"name": "Evgeny Poberezkin"
},
"bugs": {
"url": "https://github.com/epoberezkin/fast-deep-equal/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Fast deep equal",
"devDependencies": {
"benchmark": "^2.1.4",
"coveralls": "^2.13.1",
"deep-eql": "latest",
"deep-equal": "latest",
"eslint": "^4.0.0",
"lodash": "latest",
"mocha": "^3.4.2",
"nano-equal": "latest",
"nyc": "^11.0.2",
"pre-commit": "^1.2.2",
"ramda": "latest",
"shallow-equal-fuzzy": "latest",
"typescript": "^2.6.1",
"underscore": "latest"
},
"files": [
"index.js",
"index.d.ts"
],
"homepage": "https://github.com/epoberezkin/fast-deep-equal#readme",
"keywords": [
"fast",
"equal",
"deep-equal"
],
"license": "MIT",
"main": "index.js",
"name": "fast-deep-equal",
"nyc": {
"exclude": [
"**/spec/**",
"node_modules"
],
"reporter": [
"lcov",
"text-summary"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/epoberezkin/fast-deep-equal.git"
},
"scripts": {
"eslint": "eslint *.js benchmark spec",
"test": "npm run eslint && npm run test-ts && npm run test-cov",
"test-cov": "nyc npm run test-spec",
"test-spec": "mocha spec/*.spec.js -R spec",
"test-ts": "tsc --target ES5 --noImplicitAny index.d.ts"
},
"types": "index.d.ts",
"version": "2.0.1"
}
{
"plugins": [
["module-resolver", {
"root": ["./src"],
"alias": {}
}]
],
"presets": [
["env", {"loose": true, "modules": "commonjs"}]
]
}
\ No newline at end of file
module.exports = {
'extends': [
'airbnb-base',
'plugin:promise/recommended'
],
'parserOptions': {
'ecmaVersion': 9,
'ecmaFeatures': {
'jsx': false
},
'sourceType': 'module'
},
'env': {
'es6': true,
'node': true,
'jest': true
},
'plugins': [
'import',
'node',
'promise'
],
'rules': {
'arrow-parens': 'off',
'comma-dangle': [
'error',
'only-multiline'
],
'complexity': ['error', 10],
'func-names': 'off',
'global-require': 'off',
'handle-callback-err': [
'error',
'^(err|error)$'
],
'import/no-unresolved': [
'error',
{
'caseSensitive': true,
'commonjs': true,
'ignore': ['^[^.]']
}
],
'import/prefer-default-export': 'off',
'linebreak-style': 'off',
'no-catch-shadow': 'error',
'no-continue': 'off',
'no-div-regex': 'warn',
'no-else-return': 'off',
'no-param-reassign': 'off',
'no-plusplus': 'off',
'no-shadow': 'off',
'no-multi-assign': 'off',
'no-underscore-dangle': 'off',
'node/no-deprecated-api': 'error',
'node/process-exit-as-throw': 'error',
'object-curly-spacing': [
'error',
'never'
],
'operator-linebreak': [
'error',
'after',
{
'overrides': {
':': 'before',
'?': 'before'
}
}
],
'prefer-arrow-callback': 'off',
'prefer-destructuring': 'off',
'prefer-template': 'off',
'quote-props': [
1,
'as-needed',
{
'unnecessary': true
}
],
'semi': [
'error',
'never'
]
},
'globals': {
'window': true,
'document': true,
'App': true,
'Page': true,
'Component': true,
'Behavior': true,
'wx': true,
'getCurrentPages': true,
}
}
MIT License
Copyright (c) 2018 wechat-miniprogram
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# computed
小程序自定义组件扩展 behavior,计算属性 `computed` 和监听器 `watch` 的实现。在 data 或者 properties 改变时,会重新计算 `computed` 字段并触发 `watch` 监听器。
> 此 behavior 依赖开发者工具的 npm 构建。具体详情可查阅[官方 npm 文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)。
## 使用方法
需要小程序基础库版本 >= 2.6.1 的环境。
你可以直接体验一下这个代码片段,它包含了基本用法示例:[https://developers.weixin.qq.com/s/kKu4U9mX78a8](https://developers.weixin.qq.com/s/kKu4U9mX78a8)
### 安装
```
npm install --save miniprogram-computed
```
### computed 基本用法
```js
const computedBehavior = require('miniprogram-computed')
Component({
behaviors: [computedBehavior],
data: {
a: 1,
b: 1,
},
computed: {
sum(data) {
// 注意: computed 函数中不能访问 this ,只有 data 对象可供访问
// 这个函数的返回值会被设置到 this.data.sum 字段中
return data.a + data.b
},
},
methods: {
onTap() {
this.setData({
a: this.data.b,
b: this.data.a + this.data.b,
})
}
}
})
```
```xml
<view>A = {{a}}</view>
<view>B = {{b}}</view>
<view>SUM = {{sum}}</view>
<button bindtap="onTap">click</button>
```
### watch 基本用法
```js
const computedBehavior = require('miniprogram-computed')
Component({
behaviors: [computedBehavior],
data: {
a: 1,
b: 1,
sum: 2,
},
watch: {
'a, b': function(a, b) {
this.setData({
sum: a + b
})
},
},
methods: {
onTap() {
this.setData({
a: this.data.b,
b: this.data.a + this.data.b,
})
}
}
})
```
```xml
<view>A = {{a}}</view>
<view>B = {{b}}</view>
<view>SUM = {{sum}}</view>
<button bindtap="onTap">click</button>
```
## ^1.0.0 与 ^2.0.0 版本差异
这个 behavior 的 ^1.0.0 版本和 ^2.0.0 版本有较大差异。 ^2.0.0 版本基于小程序基础库 2.6.1 开始支持的 observers 定义段实现,具有较好的性能。以下是版本之间主要区别的比较。
| 项目 | ^1.0.0 | ^2.0.0 |
| ---- | ------ | ------ |
| 支持的基础库最低版本 | 2.2.3 | 2.6.1 |
| 支持 `watch` 定义段 | 否 | 是 |
| 性能 | 相对较差 | 相对较好 |
## 常见问题说明
### 我应该使用 computed 还是 watch ?
从原理上说, `watch` 的性能比 `computed` 更好;但 `computed` 的用法更简洁干净。
此外, `computed` 字段状态只能依赖于 `data` 和其他 `computed` 字段,不能访问 `this` 。如果不可避免要访问 `this` ,则必须使用 `watch` 代替。
### watch 和小程序基础库本身的 observers 有什么区别?
* 无论字段是否真的改变, `observers` 都会被触发,而 `watch` 只在字段值改变了的时候触发,并且触发时带有参数。
### 关于 ** 通配符
`watch` 字段上可以使用 `**` 通配符,是它能够监听这个字段下的子字段的变化(类似于小程序基础库本身的 observers)。[示例代码片段](https://developers.weixin.qq.com/s/wTO9PcmQ7icM)
```js
const computedBehavior = require('miniprogram-computed')
Component({
behaviors: [computedBehavior],
data: {
obj: {
a: 1,
b: 2,
}
},
watch: {
'obj.**': function(obj) {
this.setData({
sum: obj.a + obj.b
})
},
},
methods: {
onTap() {
this.setData({
'obj.a': 10
})
}
}
})
```
除此以外:
* 对于没有使用 `**` 通配符的字段,在 `watch` 检查值是否发生变化时,只会进行粗略的浅比较(使用 `===` );
* 对于使用了 `**` 通配符的字段,则会进行深比较,来尝试精确检测对象是否真的发生了变化,这要求对象字段不能包含循环(类似于 `JSON.stringify` )。
## 0.0.6
* 支持 properties。
## 0.0.7
* 修复 setData 设置 properties 会报 can't call setData in computed getter function! 问题
## 2.0.0
* 基于 observers 重构,开始支持 watch
## 2.1.0
* 支持 watch 的 `.**` 语法
const gulp = require('gulp')
const clean = require('gulp-clean')
const config = require('./tools/config')
const BuildTask = require('./tools/build')
const id = require('./package.json').name || 'miniprogram-custom-component'
// build task instance
// eslint-disable-next-line no-new
new BuildTask(id, config.entry)
// clean the generated folders and files
gulp.task('clean', gulp.series(() => gulp.src(config.distPath, {read: false, allowEmpty: true}).pipe(clean()), done => {
if (config.isDev) {
return gulp.src(config.demoDist, {read: false, allowEmpty: true})
.pipe(clean())
}
return done()
}))
// watch files and build
gulp.task('watch', gulp.series(`${id}-watch`))
// build for develop
gulp.task('dev', gulp.series(`${id}-dev`))
// build for publish
gulp.task('default', gulp.series(`${id}-default`))
This diff is collapsed.
{
"_from": "miniprogram-computed",
"_id": "miniprogram-computed@2.1.1",
"_inBundle": false,
"_integrity": "sha512-qf40nbcY8/vOqkKnAepKQY+mJn85sn7gsj8+RZI5hSmLYm0MicM25aQFjwrvjDAqASrfM1dSU9HMMyeFsh0XXQ==",
"_location": "/miniprogram-computed",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "miniprogram-computed",
"name": "miniprogram-computed",
"escapedName": "miniprogram-computed",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/miniprogram-computed/-/miniprogram-computed-2.1.1.tgz",
"_shasum": "c393e967a8f6e5ee26a4205f69ae3eb9a23b7b07",
"_spec": "miniprogram-computed",
"_where": "F:\\wechatapp\\wechatapp.shangjiadao.com",
"author": {
"name": "wechat-miniprogram"
},
"bugs": {
"url": "https://github.com/wechat-miniprogram/computed/issues"
},
"bundleDependencies": false,
"dependencies": {
"fast-deep-equal": "^2.0.1",
"rfdc": "^1.1.4"
},
"deprecated": false,
"description": "Computed & watch - wechat miniprogram custom component extend behavior",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-module-resolver": "^3.2.0",
"babel-preset-env": "^1.7.0",
"colors": "^1.3.1",
"eslint": "^5.14.1",
"eslint-config-airbnb-base": "13.1.0",
"eslint-loader": "^2.1.2",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^3.8.0",
"gulp": "^4.0.0",
"gulp-clean": "^0.4.0",
"gulp-if": "^2.0.2",
"gulp-install": "^1.1.0",
"gulp-less": "^4.0.1",
"gulp-rename": "^1.4.0",
"gulp-sourcemaps": "^2.6.5",
"jest": "^23.5.0",
"miniprogram-simulate": "^1.0.7",
"through2": "^2.0.3",
"vinyl": "^2.2.0",
"webpack": "^4.29.5",
"webpack-node-externals": "^1.7.2"
},
"homepage": "https://github.com/wechat-miniprogram/computed#readme",
"jest": {
"testEnvironment": "jsdom",
"testURL": "https://jest.test",
"collectCoverageFrom": [
"src/**/*.js"
],
"moduleDirectories": [
"node_modules",
"src"
]
},
"license": "MIT",
"main": "miniprogram_dist/index.js",
"miniprogram": "miniprogram_dist",
"name": "miniprogram-computed",
"repository": {
"type": "git",
"url": "git+https://github.com/wechat-miniprogram/computed.git"
},
"scripts": {
"build": "gulp",
"clean": "gulp clean",
"clean-dev": "gulp clean --develop",
"coverage": "jest ./test/* --coverage --bail",
"dev": "gulp dev --develop",
"dist": "npm run build",
"lint": "eslint \"src/**/*.js\"",
"lint-tools": "eslint \"tools/**/*.js\" --rule \"import/no-extraneous-dependencies: false\"",
"test": "jest ./test/* --bail",
"watch": "gulp watch --develop --watch"
},
"version": "2.1.1"
}
const deepClone = require('rfdc')({proto: true})
const deepEqual = require('fast-deep-equal')
const dataPath = require('./data-path')
const dataTracer = require('./data-tracer')
const TYPES = [String, Number, Boolean, Object, Array, null]
const TYPE_DEFAULT_VALUES = ['', 0, false, null, [], null]
const getDataOnPath = function (data, path) {
let ret = data
path.forEach((s) => {
if (typeof ret !== 'object' || ret === null) ret = undefined
else ret = ret[s]
})
return ret
}
const setDataOnPath = function (data, path, value) {
let cur = data
let index = 0
while (index < path.length - 1) {
const s = path[index++]
if (typeof s === 'number') {
if (!(cur[s] instanceof Array)) {
cur[s] = []
}
} else if (typeof cur[s] !== 'object' || cur[s] === null) {
cur[s] = {}
}
cur = cur[s]
}
cur[path[index]] = value
}
const getDataDefinition = function (data, properties) {
const ret = {}
Object.keys(data).forEach((key) => {
ret[key] = data[key]
})
if (properties) {
Object.keys(properties).forEach((key) => {
let value = null
const def = properties[key]
const typeIndex = TYPES.indexOf(def)
if (typeIndex >= 0) {
value = TYPE_DEFAULT_VALUES[typeIndex]
} else if (def.value) {
value = def.value
} else {
const typeIndex = TYPES.indexOf(def.type)
if (typeIndex >= 0) {
value = TYPE_DEFAULT_VALUES[typeIndex]
}
}
ret[key] = value
})
}
return ret
}
exports.behavior = Behavior({
lifetimes: {
created() {
this._initComputedWatchInfo()
}
},
definitionFilter(defFields) {
const computedDef = defFields.computed || {}
const watchDef = defFields.watch || {}
const observersItems = []
if (!defFields.methods) {
defFields.methods = {}
}
if (!defFields.data) {
defFields.data = {}
}
if (defFields.methods._initComputedWatchInfo) {
throw new Error('Please do not use this behavior more than once in a single component')
}
// initialize status, executed on created
const initFuncs = []
defFields.methods._initComputedWatchInfo = function () {
if (this._computedWatchInfo) return
this._computedWatchInfo = {
computedRelatedPathValues: {},
watchCurVal: {},
}
initFuncs.forEach((func) => func.call(this))
}
// handling computed
const computedUpdaters = []
Object.keys(computedDef).forEach((targetField) => {
const {path: targetPath} = dataPath.parseSingleDataPath(targetField)
const updateMethod = computedDef[targetField]
// update value and calculate related paths
const updateValueAndRelatedPaths = function () {
const oldPathValues = this._computedWatchInfo.computedRelatedPathValues[targetField]
let needUpdate = false
for (let i = 0; i < oldPathValues.length; i++) {
const {path, value: oldVal} = oldPathValues[i]
const curVal = getDataOnPath(this.data, path)
if (oldVal !== curVal) {
needUpdate = true
break
}
}
if (!needUpdate) return false
const relatedPathValues = []
const val = updateMethod(dataTracer.create(this.data, relatedPathValues))
this.setData({
[targetField]: val
})
this._computedWatchInfo.computedRelatedPathValues[targetField] = relatedPathValues
return true
}
// calculate value on registration
const relatedPathValuesOnDef = []
const initData = getDataDefinition(defFields.data, defFields.properties)
const val = updateMethod(dataTracer.create(initData, relatedPathValuesOnDef))
setDataOnPath(defFields.data, targetPath, dataTracer.unwrap(val))
initFuncs.push(function () {
const pathValues = relatedPathValuesOnDef.map(({path}) => ({
path,
value: getDataOnPath(this.data, path)
}))
this._computedWatchInfo.computedRelatedPathValues[targetField] = pathValues
})
// calculate value on setData
computedUpdaters.push(updateValueAndRelatedPaths)
})
if (computedUpdaters.length) {
// add a single observer for all computed fields
observersItems.push({
fields: '**',
observer() {
if (!this._computedWatchInfo) return
let changed
do {
changed = false
// eslint-disable-next-line no-loop-func
computedUpdaters.forEach((func) => {
if (func.call(this)) changed = true
})
} while (changed)
}
})
}
// handling watch
Object.keys(watchDef).forEach((watchPath) => {
const paths = dataPath.parseMultiDataPaths(watchPath)
// record the original value of watch targets
initFuncs.push(function () {
const curVal = paths.map(({path, options}) => {
const val = getDataOnPath(this.data, path)
return options.deepCmp ? deepClone(val) : val
})
this._computedWatchInfo.watchCurVal[watchPath] = curVal
})
// add watch observer
observersItems.push({
fields: watchPath,
observer() {
if (!this._computedWatchInfo) return
const oldVal = this._computedWatchInfo.watchCurVal[watchPath]
const originalCurValWithOptions = paths.map(({path, options}) => {
const val = getDataOnPath(this.data, path)
return {
val,
options,
}
})
const curVal = originalCurValWithOptions.map(({val, options}) => (
options.deepCmp ? deepClone(val) : val
))
this._computedWatchInfo.watchCurVal[watchPath] = curVal
let changed = false
for (let i = 0; i < curVal.length; i++) {
const options = paths[i].options
const deepCmp = options.deepCmp
if (deepCmp ? !deepEqual(oldVal[i], curVal[i]) : oldVal[i] !== curVal[i]) {
changed = true
break
}
}
if (changed) {
watchDef[watchPath].apply(this, originalCurValWithOptions.map(({val}) => val))
}
}
})
})
// register to observers
if (typeof defFields.observers !== 'object') {
defFields.observers = {}
}
if (defFields.observers instanceof Array) {
defFields.observers.push(...observersItems)
} else {
observersItems.forEach((item) => {
defFields.observers[item.fields] = item.observer
})
}
},
})
const WHITE_SPACE_CHAR_REGEXP = /^\s/
const throwParsingError = function (path, index) {
throw new Error('Parsing data path "' + path + '" failed at char "' + path[index] + '" (index ' + index + ')')
}
const parseArrIndex = function (path, state) {
const startIndex = state.index
while (state.index < state.length) {
const ch = path[state.index]
if (/^[0-9]/.test(ch)) {
state.index++
continue
}
break
}
if (startIndex === state.index) {
throwParsingError(path, state.index)
}
return parseInt(path.slice(startIndex, state.index), 10)
}
const parseIdent = function (path, state) {
const startIndex = state.index
const ch = path[startIndex]
if (/^[_a-zA-Z$]/.test(ch)) {
state.index++
while (state.index < state.length) {
const ch = path[state.index]
if (/^[_a-zA-Z0-9$]/.test(ch)) {
state.index++
continue
}
break
}
} else {
throwParsingError(path, state.index)
}
return path.slice(startIndex, state.index)
}
const parseSinglePath = function (path, state) {
const paths = [parseIdent(path, state)]
const options = {
deepCmp: false
}
while (state.index < state.length) {
const ch = path[state.index]
if (ch === '[') {
state.index++
paths.push(parseArrIndex(path, state))
const nextCh = path[state.index]
if (nextCh !== ']') throwParsingError(path, state.index)
state.index++
} else if (ch === '.') {
state.index++
const ch = path[state.index]
if (ch === '*') {
state.index++
const ch = path[state.index]
if (ch === '*') {
state.index++
options.deepCmp = true
break
}
throwParsingError(path, state.index)
}
paths.push(parseIdent(path, state))
} else {
break
}
}
return {path: paths, options}
}
const parseMultiPaths = function (path, state) {
while (WHITE_SPACE_CHAR_REGEXP.test(path[state.index])) {
state.index++
}
const ret = [parseSinglePath(path, state)]
let splitted = false
while (state.index < state.length) {
const ch = path[state.index]
if (WHITE_SPACE_CHAR_REGEXP.test(ch)) {
state.index++
} else if (ch === ',') {
splitted = true
state.index++
} else if (splitted) {
splitted = false
ret.push(parseSinglePath(path, state))
} else {
throwParsingError(path, state.index)
}
}
return ret
}
const parseEOF = function (path, state) {
if (state.index < state.length) throwParsingError(path, state.index)
}
exports.parseSingleDataPath = function (path) {
const state = {
length: path.length,
index: 0
}
const ret = parseSinglePath(path, state)
parseEOF(path, state)
return ret
}
exports.parseMultiDataPaths = function (path) {
const state = {
length: path.length,
index: 0
}
const ret = parseMultiPaths(path, state)
parseEOF(path, state)
return ret
}
const wrapData = (data, relatedPathValues, basePath) => {
if (typeof data !== 'object' || data === null) return data
const isArray = data instanceof Array
const propDef = {}
Object.keys(data).forEach((key) => {
let keyWrapper = null
propDef[key] = {
get() {
if (!keyWrapper) {
const keyPath = basePath.concat(key)
relatedPathValues.push({
path: keyPath,
value: data[key]
})
keyWrapper = wrapData(data[key], relatedPathValues, keyPath)
}
return keyWrapper
},
set() {
throw new Error('Setting data is not allowed')
},
enumerable: true
}
})
if (isArray) {
propDef.length = {
value: data.length,
enumerable: false
}
}
propDef.__rawObject__ = {
get() {
return data
},
set() {
throw new Error('Setting data is not allowed')
},
enumerable: false
}
const proto = isArray ? Array.prototype : Object.prototype
return Object.create(proto, propDef)
}
exports.create = (data, relatedPathValues) => wrapData(data, relatedPathValues, [])
exports.unwrap = (wrapped) => {
if (typeof wrapped !== 'object' || wrapped === null || typeof wrapped.__rawObject__ !== 'object') {
return wrapped
}
return wrapped.__rawObject__
}
module.exports = require('./behavior').behavior
Copyright 2019 "David Mark Clements <david.mark.clements@gmail.com>"
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
'use strict'
module.exports = rfdc
function rfdc (opts) {
opts = opts || {}
if (opts.circles) return rfdcCircles(opts)
return opts.proto ? cloneProto : clone
function cloneArray (a, fn) {
var keys = Object.keys(a)
var a2 = new Array(keys.length)
for (var i = 0; i < keys.length; i++) {
var k = keys[i]
var cur = a[k]
if (typeof cur !== 'object' || cur === null) {
a2[k] = cur
} else if (cur instanceof Date) {
a2[k] = new Date(cur)
} else {
a2[k] = fn(cur)
}
}
return a2
}
function clone (o) {
if (typeof o !== 'object' || o === null) return o
if (o instanceof Date) return new Date(o)
if (Array.isArray(o)) return cloneArray(o, clone)
var o2 = {}
for (var k in o) {
if (Object.hasOwnProperty.call(o, k) === false) continue
var cur = o[k]
if (typeof cur !== 'object' || cur === null) {
o2[k] = cur
} else if (cur instanceof Date) {
o2[k] = new Date(cur)
} else {
o2[k] = clone(cur)
}
}
return o2
}
function cloneProto (o) {
if (typeof o !== 'object' || o === null) return o
if (o instanceof Date) return new Date(o)
if (Array.isArray(o)) return cloneArray(o, cloneProto)
var o2 = {}
for (var k in o) {
var cur = o[k]
if (typeof cur !== 'object' || cur === null) {
o2[k] = cur
} else if (cur instanceof Date) {
o2[k] = new Date(cur)
} else {
o2[k] = cloneProto(cur)
}
}
return o2
}
}
function rfdcCircles (opts) {
var refs = []
var refsNew = []
return opts.proto ? cloneProto : clone
function cloneArray (a, fn) {
var keys = Object.keys(a)
var a2 = new Array(keys.length)
for (var i = 0; i < keys.length; i++) {
var k = keys[i]
var cur = a[k]
if (typeof cur !== 'object' || cur === null) {
a2[k] = cur
} else if (cur instanceof Date) {
a2[k] = new Date(cur)
} else {
var index = refs.indexOf(cur)
if (index !== -1) {
a2[k] = refsNew[index]
} else {
a2[k] = fn(cur)
}
}
}
return a2
}
function clone (o) {
if (typeof o !== 'object' || o === null) return o
if (o instanceof Date) return new Date(o)
if (Array.isArray(o)) return cloneArray(o, clone)
var o2 = {}
refs.push(o)
refsNew.push(o2)
for (var k in o) {
if (Object.hasOwnProperty.call(o, k) === false) continue
var cur = o[k]
if (typeof cur !== 'object' || cur === null) {
o2[k] = cur
} else if (cur instanceof Date) {
o2[k] = new Date(cur)
} else {
var i = refs.indexOf(cur)
if (i !== -1) {
o2[k] = refsNew[i]
} else {
o2[k] = clone(cur)
}
}
}
refs.pop()
refsNew.pop()
return o2
}
function cloneProto (o) {
if (typeof o !== 'object' || o === null) return o
if (o instanceof Date) return new Date(o)
if (Array.isArray(o)) return cloneArray(o, cloneProto)
var o2 = {}
refs.push(o)
refsNew.push(o2)
for (var k in o) {
var cur = o[k]
if (typeof cur !== 'object' || cur === null) {
o2[k] = cur
} else if (cur instanceof Date) {
o2[k] = new Date(cur)
} else {
var i = refs.indexOf(cur)
if (i !== -1) {
o2[k] = refsNew[i]
} else {
o2[k] = cloneProto(cur)
}
}
}
refs.pop()
refsNew.pop()
return o2
}
}
{
"_from": "rfdc@^1.1.4",
"_id": "rfdc@1.1.4",
"_inBundle": false,
"_integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==",
"_location": "/rfdc",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "rfdc@^1.1.4",
"name": "rfdc",
"escapedName": "rfdc",
"rawSpec": "^1.1.4",
"saveSpec": null,
"fetchSpec": "^1.1.4"
},
"_requiredBy": [
"/miniprogram-computed"
],
"_resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
"_shasum": "ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2",
"_spec": "rfdc@^1.1.4",
"_where": "F:\\wechatapp\\wechatapp.shangjiadao.com\\node_modules\\miniprogram-computed",
"author": {
"name": "David Mark Clements",
"email": "david.clements@nearform.com"
},
"bugs": {
"url": "https://github.com/davidmarkclements/rfdc/issues"
},
"bundleDependencies": false,
"dependencies": {},
"deprecated": false,
"description": "Really Fast Deep Clone",
"devDependencies": {
"codecov": "^3.4.0",
"deep-copy": "^1.4.2",
"fast-copy": "^1.2.1",
"fastbench": "^1.0.1",
"lodash.clonedeep": "^4.5.0",
"standard": "^11.0.1",
"tap": "^12.0.1"
},
"directories": {
"test": "test"
},
"homepage": "https://github.com/davidmarkclements/rfdc#readme",
"keywords": [
"object",
"obj",
"properties",
"clone",
"copy",
"deep",
"recursive",
"key",
"keys",
"values",
"prop",
"deep-clone",
"deepclone",
"deep-copy",
"deepcopy",
"fast",
"performance",
"performant",
"fastclone",
"fastcopy",
"fast-clone",
"fast-deep-clone",
"fast-copy",
"fast-deep-copy"
],
"license": "MIT",
"main": "index.js",
"name": "rfdc",
"repository": {
"type": "git",
"url": "git+https://github.com/davidmarkclements/rfdc.git"
},
"scripts": {
"bench": "node benchmark",
"ci": "standard && tap --100 --coverage-report=text-lcov test | codecov --pipe",
"cov": "tap --100 test",
"cov-ui": "tap --coverage-report=html test",
"lint": "standard --fix",
"test": "tap -R min test && npm run lint"
},
"version": "1.1.4"
}
# rfdc
Really Fast Deep Clone
[![build status](https://img.shields.io/travis/davidmarkclements/rfdc.svg)](https://travis-ci.org/davidmarkclements/rfdc)
[![coverage](https://img.shields.io/codecov/c/github/davidmarkclements/rfdc.svg)](https://codecov.io/gh/davidmarkclements/rfdc)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/)
## Usage
```js
const clone = require('rfdc')()
clone({a: 1, b: {c: 2}}) // => {a: 1, b: {c: 2}}
```
## API
### `require('rfdc')(opts = { proto: false, circles: false }) => clone(obj) => obj2`
#### `proto` option
Copy prototype properties as well as own properties into the new object.
It's marginally faster to allow enumerable properties on the prototype
to be copied into the cloned object (not onto it's prototype, directly onto the object).
To explain by way of code:
```js
require('rfdc')({ proto: false })(Object.create({a: 1})) // => {}
require('rfdc')({ proto: true })(Object.create({a: 1})) // => {a: 1}
```
Setting `proto` to `true` will provide an additional 2% performance boost.
#### `circles` option
Keeping track of circular references will slow down performance with an
additional 25% overhead. Even if an object doesn't have any circular references,
the tracking overhead is the cost. By default if an object with a circular
reference is passed to `rfdc`, it will throw (similar to how `JSON.stringify` \
would throw).
Use the `circles` option to detect and preserve circular references in the
object. If performance is important, try removing the circular reference from
the object (set to `undefined`) and then add it back manually after cloning
instead of using this option.
### Types
`rdfc` clones all JSON types:
* `Object`
* `Array`
* `Number`
* `String`
* `null`
With additional support for:
* `Date` (copied)
* `undefined` (copied)
* `Function` (referenced)
* `AsyncFunction` (referenced)
* `GeneratorFunction` (referenced)
* `arguments` (copied to a normal object)
All other types have output values that match the output
of `JSON.parse(JSON.stringify(o))`.
For instance:
```js
const rdfc = require('rdfc')()
const err = Error()
err.code = 1
JSON.parse(JSON.stringify(e)) // {code: 1}
rdfc(e) // {code: 1}
JSON.parse(JSON.stringify(new Uint8Array([1, 2, 3]))) // {'0': 1, '1': 2, '2': 3 }
rdfc(new Uint8Array([1, 2, 3])) // {'0': 1, '1': 2, '2': 3 }
JSON.parse(JSON.stringify({rx: /foo/})) // {rx: {}}
rdfc({rx: /foo/}) // {rx: {}}
```
## Benchmarks
```sh
npm run bench
```
```
benchDeepCopy*100: 549.618ms
benchLodashCloneDeep*100: 1461.134ms
benchFastCopy*100: 878.146ms
benchRfdc*100: 323.899ms
benchRfdcProto*100: 314.136ms
benchRfdcCircles*100: 384.561ms
benchRfdcCirclesProto*100: 381.775ms
```
## Tests
```sh
npm test
```
```
169 passing (342.514ms)
```
### Coverage
```sh
npm run cov
```
```
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
```
## License
MIT
'use strict'
const { test } = require('tap')
const rfdc = require('..')
const clone = rfdc()
const cloneProto = rfdc({proto: true})
const cloneCircles = rfdc({circles: true})
const cloneCirclesProto = rfdc({circles: true, proto: true})
types(clone, 'default')
types(cloneProto, 'proto option')
types(cloneCircles, 'circles option')
types(cloneCirclesProto, 'circles and proto option')
test('default – does not copy proto properties', async ({is}) => {
is(clone(Object.create({a: 1})).a, undefined, 'value not copied')
})
test('proto option – copies enumerable proto properties', async ({is}) => {
is(cloneProto(Object.create({a: 1})).a, 1, 'value copied')
})
test('circles option - circular object', async ({same, is, isNot}) => {
const o = {nest: {a: 1, b: 2}}
o.circular = o
same(cloneCircles(o), o, 'same values')
isNot(cloneCircles(o), o, 'different objects')
isNot(cloneCircles(o).nest, o.nest, 'different nested objects')
const c = cloneCircles(o)
is(c.circular, c, 'circular references point to copied parent')
isNot(c.circular, o, 'circular references do not point to original parent')
})
test('circles option – deep circular object', async ({same, is, isNot}) => {
const o = {nest: {a: 1, b: 2}}
o.nest.circular = o
same(cloneCircles(o), o, 'same values')
isNot(cloneCircles(o), o, 'different objects')
isNot(cloneCircles(o).nest, o.nest, 'different nested objects')
const c = cloneCircles(o)
is(c.nest.circular, c, 'circular references point to copied parent')
isNot(c.nest.circular, o, 'circular references do not point to original parent')
})
test('circles option alone – does not copy proto properties', async ({is}) => {
is(cloneCircles(Object.create({a: 1})).a, undefined, 'value not copied')
})
test('circles and proto option – copies enumerable proto properties', async ({is}) => {
is(cloneCirclesProto(Object.create({a: 1})).a, 1, 'value copied')
})
test('circles and proto option - circular object', async ({same, is, isNot}) => {
const o = {nest: {a: 1, b: 2}}
o.circular = o
same(cloneCirclesProto(o), o, 'same values')
isNot(cloneCirclesProto(o), o, 'different objects')
isNot(cloneCirclesProto(o).nest, o.nest, 'different nested objects')
const c = cloneCirclesProto(o)
is(c.circular, c, 'circular references point to copied parent')
isNot(c.circular, o, 'circular references do not point to original parent')
})
test('circles and proto option – deep circular object', async ({same, is, isNot}) => {
const o = {nest: {a: 1, b: 2}}
o.nest.circular = o
same(cloneCirclesProto(o), o, 'same values')
isNot(cloneCirclesProto(o), o, 'different objects')
isNot(cloneCirclesProto(o).nest, o.nest, 'different nested objects')
const c = cloneCirclesProto(o)
is(c.nest.circular, c, 'circular references point to copied parent')
isNot(c.nest.circular, o, 'circular references do not point to original parent')
})
test('circles and proto option – deep circular array', async ({same, is, isNot}) => {
const o = { nest: [1, 2] }
o.nest.push(o)
same(cloneCirclesProto(o), o, 'same values')
isNot(cloneCirclesProto(o), o, 'different objects')
isNot(cloneCirclesProto(o).nest, o.nest, 'different nested objects')
const c = cloneCirclesProto(o)
is(c.nest[2], c, 'circular references point to copied parent')
isNot(c.nest[2], o, 'circular references do not point to original parent')
})
function types (clone, label) {
test(label + ' – number', async ({is}) => {
is(clone(42), 42, 'same value')
})
test(label + ' – string', async ({is}) => {
is(clone('str'), 'str', 'same value')
})
test(label + ' – boolean', async ({is}) => {
is(clone(true), true, 'same value')
})
test(label + ' – function', async ({is}) => {
const fn = () => {}
is(clone(fn), fn, 'same function')
})
test(label + ' – async function', async ({is}) => {
const fn = async () => {}
is(clone(fn), fn, 'same function')
})
test(label + ' – generator function', async ({is}) => {
const fn = function * () {}
is(clone(fn), fn, 'same function')
})
test(label + ' – date', async ({is, isNot}) => {
const date = new Date()
is(+clone(date), +date, 'same value')
isNot(clone(date), date, 'different object')
})
test(label + ' – null', async ({is}) => {
is(clone(null), null, 'same value')
})
test(label + ' – shallow object', async ({same, isNot}) => {
const o = {a: 1, b: 2}
same(clone(o), o, 'same values')
isNot(clone(o), o, 'different object')
})
test(label + ' – shallow array', async ({same, isNot}) => {
const o = [1, 2]
same(clone(o), o, 'same values')
isNot(clone(o), o, 'different arrays')
})
test(label + ' – deep object', async ({same, isNot}) => {
const o = {nest: {a: 1, b: 2}}
same(clone(o), o, 'same values')
isNot(clone(o), o, 'different objects')
isNot(clone(o).nest, o.nest, 'different nested objects')
})
test(label + ' – deep array', async ({same, isNot}) => {
const o = [ {a: 1, b: 2}, [3] ]
same(clone(o), o, 'same values')
isNot(clone(o), o, 'different arrays')
isNot(clone(o)[0], o[0], 'different array elements')
isNot(clone(o)[1], o[1], 'different array elements')
})
test(label + ' – nested number', async ({is}) => {
is(clone({a: 1}).a, 1, 'same value')
})
test(label + ' – nested string', async ({is}) => {
is(clone({s: 'str'}).s, 'str', 'same value')
})
test(label + ' – nested boolean', async ({is}) => {
is(clone({b: true}).b, true, 'same value')
})
test(label + ' – nested function', async ({is}) => {
const fn = () => {}
is(clone({fn}).fn, fn, 'same function')
})
test(label + ' – nested async function', async ({is}) => {
const fn = async () => {}
is(clone({fn}).fn, fn, 'same function')
})
test(label + ' – nested generator function', async ({is}) => {
const fn = function * () {}
is(clone({fn}).fn, fn, 'same function')
})
test(label + ' – nested date', async ({is, isNot}) => {
const date = new Date()
is(+clone({d: date}).d, +date, 'same value')
isNot(clone({d: date}).d, date, 'different object')
})
test(label + ' – nested date in array', async ({is, isNot}) => {
const date = new Date()
is(+clone({d: [date]}).d[0], +date, 'same value')
isNot(clone({d: [date]}).d[0], date, 'different object')
is(+cloneCircles({d: [date]}).d[0], +date, 'same value')
isNot(cloneCircles({d: [date]}).d, date, 'different object')
})
test(label + ' – nested null', async ({is}) => {
is(clone({n: null}).n, null, 'same value')
})
test(label + ' – arguments', async ({isNot, same}) => {
function fn (...args) {
same(clone(arguments), args, 'same values')
isNot(clone(arguments), arguments, 'different object')
}
fn(1, 2, 3)
})
}
{
"name": "wechatapp.shangjiadao.com",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
},
"miniprogram-computed": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/miniprogram-computed/-/miniprogram-computed-2.1.1.tgz",
"integrity": "sha512-qf40nbcY8/vOqkKnAepKQY+mJn85sn7gsj8+RZI5hSmLYm0MicM25aQFjwrvjDAqASrfM1dSU9HMMyeFsh0XXQ==",
"requires": {
"fast-deep-equal": "^2.0.1",
"rfdc": "^1.1.4"
}
},
"rfdc": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
"integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug=="
}
}
}
{
"name": "wechatapp.shangjiadao.com",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git@git.server:wangxuelai/wechatapp.shangjiadao.com.git"
},
"author": "",
"license": "ISC",
"dependencies": {
"miniprogram-computed": "^2.1.1"
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment