草庐IT

微信小程序项目实战

前端 贾公子 2024-02-29 原文

目录

小程序前后端交互

自定义组件的使用

语法说明

properties 定义

案例 

微信小程序的事件对象

e.target和e.currentTarget

 微信小程序的事件传参

传参方式一: data-key='value'

传参方式二: id='value'

scroll-view 使用

 scroll-view 中使用flex布局

scroll-into-view

下拉刷新 上拉加载

微信小程序分享功能的实现方法有两种:

微信音乐播放器

页面 布局

组件共享PubSubJS

小程序使用 npm 包

背景音频api使用说明

代码示例

小程序分包流程

为什么要分包

分包形式

常规分包

独立分包

分包预下载

支付流程详细说明

 小程序获取用户唯一标识(openId)


小程序前后端交互

语法说明

  • 每个微信小程序需要事先设置通讯域名,小程序只可以跟指定的域名进行网络通信
  • 服务器域名请在 「小程序后台-开发-开发设置-服务器域名」 中进行配置
  • 默认超时时间和最大超时时间都是 60s
  • 超时时间可以在 app.json 中通过 networktimeout 配置
  • 小程序为了安全起见只支持 Https 请求 wx.request 最大并发限制 10
  • 开发中可以在微信开发工具中不校验合法域名

export default (url,data={},method='GET')=>{
    return new Promise((resolve,reject)=>{
        wx.request({
            url:'http://localhost:3000'+url,
            data,
            success:(res)=>{
                resolve(res.data)
            },
            fail:(err)=>{
                reject(err)
            }
        })
    })

}

自定义组件的使用

语法说明

新建component组件 不会生路径

在使用的父页面 json中 usingComponents注册组件

父传值通过属性 title="排行榜" 子组件 properties 接收

properties 定义

api 框架----》自定义组件

定义段类型是否必填描述最低版本
type属性的类型
optionalTypesArray属性的类型(可以指定多个)2.6.5
value属性的初始值
observerFunction属性值变化时的回调函数

案例 

子组件

<view class="header">
    <text class="title">{{title}}</text>
    <view>
        <text>{{nav}}</text>
        <text class="more"> 查看更多</text>
    </view>
</view>

// pages/index/commponents/NavHeader/NavHeader.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        title:{
            type:String,
            value:'默认值'
        },
        nav:{
            type:String,
            value:'默认值'
        }
    },
})

父组件

{
  "usingComponents": {
    "NavHeader":"../index/commponents/NavHeader/NavHeader"
  }
}
  <NavHeader title="推荐歌曲" nav='为你精心推荐'></NavHeader>

微信小程序的事件对象

e.target和e.currentTarget

在小程序的事件回调触发时,会接收一个事件对象,事件对象的参数中包含一个target和currentTarget属性,接下来说说这二者的区别。

<view id='tar-father' bindtap='click'>
  父组件
  <view id='tar-children'>子组件</view>
 </view>

event.target 为其子组件,也就是触发该事件的源头组件, event.target 为父组件,因为触发的源头也就是父组件本身

event.currentTarget 为事件所绑定的组件  event.currentTarget 始终为事件所绑定的组件

总结:target对应的是触发事件的源头组件,这个组件有可能是子组件,有可能是父组件,主要是看执行动作的区域。而currentTarget始终对应事件所绑定的组件。

 微信小程序的事件传参

1. 什么是事件委托
    将子元素的事件委托(绑定)给父元素
2. 事件委托的好处(ul 下面的100li需要添加事件,可以直接绑定再父元素上)
   1. 减少绑定的次数
   2. 后期新添加的元素也可以享用之前委托的事件
3. 事件委托的原理
   1. 冒泡
4. 触发事件的是谁
   1. 子元素
5. 如何找到触发事件的对象
   1. event.target
6. currentTarget VS target
   1. currentTarget要求绑定事件的元素一定是触发事件的元素
   2. target绑定事件的元素不一定是触发事件的元素

传参方式一: data-key='value'

ps:key 可以自定义 传递多个

<input class="" data-key="phone"   placeholder="请输入用户名"   bindinput="change"> </input>
    <input class="" data-key="password"   placeholder="请输入用户名"   bindinput="change" > </input>
 
 
#js
change(evet) {
    console.log(evet);
    let value = evet.detail.value
    let type = evet.currentTarget.dataset.key
    this.setData({
      [type]: value
    })
  },

传参方式二: id='value'

唯一值

<input class="" id="phone"   placeholder="请输入用户名"   bindinput="change"> </input>
    <input class="" id="password"   placeholder="请输入用户名"   bindinput="change" > </input>
 
