winney

It is never too old to learn.

0%
winney

本地目录

  • 实战项目目录:H:\Gitee\wx_code
  • 参考项目目录:H:\Test_Code\wx_code

云服务器目录

/usr/local/src/webCode/wx

node命令使用之前

1
nvm  use 16.13.0

接口管理

使用apifox

接口文档

项目设置——数据管理——导出数据——Markdown格式

UI框架

开源项目

微信小程序开源项目库集合

企业级微信小程序实战详解

微信小程序开源项目排行榜

微信小程序实战-音乐播放器

微信小程序初始化项目架构

微信小程序项目实战(商城)

微信小程序实战之仿今日头条

课程链接

开发测试号

申请小程序测试号

步骤

  1. 注册【使用未在微信公众平台使用过的邮箱,注册】
  2. 需要有AppID【开发——开发管理——开发设置】
  3. 新建项目【开发阶段,若没有AppID,可使用测试号,上线的时候改为自己的AppID就行】
    • 在微信开发者工具的右上角【详情】中,可以更改AppID

编译模式:

普通编译:默认打开的是首页

如果想调试某个页面,可以添加编译模式

基础库

在本地设置中,可以查看到【调试基础库】,根据对应的版本,查找对应的api

开发阶段,在本地设置中,将”不校验合法域名、web-view(业务域名)、TLS版本以及HITPS证书“勾选上

页面路径

在模拟器的下方,可以看到页面路径和页面参数

结构目录

目录结构

1
tree /f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
├─pages						_____________页面文件夹
│ ├─index _____________首页
│ │ index.js _____________首页的逻辑文件
│ │ index.json _____________首页的配置文件
│ │ index.wxml _____________首页的结构文件
│ │ index.wxss _____________首页的样式文件
│ │
│ └─logs _____________日志 页面
│ logs.js
│ logs.json
│ logs.wxml
│ logs.wxss

└─utils _____________第三方的工具js(可以删除的)
│ util.js
│ .eslintrc.js
│ app.js _____________项目的全局入口文件
│ app.json _____________全局配置文件
│ app.wxss _____________全局样式文件
│ project.config.json _____________项目的配置文件 如appid
│ project.private.config.json
│ sitemap.json _____________微信索引配置文件

sitemap配置 用来配置小程序及其页面是否允许被微信索引

app.json

快速新建目录及里面的相关文件

app.json中的pages属性中,添加"pages/demo/demo",保存之后,会在pages目录中自动创建demo目录,以及demo.wxml、demo.wxss、demo.js、demo.json

1
2
3
4
5
"pages":[
"pages/index/index",
"pages/logs/logs",
"pages/demo/demo"
],

数据绑定

列表渲染

条件渲染

模板

引用,import和include

事件详解

1
<view id="tapTest" data-hi="Weixin" bindtap="tapName"> Click me! </view>
1
2
3
4
5
Page({
tapName: function(event) {
console.log(event)
}
})

dataset

在 WXML 中,这些自定义数据以 data- 开头,多个单词由连字符 - 连接。这种写法中,连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符。如:

  • data-element-type ,最终会呈现为 event.currentTarget.dataset.elementType
  • data-elementType ,最终会呈现为 event.currentTarget.dataset.elementtype

页面配置

1
2
3
4
5
6
7
{
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信接口功能演示",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}

封装 wx.request方法

utils/request.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//request.js
const baseUrl = 'https://www.winney07.cn:8080/'

const request = (url, method, data) => {
return new Promise((resolve, reject) => {
wx.request({
url: baseUrl + url,
method: method,
data: data,
success: res => {
resolve(res.data)
},
fail: err => {
reject(err)
}
})
})
}

const get = (url, data) => {
return request(url, 'GET', data)
}

const post = (url, data) => {
return request(url, 'POST', data)
}

export default {
baseUrl,
request,
get,
post
}
使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import request from '../../utils/request.js'

// 请求接口,获取数据
async getData() {
const swiperList = await request.get('shopping/swiper');
const tabList = await request.get('shopping/good/tabList');
const goodList = await request.get('shopping/good/getGoodList');
this.setData({
swiperList,
tabList,
goodList
})
},

/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getData();
},

父子组件之间传值

父组件:

1
<good-list goodsList="{{goodsList}}"></good-list>

子组件.js:

1
2
3
4
5
6
7
8
Component({
properties: {
goodsList: {
type: Array,
value: []
}
},
})

子组件.wxml:

1
2
3
4
5
6
7
<view class="good-list">
<block
wx:for="{{ goodsList }}"
wx:key="index">
<good-card class="good-item" i="{{index}}" data="{{item}}"></good-card>
</block>
</view>

事件监听

传参

goods/category/index

1
2
3
4
5
6
7
8
9
10
<van-grid-item 
use-slot
class="grid-item"
data-groupid="{{item.groupId}}"
bindtap="toList">

.....
.....
.....
</van-grid-item>
1
2
3
4
5
6
7
8
toList(event) {
console.log('event');
console.log(event.currentTarget.dataset); // {groupid: "249480"}
let groupid = event.currentTarget.dataset.groupid;
wx.navigateTo({
url: `/pages/goods/list/index?groupid=${groupid}`
})
},

