前言不知道说什么,可是感受你们都有前言,我也不能输,那就随便唠点好了。本人是一名大三学生,是一个前端小菜鸡,这是个人第一篇掘金文章。以前和两个队友组队写了一个原创小程序——猿简历(已上线,但还有不少须要完善的)。最近初学了vue
,又涉及了一下mpvue
,因而就找了个小项目来练练手,既能再找找小程序的感受,又能练练vue的写法。起初是打算一周练手完成主要功能,因此这真的是一个很小很小的小项目,没有什么技术难点,只能分享一些小tips了。还但愿各位看官能看的高兴。css
由于我第一次写mpvue的时候,起初没法下手,不肯定要怎么构建项目,因此就把本身构建项目的步骤一并写了上来,但愿能帮助到你。html
初始化项目前,请确保本机已经正确装好node环境前端
下载vue脚手架vue
npm install vue-cli -g
复制代码
初始化项目node
vue init mpvue/mpvue-quickstart mpvue-zhirent
复制代码
进入项目根目录git
cd mpvue-zhirent
复制代码
根据package.json安装项目依赖包程序员
npm install
复制代码
启动初始化项目github
npm start || npm run dev
复制代码
当看到职人小程序时,脑子里第一反应即是基于MVVM(Model-View-ViewModel)系统架构思想。那么定义数据便成了项目开发最重要的事情。首先搜索了一下职人的相关网站,没有找到能够爬取数据的网站(也多是我没有看到),就只能定义假数据了。而说到定义假数据,第一想法就是用EasyMock(一个可视化,而且能快速生成 模拟数据的持久化服务)定义假数据了。因而手撸假数据撸到怀疑人生,这时候终于懂了程序员那句最美的情话了——“我给你写接口好吗?”。
我定义数据时添加了一个"errno":0
,为的是在请求数据时判断接口请求的状态。active和sponsor分别是沙龙数据和主办方数据。
此次开发我用了axios来请求数据。
首先,安装axios
$ npm install axios
复制代码
在build > webpack.base.config.js
文件中找到alias
为axios添加别名
alias: {
'vue': 'mpvue',
'axios':'axios/dist/axios',
'@': resolve('src')
}
复制代码
在src > utils
下添加一个axios.js
文件,封装axios
import axios from 'axios'
import qs from 'qs'
axios.defaults.timeout = 30000;
axios.defaults.baseURL ='';
axios.defaults.headers.post[ 'Content-Type' ] = 'application/x-www-form-urlencoded;charset=UTF-8';
axios.defaults.adapter = function (config) {
return new Promise((resolve, reject) => {
let data = config.method === 'get' ? config.params : qs.stringify(config.data)
wx.request({
url:config.url,
method:config.method,
data:data,
success:(res)=>{
return resolve(res)
},
fail:(err)=>{
return reject(err)
}
})
})
}
//请求拦截器
axios.interceptors.request.use(function ( request ) {
// console.log(request) //请求成功
return request
}, function ( error ) {
// console.log(error); //请求失败
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function ( response ) {
// console.log(response.data.data) //响应成功
return response;
}, function ( error ) {
// console.log(error); //响应失败
return Promise.reject(error);
});
export function get (url,params) {
return axios({
method:'get',
url:url,
params:params
})
}
复制代码
在main.js
文件中封装数据请求
import Vue from 'vue'
import App from './App'
import axios from 'axios'
// 将get请求引入
import {get} from './utils/axios'
// 将get请求挂载上去
Vue.prototype.$get=get;
Vue.prototype.getList = function (){
wx.showLoading({
title:'加载中'
}),
this.$get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
.then((res) => {
if (res.data.errno === 0) {
this.active = res.data.data.active;
this.sponsors = res.data.data.sponsors
}
})
wx.hideLoading()
}
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue(App)
app.$mount()
复制代码
$ npm i stylus stylus-loader -D
复制代码
build > webpack.base.config.js
文件中添加找到rules,添加文件处理规则{
test: /\.css$/,
loader: 'style-loader!css-loader!stylus-loader'
}
复制代码
以下图:
好了,该安装的也安装了,该配置的也配置了,终于能够开始愉快又使人激动的Coding之旅了。职人小程序包含了沙龙、主办方、个人 三个主要页面,以及沙龙详情和主办方详情两个页面,个人页面包含了我的的活动报名、主办方收藏、活动收藏等信息。接下来我将分享一些项目中遇到的坑和一些主要功能的实现。
mpvue添加页面并不像小程序那样添加页面方便。须要疯狂手动添加文件,手动配置页面信息,手动添加页面路径。每次添加新页面必定要记得去app.json
里添加路径信息!!!
1.建立页面文件
咱们须要在src/pages
目录下手动建立页面文件夹index
,并在index
文件夹下添加 index.vue
和main.js
两个文件,index.vue
则是咱们的html页面(可将index改成其它,只需在main.js
中引入便可),main.js
不可更名,若是将main改成其余名字(相关位置均作修改),都将会出现报错。
index.vue
在页面还未写任何页面结构时 须要编写以下基本结构,不然将会出现
pages/mine/main.js 出现脚本错误或者未正确调用 Page() 的报错信息
<template>
<div></div>
</template>
<script>
export default {
}
</script>
<style>
</style>
复制代码
main.js
的基本结构则为
import Vue from 'vue'
import App from './index' //若是index.vue更名为home.vue,这里则将index改成home
const app = new Vue(App)
app.$mount()
复制代码
2.添加页面
每建立一个新页面都需在src/app.json
文件中的pages手动 添加页面路径,不然将会出现
navigateTo:fail page "pages/index/main" is not found的报错信息
"pages": [
"pages/index/main"
]
复制代码
3.配置分页面
能够经过在onLoad(){}
生命周期中调用wx.setNavigationBarTitle({})
api动态设置分页面的navigationBarTitle
onLoad(){
wx.setNavigationBarTitle({
title: '受权'
})
}
复制代码
也能够手动添加main.json
文件,静态设置分页面的navigationBarTitle
{
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#224fa4",
"navigationBarTitleText": "职人",
"navigationBarTextStyle": "white"
}
复制代码
在src/app.json
下的tabBar下作相应更改便可,和小程序的tabBar自定义过程无异。这里要注意的是:通常会将tabBar的icon放在static/tabs
目录下,其余页面图标则放在images目录下,方便管理。页面的icon能够选择svg格式的话就选择svg的图片,这样有利于节省小程序的空间,但小程序的tabBar-icon暂时还不支持svg格式的图片。我在webpack.base.config.js
中设置了图片路径别名,故图片路径可直接写static/tabs/car1.png
,以下图:
"tabBar": {
"color": "#999",
"backgroundColor": "#fff",
"selectedColor": "#0c53ab",
"borderStyle": "black",
"list": [{
"text": "沙龙",
"pagePath": "pages/salon/main",
"iconPath": "static/tabs/car1.png",
"selectedIconPath": "static/tabs/car.png"
}, {
"text": "主办方",
"pagePath": "pages/sponsor/main",
"iconPath": "static/tabs/home1.png",
"selectedIconPath": "static/tabs/home.png"
}, {
"text": "个人",
"pagePath": "pages/mine/main",
"iconPath": "static/tabs/mine1.png",
"selectedIconPath": "static/tabs/mine.png"
}
],
"position": "bottom"
}
复制代码
进入小程序,跳转到受权页面,受权后跳转到沙龙页面,若是用户点击拒绝则会停留在该页面,用户下次进入小程序时,仍是能跳转到受权页面。
首先,咱们须要在进入小程序时经过调用wx.getSetting()
来判断用户是否已经受权。若是已经受权,那么咱们就能够经过调用wx.getUserInfo()
来获取用户信息,若是没有受权,则跳转到写好的受权页面。那么咱们就须要在App.vue
中的onShow或onLaunch生命周期中进行判断
// 获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
wx.getSetting({
success(res){
if (res.authSetting['scope.userInfo']) {
// 若是已经受权,能够直接调用 getUserInfo获取用户信息
wx.getUserInfo({
success: function(res) {
console.log(res.userInfo)
}
})
}else{
// 若是未受权,则跳转到受权页面
wx.reLaunch({
url: '/pages/authorize/main',
})
}
}
})
复制代码
而后,经过小程序<button>
自带的open-type
属性开放getUserInfo
能力,进行受权。再经过bindgetuserinfo
属性返回获取到的用户信息,经过判断是否获取到用户信息进行判断是否受权成功,若是成功,则跳转到沙龙首页
<button class="authorize" open-type="getUserInfo" @getuserinfo="getUserInfo">赞成受权</button>
复制代码
getUserInfo(){
wx.getSetting({
success:(res) => {
if(res.authSetting['scope.userInfo']){
wx.showLoading({
title:'加载中'
})
wx.reLaunch({
url:'/pages/salon/main'
})
wx.hideLoading()
}
}
})
}
复制代码
受权成功后,刷新页面,就能够看到打印出来的用户信息啦
当时由于本身要作的事情真的太多了,产品经理从0到1,UI从0到1,切图仔从0到1,加上两个大佬都出问题了,以为小菜鸡的本身就更不可能会了,也就专心作本身分内的事情。当本身尝试受权界面后,就很想手动艾特我两个队友进来挨打。咋肥事啊小老弟?
。
。
。
。
因此碰见任何难题时,都应该本身动手去尝试一下,不能以为别人不会你就必定也不会,必定要本身去尝试写一写!!!
这两个页面十分简单,很直观的两个数据渲染页面。两个页面写法几乎同样,我就挑一个代码少一点的来讲了。
首先,咱们区别一下onLoad & onShow
综上,咱们加载这两个页面数据,应该在onLoad生命周期中请求数据。因为以前咱们已经将数据请求封装到main.js
文件中了,故直接调用封装的方法便可。
onLoad(){
this.getList();
}
复制代码
很明显,这两个页面都须要用到数据循环,须要使用v-for嵌套时。
// 主办方页面
<div class="sponsor">
<div v-for="(item,index) in sponsors" :key="index" @click="toSponsorInfo(index)" class="card-container">
<div class="card-content">
<div class="card-icon">
<image class="icon" :src="item.avatar"></image>
</div>
<div class="card-info">
<div class="card-title">{{item.name}}</div>
<div class="card-desc">{{item.info}}</div>
</div>
<button @click.stop="collect(index)" :class="collectList[index] ? 'like' : 'unlike'">{{collectList[index] ? '已关注' : '关注'}}</button>
</div>
<div class="card-footer">
<div class="card-salon-num">共举办{{item.salonNum}}场沙龙</div>
<div v-if="item.salonNum>0" class="card-recently">最近沙龙:{{item.salons[0].title}}</div>
</div>
</div>
</div>
复制代码
而使用v-for嵌套时须要注意:在mpvue中外循环和内循环不能使用同一个index,须要更改一个其中一个的索引值标识,不然将出现
同一组件内嵌套的 v-for 不能连续使用相同的索引,目前为: index,index 的报错信息
由于咱们在项目准备时已经将stylus配置好了,即可以直接在index.vue
中使用stylus
<style lang="stylus" scoped>
</style>
复制代码
这里scoped表示该样式是该页面的私有样式,不会污染全局。
v-for="(item,index) in sponsors
每个列表的index表明着当前数据的下标值,咱们能够经过将该值传递到详情页,再请求数据并经过传递的下标值找到对应的数据,最后将拿到的数据渲染到详情页就好啦。具体作法以下:
div
标签,绑定一个事件,并将当前列表的index传递过去<div v-for="(item,index) in sponsors" :key="index" @click="toSponsorInfo(index)" class="card-container">
复制代码
<script>
中添加methods属性,在methods属性中建立写方法。// 接收传递进来的index值
toSponsorInfo(index){
// 拼接url,并将接收的index值传到要跳转的页面
const url = `/pages/sponsorInfo/main?index=${index}`
wx.navigateTo({
url
});
复制代码
onLoad(options){
let i = options.index;
wx.showLoading({
title:'加载中'
}),
this.$http
.get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
.then((res) => {
// 拿到对应index的主办方详情信息
this.sponsor = res.data.data.sponsors[i];
wx.hideLoading()
});
}
复制代码
avtive
(沙龙)数据中,给出一个
sponsors
数组,数组中表明着举办该沙龙的主办方列表,每一个主办方给出一个
name
字段。在
sponsors
(主办方)数据中,给出一个
salons
数组,数组表明着该主办方举办过的沙龙列表,每一个沙龙给出一个
title
字段。
sponsorsNum
字段,便于判断,当主办方数字大于0时,才须要进行数据遍历查找。
salonNum
字段,当举办的沙龙数量大于0时,才进行数据遍历查找。
以主办方详情为例
this.$http
.get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
.then((res) => {
// 拿到对应index的主办方详情
this.sponsor = res.data.data.sponsors[i];
// 拿到全部的沙龙数据
let salonsInfos = res.data.data.active
// 经过数组遍历拿到该主办方详情中的举办的沙龙列表
// 若是主办方举办的沙龙数量大于0,进行数组遍历
if(this.sponsor.salonNum>0){
// 遍历该主办方的举办过的沙龙列表
this.salonList = this.sponsor.salons.map(item=>{
let sal
// 遍历拿到的全部沙龙数据
salonsInfos.forEach(salon => {
// 当沙龙的主题与主办方举办的沙龙列表中的主题相同时,拿到对应的沙龙信息,并将沙龙信息替换进沙龙列表的title字段
if(salon.title === item.title) sal = salon
})
return sal
})
}
wx.hideLoading()
});
},
复制代码
这样,咱们就拿到了对应title
的沙龙列表的详细数据了,再将列表渲染到页面便可。从gif图中能够看出:个人页面由三部分组成,头部展现用户信息、客服和设置,tab栏选择选项,tab选项对应的展现页面。
展现用户头像和昵称有两种写法:
经过调用wx.getUserInfo()
来获取用户头像和昵称,可是须要用户受权,受权后才能成功获取用户头像和昵称。
使用官方的<open-data>
标签直接显示用户头像和昵称(无需受权)
<open-data type="userAvatarUrl"></open-data> //获取用户头像直接显示在小程序中
<open-data type="userNickName" ></open-data> //获取用户昵称直接显示在小程序中
复制代码
修改<open-data>
标签中的样式时需注意,这里不能像写img/image的样式同样,直接给图片一个width="50px";height="50px";border-radius="50%"
的样式,这样并不会生效。图片会以下图同样,没有任何改变。
那么怎样才能修改 <open-data>
标签的样式呢?
咱们能够添加一个display:flex
属性使图片大小可以改变,再添加一个overflow:hidden
属性使圆角可以出现。
<open-data class="thumb" type="userAvatarUrl"></open-data>
复制代码
.thumb
width 100%
height 100%
border-radius 50%
overflow hidden
display flex
复制代码
这样,一个好看的头像显示就完成啦。
由于这里只是一个小demo项目,加上客服功能还须要配置开发者信息以及添加客服帐号,实在是麻烦,就懒得弄了。可是有个icon在这里,总感受不能点击会很难受,因而就添加了个点击事件,当你好奇是啥的时候弹出一个暂不支持的提示框哈哈哈哈哈。别问、问就是懒,别说、说就是不必。
若是有想要了解一下客服功能怎么实现的盆友能够自行查看一些小程序官方文档:客服消息使用指南、客服帐号管理。
点击小齿轮就会跳转到一个设置的页面,页面分为两项,第一项是职人小程序的%¥%¥?咱也不知道咋描述,他说是关于职人就是关于职人吧。第二项是主办方中心。这两项点击又是一个跳转页面,两个页面都没啥操做,就是一个页面展现。关于职人的页面我就直接截了个图上去,毕竟咱也没有UI小姐姐,只能偷偷懒了,不会偷懒的程序猿不是好程序猿hhhh。
当点击任意tab时,样式和展现的内容都会随之变化。
嘶,这个样式咋动态显示啊?经过点击事件给dom节点添加类名吗?有点太麻烦了吧。添加一个数据进行判断吗?也好麻烦啊。
嘶,这个又要咋操做才能根据我点击的的内容展现该展现的页面啊?点击一个tab就给一个数据,经过数据来判断是否展现页面吗?这也太麻烦了。
这个看似简单,但又有些许难度的小tab栏应该怎么实现呢?
首先,咱们在数据源里定义一个navList
,让tab栏由数据渲染出来,这样咱们能够获取它的index值,再经过index来赋值。
data(){
return{
//给一个默认的changeNav值,
changeNav:0,
navList: [{name:'参加'},{name:'感兴趣'},{name:'关注'}]
}
}
复制代码
而后,咱们能够经过html的data-
属性来自定义数据,并将自定义数据与点击的index值进行绑定。
<div class="tab-item" v-for="(item,index) in navList" :key="index" :data-current="index" @click="swichNav">
复制代码
最后,经过定义的点击事件来获取自定义的数据
methods:{
swichNav(e){
const current = e.currentTarget.dataset.current
this.changeNav = current
}
}
复制代码
而样式问题咱们则经过判断改变的changeNav
值是否与index
值相等来动态添加一个active
的类名
:class="{active:changeNav == index}"
复制代码
页面的转换则经过v-if
来判断展现哪个页面
<Nothing v-if="changeNav==0" :tips="salonTip"></Nothing>
<Interest v-if="changeNav==1" :salon="interestList" @goDetail="goSalonDetail"></Interest>
<Collect v-if="changeNav==2" :sponsors="collectList" @go="goSponDetail"></Collect>
复制代码
这个小程序的报名要填信息啥啥的,以及毕竟是个活动,感受随意报名有点不太好,因此就没有写报名的小分页面,就直接展现一个没有参加的沙龙页面了。(还不是由于懒)
<button @click.stop="collect(index)" :class="collectList[index] ? 'like' : 'unlike'">{{collectList[index] ? '已关注' : '关注'}}</button>
复制代码
collectList:[]
,在页面加载时读取以前本地缓存的状态查看是否有关注,若是不存在本地缓存,就把默认数组collectList
添加到本地缓存。若是存在本地缓存,就将本地缓存的数据赋值给collectList
onShow(){
var cache = wx.getStorageSync('collectList')
if(!cache){
wx.setStorage({
key:"collectList",
data:this.collectList
})
}else{
this.collectList = cache
}
}
复制代码
methods
中编写点击事件collect(){}
methods:{
collect(index){
// 防止this指向改变
let self = this
// 拿到缓存区的collectList数组
var cache = wx.getStorageSync('collectList')
// 获取当前主办方是否被关注
var currentCache = cache[index]
// 若是没有被关注
if(!currentCache){
// 将当前列表的关注状态设置为关注
currentCache = true
wx.showLoading({
title: '加载中',
})
//将当前列表的关注状态赋值给本地缓存
cache[index] = currentCache
//从新设置缓存
wx.setStorage({
key:'collectList',
data:cache
})
// 将缓存赋值给数据源
self.collectList = cache
wx.hideLoading()
}else{
//若是存在缓存状态,则调用操做菜单
wx.showActionSheet({
itemList: ['取消关注'],
success (res) {
wx.showLoading({
title: '加载中',
})
currentCache = false
cache[index] = currentCache
wx.setStorage({
key:'collectList',
data:cache
})
self.collectList = cache
wx.hideLoading()
}
})
}
}
复制代码
这样,就实现了点击哪个主办方的关注按钮,哪个主办方便发生状态改变的功能了,那么接下来咱们则须要在详情页作相似的处理。isCollected:''
,在页面加载时读取缓存状态并赋值给数据源中的isCollected
onShow(){
// 拿到缓存区的关注信息
var cache = wx.getStorageSync('collectList')
//在页面接收参数index时将option.index赋值给数据源中的index了,因此这里直接调用了this.index
this.isCollected = cache[this.index]
},
复制代码
<button @click="collect" :class="isCollected ? 'like' : 'unlike'">{{isCollected ? '已关注' : '关注'}}</button>
复制代码
methods: {
// 接下来的操做和上面的操做没啥区别。。。我就不写了。。。
collect(){
var cache = wx.getStorageSync('collectList')
let self = this
var currentCache = cache[self.index]
if(!currentCache){
wx.showLoading({
title:'加载中'
})
currentCache = true
cache[self.index] = currentCache
wx.setStorage({
key:'collectList',
data:cache
})
self.isCollected = cache[self.index]
wx.hideLoading()
}else{
wx.showActionSheet({
itemList: ['取消关注'],
success(res){
wx.showLoading({
title:'加载中'
})
currentCache = false
cache[self.index] = currentCache
wx.setStorage({
key:'collectList',
data:cache
})
self.isCollected = cache[self.index]
wx.hideLoading()
}
})
}
},
}
复制代码
这样,就实现了基于本地缓存实现列表关注功能惹,细心的盆友可能会发现,在判断是否存在关注状态时,if和else都有共同的操做,可是我没有合在一块儿写,而是各自都写了一遍。是由于,合在一块儿写时会产生异步,当else操做还没执行完时,就执行接下来的赋值操做,致使操做不成功。解决异步能够用Promise
,可是promise也要重复两次代码,能解决问题为啥不用最简单直白的方式呢?因此懒人本懒直接在if判断里重复两次代码了。
因为以前咱们利用本地缓存实现了将感兴趣/关注的功能,那么咱们如何拿到感兴趣与关注的列表详情数据呢?一样的,咱们仍是能够经过数组遍从来获取到详细数据。就拿主办方关注来讲,咱们能够在数据加载时先拿到缓存区的主办方收藏列表,经过数组遍历拿到关注了的主办方的下角标数组,再经过遍历将对应下角标的主办方关注详情拿到。将数据渲染上去便可。
onShow(){
this.$get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
.then((res) => {
// 全部的主办方
wx.showLoading()
this.sponsors = res.data.data.sponsors;
// 拿到缓存中的主办方收藏列表
var cache = wx.getStorageSync('collectList')
// 拿到收藏的主办方的下角标数组
this.targetList = []
cache.forEach((item,i) => {
if(item) this.targetList.push(i)
});
// 拿到收藏的主办方信息列表
this.collectList = this.targetList.map(index => {
let spon
this.sponsors.forEach((item,i) => {
if(index === i) spon = item
})
return spon
})
// console.log(this.collectList)
}
复制代码
此外,点击个人页面上的任意关注/感兴趣列表,均可以跳转到对应的主办方/沙龙详情页面,咱们只须要在编写页面跳转至详情页时,将跳转的index值修改一下便可。
goSponDetail(index){
const url = `/pages/sponsorInfo/main?index=${this.targetList[index]}`
wx.navigateTo({
url
})
}
复制代码
和主办方列表关注同样,列表实现感兴趣功能我就不详细描述了。与主办方页面不一样的是,页面加载时须要判断一下是否感兴趣,若是感兴趣的话,页面加载时,我的信息就应该出如今头像列表上。当点击取消感兴趣时,则删除我的信息。因为咱们写了受权页面,给小程序进行了受权操做,因此能够直接调用wx.getUserInfo()
来获取用户信息
onShow(){
self = this
// 获取用户信息
wx.getUserInfo({
success(res){
var userInfo = res.userInfo
var nickName = userInfo.nickName
var avatarUrl = userInfo.avatarUrl
// 将用户信息赋值给数据源中的user
self.user = {
name:nickName,
avatar:avatarUrl
}
var cache = wx.getStorageSync('interestList')
self.isInterested = cache[self.index]
if(self.isInterested){
self.interestInfo.unshift(self.user)
}
}
})
},
复制代码
这个调用小程序官方apiwx.previewImage
就好啦。
因为这个操做菜单并非紧挨着底部的,因此咱们须要本身定义一个share
的组件,并在数据源给定一个isShare:false
的状态,经过状态来决定share
组件是否展现。首先,咱们点击分享按钮,将数据源中的状态改变为true
,而后再在share
组件中的取消按钮定义一个点击事件,并经过$emit
将事件传给父组件,从而实现父子组件间通讯问题。
share
组件,并给对应的item
添加对应的点击事件。分享给好友或群直接应用微信官方的button
组件中open-type
属性,并将open-type
的值设置为share
,便可实现分享给好友功能。保存沙龙海报会自动生成对应活动的二维码,没有上线就没有二维码。因此这个功能我就没有去写了。这里有一点要提的是,小程序的button
若是背景色为白色,就会出现自带的边框,给button
添加一个plain="true"
属性就行了。<div class="container">
<div class="tips" >分享该沙龙</div>
<button plain="true" open-type="share" class="type">分享给好友或群</button>
<button plain="true" class="type" @click="save">保存沙龙海报</button>
<div class="cancel" @click="cancel">取消</div>
</div>
复制代码
share:false
的数据data() {
return {
share:false
}
}
复制代码
cancel(){
this.$emit('cancel',this.share)
}
复制代码
share
组件,并在数据源中定义isShare:false
,经过isShare
来判断是否展现操做菜单,并经过v-on
(可简写为@)绑定了一个cancel事件来监听子组件的触发事件,<Share v-if="isShare" @cancel="cancelShare"></Share>
复制代码
<script>
import Share from '@/components/share/share'
export default{
data(){
return:{
isShare:false,
}
},
components:{
Share
},
methods:{
//接收子组件传递过来的数据:this.share(false),并将值赋值给isShare
cancelShare(msg){
this.isShare = msg
}
}
}
</script>
复制代码
就这样,一个简单的自定义操做菜单组件就完成了。既然说到了父子组件通讯问题,那我就再说一下vue
中,父组件如何向子组件传值,以及兄弟组件间通讯问题好了。
因为沙龙详情页面结构实在是太多模块了,因此我每一个展现模块都封装成了组件,提升代码可读性。以及有两个页面都涉及到了点击展开详情,因而我也封装成了一个组件,提升代码复用性,这里就以点击展开详情的组件来讲一说父组件如何向子组件传值的问题好了。
<template>
<div>
<div class="info-desc" :class="isEllipsis ?'ellipsis':'unellipsis'">{{info}}</div>
<div class="text-expander" @click="ellipsis" >
<text class="text-expander__content">{{isEllipsis?'展开所有':'所有收起'}}</text>
<image class="text-expander__icon" :src="isEllipsis?'/static/images/down.svg':'/static/images/up.svg'"></image>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isEllipsis:true
}
},
methods: {
ellipsis(){
this.isEllipsis =!this.isEllipsis
}
},
props:[
'info'
]
}
</script>
<style lang="stylus" scoped>
.info-desc
font-size 14px
display -webkit-box
-webkit-box-orient vertical
text-overflow ellipsis
overflow hidden
line-height 26px
text-align left
.ellipsis
-webkit-line-clamp 3
.unellipsis
-webkit-line-clamp 0
.text-expander
margin 0 auto
width 80px
height 20px
.text-expander__content
color #c4c8c7
font-size 16px
.text-expander__icon
width 15px
height 15px
</style>
复制代码
在不一样页面上,文本中展现的内容不同,因此咱们须要向父组件接收数据,父组件向子组件传递数据能够经过props
属性来传递。子组件像父组件要info
数据,经过props
来索取。
v-bind
(能够简写为@)来绑定数据。<Expander :info="salonInfo"></Expander>
复制代码
就这样,子组件就能够拿到父组件中定义的salonInfo
数据了。有兴趣的盆友还能够了解一下$on
(子组件接收数据),以及非父子组件的兄弟组件间通讯方式(建一个Vue事件bus对象,而后经过bus.$emit
触发事件,bus.$on
监听触发的事件)。
结语也不知道说些啥,但别人也都有,我也不能输!那仍是随便唠点吧QAQ。
原本是打算找个界面简洁功能稍多的小项目来练手,结果最后仍是百分之八十是在切图,不得不说,切图真的很快乐,因此个人同桌老王常常调侃我:“纯粹的前端”,固然这里指的是纯粹的切图。但一只酱毫不服输,此次采用的是easy-mock,下次写小项目就尝试jsonp请求数据,或者使用万能的爬虫爬取数据,或者全栈开发一个小项目。写项目也是一件很是快乐的一件事情,当你实现一个小功能时,或者本身想到 一个解决的办法时,真的颇有成就感(虽然这个项目真的没有什么技术难点T-T)。最后奉上源码,但愿能帮助到你们一小丢丢。