#js
change(evet) {
    console.log(evet);
    let value = evet.detail.value
    let type = evet.currentTarget.id
    this.setData({
      [type]: value
    })
    
  },

scroll-view 使用

tips y轴滚动时要计算滚动高度

 scroll-view 中使用flex布局

 enable-flex

启用 flexbox 布局。开启后,当前节点声明了 display: flex 就会成为 flex container,并作用于其孩子节点。

scroll-view 开启display:flex 设置 enable-flex 必须设置大小否则 scroll-view报错纵向布局高度

<scroll-view class="scroll" enable-flex scroll-x>
            <view class="scrollItem" wx:for='{{recommendList}}' wx:key='id'>
                <image src="{{item.picUrl}}"></image>
                <text>{{item.name}}</text>
            </view>
</scroll-view>

.scroll{
    display: flex;
    height: 300rpx;
}

scroll-into-view

值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素

scroll-into-view 是 scroll-view 的一个属性,主要作用是用于滚动到指定元素的位置,其对应值是元素的id,使用方法如下:

 <scroll-view 
    class="navScroll" 
    scroll-x 
    enable-flex 
    scroll-into-view="{{'scroll' + navid}}"
    > 
        <view class="navItem"  id="{{'scroll' + item.id}}"wx:for='{{GroupList}}' wx:key='id'>
            <view class="navContent {{navid==item.id?'active':''}}" bindtap='navChange' id="{{item.id}}">
                {{item.name}}
            </view>
        </view>
</scroll-view>

 data: {
        GroupList: [],
        navid: '',
        VideoList: [],
        isTriggered: false //下拉刷新
    },

    //tags切换
    navChange(event) {
        // 保存点击id ,视频列表赋值为空
        this.setData({
            navid: event.currentTarget.id,
            VideoList: []
        })
        //开启加载提示,发送请求
        wx.showLoading({
            title: '正在加载',
        })
        this.getVideoList(event.currentTarget.id)
    },

下拉刷新 上拉加载

下拉刷新

refresher-enabled:开启自动义下拉刷新

定义下拉刷新时触发回调 bindrefresherrefresh

refresher-triggered 设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发 控制刷新状态

上拉触底

bindscrolltolower定义上拉触底时触发回调

  •         //为了实现下拉加载数据 1.后端分页 2.前端分页
  •         // 1.后端分页 一般触发请求 告诉后端第几页 和条目数
  •         // 2.前端分页 直接返回100条 第一次1-9 触底之后10-19

    //下拉刷新
    handleRefresher() {
        console.log('下拉刷新');
        this.getVideoList(this.data.navid)
    },
    // 上拉触底
    handleToLower() {
        console.log('上拉触底');
  
        let list=this.data.VideoList
        list.push(...list)
        this.setData({
            VideoList:list
        })
    }

    data: {
        GroupList: [],
        navid: '',
        VideoList: [],
        isTriggered: false //下拉刷新
    },
  <scroll-view 
    class="view-scroll"  
    scroll-y
    refresher-enabled
    bindrefresherrefresh="handleRefresher"
    bindscrolltolower="handleToLower"
    refresher-triggered="{{isTriggered}}"
     >
        <view class="vieoItem"  wx:for='{{VideoList}}' wx:key='index'>
            <video src="{{item.data.previewUrl}}"> </video>
    </scroll-view>

微信小程序分享功能的实现方法有两种:

第一种

在page.js中实现onShareAppMessage,便可在小程序右上角选择分享该页面

监听用户点击页面内转发按钮(button 组件 open-type="share")或右上角菜单“转发”按钮的行为,并自定义转发内容。

参数类型说明最低版本
fromString转发事件来源。
button:页面内转发按钮;
menu:右上角转发菜单
1.2.4
targetObject如果 from 值是 button,则 target 是触发这次转发事件的 button,否则为 undefined1.2.4
webViewUrlString页面中包含web-view组件时,返回当前web-view的url1.6.4
onShareAppMessage: function () {
 return {
  title: '弹出分享时显示的分享标题',
  desc: '分享页面的内容',
  path: '/page/user?id=123' // 路径,传递参数到指定页面。
 }
}

判读用户点击的是那个

  onShareAppMessage: function ({from}) {
    console.log(from);
    if(from === 'button'){
      return {
        title: '来自button的转发',
        page: '/pages/video/video',
        imageUrl: '/static/images/nvsheng.jpg'
      }
    }else {
      return {
        title: '来自menu的转发',
        page: '/pages/video/video',
        imageUrl: '/static/images/nvsheng.jpg'
      }
    }
    
  }