在元素中自定义的data-属性,在event.currentTarget.dataset中。

获取URL传来的参数:

goods/list/index.js

1
2
3
4
onLoad(options) {
console.log('options');
console.log(options); // {groupid: "249480"}
},

微信小游戏

微信小游戏账号注册教程

在微信开发者中,创建小游戏项目,进入项目后,界面是黑色的。

需要注册个测试号(这个测试号是针对小游戏的)

  1. 开发测试号

  2. 申请小程序测试号

  3. 在微信开发中创建小游戏项目时,选择”测试号“,进入项目,即可正常显示

微信公众平台用户信息相关接口调整公告

报错信息

微信小程序编译遇到 ReferenceError: regeneratorRuntime is not defined 解决办法

解析方法:

打包前,在js文件中加入

1
import regeneratorRuntime from "./runtime"

在地图上显示当前位置

  1. .json页面中进行配置permission
1
2
3
4
5
6
7
8
9
10
11
12
{

"pages":[
"pages/index/index",
"pages/logs/logs"
],
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示" // 高速公路行驶持续后台定位
}
}
}
  1. 获取位置
1
2
3
4
5
6
7
8
9
10
11
wx.getLocation({
type: 'wgs84',
success: (res) => {
var latitude = res.latitude // 纬度
var longitude = res.longitude // 经度
console.log('维度')
console.log(latitude)
console.log('经度')
console.log(longitude)
}
})

微信开发者工具-关闭声音

关闭声音

