首先,我们先把全局的颜色样式设置好:

然后把 app.wxss 文件以及 style 目录下的 guide.wxss 文件中的内容清空;

轮播图组件

先在页面加载初始数据中,加入轮播图所要用到的图片地址数组:

然后在 wxml 文件中调用:

注意:为了适配每一台机器,我们在图片的标签上设置了类,以便于调整图片的大小:

组件化开发

组件

组件化开发的意义

设计原则

  • 高内聚
  • 低耦合
  • 单一职责
  • 避免过多参数

项目当中的组件

  • 歌单组件
  • 歌曲组件
  • 歌曲进度条组件
  • 歌词组件
  • 博客卡片组件
  • 博客控制组件
  • 底部弹窗组件
  • 登录组件
  • 搜索组件

自定义歌单组件

新建 playlist 组件,并且初始化:

先把歌单相关的数据加进 data 中:

详情如下:

{"_id":"08560c9e5d042a5c0174f1ca26f1d7b2","copywrier":"热门推荐","playCount":1.4641238e+06,"highQuality":false,"type":0.0,"canDislike":true,"name":"天气转热了,适合听点凉爽的歌。","alg":"cityLevel_unknow","createTime":{"$date":"2019-06-14T23:14:36.746Z"},"id":2.780381322e+09,"picUrl":"https://p2.music.126.net/Biky7TE4CtW6NjGuqoUKZg==/109951164041827987.jpg","trackCount":53.0},
{"_id":"08560c9e5d042a5c0174f1da7aa357aa","highQuality":false,"copywriter":"热门推荐","canDislike":true,"playCount":622822.6,"id":2.740107647e+09,"name":"「时空潜行」囿于昼夜的空想主义者","type":0.0,"alg":"cityLevel_unknow","createTime":{"$date":"2019-06-14T23:14:36.955Z"},"picUrl":"https://p2.music.126.net/Q0eS0avwGK04LufWM7qJug==/109951164116217181.jpg","trackCount":20.0},
{"_id":"08560c9e5d042a5c0174f1de21c7e79e","id":2.828842343e+09,"type":0.0,"name":"粤语情诗:与你听风声,观赏过夜星","picUrl":"https://p2.music.126.net/K9IcG8cU6v4_SwuQ_x2xMA==/109951164124604652.jpg","highQuality":false,"alg":"cityLevel_unknow","playCount":1.785097e+06,"trackCount":52.0,"copywriter":"热门推荐","canDislike":true,"createTime":{"$date":"2019-06-14T23:14:36.982Z"}},
{"_id":"08560c9e5d042a5d0174f1e67d1bb16f","playCount":7.719329e+06,"highQuality":false,"trackCount":950.0,"alg":"cityLevel_unknow","id":9.17794768e+08,"type":0.0,"name":"翻唱简史:日本四百首","canDislike":true,"createTime":{"$date":"2019-06-14T23:14:37.037Z"},"copywriter":"热门推荐","picUrl":"https://p2.music.126.net/NczCuurE5eVvObUjssoGjQ==/109951163788653124.jpg"},
{"_id":"08560c9e5d042a5d0174f1ea32c4c288","type":0.0,"copywriter":"热门推荐","highQuality":false,"createTime":{"$date":"2019-06-14T23:14:37.097Z"},"id":2.201879658e+09,"alg":"cityLevel_unknow","playCount":1.06749088e+08,"name":"你的青春里有没有属于你的一首歌?","picUrl":"https://p2.music.126.net/wpahk9cQCDtdzJPE52EzJQ==/109951163271025942.jpg","canDislike":true,"trackCount":169.0},
{"_id":"08560c9e5d0829820362a79f4b049d2d","alg":"cityLevel_unknow","name":"「乐队的夏天」参赛歌曲合集丨EP04更新","highQuality":false,"picUrl":"http://p2.music.126.net/2WE5C2EypEwLJd2qXFd4cw==/109951164086686815.jpg","trackCount":158.0,"createTime":{"$date":"2019-06-18T00:00:02.553Z"},"copywriter":"热门推荐","playCount":1.5742008e+06,"canDislike":true,"id":2.79477263e+09,"type":0.0}

然后在 playlist.wxml 中引用该组件并传输数据:

在组件的 playlist.js 文件中接收属性:

接下来编写 playlist.wxml 的代码:

<!--components/playlist/playlist.wxml-->
<view class="playlist-container"><image src="{{ playlist.picUrl }}" class="playlist-img"></image><text class="playlist-count">{{ playlist.playCount }}</text><view class="playlist-name">{{ playlist.name }}</view>
</view>

并完善 playlist.wxss 的代码:

/* components/playlist/playlist.wxss */
.playlist-container{width: 220rpx;position: relative;padding-bottom: 20rpx;
}.playlist-img{width: 100%;height: 220rpx;border-radius: 6rpx;
}.playlist-count{font-size: 24rpx;color: #fff;text-shadow: 1px 0 0 rgba(0, 0, 0, 0.15);position: absolute;right: 10rpx;top: 4rpx;padding-left: 40rpx;/* 在线转base64文件可以百度 */background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMiAyMCI+PGcgb3BhY2l0eT0iLjE1Ij48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiMwNDAwMDAiIGQ9Im0yMiAxNi43NzdjMCAxLjIzMy0xLjEyMSAyLjIzMy0yLjUwNiAyLjIzMy0xLjM4NCAwLTIuNTA2LTEtMi41MDYtMi4yMzN2LTIuNTUzYzAtMS4yMzQgMS4xMjItMi4yMzMgMi41MDYtMi4yMzMuMTc0IDAgLjM0My4wMTcuNTA2LjA0NnYtMS4zN2gtLjAzM2MuMDE3LS4yMi4wMzMtLjQ0MS4wMzMtLjY2NiAwLTQuNDE4LTMuNTgyLTgtOC04LTQuNDE4IDAtOCAzLjU4Mi04IDggMCAuMjI1LjAxNi40NDYuMDM0LjY2NmgtLjAzNHYxLjM3Yy4xNjMtLjAyOS4zMzMtLjA0Ni41MDUtLjA0NiAxLjM4NCAwIDIuNTA2Ljk5OSAyLjUwNiAyLjIzM3YyLjU1M2MwIDEuMjMzLTEuMTIyIDIuMjMzLTIuNTA2IDIuMjMzcy0yLjUwNS0uOTk5LTIuNTA1LTIuMjMzdi0yLjU1M2MwLS4yNTguMDU5LS41MDEuMTQ4LS43My0uMDg1LS4xNDgtLjE0OC0uMzEtLjE0OC0uNDkzdi0yLjY2N2MwLS4wMjMuMDEyLS4wNDMuMDEzLS4wNjctLjAwNC0uMDg4LS4wMTMtLjE3Ni0uMDEzLS4yNjYgMC01LjUyMyA0LjQ3Ny0xMCAxMC0xMCA1LjUyMyAwIDEwIDQuNDc3IDEwIDEwIDAgLjA5LS4wMDkuMTc4LS4wMTQuMjY2LjAwMi4wMjQuMDE0LjA0NC4wMTQuMDY3djJjMCAuMzA2LS4xNDUuNTY5LS4zNi43NTMuMjI0LjMzNC4zNi43Mi4zNiAxLjEzOHYyLjU1MiIvPjwvZz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNmZmYiIGQ9Im0yMCAxNi43NzdjMCAxLjIzMy0xLjEyMSAyLjIzMy0yLjUwNiAyLjIzMy0xLjM4NCAwLTIuNTA2LTEtMi41MDYtMi4yMzN2LTIuNTUzYzAtMS4yMzQgMS4xMjItMi4yMzMgMi41MDYtMi4yMzMuMTc0IDAgLjM0My4wMTcuNTA2LjA0NnYtMS4zN2gtLjAzM2MuMDE3LS4yMi4wMzMtLjQ0MS4wMzMtLjY2NiAwLTQuNDE4LTMuNTgyLTgtOC04LTQuNDE4IDAtOCAzLjU4Mi04IDggMCAuMjI1LjAxNi40NDYuMDM0LjY2NmgtLjAzNHYxLjM3Yy4xNjMtLjAyOS4zMzMtLjA0Ni41MDUtLjA0NiAxLjM4NCAwIDIuNTA2Ljk5OSAyLjUwNiAyLjIzM3YyLjU1M2MwIDEuMjMzLTEuMTIyIDIuMjMzLTIuNTA2IDIuMjMzcy0yLjUwNS0uOTk5LTIuNTA1LTIuMjMzdi0yLjU1M2MwLS4yNTguMDU5LS41MDEuMTQ4LS43My0uMDg1LS4xNDgtLjE0OC0uMzEtLjE0OC0uNDkzdi0yLjY2N2MwLS4wMjMuMDEyLS4wNDMuMDEzLS4wNjctLjAwNC0uMDg4LS4wMTMtLjE3Ni0uMDEzLS4yNjYgMC01LjUyMyA0LjQ3Ny0xMCAxMC0xMCA1LjUyMyAwIDEwIDQuNDc3IDEwIDEwIDAgLjA5LS4wMDkuMTc4LS4wMTQuMjY2LjAwMi4wMjQuMDE0LjA0NC4wMTQuMDY3djJjMCAuMzA2LS4xNDUuNTY5LS4zNi43NTMuMjI0LjMzNC4zNi43Mi4zNiAxLjEzOHYyLjU1MiIvPjwvc3ZnPg==);background-repeat: no-repeat;
}.playlist-name{font-size: 26rpx;line-height: 1.2;padding: 2px 0 0 6px;/* 设置最多显示两行,多出来的字显示为... */display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 2;overflow: hidden;text-overflow: ellipsis;
}