第二种

自定义按钮实现分享,在page中添加一个带有open-type='share'的button标签

点击该按钮后,即会自动触发已经在page.js中定义好的onShareAppMessage方法,实现分享功能。

(<button open-type='share'></button>)。

如上例,path属性指向的是user页面,并附带id=123的参数。我们只需在user.js的onLoad函数中,通过options查看传递过来的参数:

// user.js
Page({
 onLoad: function(options) {
  console.log(options);
 }
})

微信音乐播放器

页面 布局

  <!-- 进度条控制区域 -->
    <view class=".progerss">
        <text>{{currentTime}}</text>
        <!-- 总进度 -->
        <cview class="bar">
            <!-- 实时进度 -->
           <view class="audio-bar" style="width: {{width + 'rpx'}}">
               <!-- 小圆球 -->
               <view class="audio-circle" ></view>
           </view>
        </cview>
        <text>{{endTime}}</text>
    </view>
    <!-- 底部控制播放区域 -->
    <view class="musicControl">
        <text class="iconfont icon-iconsMusicyemianbofangmoshiShuffle"></text>

        <text class="iconfont icon-shangyishou" id="pre" bindtap='handleSwitch'></text>

        <text class="iconfont {{isPlay?'icon-zanting': 'icon-bofang'}} big" bindtap="handleMusicPlay"></text>

        <text class="iconfont icon-next" id="next" bindtap='handleSwitch'></text>

        <text class="iconfont icon-iconsMusicyemianbofangmoshiPlayList"></text>
    </view>
</view>

.musicControl {
    position: absolute;
    width: 100%;
    bottom: 40rpx;
    left: 0;
    display: flex;
    border-top: 1px solid #ffffff;
}

.musicControl text {
    width: 20%;
    height: 120rpx;
    line-height: 120rpx;
    text-align: center;
    color: #ffffff;
    font-size: 50rpx;
}

.musicControl text .big {
    font-size: 80rpx;
}

/* 进度条 */
.progerss {
    width: 640rpx;
    height: 80rpx;
    line-height: 80rpx;
    display: flex;
    position: absolute;
    bottom: 200rpx;
}

.progerss .bar {
    position: relative;
    width: 450rpx;
    height: 8rpx;
    background: rgba(0, 0, 0, .4);
    margin: auto;
}

.bar .audio-bar {
    position: absolute;
    top: 0;
    left: 0;

    height: 8rpx;
    background-color: red;
    z-index: 99;
}

.audio-circle {
    position: absolute;
    right: -18rpx;
    top: -6rpx;
    width: 18rpx;
    height: 18rpx;
    border-radius: 50%;
    background-color: #fff;
}

组件共享PubSubJS

1.使用第三方库: pubsub-js

2. 安装: npm install pubsub-js

3. 使用:

  • a) Import PubSub from ‘pubsub-js’
  • b) 订阅消息: PubSub.subscribe(‘eventName’, callback)
  • c) 发布消息: PubSub.publish(‘eventName’, data)
  • d) 取消订阅: PubSub.unsubscribe(‘eventName’)

小程序使用 npm

  • 初始化 package.json     npm init
  • 勾选允许使用 npm
  • 下载 npm
  • 构建 npm 1. 开发工具 ---> 工具 ---> 构建 npm   2. 会将 node_modules 中的包打包到 miniprogram_npm 中

背景音频api使用说明

  • BackgroundAudioManager 获取实例
  • BackgroundAudioManager.onPlay(function callback) 监听背景音频播放事件
  • BackgroundAudioManager.onPause(function callback) 监听背景音频暂停事件
  • BackgroundAudioManager.onStop(function callback) 监听背景音频停止事件
  • BackgroundAudioManager.onTimeUpdate(function callback) 监听背景音频播放进度更新事件,只有小程序在前台时会回调
  • BackgroundAudioManager.onEnded(function callback) 监听背景音频自然播放结束事件
  • BackgroundAudioManager.stop() 停止音乐
  • BackgroundAudioManager.pause() 暂停音乐

string src

音频的数据源(2.2.3 开始支持云文件ID)。默认为空字符串,当设置了新的 src 时,会自动开始播放,目前支持的格式有 m4a, aac, mp3, wav。

number startTime

音频开始播放的位置(单位:s)。

string title

音频标题,用于原生音频播放器音频标题(必填)。原生音频播放器中的分享功能,分享出去的卡片标题,也将使用该值。

number currentTime

当前音频的播放位置(单位:s),只有在有合法 src 时返回。(只读)

代码示例