1、设置配置文件

  • 方法一:在根目录中新建vue.config.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = {
runtimeCompiler: true,
devServer: {
proxy: {
// 匹配请求路径中的字符,
// 如果符合就用这个代理对象代理本次请求,路径为target的网址,
// changeOrigin为是否跨域,
// 如果不想始终传递这个前缀,可以重写路径
// pathRewrite为是否将指定字符串转换一个再发过去。
'/api': {
target: 'http://ttapi.research.itcast.cn/',
ws: true,
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
}
  • 方法二:在根目录中,新建config>index.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
dev: {
proxyTable: {
'/api': {
target: 'http://ttapi.research.itcast.cn/', // 接口的域名
secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
pathRewrite: {
'^/api': '' // '/api/a/user' =='localhost:8080/user'
}
}
}
}
}

2、axios请求方法的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
axios.get('http://ttapi.research.itcast.cn/app/v1_0/comments', {
params: {
type: 'a',
source: this.source,
offset: this.offset,
limit: this.limit
}
})
.then(function (response) {
console.log('response')
console.log(response)
})
.catch(function (error) {
console.log(error)
})

name属性是父组件传过来的,除了数组,对象,其他都不允许修改

props里面的数据,除了数组,对象,其他都不允许修改,所以不能用于页面元素的v-model属性中,因为v-model是双向绑定的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<van-field
v-model="localName"
...
/>
props: {
name: {
type: String,
required: true
}
},

data () {
return {
localName: this.name
}
},

当传递给子组件的数据既要使用又要修改,这种情况下我们可以使用v-model简写:

例如:父组件的name

1
2
:name="user.name"
@update-name="user.name=$event"

修改为:

1
v-model="user.name"

子组件也要跟着修改:

1
2
3
4
5
6
7
8
props: {
value: {
type: String,
required: true
}
},

this.$emit('input', this.localName)

v-model=”user.name”

默认传递一个名字叫value的数据: :value=”user.name”

默认监听input事件: @input=”user.name=$event”

v-model的本质还是父子组件通信,它仅仅是简化了父组件的使用,子组件还是按照原来那样使用

注:在同一个子组件传递中,v-model只能使用一次

1
2
3
4
5
这样是错误的:
<update-name
v-model="user.name"
v-model="user.photo"
/>
在同一个子组件传递中,v-model只能使用一次,如果有多个数据需要保持同步,使用.sync修饰符
1
2
3
4
<update-name
v-model="user.name"
:gender.sync="user.photo"
/>

注:我们一般把最常用的数据设计为v-model绑定,把不太常用的数据设计为.sync

销毁组件的一种方法:

1、加上v-if来控制渲染

1
2
3
4
5
<update-name
v-if="isEditNameShow"
v-model="user.name"
@close="isEditNameShow=false"
/>

在Vue中操作DOM,要给该元素添加ref属性

例如:点击头像,选择图片来修改头像

1、给input file添加ref属性

1
2
3
4
5
6
7
8
9
<input type="file" hidden ref="file">
<van-cell title="头像" is-link center @click="$refs.file.click()">
<van-image
width="30"
height="30"
round fit="cover"
:src="user.photo"
/>
</van-cell>

2、控制选择的文件类型:accept属性

1
<input type="file" hidden ref="file" accept="image/*">

3、监听文件的change事件

1
2
3
4
5
6
7
<input
type="file"
hidden
ref="file"
accept="image/*"
@change="onFileChange"
>

4、解决选择相同文件不触发change事件,手动清空file的value值

1
2
3
4
5
6
onFileChange() {
....

// 手动清空file的value值
this.$refs.file.value = ''
}

5、展示弹出层,在弹出层预览图片

1
2
3
4
5
6
7
8
9
10
11
onFileChange() {
// 展示弹出层
this.isEditPhotoShow = true

// 在弹出层预览选择的图片
const blod = window.URL.createObjectURL(this.$refs.file.files[0])
this.previewImage = blod

// 手动清空file的value值
this.$refs.file.value = ''
}

6、父传,子收。并展示预览图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
父传:
<update-photo
:image="previewImage"
/>
子收:
props: {
image: {
type: String,
required: true
}
},

<div class="update-photo">
<img :src="image">
</div>

7、修改父子组件传值的文件格式

如果Content-Type 要求是 multipart/form-data,则一定要提交FormData数据对象,专门用于文件上传的,不能提交{}对象,没用

所以要修改父组件传过来子组件的文件格式,保留原来的文件格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
父组件:
onFileChange() {
....
const blod = this.$refs.file.files[0]
this.previewImage = blod
....
}

<update-photo
:file="previewImage"
@close="isEditPhotoShow=false"
/>
子组件:
props: {
file: {
// type: String,
required: true
}
},
data () {
return {
image: window.URL.createObjectURL(this.file)
}
},

8、处理接口传参

1
2
3
4
5
6
// 文件格式处理
const fd = new FormData()
fd.append('photo', this.file)
// photo为参数名,this.file为参数值

await updateUserPhoto(fd)

9、处理图片裁切,使用 cropperjs 这个跟vue没有关系,其他项目也可以使用) 也可以在Awesome 中搜crop找裁切工具

  • 1、安装
1
cnpm install cropperjs
  • 2、引入
1
2
import 'cropperjs/dist/cropper.css'
import Cropper from 'cropperjs'
  • 3、使用:放在mounted里面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<img :src="image" ref="image">

mounted() {
const image = this.$refs.image
const cropper = new Cropper(image, {
viewMode: 1,
dragMode: 'move',
aspectRatio: 1,
// autoCropArea: 1,
cropBoxMovable: false,
cropBoxResizable: false,
background: false,
movable: true
})
console.log(cropper)
},
  • 4、让裁切区居中显示
1
2
3
4
5
6
7
8
9
.update-photo{
position: absolute;
top: 50%;
margin-top: -185px;
margin-left: -185px;
height: 370px;
left: 50%;
width:370px;
}
  • 5、获取裁切结果,getCroppedCanvas()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 data () {
return {
image: window.URL.createObjectURL(this.file),
cropper: null
}
},

使用Promise返回一个裁切对象,用于async
getCroppedCanvas() {
return new Promise(resolve => {
// 因为cropper定义在mounted中,在这里无法使用,
为了能够在这里能调用cropper,将cropper放在data中
this.cropper.getCroppedCanvas().toBlob((file) => {
resolve(file)
})
})
},

getCroppedCanvas返回的Promise对象,在这里面使用:
async onConfirm() {
.....
const file = await this.getCroppedCanvas()
const img = window.URL.createObjectURL(file)

// 文件格式处理
const fd = new FormData()
fd.append('photo', file)

await updateUserPhoto(fd)

this.$emit('close')
this.$emit('update-photo', img)

this.$toast.success('保存成功')
}

如果存在clipboard,先将它销毁

1
2
3
if(clipboard) {
clipboard.destroy();
}
Clipboard官网使用教程
复制成功和复制失败的提示
1
2
3
4
5
6
7
8
9
10
11
12
var u = navigator.userAgent;
clipboard.on('success', function(e) {
layer.msg('复制成功', {time: 1000});
e.clearSelection();
});
clipboard.on('error', function(e) {
if(u.indexOf('UCBrowser') > -1){
layer.msg('您的浏览器不支持,请长按内容手动复制!', {time: 1000});
} else {
layer.msg('复制失败', {time: 1000});
}
});
如果提示信息弹两次
1
2
3
4
5
自己写一个copySuccessTip提示信息
$(".copySuccessTip").addClass("mymove");
setTimeout(function() {
$(".copySuccessTip").removeClass("mymove");
},1000);

解决clipboard复制弹多次提示的问题

1、使用destroy(在单页面里面,回来页面,如果存在这个对象,就删除。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 如果存在clipboard,先将它销毁
if(clipboard){
clipboard.destroy();
}

var clipboard =new ClipboardJS('.copyBtn', {
text: function(trigger){
var txt = $(trigger).data( "clipboard-text");
return txt; // 返回需要复制的内容
}
});

var u = navigator.userAgent;

clipboard.on('success', function(e){
layer.msg('复制成功');
e.clearselection();
});

clipboard.on('error', function(e) {
if(u.indexOf("UCBrowser") >-1){
layer.msg('您的浏览器不支持,请长按内容手动复制!');
}else {
layer.msg('复制失败');
}
});

2、换成setTimeout,不用layer.msg(copySuccessTip的样式模拟layer.msg的样式即可)

1
2
3
4
5
6
7
clipboard.on('success', function(e){
$('.copySuccessTip').show();

setTimeout(function(){
$('.copySuccessTip').hide();
}, 1000);
});