效果如图:

播放数量细节处理

这里可以注意到,播放次数的数字过于冗余,这时候我们需要做一个数字监听器:

首先我们先建立一个对象,取到播放次数的数据:

别忘了在 data 中定义次数:

最后在 methods 中写入方法:

/*** 组件的方法列表*/
methods: {_tranNumber(num, point){let numStr = num.toString().split('.')[0]if(numStr.length < 6){// 十万以下return numStr}else if(numStr.length >=6 && numStr.length <= 8){// 十万到千万之间// 千位和百位组成的小数点后的数let decimal = numStr.substring(numStr.length - 4, numStr.length - 4 + point)return parseFloat(parseInt(num / 10000) + '.' + decimal) + '万'}else if(numStr.length > 8){let decimal = numStr.substring(numStr.length - 8, numStr.length - 8 + point)return parseFloat(parseInt(num / 100000000) + '.' + decimal) + '亿'}}}

wxml 页面的参数别忘了修改:

详解 wx-key

用一个随机排序的例子来理解一下:

在 js 文件中定义一个数组:

data:{arr: ['wxml', 'js', 'wxss', 'json']
},sort(){const length = this.data.arr.lengthfor(let i=0; i<length; i++){const x = Math.floor(Math.random() * length)const y = Math.floor(Math.random() * length)const temp = this.data.arr[x]this.data.arr[x] = this.data.arr[y]this.data.arr[y] = temp}this.setData({arr: this.data.arr})
},

然后在 wxml 文件中:

<block wx:for="{{arr}}" wx:key="*this" wx:for-item="data" wx:for-index:"idx"><!-- <view>{{idx}}: {{data}}</view> --><view><checkbox/> {{data}}</view>
</block>
<button bind:tap="sort">随机排序</button>

所以,wx:key 可以帮助我们很好的绑定住我们的数据;

因此,我们需要改改前面的代码:

这样就可以啦;

详解 promise

promise 状态:

  • pending:初始状态
  • fulfilled:成功状态
  • rejected:失败状态

详解 async await

async await 实际上指的是 ES7 的语法;

async await 默认在云函数内是支持的,而在小程序端是不支持的;

让小程序端支持的方法:

先创建一个 utils 目录,在其目录下创建一个 runtime.js:

/*** Copyright (c) 2014-present, Facebook, Inc.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.*/!(function(global) {// "use strict";var Op = Object.prototype;var hasOwn = Op.hasOwnProperty;var undefined; // More compressible than void 0.var $Symbol = typeof Symbol === "function" ? Symbol : {};var iteratorSymbol = $Symbol.iterator || "@@iterator";var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";var inModule = typeof module === "object";var runtime = global.regeneratorRuntime;if (runtime) {if (inModule) {// If regeneratorRuntime is defined globally and we're in a module,// make the exports object identical to regeneratorRuntime.module.exports = runtime;}// Don't bother evaluating the rest of this file if the runtime was// already defined globally.return;}// Define the runtime globally (as expected by generated code) as either// module.exports (if we're in a module) or a new, empty object.runtime = global.regeneratorRuntime = inModule ? module.exports : {};function wrap(innerFn, outerFn, self, tryLocsList) {// If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;var generator = Object.create(protoGenerator.prototype);var context = new Context(tryLocsList || []);// The ._invoke method unifies the implementations of the .next,// .throw, and .return methods.generator._invoke = makeInvokeMethod(innerFn, self, context);return generator;}runtime.wrap = wrap;// Try/catch helper to minimize deoptimizations. Returns a completion// record like context.tryEntries[i].completion. This interface could// have been (and was previously) designed to take a closure to be// invoked without arguments, but in all the cases we care about we// already have an existing method we want to call, so there's no need// to create a new function object. We can even get away with assuming// the method takes exactly one argument, since that happens to be true// in every case, so we don't have to touch the arguments object. The// only additional allocation required is the completion record, which// has a stable shape and so hopefully should be cheap to allocate.function tryCatch(fn, obj, arg) {try {return { type: "normal", arg: fn.call(obj, arg) };} catch (err) {return { type: "throw", arg: err };}}var GenStateSuspendedStart = "suspendedStart";var GenStateSuspendedYield = "suspendedYield";var GenStateExecuting = "executing";var GenStateCompleted = "completed";// Returning this object from the innerFn has the same effect as// breaking out of the dispatch switch statement.var ContinueSentinel = {};// Dummy constructor functions that we use as the .constructor and// .constructor.prototype properties for functions that return Generator// objects. For full spec compliance, you may wish to configure your// minifier not to mangle the names of these two functions.function Generator() {}function GeneratorFunction() {}function GeneratorFunctionPrototype() {}// This is a polyfill for %IteratorPrototype% for environments that// don't natively support it.var IteratorPrototype = {};IteratorPrototype[iteratorSymbol] = function () {return this;};var getProto = Object.getPrototypeOf;var NativeIteratorPrototype = getProto && getProto(getProto(values([])));if (NativeIteratorPrototype &&NativeIteratorPrototype !== Op &&hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {// This environment has a native %IteratorPrototype%; use it instead// of the polyfill.IteratorPrototype = NativeIteratorPrototype;}var Gp = GeneratorFunctionPrototype.prototype =Generator.prototype = Object.create(IteratorPrototype);GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;GeneratorFunctionPrototype.constructor = GeneratorFunction;GeneratorFunctionPrototype[toStringTagSymbol] =GeneratorFunction.displayName = "GeneratorFunction";// Helper for defining the .next, .throw, and .return methods of the// Iterator interface in terms of a single ._invoke method.function defineIteratorMethods(prototype) {["next", "throw", "return"].forEach(function(method) {prototype[method] = function(arg) {return this._invoke(method, arg);};});}runtime.isGeneratorFunction = function(genFun) {var ctor = typeof genFun === "function" && genFun.constructor;return ctor? ctor === GeneratorFunction ||// For the native GeneratorFunction constructor, the best we can// do is to check its .name property.(ctor.displayName || ctor.name) === "GeneratorFunction": false;};runtime.mark = function(genFun) {if (Object.setPrototypeOf) {Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);} else {genFun.__proto__ = GeneratorFunctionPrototype;if (!(toStringTagSymbol in genFun)) {genFun[toStringTagSymbol] = "GeneratorFunction";}}genFun.prototype = Object.create(Gp);return genFun;};// Within the body of any async function, `await x` is transformed to// `yield regeneratorRuntime.awrap(x)`, so that the runtime can test// `hasOwn.call(value, "__await")` to determine if the yielded value is// meant to be awaited.runtime.awrap = function(arg) {return { __await: arg };};function AsyncIterator(generator) {function invoke(method, arg, resolve, reject) {var record = tryCatch(generator[method], generator, arg);if (record.type === "throw") {reject(record.arg);} else {var result = record.arg;var value = result.value;if (value &&typeof value === "object" &&hasOwn.call(value, "__await")) {return Promise.resolve(value.__await).then(function(value) {invoke("next", value, resolve, reject);}, function(err) {invoke("throw", err, resolve, reject);});}return Promise.resolve(value).then(function(unwrapped) {// When a yielded Promise is resolved, its final value becomes// the .value of the Promise<{value,done}> result for the// current iteration.result.value = unwrapped;resolve(result);}, function(error) {// If a rejected Promise was yielded, throw the rejection back// into the async generator function so it can be handled there.return invoke("throw", error, resolve, reject);});}}var previousPromise;function enqueue(method, arg) {function callInvokeWithMethodAndArg() {return new Promise(function(resolve, reject) {invoke(method, arg, resolve, reject);});}return previousPromise =// If enqueue has been called before, then we want to wait until// all previous Promises have been resolved before calling invoke,// so that results are always delivered in the correct order. If// enqueue has not been called before, then it is important to// call invoke immediately, without waiting on a callback to fire,// so that the async generator function has the opportunity to do// any necessary setup in a predictable way. This predictability// is why the Promise constructor synchronously invokes its// executor callback, and why async functions synchronously// execute code before the first await. Since we implement simple// async functions in terms of async generators, it is especially// important to get this right, even though it requires care.previousPromise ? previousPromise.then(callInvokeWithMethodAndArg,// Avoid propagating failures to Promises returned by later// invocations of the iterator.callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();}// Define the unified helper method that is used to implement .next,// .throw, and .return (see defineIteratorMethods).this._invoke = enqueue;}defineIteratorMethods(AsyncIterator.prototype);AsyncIterator.prototype[asyncIteratorSymbol] = function () {return this;};runtime.AsyncIterator = AsyncIterator;// Note that simple async functions are implemented on top of// AsyncIterator objects; they just return a Promise for the value of// the final result produced by the iterator.runtime.async = function(innerFn, outerFn, self, tryLocsList) {var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList));return runtime.isGeneratorFunction(outerFn)? iter // If outerFn is a generator, return the full iterator.: iter.next().then(function(result) {return result.done ? result.value : iter.next();});};function makeInvokeMethod(innerFn, self, context) {var state = GenStateSuspendedStart;return function invoke(method, arg) {if (state === GenStateExecuting) {throw new Error("Generator is already running");}if (state === GenStateCompleted) {if (method === "throw") {throw arg;}// Be forgiving, per 25.3.3.3.3 of the spec:// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresumereturn doneResult();}context.method = method;context.arg = arg;while (true) {var delegate = context.delegate;if (delegate) {var delegateResult = maybeInvokeDelegate(delegate, context);if (delegateResult) {if (delegateResult === ContinueSentinel) continue;return delegateResult;}}if (context.method === "next") {// Setting context._sent for legacy support of Babel's// function.sent implementation.context.sent = context._sent = context.arg;} else if (context.method === "throw") {if (state === GenStateSuspendedStart) {state = GenStateCompleted;throw context.arg;}context.dispatchException(context.arg);} else if (context.method === "return") {context.abrupt("return", context.arg);}state = GenStateExecuting;var record = tryCatch(innerFn, self, context);if (record.type === "normal") {// If an exception is thrown from innerFn, we leave state ===// GenStateExecuting and loop back for another invocation.state = context.done? GenStateCompleted: GenStateSuspendedYield;if (record.arg === ContinueSentinel) {continue;}return {value: record.arg,done: context.done};} else if (record.type === "throw") {state = GenStateCompleted;// Dispatch the exception by looping back around to the// context.dispatchException(context.arg) call above.context.method = "throw";context.arg = record.arg;}}};}// Call delegate.iterator[context.method](context.arg) and handle the// result, either by returning a { value, done } result from the// delegate iterator, or by modifying context.method and context.arg,// setting context.delegate to null, and returning the ContinueSentinel.function maybeInvokeDelegate(delegate, context) {var method = delegate.iterator[context.method];if (method === undefined) {// A .throw or .return when the delegate iterator has no .throw// method always terminates the yield* loop.context.delegate = null;if (context.method === "throw") {if (delegate.iterator.return) {// If the delegate iterator has a return method, give it a// chance to clean up.context.method = "return";context.arg = undefined;maybeInvokeDelegate(delegate, context);if (context.method === "throw") {// If maybeInvokeDelegate(context) changed context.method from// "return" to "throw", let that override the TypeError below.return ContinueSentinel;}}context.method = "throw";context.arg = new TypeError("The iterator does not provide a 'throw' method");}return ContinueSentinel;}var record = tryCatch(method, delegate.iterator, context.arg);if (record.type === "throw") {context.method = "throw";context.arg = record.arg;context.delegate = null;return ContinueSentinel;}var info = record.arg;if (! info) {context.method = "throw";context.arg = new TypeError("iterator result is not an object");context.delegate = null;return ContinueSentinel;}if (info.done) {// Assign the result of the finished delegate to the temporary// variable specified by delegate.resultName (see delegateYield).context[delegate.resultName] = info.value;// Resume execution at the desired location (see delegateYield).context.next = delegate.nextLoc;// If context.method was "throw" but the delegate handled the// exception, let the outer generator proceed normally. If// context.method was "next", forget context.arg since it has been// "consumed" by the delegate iterator. If context.method was// "return", allow the original .return call to continue in the// outer generator.if (context.method !== "return") {context.method = "next";context.arg = undefined;}} else {// Re-yield the result returned by the delegate method.return info;}// The delegate iterator is finished, so forget it and continue with// the outer generator.context.delegate = null;return ContinueSentinel;}// Define Generator.prototype.{next,throw,return} in terms of the// unified ._invoke helper method.defineIteratorMethods(Gp);Gp[toStringTagSymbol] = "Generator";// A Generator should always return itself as the iterator object when the// @@iterator function is called on it. Some browsers' implementations of the// iterator prototype chain incorrectly implement this, causing the Generator// object to not be returned from this call. This ensures that doesn't happen.// See https://github.com/facebook/regenerator/issues/274 for more details.Gp[iteratorSymbol] = function() {return this;};Gp.toString = function() {return "[object Generator]";};function pushTryEntry(locs) {var entry = { tryLoc: locs[0] };if (1 in locs) {entry.catchLoc = locs[1];}if (2 in locs) {entry.finallyLoc = locs[2];entry.afterLoc = locs[3];}this.tryEntries.push(entry);}function resetTryEntry(entry) {var record = entry.completion || {};record.type = "normal";delete record.arg;entry.completion = record;}function Context(tryLocsList) {// The root entry object (effectively a try statement without a catch// or a finally block) gives us a place to store values thrown from// locations where there is no enclosing try statement.this.tryEntries = [{ tryLoc: "root" }];tryLocsList.forEach(pushTryEntry, this);this.reset(true);}runtime.keys = function(object) {var keys = [];for (var key in object) {keys.push(key);}keys.reverse();// Rather than returning an object with a next method, we keep// things simple and return the next function itself.return function next() {while (keys.length) {var key = keys.pop();if (key in object) {next.value = key;next.done = false;return next;}}// To avoid creating an additional object, we just hang the .value// and .done properties off the next function object itself. This// also ensures that the minifier will not anonymize the function.next.done = true;return next;};};function values(iterable) {if (iterable) {var iteratorMethod = iterable[iteratorSymbol];if (iteratorMethod) {return iteratorMethod.call(iterable);}if (typeof iterable.next === "function") {return iterable;}if (!isNaN(iterable.length)) {var i = -1, next = function next() {while (++i < iterable.length) {if (hasOwn.call(iterable, i)) {next.value = iterable[i];next.done = false;return next;}}next.value = undefined;next.done = true;return next;};return next.next = next;}}// Return an iterator with no values.return { next: doneResult };}runtime.values = values;function doneResult() {return { value: undefined, done: true };}Context.prototype = {constructor: Context,reset: function(skipTempReset) {this.prev = 0;this.next = 0;// Resetting context._sent for legacy support of Babel's// function.sent implementation.this.sent = this._sent = undefined;this.done = false;this.delegate = null;this.method = "next";this.arg = undefined;this.tryEntries.forEach(resetTryEntry);if (!skipTempReset) {for (var name in this) {// Not sure about the optimal order of these conditions:if (name.charAt(0) === "t" &&hasOwn.call(this, name) &&!isNaN(+name.slice(1))) {this[name] = undefined;}}}},stop: function() {this.done = true;var rootEntry = this.tryEntries[0];var rootRecord = rootEntry.completion;if (rootRecord.type === "throw") {throw rootRecord.arg;}return this.rval;},dispatchException: function(exception) {if (this.done) {throw exception;}var context = this;function handle(loc, caught) {record.type = "throw";record.arg = exception;context.next = loc;if (caught) {// If the dispatched exception was caught by a catch block,// then let that catch block handle the exception normally.context.method = "next";context.arg = undefined;}return !! caught;}for (var i = this.tryEntries.length - 1; i >= 0; --i) {var entry = this.tryEntries[i];var record = entry.completion;if (entry.tryLoc === "root") {// Exception thrown outside of any try block that could handle// it, so set the completion value of the entire function to// throw the exception.return handle("end");}if (entry.tryLoc <= this.prev) {var hasCatch = hasOwn.call(entry, "catchLoc");var hasFinally = hasOwn.call(entry, "finallyLoc");if (hasCatch && hasFinally) {if (this.prev < entry.catchLoc) {return handle(entry.catchLoc, true);} else if (this.prev < entry.finallyLoc) {return handle(entry.finallyLoc);}} else if (hasCatch) {if (this.prev < entry.catchLoc) {return handle(entry.catchLoc, true);}} else if (hasFinally) {if (this.prev < entry.finallyLoc) {return handle(entry.finallyLoc);}} else {throw new Error("try statement without catch or finally");}}}},abrupt: function(type, arg) {for (var i = this.tryEntries.length - 1; i >= 0; --i) {var entry = this.tryEntries[i];if (entry.tryLoc <= this.prev &&hasOwn.call(entry, "finallyLoc") &&this.prev < entry.finallyLoc) {var finallyEntry = entry;break;}}if (finallyEntry &&(type === "break" ||type === "continue") &&finallyEntry.tryLoc <= arg &&arg <= finallyEntry.finallyLoc) {// Ignore the finally entry if control is not jumping to a// location outside the try/catch block.finallyEntry = null;}var record = finallyEntry ? finallyEntry.completion : {};record.type = type;record.arg = arg;if (finallyEntry) {this.method = "next";this.next = finallyEntry.finallyLoc;return ContinueSentinel;}return this.complete(record);},complete: function(record, afterLoc) {if (record.type === "throw") {throw record.arg;}if (record.type === "break" ||record.type === "continue") {this.next = record.arg;} else if (record.type === "return") {this.rval = this.arg = record.arg;this.method = "return";this.next = "end";} else if (record.type === "normal" && afterLoc) {this.next = afterLoc;}return ContinueSentinel;},finish: function(finallyLoc) {for (var i = this.tryEntries.length - 1; i >= 0; --i) {var entry = this.tryEntries[i];if (entry.finallyLoc === finallyLoc) {this.complete(entry.completion, entry.afterLoc);resetTryEntry(entry);return ContinueSentinel;}}},"catch": function(tryLoc) {for (var i = this.tryEntries.length - 1; i >= 0; --i) {var entry = this.tryEntries[i];if (entry.tryLoc === tryLoc) {var record = entry.completion;if (record.type === "throw") {var thrown = record.arg;resetTryEntry(entry);}return thrown;}}// The context.catch method must only be called with a location// argument that corresponds to a known catch block.throw new Error("illegal catch attempt");},delegateYield: function(iterable, resultName, nextLoc) {this.delegate = {iterator: values(iterable),resultName: resultName,nextLoc: nextLoc};if (this.method === "next") {// Deliberately forget the last sent value so that we don't// accidentally pass it on to the delegate.this.arg = undefined;}return ContinueSentinel;}};})(// In sloppy mode, unbound `this` refers to the global object, fallback to// Function constructor if we're in global strict mode. That is sadly a form// of indirect eval which violates Content Security Policy.(function() { return this })() || Function("return this")());

然后在相应的页面的 js 文件中引用:

import regeneratorRuntime from '../../utils/runtime.js'

读取歌单数据并插入云数据库

先在云函数下新建一个 getPlaylist 函数:

然后打开其目录的终端,安装第三方库,具体步骤参照 https://github.com/request/request-promise:


然后编写里面的代码:

// 云函数入口文件
const cloud = require('wx-server-sdk')cloud.init()// 初始化云数据库
const db = cloud.database()//文档 cheat sheet
const rp = require('request-promise')const URL = 'http://musicapi.xiecheng.live/personalized'// 云函数入口函数
exports.main = async (event, context) => {const playlist = await rp(URL).then((res) => {return JSON.parse(res).result})// 遍历数组存入云数据库中for(let i=0, len = playlist.length; i<len; i++){await db.collection('playlist').add({data: {//扩展运算符...playlist[i],//获取当前服务器时间作为创建时间createTime: db.serverDate(),}}).then((res) => {console.log('插入成功')}).catch((err) => {console.error('插入失败')})}}

上传并部署到云端之后,新建一个 playlist 云数据库,并在云端进行测试;

歌单数据去重

云开发小程序项目实战 二相关推荐

  1. 视频教程-微信小程序项目实战之我画你猜视频课程-微信开发

    微信小程序项目实战之我画你猜视频课程 精通PHP软件开发和WEB前端开发技术,熟悉PHP.Java.Javascript.HTML等语言,熟悉HTTP协议及W3C相关互联网规范,曾在山西某知名公司担任 ...

  2. 视频教程-微信小程序项目实战:电影购票系统-微信开发

    微信小程序项目实战:电影购票系统 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过100 ...

  3. 视频教程-微信小程序项目实战:高仿iOS计算器-微信开发

    微信小程序项目实战:高仿iOS计算器 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过1 ...

  4. 微信小程序项目实战之豆瓣天气

    概述 微信小程序项目实战之豆瓣天气 详细 代码下载:http://www.demodashi.com/demo/10943.html 一.准备工作 1.注册微信小程序 2.在小程序设置中设置reque ...

  5. 微信小程序项目实战之天气预报

    概述 微信小程序项目实战之天气预报 详细 代码下载:http://www.demodashi.com/demo/10634.html 一.准备工作 1.注册微信小程序 2.注册和风天气账号 3.注册百 ...

  6. 微信小程序项目实战:快递查询-李宁-专题视频课程

    微信小程序项目实战:快递查询-1303人已学习 课程介绍         本课程主要介绍了scrollview布局,以及如何通过第三方API获取并处理数据. 课程收益     本课程主要介绍了如何实现 ...

  7. 微信小程序项目实战:电影购票系统-李宁-专题视频课程

    微信小程序项目实战:电影购票系统-1644人已学习 课程介绍         本课程主要介绍了scrollview布局,导航.从服务端获取数据,以及处理数据的方法. 课程收益     本课程的目标是让 ...

  8. 微信小程序网悦新闻开发--小程序配置(二)

    目录 微信小程序网悦新闻开发--功能介绍(一) 微信小程序网悦新闻开发--小程序配置(二) 微信小程序网悦新闻开发--首页模块开发(三) 微信小程序网悦新闻开发--视频模块开发(四) 微信小程序网悦新 ...

  9. 攀哥の茶–奶茶小程序 项目实战说明

    攀哥の茶–奶茶小程序 项目实战说明 实战目的 巩固之前所学的vue2 +uniapp +小程序 学会独立思考与解决问题 学会团队协作与相关工具git等的使用 项目介绍 ​ 奶茶店点单小程序 ​ 程序主 ...

最新文章

  1. 给 Windows 驱动程序安装提速
  2. 青龙面板PM2报错修复方法
  3. 浅谈《刺客信条》的叙事:刺客和圣殿骑士的冲突与融合
  4. matlab 次坐标轴 标注,matlab标注坐标轴
  5. 论文浅尝 - ICLR2021 | BERTology 遇上生物学:在蛋白质语言模型中解释注意力
  6. 这个充电宝太黑科技了,又小又不用自己带线,长见识了~
  7. 测试开发之软件测试模型
  8. SqlServer三种常用窗口函数
  9. 利用构造函数实现累加
  10. Java程序设计应用教程_Java程序设计及应用开发教程.pdf
  11. 数据库精选 60 道高频面试题(含答案),值得收藏
  12. Python基础编程(一)
  13. marlin固件烧录教程_marlin固件中文(marlin固件下载)
  14. 图片清晰度差怎么修复成高清图片
  15. 浅析集线器、交换机、路由器
  16. spyder 更改默认工作目录的最优方法
  17. 工程经济有何难,思维导图来助阵
  18. findIndex()
  19. 命运被转折改变--掌握java高性能分布式服务和海量大数据技术体系(第二期)
  20. 常用的数学符号sup(上确界) 和 inf(下确界)以及少量数学公式的markdown模式下latex 格式 编写

热门文章

  1. 陆家嘴vs静安寺,谁站在魔都职场名媛鄙视链顶端?
  2. 关于 Linux 输入正确账号密码显示 Sorry,that did not work的问题
  3. 网易100件事任务清单html,人生必做的100件事清单
  4. VirtualLab基础实验教程-3.迈克尔逊干涉仪
  5. 十一年的帆软成长史,一群年轻人的事业,不忘初心,坚持做好产品
  6. 手机技巧:小米MIUI 14 系统优化超级实用技巧,彻底和广告说再见
  7. 校安行 | 电子学生证真的有用吗?
  8. 上周技术关注:函数式编程另类指南
  9. 21杭电计算机考研回忆录
  10. 诚之和:警惕不良剧本推理游戏伤害未成年人