//获取全局app应用实例
const app = getApp()
import PubSub from 'pubsub-js'
import request from '../../utils/request'
import moment from 'moment'
Page({


    data: {
        isPlay: false,
        song: {},
        id: '',
        linkUrl: '',
        currentTime: '00:00',
        endTime: '00:00',
        width: 0
    },


    onLoad: function (options) {
        this.setData({
            id: options.id
        })
        this.getMusicInfo(options.id)
        //判断当前音乐是否在播放
        console.log(app.globalData.musicId, app.globalData.isMUsicPlay + '全局de', options.id + '点击');
        if (app.globalData.isMUsicPlay && app.globalData.musicId == options.id) {
            this.setData({
                isPlay: true
            })
        }
        //创建音乐播放的实例对象
        this.getBackgroundAudioManager = wx.getBackgroundAudioManager()
        // 监听用户点击操作系统的播放暂停
        this.getBackgroundAudioManager.onPlay(() => {
            this.changePlayStatus(true)
            app.globalData.musicId = options.id
        })
        this.getBackgroundAudioManager.onPause(() => {
            this.changePlayStatus(false)
        })
        this.getBackgroundAudioManager.onStop(() => {
            this.changePlayStatus(false)
        })
        // 监听音乐实时播放进度
        this.getBackgroundAudioManager.onTimeUpdate(() => {
            // 实时时长
            let time = this.getBackgroundAudioManager.currentTime
            // 总时长
            let totalTime = this.getBackgroundAudioManager.duration
            let currentTime = moment(time * 1000).format('mm:ss')
            let width = time / totalTime * 450
            this.setData({
                currentTime,
                width
            })
        })
        //监听音乐播放自然结束
        this.getBackgroundAudioManager.onEnded(() => {
            //自动换至下一首音乐,并自动播放
            PubSub.publish('SwitchType', 'next');

            //将进度条长度恢复为0
            this.setData({
                width: 0,
                endTime: '00:00'
            })
        })
    },
    //获取音乐详情
    async getMusicInfo(ids) {
        let {
            songs
        } = await request('/song/detail', {
            ids
        })
        //转换总实长
        let endTime = moment(songs[0].dt).format('mm:ss')
        this.setData({
            song: songs[0],
            endTime
        })
        wx.setNavigationBarTitle({
            title: this.data.song.name,
        })
    },
    //修改状态功能函数
    changePlayStatus(isPlay) {
        this.setData({
            isPlay
        })
        //修改应用全局状态
        app.globalData.isMUsicPlay = isPlay
    },
    // 点击开始暂停按钮
    handleMusicPlay() {
        let isPlay = !this.data.isPlay
        this.setData({
            isPlay
        })
        let {
            id,
            linkUrl
        } = this.data
        this.musicControl(isPlay, id, linkUrl)
    },
    // 播放 暂停 功能回调
    async musicControl(isPlay, id, linkUrl) {
        //性能优化: 没有url时再去请求 有的时候就拿全局变量
        if (!linkUrl) {
            let {
                data
            } = await request('/song/url', {
                id
            })
            linkUrl = data[0].url
            this.setData({
                linkUrl
            })
        }
        if (isPlay) { //播放
            // 必须给实例添加url 和title
            this.getBackgroundAudioManager.src = linkUrl
            this.getBackgroundAudioManager.title = this.data.song.name
        } else { //暂停
            this.getBackgroundAudioManager.pause()
        }

    },
    // 点击切歌的回调
    handleSwitch(e) {
        let type = e.currentTarget.id
        //关闭当前音乐
        this.getBackgroundAudioManager.stop()
        //发布消息
        PubSub.publish('SwitchType', type);
        //订阅消息拿到回传id
        PubSub.subscribe('musiceId', (msg, id) => {
            console.log(msg, id);
            //获取音乐信息
            this.getMusicInfo(id)
            //自动播放
            this.musicControl(true, id)
            //接收到之后取消订阅
            PubSub.unsubscribe('musiceId');

        })
    }

})

小程序分包流程

为什么要分包

  • 1. 小程序要求压缩包体积不能大于 2M,否则无法发布
  • 2. 实际开发中小程序体积如果大于 2M 就需要使用分包机制进行发布上传
  • 3. 分包后可解决 2M 限制,并且能分包加载内容,提高性能
  • 4. 分包后单个包的体积不能大于 2M
  • 5. 分包后所有包的体积不能大于 16M

分包形式

  • 1. 常规分包
  • 2. 独立分包
  • 3. 分包预下载

常规分包

1. 开发者通过在 app.json subpackages 字段声明项目分包结构

2. 特点:

a) 加载小程序的时候先加载主包,当需要访问分包的页面时候才加载分包内容

b) 分包的页面可以访问主包的文件,数据,图片等资源

c) 主包:

主包来源: 除了分包以外的内容都会被打包到主包中 通常放置启动页/tabBar 页面

  "subPackages":[
        {
            "root": "songPackge",
            "pages": [
                "pages/recommendSong/recommendSong",
                "pages/songDetail/songDetail"
            ]
        }
    ],

独立分包

1. 设置 independent true
2. 特点 :
a) 独立分包可单独访问分包的内容,不需要下载主包
b) 独立分包不能依赖主包或者其他包的内容
3. 使用场景
a) 通常某些页面和当前小程序的其他页面关联不大的时候可进行独立分包
b) 如:临时加的广告页 || 活动页

分包预下载

配置
a) app.json 中设置 preloadRule 选项
b) key( 页面路径 ): {packages: [ 预下载的包名 || 预下载的包的根路径 ])}
2. 特点 :
a) 在加载当前包的时候可以设置预下载其他的包
b) 缩短用户等待时间,提高用户体验

支付流程详细说明

  • 1. 用户在小程序客服端下单(包含用户及商品信息)
  • 2. 小程序客户端发送下单支付请求给商家服务器
  • 3. 商家服务器同微信服务器对接获取唯一标识 openID
  • 4. 商家服务器根据 openId 生成商户订单(包含商户信息)
  • 5. 商家服务器发送请求调用统一下单 API 获取预支付订单信息
  • a) 接口地址: https://api.mch.weixin.qq.com/pay/unifiedorder
  • 6. 商家对预支付信息签名加密后返回给小程序客户端
  • a) 签名方式: MD5
    b) 签名字段:小程序 ID, 时间戳, 随机串,数据包,签名方式
    c) 参考地址
    :
    https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3
    7. 用户确认支付(鉴权调起支付)
    a) API: wx.requestPayment()
    8. 微信服务器返回支付结果给小程序客户端
    9. 微信服务器推送支付结果给商家服务器端

 小程序获取用户唯一标识(openId

1. wx.login()

  • 2. 发送 code 给服务器端
  • 3. 服务器端发送请求携带参数(code, appSecret, appId)给微信服务器获取 openId
  • a) 接口地址:
  • 4. appSecretappId 在小程序首页获取
  • 5. 服务器获取 openId 后进行加密返回给前端

 

有关微信小程序项目实战的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  3. ruby-on-rails - 新 Rails 项目 : 'bundle install' can't install rails in gemfile - 2

    我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="

  4. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  5. 微信小程序通过字典表匹配对应数据 - 2

    前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立

  6. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  7. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  8. ruby - 如何在 Ruby 字符串中插入项目符号字符? - 2

    我正在尝试创建一个带有项目符号字符的Ruby1.9.3字符串。str="•"+"helloworld"但是,当我输入它时,我收到有关非ASCII字符的语法错误。我该怎么做? 最佳答案 你可以把Unicode字符放在那里。str="\u2022"+"helloworld" 关于ruby-如何在Ruby字符串中插入项目符号字符?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1195

  9. ruby - 在 Rails 项目中测试本地版本的 gem - 2

    我的Rails站点使用了一个确实不是很好的gem。每次我需要做一些新的事情时,我最终不得不花费与向实际Rails项目添加代码一样多的时间来为gem添加功能。但我不介意,我将我的Gemfile设置为指向我的gem的GitHub分支(我尝试提交PR,但维护者似乎已经下台)。问题是我真的没有找到一种合理的方法来测试我添加到gem的新东西。在railsc中测试它会特别好,但我能想到的唯一方法是a)更改~/.rvm/gems/.../foo。rb,这看起来不对或者b)升级版本,推送到Github,然后运行​​bundleup,这除了耗时之外显然是一场灾难,因为我不确定我所做的promise是否正

  10. ruby - 合并 nanoc 中的项目 - 2

    我一直在尝试使用nanoc用于生成静态网站。我需要组织一个复杂的排列页面,我想让我的内容保持干燥。包含或合并的概念在nanoc系统中如何运作?我已阅读文档,但似乎找不到我想要的内容。例如:我如何获取两个部分内容项并将它们合并到一个新的内容项中。在staticmatic您可以在您的页面中执行以下操作。=partial('partials/shared/navigation')类似的约定在nanoc中如何运作? 最佳答案 这里是nanoc的作者。在nanoc中,部分是布局。因此,您可以拥有layouts/partials/shared/

随机推荐